@ainetwork/adk-provider-model-azure 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -25,7 +25,11 @@ __export(index_exports, {
25
25
  module.exports = __toCommonJS(index_exports);
26
26
  var import_modules = require("@ainetwork/adk/modules");
27
27
  var import_memory = require("@ainetwork/adk/types/memory");
28
+ var import_logger = require("@ainetwork/adk/utils/logger");
28
29
  var import_openai = require("openai");
30
+ var DEFAULT_TIMEOUT_MS = 12e4;
31
+ var DEFAULT_MAX_RETRIES = 1;
32
+ var PROVIDER = "azure-openai";
29
33
  var AzureOpenAI = class extends import_modules.BaseModel {
30
34
  client;
31
35
  modelName;
@@ -35,7 +39,9 @@ var AzureOpenAI = class extends import_modules.BaseModel {
35
39
  baseUrl,
36
40
  apiKey,
37
41
  apiVersion,
38
- modelName
42
+ modelName,
43
+ timeout = DEFAULT_TIMEOUT_MS,
44
+ maxRetries = DEFAULT_MAX_RETRIES
39
45
  }) {
40
46
  super();
41
47
  const options = {
@@ -43,7 +49,9 @@ var AzureOpenAI = class extends import_modules.BaseModel {
43
49
  apiKey,
44
50
  deployment,
45
51
  apiVersion,
46
- baseURL: baseUrl
52
+ baseURL: baseUrl,
53
+ timeout,
54
+ maxRetries
47
55
  };
48
56
  this.client = new import_openai.AzureOpenAI(options);
49
57
  this.modelName = modelName;
@@ -53,12 +61,36 @@ var AzureOpenAI = class extends import_modules.BaseModel {
53
61
  case import_memory.MessageRole.USER:
54
62
  return "user";
55
63
  case import_memory.MessageRole.MODEL:
64
+ return "assistant";
56
65
  case import_memory.MessageRole.SYSTEM:
57
66
  return "system";
58
67
  default:
59
68
  return "system";
60
69
  }
61
70
  }
71
+ logCallStart(method, messageCount, toolCount = 0) {
72
+ import_logger.loggers.model.debug(`[${PROVIDER}] ${method} start`, {
73
+ provider: PROVIDER,
74
+ model: this.modelName,
75
+ messageCount,
76
+ toolCount
77
+ });
78
+ }
79
+ logCallSuccess(method, startedAt) {
80
+ import_logger.loggers.model.info(`[${PROVIDER}] ${method} complete`, {
81
+ provider: PROVIDER,
82
+ model: this.modelName,
83
+ durationMs: Date.now() - startedAt
84
+ });
85
+ }
86
+ logCallFailure(method, startedAt, error) {
87
+ import_logger.loggers.model.error(`[${PROVIDER}] ${method} failed`, {
88
+ provider: PROVIDER,
89
+ model: this.modelName,
90
+ durationMs: Date.now() - startedAt,
91
+ error: error instanceof Error ? error.message : String(error)
92
+ });
93
+ }
62
94
  generateMessages(params) {
63
95
  const { query, thread, systemPrompt } = params;
64
96
  const messages = !systemPrompt ? [] : [{ role: "system", content: systemPrompt.trim() }];
@@ -96,55 +128,117 @@ var AzureOpenAI = class extends import_modules.BaseModel {
96
128
  });
97
129
  }
98
130
  async fetch(messages, options) {
99
- const response = await this.client.chat.completions.create({
100
- model: this.modelName,
101
- messages,
102
- reasoning_effort: options?.reasoning,
103
- verbosity: options?.verbosity
104
- });
105
- return {
106
- content: response.choices[0].message.content || void 0
107
- };
108
- }
109
- async fetchWithContextMessage(messages, functions, options) {
110
- if (functions.length > 0) {
131
+ const startedAt = Date.now();
132
+ this.logCallStart("fetch", messages.length);
133
+ try {
111
134
  const response = await this.client.chat.completions.create({
112
135
  model: this.modelName,
113
136
  messages,
114
- tools: functions,
115
- tool_choice: options?.toolChoice ?? "auto",
116
137
  reasoning_effort: options?.reasoning,
117
138
  verbosity: options?.verbosity
118
139
  });
119
- const { content, tool_calls } = response.choices[0].message;
120
- const toolCalls = tool_calls?.map(
121
- (value) => {
122
- const v = value;
123
- return {
124
- name: v.function.name,
125
- // FIXME: value.function.arguments could not be a valid JSON
126
- arguments: JSON.parse(v.function.arguments)
127
- };
128
- }
129
- );
140
+ this.logCallSuccess("fetch", startedAt);
130
141
  return {
131
- content: content || void 0,
132
- toolCalls
142
+ content: response.choices[0].message.content || void 0
133
143
  };
144
+ } catch (error) {
145
+ this.logCallFailure("fetch", startedAt, error);
146
+ throw error;
134
147
  }
135
- return await this.fetch(messages);
148
+ }
149
+ async fetchWithContextMessage(messages, functions, options) {
150
+ if (functions.length > 0) {
151
+ const startedAt = Date.now();
152
+ this.logCallStart(
153
+ "fetchWithContextMessage",
154
+ messages.length,
155
+ functions.length
156
+ );
157
+ try {
158
+ const response = await this.client.chat.completions.create({
159
+ model: this.modelName,
160
+ messages,
161
+ tools: functions,
162
+ tool_choice: options?.toolChoice ?? "auto",
163
+ reasoning_effort: options?.reasoning,
164
+ verbosity: options?.verbosity
165
+ });
166
+ this.logCallSuccess("fetchWithContextMessage", startedAt);
167
+ const { content, tool_calls } = response.choices[0].message;
168
+ const toolCalls = tool_calls?.map(
169
+ (value) => {
170
+ const v = value;
171
+ let args;
172
+ try {
173
+ args = JSON.parse(v.function.arguments);
174
+ } catch {
175
+ args = { __raw: v.function.arguments };
176
+ }
177
+ return {
178
+ name: v.function.name,
179
+ arguments: args
180
+ };
181
+ }
182
+ );
183
+ return {
184
+ content: content || void 0,
185
+ toolCalls
186
+ };
187
+ } catch (error) {
188
+ this.logCallFailure("fetchWithContextMessage", startedAt, error);
189
+ throw error;
190
+ }
191
+ }
192
+ return await this.fetch(messages, options);
136
193
  }
137
194
  async fetchStreamWithContextMessage(messages, functions, options) {
138
- const stream = await this.client.chat.completions.create({
139
- model: this.modelName,
140
- messages,
141
- tools: functions,
142
- tool_choice: functions.length > 0 ? options?.toolChoice ?? "auto" : "none",
143
- stream: true,
144
- reasoning_effort: options?.reasoning,
145
- verbosity: options?.verbosity
146
- });
147
- return this.createOpenAIStreamAdapter(stream);
195
+ const startedAt = Date.now();
196
+ this.logCallStart(
197
+ "fetchStreamWithContextMessage",
198
+ messages.length,
199
+ functions.length
200
+ );
201
+ try {
202
+ const stream = await this.client.chat.completions.create({
203
+ model: this.modelName,
204
+ messages,
205
+ ...functions.length > 0 ? {
206
+ tools: functions,
207
+ tool_choice: options?.toolChoice ?? "auto"
208
+ } : {},
209
+ stream: true,
210
+ reasoning_effort: options?.reasoning,
211
+ verbosity: options?.verbosity
212
+ });
213
+ return this.withStreamLogging(
214
+ this.createOpenAIStreamAdapter(stream),
215
+ "fetchStreamWithContextMessage",
216
+ startedAt
217
+ );
218
+ } catch (error) {
219
+ this.logCallFailure("fetchStreamWithContextMessage", startedAt, error);
220
+ throw error;
221
+ }
222
+ }
223
+ /**
224
+ * Wraps an LLMStream so the completion log covers the whole stream
225
+ * lifetime, not just the initial request.
226
+ */
227
+ withStreamLogging(stream, method, startedAt) {
228
+ const logSuccess = () => this.logCallSuccess(method, startedAt);
229
+ const logFailure = (error) => this.logCallFailure(method, startedAt, error);
230
+ return {
231
+ ...stream,
232
+ async *[Symbol.asyncIterator]() {
233
+ try {
234
+ yield* stream;
235
+ logSuccess();
236
+ } catch (error) {
237
+ logFailure(error);
238
+ throw error;
239
+ }
240
+ }
241
+ };
148
242
  }
149
243
  // NOTE(yoojin): Need to switch API Stream type to LLMStream.
150
244
  createOpenAIStreamAdapter(openaiStream) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts"],"sourcesContent":["import {\n\ttype AssistantToolCallTurn,\n\tBaseModel,\n\ttype ModelFetchOptions,\n\ttype ToolResultMessage,\n} from \"@ainetwork/adk/modules\";\nimport type {\n\tConnectorTool,\n\tFetchResponse,\n\tToolCall,\n} from \"@ainetwork/adk/types/connector\";\nimport {\n\ttype MessageObject,\n\tMessageRole,\n\ttype ThreadObject,\n} from \"@ainetwork/adk/types/memory\";\nimport type {\n\tLLMStream,\n\tStreamChunk,\n\tToolCallDelta,\n} from \"@ainetwork/adk/types/stream\";\nimport { AzureOpenAI as AzureOpenAIClient } from \"openai\";\nimport type {\n\tChatCompletionMessageParam as CCMessageParam,\n\tChatCompletionChunk,\n\tChatCompletionMessageFunctionToolCall,\n\tChatCompletionMessageToolCall,\n\tChatCompletionTool,\n} from \"openai/resources\";\n\nexport interface AzureOpenAIConfig {\n\tendpoint?: string;\n\tdeployment?: string;\n\tbaseUrl?: string;\n\tapiKey: string;\n\tapiVersion: string;\n\tmodelName: string;\n}\n\nexport class AzureOpenAI extends BaseModel<CCMessageParam, ChatCompletionTool> {\n\tprivate client: AzureOpenAIClient;\n\tprivate modelName: string;\n\n\tconstructor({\n\t\tendpoint,\n\t\tdeployment,\n\t\tbaseUrl,\n\t\tapiKey,\n\t\tapiVersion,\n\t\tmodelName,\n\t}: AzureOpenAIConfig) {\n\t\tsuper();\n\t\tconst options = {\n\t\t\tendpoint,\n\t\t\tapiKey,\n\t\t\tdeployment,\n\t\t\tapiVersion,\n\t\t\tbaseURL: baseUrl,\n\t\t};\n\t\tthis.client = new AzureOpenAIClient(options);\n\t\tthis.modelName = modelName;\n\t}\n\n\tprivate getMessageRole(role: MessageRole) {\n\t\tswitch (role) {\n\t\t\tcase MessageRole.USER:\n\t\t\t\treturn \"user\";\n\t\t\tcase MessageRole.MODEL:\n\t\t\tcase MessageRole.SYSTEM:\n\t\t\t\treturn \"system\";\n\t\t\tdefault:\n\t\t\t\treturn \"system\"; /*FIXME*/\n\t\t}\n\t}\n\n\tgenerateMessages(params: {\n\t\tquery: string;\n\t\tthread?: ThreadObject;\n\t\tsystemPrompt?: string;\n\t}): CCMessageParam[] {\n\t\tconst { query, thread, systemPrompt } = params;\n\t\tconst messages: CCMessageParam[] = !systemPrompt\n\t\t\t? []\n\t\t\t: [{ role: \"system\", content: systemPrompt.trim() }];\n\t\tconst sessionContent: CCMessageParam[] = !thread\n\t\t\t? []\n\t\t\t: thread.messages.map((message: MessageObject) => {\n\t\t\t\t\t// Prefer the real query stashed in metadata when a display text was\n\t\t\t\t\t// shown in its place (displayQuery), so multi-turn history carries\n\t\t\t\t\t// the actual query the model saw on the first turn — not the short\n\t\t\t\t\t// label. Falls back to the stored content otherwise.\n\t\t\t\t\tconst content =\n\t\t\t\t\t\ttypeof message.metadata?.query === \"string\"\n\t\t\t\t\t\t\t? message.metadata.query\n\t\t\t\t\t\t\t: (message.content.parts[0] as string);\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: this.getMessageRole(message.role),\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t};\n\t\t\t\t});\n\t\tconst userContent: CCMessageParam = { role: \"user\", content: query };\n\t\treturn messages.concat(sessionContent).concat(userContent);\n\t}\n\n\tappendAssistantToolCallTurn(\n\t\tmessages: CCMessageParam[],\n\t\tturn: AssistantToolCallTurn,\n\t): void {\n\t\tmessages.push({\n\t\t\trole: \"assistant\",\n\t\t\tcontent: turn.content,\n\t\t\ttool_calls: turn.toolCalls.map(\n\t\t\t\t(tc): ChatCompletionMessageFunctionToolCall => ({\n\t\t\t\t\tid: tc.id,\n\t\t\t\t\ttype: \"function\",\n\t\t\t\t\tfunction: {\n\t\t\t\t\t\tname: tc.function.name,\n\t\t\t\t\t\targuments: tc.function.arguments,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t),\n\t\t});\n\t}\n\n\tappendToolResult(\n\t\tmessages: CCMessageParam[],\n\t\tresult: ToolResultMessage,\n\t): void {\n\t\tmessages.push({\n\t\t\trole: \"tool\",\n\t\t\ttool_call_id: result.toolCallId,\n\t\t\tcontent: result.content,\n\t\t});\n\t}\n\n\tasync fetch(\n\t\tmessages: CCMessageParam[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<FetchResponse> {\n\t\tconst response = await this.client.chat.completions.create({\n\t\t\tmodel: this.modelName,\n\t\t\tmessages,\n\t\t\treasoning_effort: options?.reasoning,\n\t\t\tverbosity: options?.verbosity,\n\t\t});\n\n\t\treturn {\n\t\t\tcontent: response.choices[0].message.content || undefined,\n\t\t};\n\t}\n\n\tasync fetchWithContextMessage(\n\t\tmessages: CCMessageParam[],\n\t\tfunctions: ChatCompletionTool[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<FetchResponse> {\n\t\tif (functions.length > 0) {\n\t\t\tconst response = await this.client.chat.completions.create({\n\t\t\t\tmodel: this.modelName,\n\t\t\t\tmessages,\n\t\t\t\ttools: functions,\n\t\t\t\ttool_choice: options?.toolChoice ?? \"auto\",\n\t\t\t\treasoning_effort: options?.reasoning,\n\t\t\t\tverbosity: options?.verbosity,\n\t\t\t});\n\n\t\t\tconst { content, tool_calls } = response.choices[0].message;\n\n\t\t\tconst toolCalls: ToolCall[] | undefined = tool_calls?.map(\n\t\t\t\t(value: ChatCompletionMessageToolCall) => {\n\t\t\t\t\tconst v = value as ChatCompletionMessageFunctionToolCall;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tname: v.function.name,\n\t\t\t\t\t\t// FIXME: value.function.arguments could not be a valid JSON\n\t\t\t\t\t\targuments: JSON.parse(v.function.arguments),\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tcontent: content || undefined,\n\t\t\t\ttoolCalls,\n\t\t\t};\n\t\t}\n\t\treturn await this.fetch(messages);\n\t}\n\n\tasync fetchStreamWithContextMessage(\n\t\tmessages: CCMessageParam[],\n\t\tfunctions: ChatCompletionTool[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<LLMStream> {\n\t\tconst stream = await this.client.chat.completions.create({\n\t\t\tmodel: this.modelName,\n\t\t\tmessages,\n\t\t\ttools: functions,\n\t\t\ttool_choice:\n\t\t\t\tfunctions.length > 0 ? (options?.toolChoice ?? \"auto\") : \"none\",\n\t\t\tstream: true,\n\t\t\treasoning_effort: options?.reasoning,\n\t\t\tverbosity: options?.verbosity,\n\t\t});\n\t\treturn this.createOpenAIStreamAdapter(stream);\n\t}\n\n\t// NOTE(yoojin): Need to switch API Stream type to LLMStream.\n\tprivate createOpenAIStreamAdapter(\n\t\topenaiStream: AsyncIterable<ChatCompletionChunk>,\n\t): LLMStream {\n\t\treturn {\n\t\t\tasync *[Symbol.asyncIterator](): AsyncIterator<StreamChunk> {\n\t\t\t\tfor await (const openaiChunk of openaiStream) {\n\t\t\t\t\tconst choice = openaiChunk.choices[0];\n\t\t\t\t\tif (choice) {\n\t\t\t\t\t\tconst streamChunk: StreamChunk = {\n\t\t\t\t\t\t\tdelta: {\n\t\t\t\t\t\t\t\trole: choice.delta?.role || undefined,\n\t\t\t\t\t\t\t\tcontent: choice.delta?.content || undefined,\n\t\t\t\t\t\t\t\ttool_calls: choice.delta?.tool_calls?.map(\n\t\t\t\t\t\t\t\t\t(tc) =>\n\t\t\t\t\t\t\t\t\t\t({\n\t\t\t\t\t\t\t\t\t\t\tindex: tc.index,\n\t\t\t\t\t\t\t\t\t\t\tid: tc.id,\n\t\t\t\t\t\t\t\t\t\t\ttype: tc.type,\n\t\t\t\t\t\t\t\t\t\t\tfunction: tc.function,\n\t\t\t\t\t\t\t\t\t\t}) as ToolCallDelta,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tfinish_reason: choice.finish_reason as any,\n\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\tprovider: \"openai\",\n\t\t\t\t\t\t\t\tmodel: openaiChunk.model,\n\t\t\t\t\t\t\t\tid: openaiChunk.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t\tyield streamChunk;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tmetadata: { provider: \"openai\" },\n\t\t};\n\t}\n\n\tconvertToolsToFunctions(tools: ConnectorTool[]): ChatCompletionTool[] {\n\t\tconst functions: ChatCompletionTool[] = [];\n\t\tfor (const tool of tools) {\n\t\t\tfunctions.push({\n\t\t\t\ttype: \"function\",\n\t\t\t\tfunction: {\n\t\t\t\t\tname: tool.toolName,\n\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\tparameters: tool.inputSchema,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\treturn functions;\n\t}\n}\n"],"mappings":"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","names":["AzureOpenAIClient"]}
1
+ {"version":3,"sources":["../index.ts"],"sourcesContent":["import {\n\ttype AssistantToolCallTurn,\n\tBaseModel,\n\ttype ModelFetchOptions,\n\ttype ToolResultMessage,\n} from \"@ainetwork/adk/modules\";\nimport type {\n\tConnectorTool,\n\tFetchResponse,\n\tToolCall,\n} from \"@ainetwork/adk/types/connector\";\nimport {\n\ttype MessageObject,\n\tMessageRole,\n\ttype ThreadObject,\n} from \"@ainetwork/adk/types/memory\";\nimport type {\n\tLLMStream,\n\tStreamChunk,\n\tToolCallDelta,\n} from \"@ainetwork/adk/types/stream\";\nimport { loggers } from \"@ainetwork/adk/utils/logger\";\nimport { AzureOpenAI as AzureOpenAIClient } from \"openai\";\nimport type {\n\tChatCompletionMessageParam as CCMessageParam,\n\tChatCompletionChunk,\n\tChatCompletionMessageFunctionToolCall,\n\tChatCompletionMessageToolCall,\n\tChatCompletionTool,\n} from \"openai/resources\";\n\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst DEFAULT_MAX_RETRIES = 1;\nconst PROVIDER = \"azure-openai\";\n\nexport interface AzureOpenAIConfig {\n\tendpoint?: string;\n\tdeployment?: string;\n\tbaseUrl?: string;\n\tapiKey: string;\n\tapiVersion: string;\n\tmodelName: string;\n\t/** Request timeout in milliseconds. Defaults to 120000 (2 minutes). */\n\ttimeout?: number;\n\t/** Maximum number of automatic retries. Defaults to 1. */\n\tmaxRetries?: number;\n}\n\nexport class AzureOpenAI extends BaseModel<CCMessageParam, ChatCompletionTool> {\n\tprivate client: AzureOpenAIClient;\n\tprivate modelName: string;\n\n\tconstructor({\n\t\tendpoint,\n\t\tdeployment,\n\t\tbaseUrl,\n\t\tapiKey,\n\t\tapiVersion,\n\t\tmodelName,\n\t\ttimeout = DEFAULT_TIMEOUT_MS,\n\t\tmaxRetries = DEFAULT_MAX_RETRIES,\n\t}: AzureOpenAIConfig) {\n\t\tsuper();\n\t\tconst options = {\n\t\t\tendpoint,\n\t\t\tapiKey,\n\t\t\tdeployment,\n\t\t\tapiVersion,\n\t\t\tbaseURL: baseUrl,\n\t\t\ttimeout,\n\t\t\tmaxRetries,\n\t\t};\n\t\tthis.client = new AzureOpenAIClient(options);\n\t\tthis.modelName = modelName;\n\t}\n\n\tprivate getMessageRole(role: MessageRole) {\n\t\tswitch (role) {\n\t\t\tcase MessageRole.USER:\n\t\t\t\treturn \"user\";\n\t\t\tcase MessageRole.MODEL:\n\t\t\t\treturn \"assistant\";\n\t\t\tcase MessageRole.SYSTEM:\n\t\t\t\treturn \"system\";\n\t\t\tdefault:\n\t\t\t\treturn \"system\";\n\t\t}\n\t}\n\n\tprivate logCallStart(method: string, messageCount: number, toolCount = 0) {\n\t\tloggers.model.debug(`[${PROVIDER}] ${method} start`, {\n\t\t\tprovider: PROVIDER,\n\t\t\tmodel: this.modelName,\n\t\t\tmessageCount,\n\t\t\ttoolCount,\n\t\t});\n\t}\n\n\tprivate logCallSuccess(method: string, startedAt: number) {\n\t\tloggers.model.info(`[${PROVIDER}] ${method} complete`, {\n\t\t\tprovider: PROVIDER,\n\t\t\tmodel: this.modelName,\n\t\t\tdurationMs: Date.now() - startedAt,\n\t\t});\n\t}\n\n\tprivate logCallFailure(method: string, startedAt: number, error: unknown) {\n\t\tloggers.model.error(`[${PROVIDER}] ${method} failed`, {\n\t\t\tprovider: PROVIDER,\n\t\t\tmodel: this.modelName,\n\t\t\tdurationMs: Date.now() - startedAt,\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t});\n\t}\n\n\tgenerateMessages(params: {\n\t\tquery: string;\n\t\tthread?: ThreadObject;\n\t\tsystemPrompt?: string;\n\t}): CCMessageParam[] {\n\t\tconst { query, thread, systemPrompt } = params;\n\t\tconst messages: CCMessageParam[] = !systemPrompt\n\t\t\t? []\n\t\t\t: [{ role: \"system\", content: systemPrompt.trim() }];\n\t\tconst sessionContent: CCMessageParam[] = !thread\n\t\t\t? []\n\t\t\t: thread.messages.map((message: MessageObject) => {\n\t\t\t\t\t// Prefer the real query stashed in metadata when a display text was\n\t\t\t\t\t// shown in its place (displayQuery), so multi-turn history carries\n\t\t\t\t\t// the actual query the model saw on the first turn — not the short\n\t\t\t\t\t// label. Falls back to the stored content otherwise.\n\t\t\t\t\tconst content =\n\t\t\t\t\t\ttypeof message.metadata?.query === \"string\"\n\t\t\t\t\t\t\t? message.metadata.query\n\t\t\t\t\t\t\t: (message.content.parts[0] as string);\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: this.getMessageRole(message.role),\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t};\n\t\t\t\t});\n\t\tconst userContent: CCMessageParam = { role: \"user\", content: query };\n\t\treturn messages.concat(sessionContent).concat(userContent);\n\t}\n\n\tappendAssistantToolCallTurn(\n\t\tmessages: CCMessageParam[],\n\t\tturn: AssistantToolCallTurn,\n\t): void {\n\t\tmessages.push({\n\t\t\trole: \"assistant\",\n\t\t\tcontent: turn.content,\n\t\t\ttool_calls: turn.toolCalls.map(\n\t\t\t\t(tc): ChatCompletionMessageFunctionToolCall => ({\n\t\t\t\t\tid: tc.id,\n\t\t\t\t\ttype: \"function\",\n\t\t\t\t\tfunction: {\n\t\t\t\t\t\tname: tc.function.name,\n\t\t\t\t\t\targuments: tc.function.arguments,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t),\n\t\t});\n\t}\n\n\tappendToolResult(\n\t\tmessages: CCMessageParam[],\n\t\tresult: ToolResultMessage,\n\t): void {\n\t\tmessages.push({\n\t\t\trole: \"tool\",\n\t\t\ttool_call_id: result.toolCallId,\n\t\t\tcontent: result.content,\n\t\t});\n\t}\n\n\tasync fetch(\n\t\tmessages: CCMessageParam[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<FetchResponse> {\n\t\tconst startedAt = Date.now();\n\t\tthis.logCallStart(\"fetch\", messages.length);\n\t\ttry {\n\t\t\tconst response = await this.client.chat.completions.create({\n\t\t\t\tmodel: this.modelName,\n\t\t\t\tmessages,\n\t\t\t\treasoning_effort: options?.reasoning,\n\t\t\t\tverbosity: options?.verbosity,\n\t\t\t});\n\t\t\tthis.logCallSuccess(\"fetch\", startedAt);\n\n\t\t\treturn {\n\t\t\t\tcontent: response.choices[0].message.content || undefined,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tthis.logCallFailure(\"fetch\", startedAt, error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync fetchWithContextMessage(\n\t\tmessages: CCMessageParam[],\n\t\tfunctions: ChatCompletionTool[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<FetchResponse> {\n\t\tif (functions.length > 0) {\n\t\t\tconst startedAt = Date.now();\n\t\t\tthis.logCallStart(\n\t\t\t\t\"fetchWithContextMessage\",\n\t\t\t\tmessages.length,\n\t\t\t\tfunctions.length,\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tconst response = await this.client.chat.completions.create({\n\t\t\t\t\tmodel: this.modelName,\n\t\t\t\t\tmessages,\n\t\t\t\t\ttools: functions,\n\t\t\t\t\ttool_choice: options?.toolChoice ?? \"auto\",\n\t\t\t\t\treasoning_effort: options?.reasoning,\n\t\t\t\t\tverbosity: options?.verbosity,\n\t\t\t\t});\n\t\t\t\tthis.logCallSuccess(\"fetchWithContextMessage\", startedAt);\n\n\t\t\t\tconst { content, tool_calls } = response.choices[0].message;\n\n\t\t\t\tconst toolCalls: ToolCall[] | undefined = tool_calls?.map(\n\t\t\t\t\t(value: ChatCompletionMessageToolCall) => {\n\t\t\t\t\t\tconst v = value as ChatCompletionMessageFunctionToolCall;\n\t\t\t\t\t\tlet args: Record<string, unknown>;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\targs = JSON.parse(v.function.arguments);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Forward the raw argument string so the caller can surface it\n\t\t\t\t\t\t\t// (and the model can self-correct) instead of killing the fetch.\n\t\t\t\t\t\t\targs = { __raw: v.function.arguments };\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tname: v.function.name,\n\t\t\t\t\t\t\targuments: args,\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: content || undefined,\n\t\t\t\t\ttoolCalls,\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tthis.logCallFailure(\"fetchWithContextMessage\", startedAt, error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\treturn await this.fetch(messages, options);\n\t}\n\n\tasync fetchStreamWithContextMessage(\n\t\tmessages: CCMessageParam[],\n\t\tfunctions: ChatCompletionTool[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<LLMStream> {\n\t\tconst startedAt = Date.now();\n\t\tthis.logCallStart(\n\t\t\t\"fetchStreamWithContextMessage\",\n\t\t\tmessages.length,\n\t\t\tfunctions.length,\n\t\t);\n\t\ttry {\n\t\t\tconst stream = await this.client.chat.completions.create({\n\t\t\t\tmodel: this.modelName,\n\t\t\t\tmessages,\n\t\t\t\t...(functions.length > 0\n\t\t\t\t\t? {\n\t\t\t\t\t\t\ttools: functions,\n\t\t\t\t\t\t\ttool_choice: options?.toolChoice ?? \"auto\",\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\n\t\t\t\tstream: true,\n\t\t\t\treasoning_effort: options?.reasoning,\n\t\t\t\tverbosity: options?.verbosity,\n\t\t\t});\n\t\t\treturn this.withStreamLogging(\n\t\t\t\tthis.createOpenAIStreamAdapter(stream),\n\t\t\t\t\"fetchStreamWithContextMessage\",\n\t\t\t\tstartedAt,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthis.logCallFailure(\"fetchStreamWithContextMessage\", startedAt, error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Wraps an LLMStream so the completion log covers the whole stream\n\t * lifetime, not just the initial request.\n\t */\n\tprivate withStreamLogging(\n\t\tstream: LLMStream,\n\t\tmethod: string,\n\t\tstartedAt: number,\n\t): LLMStream {\n\t\tconst logSuccess = () => this.logCallSuccess(method, startedAt);\n\t\tconst logFailure = (error: unknown) =>\n\t\t\tthis.logCallFailure(method, startedAt, error);\n\t\treturn {\n\t\t\t...stream,\n\t\t\tasync *[Symbol.asyncIterator](): AsyncIterator<StreamChunk> {\n\t\t\t\ttry {\n\t\t\t\t\tyield* stream;\n\t\t\t\t\tlogSuccess();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogFailure(error);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\t// NOTE(yoojin): Need to switch API Stream type to LLMStream.\n\tprivate createOpenAIStreamAdapter(\n\t\topenaiStream: AsyncIterable<ChatCompletionChunk>,\n\t): LLMStream {\n\t\treturn {\n\t\t\tasync *[Symbol.asyncIterator](): AsyncIterator<StreamChunk> {\n\t\t\t\tfor await (const openaiChunk of openaiStream) {\n\t\t\t\t\tconst choice = openaiChunk.choices[0];\n\t\t\t\t\tif (choice) {\n\t\t\t\t\t\tconst streamChunk: StreamChunk = {\n\t\t\t\t\t\t\tdelta: {\n\t\t\t\t\t\t\t\trole: choice.delta?.role || undefined,\n\t\t\t\t\t\t\t\tcontent: choice.delta?.content || undefined,\n\t\t\t\t\t\t\t\ttool_calls: choice.delta?.tool_calls?.map(\n\t\t\t\t\t\t\t\t\t(tc) =>\n\t\t\t\t\t\t\t\t\t\t({\n\t\t\t\t\t\t\t\t\t\t\tindex: tc.index,\n\t\t\t\t\t\t\t\t\t\t\tid: tc.id,\n\t\t\t\t\t\t\t\t\t\t\ttype: tc.type,\n\t\t\t\t\t\t\t\t\t\t\tfunction: tc.function,\n\t\t\t\t\t\t\t\t\t\t}) as ToolCallDelta,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tfinish_reason: choice.finish_reason as any,\n\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\tprovider: \"openai\",\n\t\t\t\t\t\t\t\tmodel: openaiChunk.model,\n\t\t\t\t\t\t\t\tid: openaiChunk.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t\tyield streamChunk;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tmetadata: { provider: \"openai\" },\n\t\t};\n\t}\n\n\tconvertToolsToFunctions(tools: ConnectorTool[]): ChatCompletionTool[] {\n\t\tconst functions: ChatCompletionTool[] = [];\n\t\tfor (const tool of tools) {\n\t\t\tfunctions.push({\n\t\t\t\ttype: \"function\",\n\t\t\t\tfunction: {\n\t\t\t\t\tname: tool.toolName,\n\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\tparameters: tool.inputSchema,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\treturn functions;\n\t}\n}\n"],"mappings":"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","names":["AzureOpenAIClient"]}
package/dist/index.d.cts CHANGED
@@ -11,12 +11,19 @@ interface AzureOpenAIConfig {
11
11
  apiKey: string;
12
12
  apiVersion: string;
13
13
  modelName: string;
14
+ /** Request timeout in milliseconds. Defaults to 120000 (2 minutes). */
15
+ timeout?: number;
16
+ /** Maximum number of automatic retries. Defaults to 1. */
17
+ maxRetries?: number;
14
18
  }
15
19
  declare class AzureOpenAI extends BaseModel<ChatCompletionMessageParam, ChatCompletionTool> {
16
20
  private client;
17
21
  private modelName;
18
- constructor({ endpoint, deployment, baseUrl, apiKey, apiVersion, modelName, }: AzureOpenAIConfig);
22
+ constructor({ endpoint, deployment, baseUrl, apiKey, apiVersion, modelName, timeout, maxRetries, }: AzureOpenAIConfig);
19
23
  private getMessageRole;
24
+ private logCallStart;
25
+ private logCallSuccess;
26
+ private logCallFailure;
20
27
  generateMessages(params: {
21
28
  query: string;
22
29
  thread?: ThreadObject;
@@ -27,6 +34,11 @@ declare class AzureOpenAI extends BaseModel<ChatCompletionMessageParam, ChatComp
27
34
  fetch(messages: ChatCompletionMessageParam[], options?: ModelFetchOptions): Promise<FetchResponse>;
28
35
  fetchWithContextMessage(messages: ChatCompletionMessageParam[], functions: ChatCompletionTool[], options?: ModelFetchOptions): Promise<FetchResponse>;
29
36
  fetchStreamWithContextMessage(messages: ChatCompletionMessageParam[], functions: ChatCompletionTool[], options?: ModelFetchOptions): Promise<LLMStream>;
37
+ /**
38
+ * Wraps an LLMStream so the completion log covers the whole stream
39
+ * lifetime, not just the initial request.
40
+ */
41
+ private withStreamLogging;
30
42
  private createOpenAIStreamAdapter;
31
43
  convertToolsToFunctions(tools: ConnectorTool[]): ChatCompletionTool[];
32
44
  }
package/dist/index.d.ts CHANGED
@@ -11,12 +11,19 @@ interface AzureOpenAIConfig {
11
11
  apiKey: string;
12
12
  apiVersion: string;
13
13
  modelName: string;
14
+ /** Request timeout in milliseconds. Defaults to 120000 (2 minutes). */
15
+ timeout?: number;
16
+ /** Maximum number of automatic retries. Defaults to 1. */
17
+ maxRetries?: number;
14
18
  }
15
19
  declare class AzureOpenAI extends BaseModel<ChatCompletionMessageParam, ChatCompletionTool> {
16
20
  private client;
17
21
  private modelName;
18
- constructor({ endpoint, deployment, baseUrl, apiKey, apiVersion, modelName, }: AzureOpenAIConfig);
22
+ constructor({ endpoint, deployment, baseUrl, apiKey, apiVersion, modelName, timeout, maxRetries, }: AzureOpenAIConfig);
19
23
  private getMessageRole;
24
+ private logCallStart;
25
+ private logCallSuccess;
26
+ private logCallFailure;
20
27
  generateMessages(params: {
21
28
  query: string;
22
29
  thread?: ThreadObject;
@@ -27,6 +34,11 @@ declare class AzureOpenAI extends BaseModel<ChatCompletionMessageParam, ChatComp
27
34
  fetch(messages: ChatCompletionMessageParam[], options?: ModelFetchOptions): Promise<FetchResponse>;
28
35
  fetchWithContextMessage(messages: ChatCompletionMessageParam[], functions: ChatCompletionTool[], options?: ModelFetchOptions): Promise<FetchResponse>;
29
36
  fetchStreamWithContextMessage(messages: ChatCompletionMessageParam[], functions: ChatCompletionTool[], options?: ModelFetchOptions): Promise<LLMStream>;
37
+ /**
38
+ * Wraps an LLMStream so the completion log covers the whole stream
39
+ * lifetime, not just the initial request.
40
+ */
41
+ private withStreamLogging;
30
42
  private createOpenAIStreamAdapter;
31
43
  convertToolsToFunctions(tools: ConnectorTool[]): ChatCompletionTool[];
32
44
  }
package/dist/index.js CHANGED
@@ -5,7 +5,11 @@ import {
5
5
  import {
6
6
  MessageRole
7
7
  } from "@ainetwork/adk/types/memory";
8
+ import { loggers } from "@ainetwork/adk/utils/logger";
8
9
  import { AzureOpenAI as AzureOpenAIClient } from "openai";
10
+ var DEFAULT_TIMEOUT_MS = 12e4;
11
+ var DEFAULT_MAX_RETRIES = 1;
12
+ var PROVIDER = "azure-openai";
9
13
  var AzureOpenAI = class extends BaseModel {
10
14
  client;
11
15
  modelName;
@@ -15,7 +19,9 @@ var AzureOpenAI = class extends BaseModel {
15
19
  baseUrl,
16
20
  apiKey,
17
21
  apiVersion,
18
- modelName
22
+ modelName,
23
+ timeout = DEFAULT_TIMEOUT_MS,
24
+ maxRetries = DEFAULT_MAX_RETRIES
19
25
  }) {
20
26
  super();
21
27
  const options = {
@@ -23,7 +29,9 @@ var AzureOpenAI = class extends BaseModel {
23
29
  apiKey,
24
30
  deployment,
25
31
  apiVersion,
26
- baseURL: baseUrl
32
+ baseURL: baseUrl,
33
+ timeout,
34
+ maxRetries
27
35
  };
28
36
  this.client = new AzureOpenAIClient(options);
29
37
  this.modelName = modelName;
@@ -33,12 +41,36 @@ var AzureOpenAI = class extends BaseModel {
33
41
  case MessageRole.USER:
34
42
  return "user";
35
43
  case MessageRole.MODEL:
44
+ return "assistant";
36
45
  case MessageRole.SYSTEM:
37
46
  return "system";
38
47
  default:
39
48
  return "system";
40
49
  }
41
50
  }
51
+ logCallStart(method, messageCount, toolCount = 0) {
52
+ loggers.model.debug(`[${PROVIDER}] ${method} start`, {
53
+ provider: PROVIDER,
54
+ model: this.modelName,
55
+ messageCount,
56
+ toolCount
57
+ });
58
+ }
59
+ logCallSuccess(method, startedAt) {
60
+ loggers.model.info(`[${PROVIDER}] ${method} complete`, {
61
+ provider: PROVIDER,
62
+ model: this.modelName,
63
+ durationMs: Date.now() - startedAt
64
+ });
65
+ }
66
+ logCallFailure(method, startedAt, error) {
67
+ loggers.model.error(`[${PROVIDER}] ${method} failed`, {
68
+ provider: PROVIDER,
69
+ model: this.modelName,
70
+ durationMs: Date.now() - startedAt,
71
+ error: error instanceof Error ? error.message : String(error)
72
+ });
73
+ }
42
74
  generateMessages(params) {
43
75
  const { query, thread, systemPrompt } = params;
44
76
  const messages = !systemPrompt ? [] : [{ role: "system", content: systemPrompt.trim() }];
@@ -76,55 +108,117 @@ var AzureOpenAI = class extends BaseModel {
76
108
  });
77
109
  }
78
110
  async fetch(messages, options) {
79
- const response = await this.client.chat.completions.create({
80
- model: this.modelName,
81
- messages,
82
- reasoning_effort: options?.reasoning,
83
- verbosity: options?.verbosity
84
- });
85
- return {
86
- content: response.choices[0].message.content || void 0
87
- };
88
- }
89
- async fetchWithContextMessage(messages, functions, options) {
90
- if (functions.length > 0) {
111
+ const startedAt = Date.now();
112
+ this.logCallStart("fetch", messages.length);
113
+ try {
91
114
  const response = await this.client.chat.completions.create({
92
115
  model: this.modelName,
93
116
  messages,
94
- tools: functions,
95
- tool_choice: options?.toolChoice ?? "auto",
96
117
  reasoning_effort: options?.reasoning,
97
118
  verbosity: options?.verbosity
98
119
  });
99
- const { content, tool_calls } = response.choices[0].message;
100
- const toolCalls = tool_calls?.map(
101
- (value) => {
102
- const v = value;
103
- return {
104
- name: v.function.name,
105
- // FIXME: value.function.arguments could not be a valid JSON
106
- arguments: JSON.parse(v.function.arguments)
107
- };
108
- }
109
- );
120
+ this.logCallSuccess("fetch", startedAt);
110
121
  return {
111
- content: content || void 0,
112
- toolCalls
122
+ content: response.choices[0].message.content || void 0
113
123
  };
124
+ } catch (error) {
125
+ this.logCallFailure("fetch", startedAt, error);
126
+ throw error;
114
127
  }
115
- return await this.fetch(messages);
128
+ }
129
+ async fetchWithContextMessage(messages, functions, options) {
130
+ if (functions.length > 0) {
131
+ const startedAt = Date.now();
132
+ this.logCallStart(
133
+ "fetchWithContextMessage",
134
+ messages.length,
135
+ functions.length
136
+ );
137
+ try {
138
+ const response = await this.client.chat.completions.create({
139
+ model: this.modelName,
140
+ messages,
141
+ tools: functions,
142
+ tool_choice: options?.toolChoice ?? "auto",
143
+ reasoning_effort: options?.reasoning,
144
+ verbosity: options?.verbosity
145
+ });
146
+ this.logCallSuccess("fetchWithContextMessage", startedAt);
147
+ const { content, tool_calls } = response.choices[0].message;
148
+ const toolCalls = tool_calls?.map(
149
+ (value) => {
150
+ const v = value;
151
+ let args;
152
+ try {
153
+ args = JSON.parse(v.function.arguments);
154
+ } catch {
155
+ args = { __raw: v.function.arguments };
156
+ }
157
+ return {
158
+ name: v.function.name,
159
+ arguments: args
160
+ };
161
+ }
162
+ );
163
+ return {
164
+ content: content || void 0,
165
+ toolCalls
166
+ };
167
+ } catch (error) {
168
+ this.logCallFailure("fetchWithContextMessage", startedAt, error);
169
+ throw error;
170
+ }
171
+ }
172
+ return await this.fetch(messages, options);
116
173
  }
117
174
  async fetchStreamWithContextMessage(messages, functions, options) {
118
- const stream = await this.client.chat.completions.create({
119
- model: this.modelName,
120
- messages,
121
- tools: functions,
122
- tool_choice: functions.length > 0 ? options?.toolChoice ?? "auto" : "none",
123
- stream: true,
124
- reasoning_effort: options?.reasoning,
125
- verbosity: options?.verbosity
126
- });
127
- return this.createOpenAIStreamAdapter(stream);
175
+ const startedAt = Date.now();
176
+ this.logCallStart(
177
+ "fetchStreamWithContextMessage",
178
+ messages.length,
179
+ functions.length
180
+ );
181
+ try {
182
+ const stream = await this.client.chat.completions.create({
183
+ model: this.modelName,
184
+ messages,
185
+ ...functions.length > 0 ? {
186
+ tools: functions,
187
+ tool_choice: options?.toolChoice ?? "auto"
188
+ } : {},
189
+ stream: true,
190
+ reasoning_effort: options?.reasoning,
191
+ verbosity: options?.verbosity
192
+ });
193
+ return this.withStreamLogging(
194
+ this.createOpenAIStreamAdapter(stream),
195
+ "fetchStreamWithContextMessage",
196
+ startedAt
197
+ );
198
+ } catch (error) {
199
+ this.logCallFailure("fetchStreamWithContextMessage", startedAt, error);
200
+ throw error;
201
+ }
202
+ }
203
+ /**
204
+ * Wraps an LLMStream so the completion log covers the whole stream
205
+ * lifetime, not just the initial request.
206
+ */
207
+ withStreamLogging(stream, method, startedAt) {
208
+ const logSuccess = () => this.logCallSuccess(method, startedAt);
209
+ const logFailure = (error) => this.logCallFailure(method, startedAt, error);
210
+ return {
211
+ ...stream,
212
+ async *[Symbol.asyncIterator]() {
213
+ try {
214
+ yield* stream;
215
+ logSuccess();
216
+ } catch (error) {
217
+ logFailure(error);
218
+ throw error;
219
+ }
220
+ }
221
+ };
128
222
  }
129
223
  // NOTE(yoojin): Need to switch API Stream type to LLMStream.
130
224
  createOpenAIStreamAdapter(openaiStream) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts"],"sourcesContent":["import {\n\ttype AssistantToolCallTurn,\n\tBaseModel,\n\ttype ModelFetchOptions,\n\ttype ToolResultMessage,\n} from \"@ainetwork/adk/modules\";\nimport type {\n\tConnectorTool,\n\tFetchResponse,\n\tToolCall,\n} from \"@ainetwork/adk/types/connector\";\nimport {\n\ttype MessageObject,\n\tMessageRole,\n\ttype ThreadObject,\n} from \"@ainetwork/adk/types/memory\";\nimport type {\n\tLLMStream,\n\tStreamChunk,\n\tToolCallDelta,\n} from \"@ainetwork/adk/types/stream\";\nimport { AzureOpenAI as AzureOpenAIClient } from \"openai\";\nimport type {\n\tChatCompletionMessageParam as CCMessageParam,\n\tChatCompletionChunk,\n\tChatCompletionMessageFunctionToolCall,\n\tChatCompletionMessageToolCall,\n\tChatCompletionTool,\n} from \"openai/resources\";\n\nexport interface AzureOpenAIConfig {\n\tendpoint?: string;\n\tdeployment?: string;\n\tbaseUrl?: string;\n\tapiKey: string;\n\tapiVersion: string;\n\tmodelName: string;\n}\n\nexport class AzureOpenAI extends BaseModel<CCMessageParam, ChatCompletionTool> {\n\tprivate client: AzureOpenAIClient;\n\tprivate modelName: string;\n\n\tconstructor({\n\t\tendpoint,\n\t\tdeployment,\n\t\tbaseUrl,\n\t\tapiKey,\n\t\tapiVersion,\n\t\tmodelName,\n\t}: AzureOpenAIConfig) {\n\t\tsuper();\n\t\tconst options = {\n\t\t\tendpoint,\n\t\t\tapiKey,\n\t\t\tdeployment,\n\t\t\tapiVersion,\n\t\t\tbaseURL: baseUrl,\n\t\t};\n\t\tthis.client = new AzureOpenAIClient(options);\n\t\tthis.modelName = modelName;\n\t}\n\n\tprivate getMessageRole(role: MessageRole) {\n\t\tswitch (role) {\n\t\t\tcase MessageRole.USER:\n\t\t\t\treturn \"user\";\n\t\t\tcase MessageRole.MODEL:\n\t\t\tcase MessageRole.SYSTEM:\n\t\t\t\treturn \"system\";\n\t\t\tdefault:\n\t\t\t\treturn \"system\"; /*FIXME*/\n\t\t}\n\t}\n\n\tgenerateMessages(params: {\n\t\tquery: string;\n\t\tthread?: ThreadObject;\n\t\tsystemPrompt?: string;\n\t}): CCMessageParam[] {\n\t\tconst { query, thread, systemPrompt } = params;\n\t\tconst messages: CCMessageParam[] = !systemPrompt\n\t\t\t? []\n\t\t\t: [{ role: \"system\", content: systemPrompt.trim() }];\n\t\tconst sessionContent: CCMessageParam[] = !thread\n\t\t\t? []\n\t\t\t: thread.messages.map((message: MessageObject) => {\n\t\t\t\t\t// Prefer the real query stashed in metadata when a display text was\n\t\t\t\t\t// shown in its place (displayQuery), so multi-turn history carries\n\t\t\t\t\t// the actual query the model saw on the first turn — not the short\n\t\t\t\t\t// label. Falls back to the stored content otherwise.\n\t\t\t\t\tconst content =\n\t\t\t\t\t\ttypeof message.metadata?.query === \"string\"\n\t\t\t\t\t\t\t? message.metadata.query\n\t\t\t\t\t\t\t: (message.content.parts[0] as string);\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: this.getMessageRole(message.role),\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t};\n\t\t\t\t});\n\t\tconst userContent: CCMessageParam = { role: \"user\", content: query };\n\t\treturn messages.concat(sessionContent).concat(userContent);\n\t}\n\n\tappendAssistantToolCallTurn(\n\t\tmessages: CCMessageParam[],\n\t\tturn: AssistantToolCallTurn,\n\t): void {\n\t\tmessages.push({\n\t\t\trole: \"assistant\",\n\t\t\tcontent: turn.content,\n\t\t\ttool_calls: turn.toolCalls.map(\n\t\t\t\t(tc): ChatCompletionMessageFunctionToolCall => ({\n\t\t\t\t\tid: tc.id,\n\t\t\t\t\ttype: \"function\",\n\t\t\t\t\tfunction: {\n\t\t\t\t\t\tname: tc.function.name,\n\t\t\t\t\t\targuments: tc.function.arguments,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t),\n\t\t});\n\t}\n\n\tappendToolResult(\n\t\tmessages: CCMessageParam[],\n\t\tresult: ToolResultMessage,\n\t): void {\n\t\tmessages.push({\n\t\t\trole: \"tool\",\n\t\t\ttool_call_id: result.toolCallId,\n\t\t\tcontent: result.content,\n\t\t});\n\t}\n\n\tasync fetch(\n\t\tmessages: CCMessageParam[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<FetchResponse> {\n\t\tconst response = await this.client.chat.completions.create({\n\t\t\tmodel: this.modelName,\n\t\t\tmessages,\n\t\t\treasoning_effort: options?.reasoning,\n\t\t\tverbosity: options?.verbosity,\n\t\t});\n\n\t\treturn {\n\t\t\tcontent: response.choices[0].message.content || undefined,\n\t\t};\n\t}\n\n\tasync fetchWithContextMessage(\n\t\tmessages: CCMessageParam[],\n\t\tfunctions: ChatCompletionTool[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<FetchResponse> {\n\t\tif (functions.length > 0) {\n\t\t\tconst response = await this.client.chat.completions.create({\n\t\t\t\tmodel: this.modelName,\n\t\t\t\tmessages,\n\t\t\t\ttools: functions,\n\t\t\t\ttool_choice: options?.toolChoice ?? \"auto\",\n\t\t\t\treasoning_effort: options?.reasoning,\n\t\t\t\tverbosity: options?.verbosity,\n\t\t\t});\n\n\t\t\tconst { content, tool_calls } = response.choices[0].message;\n\n\t\t\tconst toolCalls: ToolCall[] | undefined = tool_calls?.map(\n\t\t\t\t(value: ChatCompletionMessageToolCall) => {\n\t\t\t\t\tconst v = value as ChatCompletionMessageFunctionToolCall;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tname: v.function.name,\n\t\t\t\t\t\t// FIXME: value.function.arguments could not be a valid JSON\n\t\t\t\t\t\targuments: JSON.parse(v.function.arguments),\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tcontent: content || undefined,\n\t\t\t\ttoolCalls,\n\t\t\t};\n\t\t}\n\t\treturn await this.fetch(messages);\n\t}\n\n\tasync fetchStreamWithContextMessage(\n\t\tmessages: CCMessageParam[],\n\t\tfunctions: ChatCompletionTool[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<LLMStream> {\n\t\tconst stream = await this.client.chat.completions.create({\n\t\t\tmodel: this.modelName,\n\t\t\tmessages,\n\t\t\ttools: functions,\n\t\t\ttool_choice:\n\t\t\t\tfunctions.length > 0 ? (options?.toolChoice ?? \"auto\") : \"none\",\n\t\t\tstream: true,\n\t\t\treasoning_effort: options?.reasoning,\n\t\t\tverbosity: options?.verbosity,\n\t\t});\n\t\treturn this.createOpenAIStreamAdapter(stream);\n\t}\n\n\t// NOTE(yoojin): Need to switch API Stream type to LLMStream.\n\tprivate createOpenAIStreamAdapter(\n\t\topenaiStream: AsyncIterable<ChatCompletionChunk>,\n\t): LLMStream {\n\t\treturn {\n\t\t\tasync *[Symbol.asyncIterator](): AsyncIterator<StreamChunk> {\n\t\t\t\tfor await (const openaiChunk of openaiStream) {\n\t\t\t\t\tconst choice = openaiChunk.choices[0];\n\t\t\t\t\tif (choice) {\n\t\t\t\t\t\tconst streamChunk: StreamChunk = {\n\t\t\t\t\t\t\tdelta: {\n\t\t\t\t\t\t\t\trole: choice.delta?.role || undefined,\n\t\t\t\t\t\t\t\tcontent: choice.delta?.content || undefined,\n\t\t\t\t\t\t\t\ttool_calls: choice.delta?.tool_calls?.map(\n\t\t\t\t\t\t\t\t\t(tc) =>\n\t\t\t\t\t\t\t\t\t\t({\n\t\t\t\t\t\t\t\t\t\t\tindex: tc.index,\n\t\t\t\t\t\t\t\t\t\t\tid: tc.id,\n\t\t\t\t\t\t\t\t\t\t\ttype: tc.type,\n\t\t\t\t\t\t\t\t\t\t\tfunction: tc.function,\n\t\t\t\t\t\t\t\t\t\t}) as ToolCallDelta,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tfinish_reason: choice.finish_reason as any,\n\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\tprovider: \"openai\",\n\t\t\t\t\t\t\t\tmodel: openaiChunk.model,\n\t\t\t\t\t\t\t\tid: openaiChunk.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t\tyield streamChunk;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tmetadata: { provider: \"openai\" },\n\t\t};\n\t}\n\n\tconvertToolsToFunctions(tools: ConnectorTool[]): ChatCompletionTool[] {\n\t\tconst functions: ChatCompletionTool[] = [];\n\t\tfor (const tool of tools) {\n\t\t\tfunctions.push({\n\t\t\t\ttype: \"function\",\n\t\t\t\tfunction: {\n\t\t\t\t\tname: tool.toolName,\n\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\tparameters: tool.inputSchema,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\treturn functions;\n\t}\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../index.ts"],"sourcesContent":["import {\n\ttype AssistantToolCallTurn,\n\tBaseModel,\n\ttype ModelFetchOptions,\n\ttype ToolResultMessage,\n} from \"@ainetwork/adk/modules\";\nimport type {\n\tConnectorTool,\n\tFetchResponse,\n\tToolCall,\n} from \"@ainetwork/adk/types/connector\";\nimport {\n\ttype MessageObject,\n\tMessageRole,\n\ttype ThreadObject,\n} from \"@ainetwork/adk/types/memory\";\nimport type {\n\tLLMStream,\n\tStreamChunk,\n\tToolCallDelta,\n} from \"@ainetwork/adk/types/stream\";\nimport { loggers } from \"@ainetwork/adk/utils/logger\";\nimport { AzureOpenAI as AzureOpenAIClient } from \"openai\";\nimport type {\n\tChatCompletionMessageParam as CCMessageParam,\n\tChatCompletionChunk,\n\tChatCompletionMessageFunctionToolCall,\n\tChatCompletionMessageToolCall,\n\tChatCompletionTool,\n} from \"openai/resources\";\n\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst DEFAULT_MAX_RETRIES = 1;\nconst PROVIDER = \"azure-openai\";\n\nexport interface AzureOpenAIConfig {\n\tendpoint?: string;\n\tdeployment?: string;\n\tbaseUrl?: string;\n\tapiKey: string;\n\tapiVersion: string;\n\tmodelName: string;\n\t/** Request timeout in milliseconds. Defaults to 120000 (2 minutes). */\n\ttimeout?: number;\n\t/** Maximum number of automatic retries. Defaults to 1. */\n\tmaxRetries?: number;\n}\n\nexport class AzureOpenAI extends BaseModel<CCMessageParam, ChatCompletionTool> {\n\tprivate client: AzureOpenAIClient;\n\tprivate modelName: string;\n\n\tconstructor({\n\t\tendpoint,\n\t\tdeployment,\n\t\tbaseUrl,\n\t\tapiKey,\n\t\tapiVersion,\n\t\tmodelName,\n\t\ttimeout = DEFAULT_TIMEOUT_MS,\n\t\tmaxRetries = DEFAULT_MAX_RETRIES,\n\t}: AzureOpenAIConfig) {\n\t\tsuper();\n\t\tconst options = {\n\t\t\tendpoint,\n\t\t\tapiKey,\n\t\t\tdeployment,\n\t\t\tapiVersion,\n\t\t\tbaseURL: baseUrl,\n\t\t\ttimeout,\n\t\t\tmaxRetries,\n\t\t};\n\t\tthis.client = new AzureOpenAIClient(options);\n\t\tthis.modelName = modelName;\n\t}\n\n\tprivate getMessageRole(role: MessageRole) {\n\t\tswitch (role) {\n\t\t\tcase MessageRole.USER:\n\t\t\t\treturn \"user\";\n\t\t\tcase MessageRole.MODEL:\n\t\t\t\treturn \"assistant\";\n\t\t\tcase MessageRole.SYSTEM:\n\t\t\t\treturn \"system\";\n\t\t\tdefault:\n\t\t\t\treturn \"system\";\n\t\t}\n\t}\n\n\tprivate logCallStart(method: string, messageCount: number, toolCount = 0) {\n\t\tloggers.model.debug(`[${PROVIDER}] ${method} start`, {\n\t\t\tprovider: PROVIDER,\n\t\t\tmodel: this.modelName,\n\t\t\tmessageCount,\n\t\t\ttoolCount,\n\t\t});\n\t}\n\n\tprivate logCallSuccess(method: string, startedAt: number) {\n\t\tloggers.model.info(`[${PROVIDER}] ${method} complete`, {\n\t\t\tprovider: PROVIDER,\n\t\t\tmodel: this.modelName,\n\t\t\tdurationMs: Date.now() - startedAt,\n\t\t});\n\t}\n\n\tprivate logCallFailure(method: string, startedAt: number, error: unknown) {\n\t\tloggers.model.error(`[${PROVIDER}] ${method} failed`, {\n\t\t\tprovider: PROVIDER,\n\t\t\tmodel: this.modelName,\n\t\t\tdurationMs: Date.now() - startedAt,\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t});\n\t}\n\n\tgenerateMessages(params: {\n\t\tquery: string;\n\t\tthread?: ThreadObject;\n\t\tsystemPrompt?: string;\n\t}): CCMessageParam[] {\n\t\tconst { query, thread, systemPrompt } = params;\n\t\tconst messages: CCMessageParam[] = !systemPrompt\n\t\t\t? []\n\t\t\t: [{ role: \"system\", content: systemPrompt.trim() }];\n\t\tconst sessionContent: CCMessageParam[] = !thread\n\t\t\t? []\n\t\t\t: thread.messages.map((message: MessageObject) => {\n\t\t\t\t\t// Prefer the real query stashed in metadata when a display text was\n\t\t\t\t\t// shown in its place (displayQuery), so multi-turn history carries\n\t\t\t\t\t// the actual query the model saw on the first turn — not the short\n\t\t\t\t\t// label. Falls back to the stored content otherwise.\n\t\t\t\t\tconst content =\n\t\t\t\t\t\ttypeof message.metadata?.query === \"string\"\n\t\t\t\t\t\t\t? message.metadata.query\n\t\t\t\t\t\t\t: (message.content.parts[0] as string);\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: this.getMessageRole(message.role),\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t};\n\t\t\t\t});\n\t\tconst userContent: CCMessageParam = { role: \"user\", content: query };\n\t\treturn messages.concat(sessionContent).concat(userContent);\n\t}\n\n\tappendAssistantToolCallTurn(\n\t\tmessages: CCMessageParam[],\n\t\tturn: AssistantToolCallTurn,\n\t): void {\n\t\tmessages.push({\n\t\t\trole: \"assistant\",\n\t\t\tcontent: turn.content,\n\t\t\ttool_calls: turn.toolCalls.map(\n\t\t\t\t(tc): ChatCompletionMessageFunctionToolCall => ({\n\t\t\t\t\tid: tc.id,\n\t\t\t\t\ttype: \"function\",\n\t\t\t\t\tfunction: {\n\t\t\t\t\t\tname: tc.function.name,\n\t\t\t\t\t\targuments: tc.function.arguments,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t),\n\t\t});\n\t}\n\n\tappendToolResult(\n\t\tmessages: CCMessageParam[],\n\t\tresult: ToolResultMessage,\n\t): void {\n\t\tmessages.push({\n\t\t\trole: \"tool\",\n\t\t\ttool_call_id: result.toolCallId,\n\t\t\tcontent: result.content,\n\t\t});\n\t}\n\n\tasync fetch(\n\t\tmessages: CCMessageParam[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<FetchResponse> {\n\t\tconst startedAt = Date.now();\n\t\tthis.logCallStart(\"fetch\", messages.length);\n\t\ttry {\n\t\t\tconst response = await this.client.chat.completions.create({\n\t\t\t\tmodel: this.modelName,\n\t\t\t\tmessages,\n\t\t\t\treasoning_effort: options?.reasoning,\n\t\t\t\tverbosity: options?.verbosity,\n\t\t\t});\n\t\t\tthis.logCallSuccess(\"fetch\", startedAt);\n\n\t\t\treturn {\n\t\t\t\tcontent: response.choices[0].message.content || undefined,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tthis.logCallFailure(\"fetch\", startedAt, error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync fetchWithContextMessage(\n\t\tmessages: CCMessageParam[],\n\t\tfunctions: ChatCompletionTool[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<FetchResponse> {\n\t\tif (functions.length > 0) {\n\t\t\tconst startedAt = Date.now();\n\t\t\tthis.logCallStart(\n\t\t\t\t\"fetchWithContextMessage\",\n\t\t\t\tmessages.length,\n\t\t\t\tfunctions.length,\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tconst response = await this.client.chat.completions.create({\n\t\t\t\t\tmodel: this.modelName,\n\t\t\t\t\tmessages,\n\t\t\t\t\ttools: functions,\n\t\t\t\t\ttool_choice: options?.toolChoice ?? \"auto\",\n\t\t\t\t\treasoning_effort: options?.reasoning,\n\t\t\t\t\tverbosity: options?.verbosity,\n\t\t\t\t});\n\t\t\t\tthis.logCallSuccess(\"fetchWithContextMessage\", startedAt);\n\n\t\t\t\tconst { content, tool_calls } = response.choices[0].message;\n\n\t\t\t\tconst toolCalls: ToolCall[] | undefined = tool_calls?.map(\n\t\t\t\t\t(value: ChatCompletionMessageToolCall) => {\n\t\t\t\t\t\tconst v = value as ChatCompletionMessageFunctionToolCall;\n\t\t\t\t\t\tlet args: Record<string, unknown>;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\targs = JSON.parse(v.function.arguments);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Forward the raw argument string so the caller can surface it\n\t\t\t\t\t\t\t// (and the model can self-correct) instead of killing the fetch.\n\t\t\t\t\t\t\targs = { __raw: v.function.arguments };\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tname: v.function.name,\n\t\t\t\t\t\t\targuments: args,\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: content || undefined,\n\t\t\t\t\ttoolCalls,\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tthis.logCallFailure(\"fetchWithContextMessage\", startedAt, error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\treturn await this.fetch(messages, options);\n\t}\n\n\tasync fetchStreamWithContextMessage(\n\t\tmessages: CCMessageParam[],\n\t\tfunctions: ChatCompletionTool[],\n\t\toptions?: ModelFetchOptions,\n\t): Promise<LLMStream> {\n\t\tconst startedAt = Date.now();\n\t\tthis.logCallStart(\n\t\t\t\"fetchStreamWithContextMessage\",\n\t\t\tmessages.length,\n\t\t\tfunctions.length,\n\t\t);\n\t\ttry {\n\t\t\tconst stream = await this.client.chat.completions.create({\n\t\t\t\tmodel: this.modelName,\n\t\t\t\tmessages,\n\t\t\t\t...(functions.length > 0\n\t\t\t\t\t? {\n\t\t\t\t\t\t\ttools: functions,\n\t\t\t\t\t\t\ttool_choice: options?.toolChoice ?? \"auto\",\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\n\t\t\t\tstream: true,\n\t\t\t\treasoning_effort: options?.reasoning,\n\t\t\t\tverbosity: options?.verbosity,\n\t\t\t});\n\t\t\treturn this.withStreamLogging(\n\t\t\t\tthis.createOpenAIStreamAdapter(stream),\n\t\t\t\t\"fetchStreamWithContextMessage\",\n\t\t\t\tstartedAt,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthis.logCallFailure(\"fetchStreamWithContextMessage\", startedAt, error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Wraps an LLMStream so the completion log covers the whole stream\n\t * lifetime, not just the initial request.\n\t */\n\tprivate withStreamLogging(\n\t\tstream: LLMStream,\n\t\tmethod: string,\n\t\tstartedAt: number,\n\t): LLMStream {\n\t\tconst logSuccess = () => this.logCallSuccess(method, startedAt);\n\t\tconst logFailure = (error: unknown) =>\n\t\t\tthis.logCallFailure(method, startedAt, error);\n\t\treturn {\n\t\t\t...stream,\n\t\t\tasync *[Symbol.asyncIterator](): AsyncIterator<StreamChunk> {\n\t\t\t\ttry {\n\t\t\t\t\tyield* stream;\n\t\t\t\t\tlogSuccess();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogFailure(error);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\t// NOTE(yoojin): Need to switch API Stream type to LLMStream.\n\tprivate createOpenAIStreamAdapter(\n\t\topenaiStream: AsyncIterable<ChatCompletionChunk>,\n\t): LLMStream {\n\t\treturn {\n\t\t\tasync *[Symbol.asyncIterator](): AsyncIterator<StreamChunk> {\n\t\t\t\tfor await (const openaiChunk of openaiStream) {\n\t\t\t\t\tconst choice = openaiChunk.choices[0];\n\t\t\t\t\tif (choice) {\n\t\t\t\t\t\tconst streamChunk: StreamChunk = {\n\t\t\t\t\t\t\tdelta: {\n\t\t\t\t\t\t\t\trole: choice.delta?.role || undefined,\n\t\t\t\t\t\t\t\tcontent: choice.delta?.content || undefined,\n\t\t\t\t\t\t\t\ttool_calls: choice.delta?.tool_calls?.map(\n\t\t\t\t\t\t\t\t\t(tc) =>\n\t\t\t\t\t\t\t\t\t\t({\n\t\t\t\t\t\t\t\t\t\t\tindex: tc.index,\n\t\t\t\t\t\t\t\t\t\t\tid: tc.id,\n\t\t\t\t\t\t\t\t\t\t\ttype: tc.type,\n\t\t\t\t\t\t\t\t\t\t\tfunction: tc.function,\n\t\t\t\t\t\t\t\t\t\t}) as ToolCallDelta,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tfinish_reason: choice.finish_reason as any,\n\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\tprovider: \"openai\",\n\t\t\t\t\t\t\t\tmodel: openaiChunk.model,\n\t\t\t\t\t\t\t\tid: openaiChunk.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t\tyield streamChunk;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tmetadata: { provider: \"openai\" },\n\t\t};\n\t}\n\n\tconvertToolsToFunctions(tools: ConnectorTool[]): ChatCompletionTool[] {\n\t\tconst functions: ChatCompletionTool[] = [];\n\t\tfor (const tool of tools) {\n\t\t\tfunctions.push({\n\t\t\t\ttype: \"function\",\n\t\t\t\tfunction: {\n\t\t\t\t\tname: tool.toolName,\n\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\tparameters: tool.inputSchema,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\treturn functions;\n\t}\n}\n"],"mappings":"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","names":[]}
package/index.ts CHANGED
@@ -19,6 +19,7 @@ import type {
19
19
  StreamChunk,
20
20
  ToolCallDelta,
21
21
  } from "@ainetwork/adk/types/stream";
22
+ import { loggers } from "@ainetwork/adk/utils/logger";
22
23
  import { AzureOpenAI as AzureOpenAIClient } from "openai";
23
24
  import type {
24
25
  ChatCompletionMessageParam as CCMessageParam,
@@ -28,6 +29,10 @@ import type {
28
29
  ChatCompletionTool,
29
30
  } from "openai/resources";
30
31
 
32
+ const DEFAULT_TIMEOUT_MS = 120_000;
33
+ const DEFAULT_MAX_RETRIES = 1;
34
+ const PROVIDER = "azure-openai";
35
+
31
36
  export interface AzureOpenAIConfig {
32
37
  endpoint?: string;
33
38
  deployment?: string;
@@ -35,6 +40,10 @@ export interface AzureOpenAIConfig {
35
40
  apiKey: string;
36
41
  apiVersion: string;
37
42
  modelName: string;
43
+ /** Request timeout in milliseconds. Defaults to 120000 (2 minutes). */
44
+ timeout?: number;
45
+ /** Maximum number of automatic retries. Defaults to 1. */
46
+ maxRetries?: number;
38
47
  }
39
48
 
40
49
  export class AzureOpenAI extends BaseModel<CCMessageParam, ChatCompletionTool> {
@@ -48,6 +57,8 @@ export class AzureOpenAI extends BaseModel<CCMessageParam, ChatCompletionTool> {
48
57
  apiKey,
49
58
  apiVersion,
50
59
  modelName,
60
+ timeout = DEFAULT_TIMEOUT_MS,
61
+ maxRetries = DEFAULT_MAX_RETRIES,
51
62
  }: AzureOpenAIConfig) {
52
63
  super();
53
64
  const options = {
@@ -56,6 +67,8 @@ export class AzureOpenAI extends BaseModel<CCMessageParam, ChatCompletionTool> {
56
67
  deployment,
57
68
  apiVersion,
58
69
  baseURL: baseUrl,
70
+ timeout,
71
+ maxRetries,
59
72
  };
60
73
  this.client = new AzureOpenAIClient(options);
61
74
  this.modelName = modelName;
@@ -66,13 +79,40 @@ export class AzureOpenAI extends BaseModel<CCMessageParam, ChatCompletionTool> {
66
79
  case MessageRole.USER:
67
80
  return "user";
68
81
  case MessageRole.MODEL:
82
+ return "assistant";
69
83
  case MessageRole.SYSTEM:
70
84
  return "system";
71
85
  default:
72
- return "system"; /*FIXME*/
86
+ return "system";
73
87
  }
74
88
  }
75
89
 
90
+ private logCallStart(method: string, messageCount: number, toolCount = 0) {
91
+ loggers.model.debug(`[${PROVIDER}] ${method} start`, {
92
+ provider: PROVIDER,
93
+ model: this.modelName,
94
+ messageCount,
95
+ toolCount,
96
+ });
97
+ }
98
+
99
+ private logCallSuccess(method: string, startedAt: number) {
100
+ loggers.model.info(`[${PROVIDER}] ${method} complete`, {
101
+ provider: PROVIDER,
102
+ model: this.modelName,
103
+ durationMs: Date.now() - startedAt,
104
+ });
105
+ }
106
+
107
+ private logCallFailure(method: string, startedAt: number, error: unknown) {
108
+ loggers.model.error(`[${PROVIDER}] ${method} failed`, {
109
+ provider: PROVIDER,
110
+ model: this.modelName,
111
+ durationMs: Date.now() - startedAt,
112
+ error: error instanceof Error ? error.message : String(error),
113
+ });
114
+ }
115
+
76
116
  generateMessages(params: {
77
117
  query: string;
78
118
  thread?: ThreadObject;
@@ -137,16 +177,24 @@ export class AzureOpenAI extends BaseModel<CCMessageParam, ChatCompletionTool> {
137
177
  messages: CCMessageParam[],
138
178
  options?: ModelFetchOptions,
139
179
  ): Promise<FetchResponse> {
140
- const response = await this.client.chat.completions.create({
141
- model: this.modelName,
142
- messages,
143
- reasoning_effort: options?.reasoning,
144
- verbosity: options?.verbosity,
145
- });
180
+ const startedAt = Date.now();
181
+ this.logCallStart("fetch", messages.length);
182
+ try {
183
+ const response = await this.client.chat.completions.create({
184
+ model: this.modelName,
185
+ messages,
186
+ reasoning_effort: options?.reasoning,
187
+ verbosity: options?.verbosity,
188
+ });
189
+ this.logCallSuccess("fetch", startedAt);
146
190
 
147
- return {
148
- content: response.choices[0].message.content || undefined,
149
- };
191
+ return {
192
+ content: response.choices[0].message.content || undefined,
193
+ };
194
+ } catch (error) {
195
+ this.logCallFailure("fetch", startedAt, error);
196
+ throw error;
197
+ }
150
198
  }
151
199
 
152
200
  async fetchWithContextMessage(
@@ -155,34 +203,53 @@ export class AzureOpenAI extends BaseModel<CCMessageParam, ChatCompletionTool> {
155
203
  options?: ModelFetchOptions,
156
204
  ): Promise<FetchResponse> {
157
205
  if (functions.length > 0) {
158
- const response = await this.client.chat.completions.create({
159
- model: this.modelName,
160
- messages,
161
- tools: functions,
162
- tool_choice: options?.toolChoice ?? "auto",
163
- reasoning_effort: options?.reasoning,
164
- verbosity: options?.verbosity,
165
- });
206
+ const startedAt = Date.now();
207
+ this.logCallStart(
208
+ "fetchWithContextMessage",
209
+ messages.length,
210
+ functions.length,
211
+ );
212
+ try {
213
+ const response = await this.client.chat.completions.create({
214
+ model: this.modelName,
215
+ messages,
216
+ tools: functions,
217
+ tool_choice: options?.toolChoice ?? "auto",
218
+ reasoning_effort: options?.reasoning,
219
+ verbosity: options?.verbosity,
220
+ });
221
+ this.logCallSuccess("fetchWithContextMessage", startedAt);
166
222
 
167
- const { content, tool_calls } = response.choices[0].message;
223
+ const { content, tool_calls } = response.choices[0].message;
168
224
 
169
- const toolCalls: ToolCall[] | undefined = tool_calls?.map(
170
- (value: ChatCompletionMessageToolCall) => {
171
- const v = value as ChatCompletionMessageFunctionToolCall;
172
- return {
173
- name: v.function.name,
174
- // FIXME: value.function.arguments could not be a valid JSON
175
- arguments: JSON.parse(v.function.arguments),
176
- };
177
- },
178
- );
225
+ const toolCalls: ToolCall[] | undefined = tool_calls?.map(
226
+ (value: ChatCompletionMessageToolCall) => {
227
+ const v = value as ChatCompletionMessageFunctionToolCall;
228
+ let args: Record<string, unknown>;
229
+ try {
230
+ args = JSON.parse(v.function.arguments);
231
+ } catch {
232
+ // Forward the raw argument string so the caller can surface it
233
+ // (and the model can self-correct) instead of killing the fetch.
234
+ args = { __raw: v.function.arguments };
235
+ }
236
+ return {
237
+ name: v.function.name,
238
+ arguments: args,
239
+ };
240
+ },
241
+ );
179
242
 
180
- return {
181
- content: content || undefined,
182
- toolCalls,
183
- };
243
+ return {
244
+ content: content || undefined,
245
+ toolCalls,
246
+ };
247
+ } catch (error) {
248
+ this.logCallFailure("fetchWithContextMessage", startedAt, error);
249
+ throw error;
250
+ }
184
251
  }
185
- return await this.fetch(messages);
252
+ return await this.fetch(messages, options);
186
253
  }
187
254
 
188
255
  async fetchStreamWithContextMessage(
@@ -190,17 +257,61 @@ export class AzureOpenAI extends BaseModel<CCMessageParam, ChatCompletionTool> {
190
257
  functions: ChatCompletionTool[],
191
258
  options?: ModelFetchOptions,
192
259
  ): Promise<LLMStream> {
193
- const stream = await this.client.chat.completions.create({
194
- model: this.modelName,
195
- messages,
196
- tools: functions,
197
- tool_choice:
198
- functions.length > 0 ? (options?.toolChoice ?? "auto") : "none",
199
- stream: true,
200
- reasoning_effort: options?.reasoning,
201
- verbosity: options?.verbosity,
202
- });
203
- return this.createOpenAIStreamAdapter(stream);
260
+ const startedAt = Date.now();
261
+ this.logCallStart(
262
+ "fetchStreamWithContextMessage",
263
+ messages.length,
264
+ functions.length,
265
+ );
266
+ try {
267
+ const stream = await this.client.chat.completions.create({
268
+ model: this.modelName,
269
+ messages,
270
+ ...(functions.length > 0
271
+ ? {
272
+ tools: functions,
273
+ tool_choice: options?.toolChoice ?? "auto",
274
+ }
275
+ : {}),
276
+ stream: true,
277
+ reasoning_effort: options?.reasoning,
278
+ verbosity: options?.verbosity,
279
+ });
280
+ return this.withStreamLogging(
281
+ this.createOpenAIStreamAdapter(stream),
282
+ "fetchStreamWithContextMessage",
283
+ startedAt,
284
+ );
285
+ } catch (error) {
286
+ this.logCallFailure("fetchStreamWithContextMessage", startedAt, error);
287
+ throw error;
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Wraps an LLMStream so the completion log covers the whole stream
293
+ * lifetime, not just the initial request.
294
+ */
295
+ private withStreamLogging(
296
+ stream: LLMStream,
297
+ method: string,
298
+ startedAt: number,
299
+ ): LLMStream {
300
+ const logSuccess = () => this.logCallSuccess(method, startedAt);
301
+ const logFailure = (error: unknown) =>
302
+ this.logCallFailure(method, startedAt, error);
303
+ return {
304
+ ...stream,
305
+ async *[Symbol.asyncIterator](): AsyncIterator<StreamChunk> {
306
+ try {
307
+ yield* stream;
308
+ logSuccess();
309
+ } catch (error) {
310
+ logFailure(error);
311
+ throw error;
312
+ }
313
+ },
314
+ };
204
315
  }
205
316
 
206
317
  // NOTE(yoojin): Need to switch API Stream type to LLMStream.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainetwork/adk-provider-model-azure",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "author": "AI Network (https://ainetwork.ai)",
5
5
  "type": "module",
6
6
  "engines": {
@@ -24,7 +24,7 @@
24
24
  "openai": "^6.9.1"
25
25
  },
26
26
  "peerDependencies": {
27
- "@ainetwork/adk": "^0.8.1"
27
+ "@ainetwork/adk": "^0.8.2"
28
28
  },
29
29
  "devDependencies": {
30
30
  "typescript": "^5.0.0"
@@ -33,5 +33,5 @@
33
33
  "publishConfig": {
34
34
  "access": "public"
35
35
  },
36
- "gitHead": "0311126e46aa284e104723bcc5141f4072ba00be"
36
+ "gitHead": "abcf4759a1c143abcacc59dfbf2f1b8aa0f9f17b"
37
37
  }
@@ -0,0 +1,265 @@
1
+ import { loggers } from "@ainetwork/adk/utils/logger";
2
+ import type { ChatCompletionMessageParam } from "openai/resources";
3
+ import { AzureOpenAI } from "../index";
4
+
5
+ const mockCreate = jest.fn();
6
+
7
+ jest.mock("openai", () => ({
8
+ AzureOpenAI: jest.fn().mockImplementation(() => ({
9
+ chat: { completions: { create: mockCreate } },
10
+ })),
11
+ }));
12
+
13
+ const MockedClient = jest.requireMock("openai").AzureOpenAI as jest.Mock;
14
+
15
+ function makeProvider(extra?: {
16
+ timeout?: number;
17
+ maxRetries?: number;
18
+ }): AzureOpenAI {
19
+ return new AzureOpenAI({
20
+ endpoint: "https://example.azure.test",
21
+ apiKey: "test-key",
22
+ apiVersion: "2024-10-21",
23
+ modelName: "gpt-test",
24
+ ...extra,
25
+ });
26
+ }
27
+
28
+ function textResponse(content: string | null) {
29
+ return { choices: [{ message: { content } }] };
30
+ }
31
+
32
+ async function* fakeStream() {
33
+ yield {
34
+ id: "c1",
35
+ model: "gpt-test",
36
+ choices: [
37
+ { delta: { role: "assistant", content: "hi" }, finish_reason: null },
38
+ ],
39
+ };
40
+ yield {
41
+ id: "c1",
42
+ model: "gpt-test",
43
+ choices: [{ delta: {}, finish_reason: "stop" }],
44
+ };
45
+ }
46
+
47
+ const userMessages: ChatCompletionMessageParam[] = [
48
+ { role: "user", content: "hello" },
49
+ ];
50
+
51
+ beforeEach(() => {
52
+ mockCreate.mockReset();
53
+ MockedClient.mockClear();
54
+ });
55
+
56
+ describe("AzureOpenAI client configuration", () => {
57
+ it("passes default timeout (120000ms) and maxRetries (1) to the client", () => {
58
+ makeProvider();
59
+ expect(MockedClient).toHaveBeenCalledWith(
60
+ expect.objectContaining({ timeout: 120_000, maxRetries: 1 }),
61
+ );
62
+ });
63
+
64
+ it("honors configured timeout and maxRetries", () => {
65
+ makeProvider({ timeout: 5_000, maxRetries: 3 });
66
+ expect(MockedClient).toHaveBeenCalledWith(
67
+ expect.objectContaining({ timeout: 5_000, maxRetries: 3 }),
68
+ );
69
+ });
70
+ });
71
+
72
+ describe("AzureOpenAI empty-tools guard", () => {
73
+ it("omits tools/tool_choice on the non-streaming path when functions are empty", async () => {
74
+ mockCreate.mockResolvedValue(textResponse("ok"));
75
+ const provider = makeProvider();
76
+
77
+ await provider.fetchWithContextMessage(userMessages, []);
78
+
79
+ expect(mockCreate).toHaveBeenCalledTimes(1);
80
+ const payload = mockCreate.mock.calls[0][0];
81
+ expect(payload).not.toHaveProperty("tools");
82
+ expect(payload).not.toHaveProperty("tool_choice");
83
+ });
84
+
85
+ it("omits tools/tool_choice on the streaming path when functions are empty", async () => {
86
+ mockCreate.mockResolvedValue(fakeStream());
87
+ const provider = makeProvider();
88
+
89
+ await provider.fetchStreamWithContextMessage(userMessages, []);
90
+
91
+ const payload = mockCreate.mock.calls[0][0];
92
+ expect(payload).not.toHaveProperty("tools");
93
+ expect(payload).not.toHaveProperty("tool_choice");
94
+ expect(payload.stream).toBe(true);
95
+ });
96
+
97
+ it("still sends tools/tool_choice on the streaming path when functions exist", async () => {
98
+ mockCreate.mockResolvedValue(fakeStream());
99
+ const provider = makeProvider();
100
+ const tools = provider.convertToolsToFunctions([
101
+ { toolName: "t1", description: "d", inputSchema: { type: "object" } },
102
+ // biome-ignore lint/suspicious/noExplicitAny: minimal test fixture
103
+ ] as any);
104
+
105
+ await provider.fetchStreamWithContextMessage(userMessages, tools);
106
+
107
+ const payload = mockCreate.mock.calls[0][0];
108
+ expect(payload.tools).toHaveLength(1);
109
+ expect(payload.tool_choice).toBe("auto");
110
+ });
111
+ });
112
+
113
+ describe("AzureOpenAI tool-call argument parsing", () => {
114
+ it("forwards malformed tool-call JSON as {__raw} instead of throwing", async () => {
115
+ mockCreate.mockResolvedValue({
116
+ choices: [
117
+ {
118
+ message: {
119
+ content: null,
120
+ tool_calls: [
121
+ {
122
+ id: "call_1",
123
+ type: "function",
124
+ function: { name: "search", arguments: "{not json" },
125
+ },
126
+ ],
127
+ },
128
+ },
129
+ ],
130
+ });
131
+ const provider = makeProvider();
132
+ const tools = provider.convertToolsToFunctions([
133
+ { toolName: "search", description: "d", inputSchema: { type: "object" } },
134
+ // biome-ignore lint/suspicious/noExplicitAny: minimal test fixture
135
+ ] as any);
136
+
137
+ const result = await provider.fetchWithContextMessage(userMessages, tools);
138
+
139
+ expect(result.toolCalls).toEqual([
140
+ { name: "search", arguments: { __raw: "{not json" } },
141
+ ]);
142
+ });
143
+ });
144
+
145
+ describe("AzureOpenAI options forwarding", () => {
146
+ it("forwards ModelFetchOptions when falling back to fetch on empty functions", async () => {
147
+ mockCreate.mockResolvedValue(textResponse("ok"));
148
+ const provider = makeProvider();
149
+
150
+ await provider.fetchWithContextMessage(userMessages, [], {
151
+ reasoning: "low",
152
+ verbosity: "high",
153
+ });
154
+
155
+ const payload = mockCreate.mock.calls[0][0];
156
+ expect(payload.reasoning_effort).toBe("low");
157
+ expect(payload.verbosity).toBe("high");
158
+ });
159
+ });
160
+
161
+ describe("AzureOpenAI role mapping", () => {
162
+ it("maps MODEL history messages to role 'assistant'", () => {
163
+ const provider = makeProvider();
164
+ const messages = provider.generateMessages({
165
+ query: "next",
166
+ thread: {
167
+ messages: [
168
+ {
169
+ messageId: "m1",
170
+ role: "MODEL",
171
+ timestamp: 1,
172
+ content: { type: "text", parts: ["previous answer"] },
173
+ },
174
+ ],
175
+ // biome-ignore lint/suspicious/noExplicitAny: minimal test fixture
176
+ } as any,
177
+ });
178
+
179
+ const history = messages.find((m) => m.content === "previous answer");
180
+ expect(history?.role).toBe("assistant");
181
+ });
182
+ });
183
+
184
+ describe("AzureOpenAI model-call logging", () => {
185
+ let debugSpy: jest.SpyInstance;
186
+ let infoSpy: jest.SpyInstance;
187
+ let errorSpy: jest.SpyInstance;
188
+
189
+ beforeEach(() => {
190
+ debugSpy = jest.spyOn(loggers.model, "debug").mockImplementation();
191
+ infoSpy = jest.spyOn(loggers.model, "info").mockImplementation();
192
+ errorSpy = jest.spyOn(loggers.model, "error").mockImplementation();
193
+ });
194
+
195
+ afterEach(() => {
196
+ debugSpy.mockRestore();
197
+ infoSpy.mockRestore();
198
+ errorSpy.mockRestore();
199
+ });
200
+
201
+ it("logs start (debug) and completion (info with durationMs) on fetch", async () => {
202
+ mockCreate.mockResolvedValue(textResponse("ok"));
203
+ const provider = makeProvider();
204
+
205
+ await provider.fetch(userMessages);
206
+
207
+ expect(debugSpy).toHaveBeenCalledWith(
208
+ expect.stringContaining("fetch"),
209
+ expect.objectContaining({
210
+ provider: "azure-openai",
211
+ model: "gpt-test",
212
+ messageCount: 1,
213
+ }),
214
+ );
215
+ expect(infoSpy).toHaveBeenCalledWith(
216
+ expect.stringContaining("fetch"),
217
+ expect.objectContaining({ durationMs: expect.any(Number) }),
218
+ );
219
+ });
220
+
221
+ it("never logs message contents or the apiKey", async () => {
222
+ mockCreate.mockResolvedValue(textResponse("ok"));
223
+ const provider = makeProvider();
224
+
225
+ await provider.fetch([{ role: "user", content: "SECRET-CONTENT" }]);
226
+
227
+ const allMeta = JSON.stringify([
228
+ ...debugSpy.mock.calls,
229
+ ...infoSpy.mock.calls,
230
+ ]);
231
+ expect(allMeta).not.toContain("SECRET-CONTENT");
232
+ expect(allMeta).not.toContain("test-key");
233
+ });
234
+
235
+ it("logs an error with the failure message and rethrows", async () => {
236
+ mockCreate.mockRejectedValue(new Error("boom"));
237
+ const provider = makeProvider();
238
+
239
+ await expect(provider.fetch(userMessages)).rejects.toThrow("boom");
240
+ expect(errorSpy).toHaveBeenCalledWith(
241
+ expect.any(String),
242
+ expect.objectContaining({ error: "boom" }),
243
+ );
244
+ });
245
+
246
+ it("logs stream completion only after the stream is fully consumed", async () => {
247
+ mockCreate.mockResolvedValue(fakeStream());
248
+ const provider = makeProvider();
249
+
250
+ const stream = await provider.fetchStreamWithContextMessage(
251
+ userMessages,
252
+ [],
253
+ );
254
+ expect(infoSpy).not.toHaveBeenCalled();
255
+
256
+ for await (const _chunk of stream) {
257
+ // drain
258
+ }
259
+
260
+ expect(infoSpy).toHaveBeenCalledWith(
261
+ expect.any(String),
262
+ expect.objectContaining({ durationMs: expect.any(Number) }),
263
+ );
264
+ });
265
+ });
package/tsup.config.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { defineConfig } from 'tsup'
1
+ import { defineConfig } from "tsup";
2
2
 
3
3
  export default defineConfig({
4
- entry: ['index.ts'],
5
- format: ['cjs', 'esm'],
6
- dts: true,
7
- sourcemap: true,
8
- clean: true,
9
- })
4
+ entry: ["index.ts"],
5
+ format: ["cjs", "esm"],
6
+ dts: true,
7
+ sourcemap: true,
8
+ clean: true,
9
+ });