Skip to content

Commit 0c558fb

Browse files
authored
chore(openai): reorg chat models (#9220)
1 parent 401bb68 commit 0c558fb

27 files changed

+3769
-3640
lines changed

libs/providers/langchain-openai/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,15 @@
7676
"types": "./dist/index.d.ts",
7777
"exports": {
7878
".": {
79+
"input": "./src/index.ts",
7980
"import": {
8081
"types": "./dist/index.d.ts",
8182
"default": "./dist/index.js"
8283
},
8384
"require": {
8485
"types": "./dist/index.d.cts",
8586
"default": "./dist/index.cjs"
86-
},
87-
"input": "./src/index.ts"
87+
}
8888
},
8989
"./package.json": "./package.json"
9090
},
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
import { AzureOpenAI as AzureOpenAIClient } from "openai";
2+
import { getEnv, getEnvironmentVariable } from "@langchain/core/utils/env";
3+
import type { Serialized } from "@langchain/core/load/serializable";
4+
import { ChatOpenAICallOptions } from "../../chat_models/index.js";
5+
import {
6+
OpenAIEndpointConfig,
7+
getEndpoint,
8+
normalizeHeaders,
9+
} from "../../utils/azure.js";
10+
import { AzureOpenAIChatInput, OpenAICoreRequestOptions } from "../../types.js";
11+
import {
12+
BaseChatOpenAI,
13+
BaseChatOpenAIFields,
14+
} from "../../chat_models/base.js";
15+
16+
export const AZURE_ALIASES = {
17+
openAIApiKey: "openai_api_key",
18+
openAIApiVersion: "openai_api_version",
19+
openAIBasePath: "openai_api_base",
20+
deploymentName: "deployment_name",
21+
azureOpenAIEndpoint: "azure_endpoint",
22+
azureOpenAIApiVersion: "openai_api_version",
23+
azureOpenAIBasePath: "openai_api_base",
24+
azureOpenAIApiDeploymentName: "deployment_name",
25+
};
26+
27+
export const AZURE_SECRETS = {
28+
azureOpenAIApiKey: "AZURE_OPENAI_API_KEY",
29+
};
30+
31+
export const AZURE_SERIALIZABLE_KEYS = [
32+
"azureOpenAIApiKey",
33+
"azureOpenAIApiVersion",
34+
"azureOpenAIBasePath",
35+
"azureOpenAIEndpoint",
36+
"azureOpenAIApiInstanceName",
37+
"azureOpenAIApiDeploymentName",
38+
"deploymentName",
39+
"openAIApiKey",
40+
"openAIApiVersion",
41+
];
42+
43+
export interface AzureChatOpenAIFields
44+
extends BaseChatOpenAIFields,
45+
Partial<AzureOpenAIChatInput> {
46+
/**
47+
* Whether to use the responses API for all requests. If `false` the responses API will be used
48+
* only when required in order to fulfill the request.
49+
*/
50+
useResponsesApi?: boolean;
51+
}
52+
53+
export function _constructAzureFields(
54+
this: Partial<AzureOpenAIChatInput>,
55+
fields?: AzureChatOpenAIFields
56+
) {
57+
this.azureOpenAIApiKey =
58+
fields?.azureOpenAIApiKey ??
59+
fields?.openAIApiKey ??
60+
fields?.apiKey ??
61+
getEnvironmentVariable("AZURE_OPENAI_API_KEY");
62+
63+
this.azureOpenAIApiInstanceName =
64+
fields?.azureOpenAIApiInstanceName ??
65+
getEnvironmentVariable("AZURE_OPENAI_API_INSTANCE_NAME");
66+
67+
this.azureOpenAIApiDeploymentName =
68+
fields?.azureOpenAIApiDeploymentName ??
69+
fields?.deploymentName ??
70+
getEnvironmentVariable("AZURE_OPENAI_API_DEPLOYMENT_NAME");
71+
72+
this.azureOpenAIApiVersion =
73+
fields?.azureOpenAIApiVersion ??
74+
fields?.openAIApiVersion ??
75+
getEnvironmentVariable("AZURE_OPENAI_API_VERSION");
76+
77+
this.azureOpenAIBasePath =
78+
fields?.azureOpenAIBasePath ??
79+
getEnvironmentVariable("AZURE_OPENAI_BASE_PATH");
80+
81+
this.azureOpenAIEndpoint =
82+
fields?.azureOpenAIEndpoint ??
83+
getEnvironmentVariable("AZURE_OPENAI_ENDPOINT");
84+
85+
this.azureADTokenProvider = fields?.azureADTokenProvider;
86+
87+
if (!this.azureOpenAIApiKey && !this.apiKey && !this.azureADTokenProvider) {
88+
throw new Error("Azure OpenAI API key or Token Provider not found");
89+
}
90+
}
91+
92+
export function _getAzureClientOptions(
93+
this: BaseChatOpenAI<ChatOpenAICallOptions> & Partial<AzureOpenAIChatInput>,
94+
options: OpenAICoreRequestOptions | undefined
95+
): OpenAICoreRequestOptions {
96+
if (!this.client) {
97+
const openAIEndpointConfig: OpenAIEndpointConfig = {
98+
azureOpenAIApiDeploymentName: this.azureOpenAIApiDeploymentName,
99+
azureOpenAIApiInstanceName: this.azureOpenAIApiInstanceName,
100+
azureOpenAIApiKey: this.azureOpenAIApiKey,
101+
azureOpenAIBasePath: this.azureOpenAIBasePath,
102+
azureADTokenProvider: this.azureADTokenProvider,
103+
baseURL: this.clientConfig.baseURL,
104+
azureOpenAIEndpoint: this.azureOpenAIEndpoint,
105+
};
106+
107+
const endpoint = getEndpoint(openAIEndpointConfig);
108+
109+
const params = {
110+
...this.clientConfig,
111+
baseURL: endpoint,
112+
timeout: this.timeout,
113+
maxRetries: 0,
114+
};
115+
116+
if (!this.azureADTokenProvider) {
117+
params.apiKey = openAIEndpointConfig.azureOpenAIApiKey;
118+
}
119+
120+
if (!params.baseURL) {
121+
delete params.baseURL;
122+
}
123+
124+
let env = getEnv();
125+
if (env === "node" || env === "deno") {
126+
env = `(${env}/${process.version}; ${process.platform}; ${process.arch})`;
127+
}
128+
129+
const defaultHeaders = normalizeHeaders(params.defaultHeaders);
130+
params.defaultHeaders = {
131+
...params.defaultHeaders,
132+
"User-Agent": defaultHeaders["User-Agent"]
133+
? `langchainjs-azure-openai/2.0.0 (${env})${defaultHeaders["User-Agent"]}`
134+
: `langchainjs-azure-openai/2.0.0 (${env})`,
135+
};
136+
137+
this.client = new AzureOpenAIClient({
138+
apiVersion: this.azureOpenAIApiVersion,
139+
azureADTokenProvider: this.azureADTokenProvider,
140+
deployment: this.azureOpenAIApiDeploymentName,
141+
...params,
142+
});
143+
}
144+
const requestOptions = {
145+
...this.clientConfig,
146+
...options,
147+
} as OpenAICoreRequestOptions;
148+
if (this.azureOpenAIApiKey) {
149+
requestOptions.headers = {
150+
"api-key": this.azureOpenAIApiKey,
151+
...requestOptions.headers,
152+
};
153+
requestOptions.query = {
154+
"api-version": this.azureOpenAIApiVersion,
155+
...requestOptions.query,
156+
};
157+
}
158+
return requestOptions;
159+
}
160+
161+
export function _serializeAzureChat(
162+
this: BaseChatOpenAI<ChatOpenAICallOptions> & Partial<AzureOpenAIChatInput>,
163+
input: Serialized
164+
) {
165+
const json = input;
166+
167+
function isRecord(obj: unknown): obj is Record<string, unknown> {
168+
return typeof obj === "object" && obj != null;
169+
}
170+
171+
if (isRecord(json) && isRecord(json.kwargs)) {
172+
delete json.kwargs.azure_openai_base_path;
173+
delete json.kwargs.azure_openai_api_deployment_name;
174+
delete json.kwargs.azure_openai_api_key;
175+
delete json.kwargs.azure_openai_api_version;
176+
delete json.kwargs.azure_open_ai_base_path;
177+
178+
if (!json.kwargs.azure_endpoint && this.azureOpenAIEndpoint) {
179+
json.kwargs.azure_endpoint = this.azureOpenAIEndpoint;
180+
}
181+
if (!json.kwargs.azure_endpoint && this.azureOpenAIBasePath) {
182+
const parts = this.azureOpenAIBasePath.split("/openai/deployments/");
183+
if (parts.length === 2 && parts[0].startsWith("http")) {
184+
const [endpoint] = parts;
185+
json.kwargs.azure_endpoint = endpoint;
186+
}
187+
}
188+
if (!json.kwargs.azure_endpoint && this.azureOpenAIApiInstanceName) {
189+
json.kwargs.azure_endpoint = `https://${this.azureOpenAIApiInstanceName}.openai.azure.com/`;
190+
}
191+
if (!json.kwargs.deployment_name && this.azureOpenAIApiDeploymentName) {
192+
json.kwargs.deployment_name = this.azureOpenAIApiDeploymentName;
193+
}
194+
if (!json.kwargs.deployment_name && this.azureOpenAIBasePath) {
195+
const parts = this.azureOpenAIBasePath.split("/openai/deployments/");
196+
if (parts.length === 2) {
197+
const [, deployment] = parts;
198+
json.kwargs.deployment_name = deployment;
199+
}
200+
}
201+
202+
if (
203+
json.kwargs.azure_endpoint &&
204+
json.kwargs.deployment_name &&
205+
json.kwargs.openai_api_base
206+
) {
207+
delete json.kwargs.openai_api_base;
208+
}
209+
if (
210+
json.kwargs.azure_openai_api_instance_name &&
211+
json.kwargs.azure_endpoint
212+
) {
213+
delete json.kwargs.azure_openai_api_instance_name;
214+
}
215+
}
216+
217+
return json;
218+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { LangSmithParams } from "@langchain/core/language_models/chat_models";
2+
import { Serialized } from "@langchain/core/load/serializable";
3+
import {
4+
ChatOpenAICompletions,
5+
ChatOpenAICompletionsCallOptions,
6+
} from "../../chat_models/completions.js";
7+
import { AzureOpenAIChatInput, OpenAICoreRequestOptions } from "../../types.js";
8+
import {
9+
_constructAzureFields,
10+
_getAzureClientOptions,
11+
_serializeAzureChat,
12+
AZURE_ALIASES,
13+
AZURE_SECRETS,
14+
AZURE_SERIALIZABLE_KEYS,
15+
AzureChatOpenAIFields,
16+
} from "./common.js";
17+
18+
export class AzureChatOpenAICompletions<
19+
CallOptions extends ChatOpenAICompletionsCallOptions = ChatOpenAICompletionsCallOptions
20+
>
21+
extends ChatOpenAICompletions<CallOptions>
22+
implements Partial<AzureOpenAIChatInput>
23+
{
24+
azureOpenAIApiVersion?: string;
25+
26+
azureOpenAIApiKey?: string;
27+
28+
azureADTokenProvider?: () => Promise<string>;
29+
30+
azureOpenAIApiInstanceName?: string;
31+
32+
azureOpenAIApiDeploymentName?: string;
33+
34+
azureOpenAIBasePath?: string;
35+
36+
azureOpenAIEndpoint?: string;
37+
38+
_llmType(): string {
39+
return "azure_openai";
40+
}
41+
42+
get lc_aliases(): Record<string, string> {
43+
return {
44+
...super.lc_aliases,
45+
...AZURE_ALIASES,
46+
};
47+
}
48+
49+
get lc_secrets(): { [key: string]: string } | undefined {
50+
return {
51+
...super.lc_secrets,
52+
...AZURE_SECRETS,
53+
};
54+
}
55+
56+
get lc_serializable_keys(): string[] {
57+
return [...super.lc_serializable_keys, ...AZURE_SERIALIZABLE_KEYS];
58+
}
59+
60+
getLsParams(options: this["ParsedCallOptions"]): LangSmithParams {
61+
const params = super.getLsParams(options);
62+
params.ls_provider = "azure";
63+
return params;
64+
}
65+
66+
constructor(fields?: AzureChatOpenAIFields) {
67+
super(fields);
68+
_constructAzureFields.call(this, fields);
69+
}
70+
71+
override _getClientOptions(
72+
options: OpenAICoreRequestOptions | undefined
73+
): OpenAICoreRequestOptions {
74+
return _getAzureClientOptions.call(this, options);
75+
}
76+
77+
override toJSON(): Serialized {
78+
return _serializeAzureChat.call(this, super.toJSON());
79+
}
80+
}

0 commit comments

Comments
 (0)