diff --git a/packages/langchain/README.md b/packages/langchain/README.md index 0eb2f2a4..cc787984 100644 --- a/packages/langchain/README.md +++ b/packages/langchain/README.md @@ -13,11 +13,7 @@ $ npm install @langchain/openai // if you want to use OpenAI models ## Pre-requisites - [Enable the AI Core service in BTP](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/initial-setup). -- Project configured with Node.js v20 or higher and native ESM support enabled. -- For testing your application locally: - - Download a service key for your AI Core service instance. - - Create a `.env` file in the sample-code directory. - - Add an entry `AICORE_SERVICE_KEY=''`. +- Ensure the project is configured with Node.js v20 or higher, along with native ESM support. ## Usage diff --git a/packages/langchain/src/index.ts b/packages/langchain/src/index.ts index f279b95a..75d19020 100644 --- a/packages/langchain/src/index.ts +++ b/packages/langchain/src/index.ts @@ -1,4 +1,4 @@ -export { OpenAiChatClient, OpenAiEmbeddingClient } from './openai/index.js'; +export { AzureOpenAiChatClient, AzureOpenAiEmbeddingClient } from './openai/index.js'; export type { OpenAiChatModelInput, OpenAiEmbeddingInput, diff --git a/packages/langchain/src/openai/chat.ts b/packages/langchain/src/openai/chat.ts index cee8f8b6..4546f337 100644 --- a/packages/langchain/src/openai/chat.ts +++ b/packages/langchain/src/openai/chat.ts @@ -3,31 +3,26 @@ import { BaseMessage } from '@langchain/core/messages'; import type { ChatResult } from '@langchain/core/outputs'; import { AzureChatOpenAI, AzureOpenAI } from '@langchain/openai'; import { OpenAiChatClient as OpenAiChatClientBase } from '@sap-ai-sdk/foundation-models'; -import { mapLangchainToAiClient, mapResponseToChatResult } from './util.js'; +import { mapLangchainToAiClient, mapResponseToChatResult, toArrayOrUndefined } from './util.js'; import type { OpenAiChatModelInput, OpenAiChatCallOptions } from './types.js'; /** * OpenAI Language Model Wrapper to generate texts. */ -export class OpenAiChatClient extends AzureChatOpenAI { +export class AzureOpenAiChatClient extends AzureChatOpenAI { declare CallOptions: OpenAiChatCallOptions; private openAiChatClient: OpenAiChatClientBase; constructor(fields: OpenAiChatModelInput) { const defaultValues = new AzureOpenAI({ apiKey: 'dummy' }); - const stop = fields.stop - ? Array.isArray(fields.stop) - ? fields.stop - : [fields.stop] - : defaultValues.stop; + const stop = toArrayOrUndefined(fields.stop); super({ ...defaultValues, ...fields, stop, // overrides the apikey values as they are not applicable for BTP azureOpenAIApiKey: undefined, - openAIApiKey: undefined, - apiKey: 'dummy' + openAIApiKey: undefined }); this.openAiChatClient = new OpenAiChatClientBase({ ...fields }); diff --git a/packages/langchain/src/openai/embedding.ts b/packages/langchain/src/openai/embedding.ts index 82e9c159..ffc5610f 100644 --- a/packages/langchain/src/openai/embedding.ts +++ b/packages/langchain/src/openai/embedding.ts @@ -10,7 +10,7 @@ import { OpenAiEmbeddingInput } from './types.js'; /** * OpenAI GPT Language Model Wrapper to embed texts. */ -export class OpenAiEmbeddingClient extends AzureOpenAIEmbeddings { +export class AzureOpenAiEmbeddingClient extends AzureOpenAIEmbeddings { private btpOpenAIClient: OpenAiEmbeddingClientBase; constructor(fields: OpenAiEmbeddingInput) { @@ -45,9 +45,8 @@ export class OpenAiEmbeddingClient extends AzureOpenAIEmbeddings { private async createEmbedding( query: OpenAiEmbeddingParameters ): Promise { - const res = await this.caller.callWithOptions({}, () => + return this.caller.callWithOptions({}, () => this.btpOpenAIClient.run(query) ); - return res; } } diff --git a/packages/langchain/src/openai/util.test.ts b/packages/langchain/src/openai/util.test.ts index 8ce5cc62..e00d191c 100644 --- a/packages/langchain/src/openai/util.test.ts +++ b/packages/langchain/src/openai/util.test.ts @@ -14,7 +14,7 @@ import { parseMockResponse } from '../../../../test-util/mock-http.js'; import { mapResponseToChatResult } from './util.js'; -import { OpenAiChatClient } from './chat.js'; +import { AzureOpenAiChatClient } from './chat.js'; const openAiMockResponse = parseMockResponse( 'foundation-models', @@ -98,7 +98,7 @@ describe('Mapping Functions', () => { chatCompletionEndpoint ); - const client = new OpenAiChatClient({ deploymentId: '1234' }); + const client = new AzureOpenAiChatClient({ deploymentId: '1234' }); const runSpy = jest.spyOn(OpenAiChatClientBase.prototype, 'run'); await client.generate([[langchainPrompt]]); expect(runSpy).toHaveBeenCalledWith(request); diff --git a/packages/langchain/src/openai/util.ts b/packages/langchain/src/openai/util.ts index 2d1b989a..2a30ec57 100644 --- a/packages/langchain/src/openai/util.ts +++ b/packages/langchain/src/openai/util.ts @@ -15,7 +15,7 @@ import type { OpenAiChatCompletionParameters } from '@sap-ai-sdk/foundation-models'; import { zodToJsonSchema } from 'zod-to-json-schema'; -import { OpenAiChatClient } from './chat.js'; +import { AzureOpenAiChatClient } from './chat.js'; import { OpenAiChatCallOptions } from './types.js'; /** @@ -143,6 +143,18 @@ export function mapBaseMessageToOpenAiChatMessage( } as OpenAiChatMessage; } +/** + * Converts a value to an array or returns undefined. + * @param value - The value to convert. + * @returns The value as an array, undefined if the input is falsy, or the original array if input is already an array. + */ +export function toArrayOrUndefined(value?: T | T[]): T[] | undefined { + if(value === undefined) { + return undefined; + } + return Array.isArray(value) ? value : [value]; +} + /** * Checks if a given array is a structured tool array. * @param tools - The array to check. @@ -159,15 +171,15 @@ export function isStructuredToolArray( } /** - * Maps the langchain's input interface to our own client's input interface + * Maps Langchain's input interface to our own client's input interface * @param client The Langchain OpenAI client * @param options The Langchain call options * @param messages The messages to be send - * @returns A AI SDK compatibile request + * @returns An AI SDK compatibile request * @internal */ export function mapLangchainToAiClient( - client: OpenAiChatClient, + client: AzureOpenAiChatClient, options: OpenAiChatCallOptions, messages: BaseMessage[] ): OpenAiChatCompletionParameters {