@databricks/ai-sdk-provider 0.0.1 → 0.1.1
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/LICENSE +69 -0
- package/NOTICE +16 -0
- package/README.md +167 -1
- package/dist/index.cjs +2046 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +181 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +181 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2009 -0
- package/dist/index.js.map +1 -0
- package/package.json +87 -7
- package/index.js +0 -2
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2046 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
//#region rolldown:runtime
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
const __ai_sdk_provider_utils = __toESM(require("@ai-sdk/provider-utils"));
|
|
26
|
+
const zod_v4 = __toESM(require("zod/v4"));
|
|
27
|
+
const node_crypto = __toESM(require("node:crypto"));
|
|
28
|
+
const __ai_sdk_provider = __toESM(require("@ai-sdk/provider"));
|
|
29
|
+
|
|
30
|
+
//#region src/chat-agent-language-model/chat-agent-schema.ts
|
|
31
|
+
const chatAgentToolCallSchema = zod_v4.z.object({
|
|
32
|
+
type: zod_v4.z.literal("function"),
|
|
33
|
+
function: zod_v4.z.object({
|
|
34
|
+
name: zod_v4.z.string(),
|
|
35
|
+
arguments: zod_v4.z.string()
|
|
36
|
+
}),
|
|
37
|
+
id: zod_v4.z.string()
|
|
38
|
+
});
|
|
39
|
+
const chatAgentAssistantMessageSchema = zod_v4.z.object({
|
|
40
|
+
role: zod_v4.z.literal("assistant"),
|
|
41
|
+
content: zod_v4.z.string(),
|
|
42
|
+
id: zod_v4.z.string(),
|
|
43
|
+
name: zod_v4.z.string().optional(),
|
|
44
|
+
tool_calls: zod_v4.z.array(chatAgentToolCallSchema).optional()
|
|
45
|
+
});
|
|
46
|
+
const chatAgentToolMessageSchema = zod_v4.z.object({
|
|
47
|
+
role: zod_v4.z.literal("tool"),
|
|
48
|
+
name: zod_v4.z.string(),
|
|
49
|
+
content: zod_v4.z.string(),
|
|
50
|
+
tool_call_id: zod_v4.z.string(),
|
|
51
|
+
id: zod_v4.z.string(),
|
|
52
|
+
attachments: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.unknown()).optional()
|
|
53
|
+
});
|
|
54
|
+
const chatAgentUserMessageSchema = zod_v4.z.object({
|
|
55
|
+
role: zod_v4.z.literal("user"),
|
|
56
|
+
content: zod_v4.z.string(),
|
|
57
|
+
id: zod_v4.z.string()
|
|
58
|
+
});
|
|
59
|
+
const chatAgentMessageSchema = zod_v4.z.discriminatedUnion("role", [
|
|
60
|
+
chatAgentAssistantMessageSchema,
|
|
61
|
+
chatAgentToolMessageSchema,
|
|
62
|
+
chatAgentUserMessageSchema
|
|
63
|
+
]);
|
|
64
|
+
const chatAgentChunkSchema = zod_v4.z.object({
|
|
65
|
+
id: zod_v4.z.string(),
|
|
66
|
+
delta: chatAgentMessageSchema
|
|
67
|
+
});
|
|
68
|
+
const chatAgentResponseSchema = zod_v4.z.object({
|
|
69
|
+
id: zod_v4.z.string(),
|
|
70
|
+
messages: zod_v4.z.array(chatAgentMessageSchema)
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/tools.ts
|
|
75
|
+
const DATABRICKS_TOOL_CALL_ID = "databricks-tool-call";
|
|
76
|
+
/**
|
|
77
|
+
* The AI-SDK requires that tools used by the model are defined ahead of time.
|
|
78
|
+
*
|
|
79
|
+
* Since tool calls can be orchestrated by Databricks' agents we don't know the name, input, or output schemas
|
|
80
|
+
* of the tools until the model is called.
|
|
81
|
+
*
|
|
82
|
+
* In the DatabricksProvider we transform all tool calls to fit this definition, and keep the
|
|
83
|
+
* original name as part of the metadata. This allows us to parse any tool orchestrated by Databricks' agents,
|
|
84
|
+
* while still being able to render the tool call and result in the UI, and pass it back to the model with the correct name.
|
|
85
|
+
*/
|
|
86
|
+
const DATABRICKS_TOOL_DEFINITION = {
|
|
87
|
+
name: DATABRICKS_TOOL_CALL_ID,
|
|
88
|
+
description: "Databricks tool call",
|
|
89
|
+
inputSchema: zod_v4.z.any(),
|
|
90
|
+
outputSchema: zod_v4.z.any()
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/chat-agent-language-model/chat-agent-convert-to-message-parts.ts
|
|
95
|
+
const convertChatAgentChunkToMessagePart = (chunk) => {
|
|
96
|
+
const parts = [];
|
|
97
|
+
if (chunk.delta.role === "assistant") {
|
|
98
|
+
if (chunk.delta.content) parts.push({
|
|
99
|
+
type: "text-delta",
|
|
100
|
+
id: chunk.delta.id,
|
|
101
|
+
delta: chunk.delta.content
|
|
102
|
+
});
|
|
103
|
+
chunk.delta.tool_calls?.forEach((toolCall) => {
|
|
104
|
+
parts.push({
|
|
105
|
+
type: "tool-call",
|
|
106
|
+
toolCallId: toolCall.id,
|
|
107
|
+
input: toolCall.function.arguments,
|
|
108
|
+
toolName: toolCall.function.name
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
} else if (chunk.delta.role === "tool") parts.push({
|
|
112
|
+
type: "tool-result",
|
|
113
|
+
toolCallId: chunk.delta.tool_call_id,
|
|
114
|
+
result: chunk.delta.content,
|
|
115
|
+
toolName: DATABRICKS_TOOL_CALL_ID
|
|
116
|
+
});
|
|
117
|
+
return parts;
|
|
118
|
+
};
|
|
119
|
+
const convertChatAgentResponseToMessagePart = (response) => {
|
|
120
|
+
const parts = [];
|
|
121
|
+
for (const message of response.messages) if (message.role === "assistant") {
|
|
122
|
+
parts.push({
|
|
123
|
+
type: "text",
|
|
124
|
+
text: message.content
|
|
125
|
+
});
|
|
126
|
+
for (const part of message.tool_calls ?? []) parts.push({
|
|
127
|
+
type: "tool-call",
|
|
128
|
+
toolCallId: part.id,
|
|
129
|
+
input: part.function.arguments,
|
|
130
|
+
toolName: part.function.name
|
|
131
|
+
});
|
|
132
|
+
} else if (message.role === "tool") parts.push({
|
|
133
|
+
type: "tool-result",
|
|
134
|
+
toolCallId: message.tool_call_id,
|
|
135
|
+
result: message.content,
|
|
136
|
+
toolName: DATABRICKS_TOOL_CALL_ID
|
|
137
|
+
});
|
|
138
|
+
return parts;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region src/chat-agent-language-model/chat-agent-convert-to-input.ts
|
|
143
|
+
const convertLanguageModelV2PromptToChatAgentResponse = (prompt) => {
|
|
144
|
+
const messages = [];
|
|
145
|
+
let messageIndex = 0;
|
|
146
|
+
for (const msg of prompt) switch (msg.role) {
|
|
147
|
+
case "system": break;
|
|
148
|
+
case "user": {
|
|
149
|
+
const converted = convertUserMessage(msg, messageIndex);
|
|
150
|
+
messages.push(converted);
|
|
151
|
+
messageIndex++;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
case "assistant": {
|
|
155
|
+
const converted = convertAssistantMessage(msg, messageIndex);
|
|
156
|
+
messages.push(...converted);
|
|
157
|
+
messageIndex += converted.length;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
case "tool": {
|
|
161
|
+
const converted = convertToolMessage(msg, messageIndex);
|
|
162
|
+
messages.push(...converted);
|
|
163
|
+
messageIndex += converted.length;
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return messages;
|
|
168
|
+
};
|
|
169
|
+
const convertUserMessage = (msg, messageIndex) => {
|
|
170
|
+
const text = (msg.content ?? []).filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
171
|
+
return {
|
|
172
|
+
role: "user",
|
|
173
|
+
content: text,
|
|
174
|
+
id: `user-${messageIndex}`
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
const convertAssistantMessage = (msg, startIndex) => {
|
|
178
|
+
const messages = [];
|
|
179
|
+
let messageIndex = startIndex;
|
|
180
|
+
const textContent = (msg.content ?? []).filter((part) => part.type === "text" || part.type === "reasoning").map((part) => part.type === "text" ? part.text : part.text).join("\n");
|
|
181
|
+
const toolCalls = (msg.content ?? []).filter((part) => part.type === "tool-call").map((call) => ({
|
|
182
|
+
type: "function",
|
|
183
|
+
id: call.toolCallId,
|
|
184
|
+
function: {
|
|
185
|
+
name: call.toolName,
|
|
186
|
+
arguments: typeof call.input === "string" ? call.input : JSON.stringify(call.input ?? {})
|
|
187
|
+
}
|
|
188
|
+
}));
|
|
189
|
+
messages.push({
|
|
190
|
+
role: "assistant",
|
|
191
|
+
content: textContent,
|
|
192
|
+
id: `assistant-${messageIndex++}`,
|
|
193
|
+
tool_calls: toolCalls.length > 0 ? toolCalls : void 0
|
|
194
|
+
});
|
|
195
|
+
for (const part of msg.content ?? []) if (part.type === "tool-result") messages.push({
|
|
196
|
+
role: "tool",
|
|
197
|
+
name: part.toolName,
|
|
198
|
+
content: convertToolResultOutput(part.output),
|
|
199
|
+
tool_call_id: part.toolCallId,
|
|
200
|
+
id: `tool-${messageIndex++}`
|
|
201
|
+
});
|
|
202
|
+
return messages;
|
|
203
|
+
};
|
|
204
|
+
const convertToolMessage = (msg, startIndex) => {
|
|
205
|
+
const messages = [];
|
|
206
|
+
let messageIndex = startIndex;
|
|
207
|
+
for (const part of msg.content ?? []) if (part.type === "tool-result") messages.push({
|
|
208
|
+
role: "tool",
|
|
209
|
+
name: part.toolName,
|
|
210
|
+
content: convertToolResultOutput(part.output),
|
|
211
|
+
tool_call_id: part.toolCallId,
|
|
212
|
+
id: `tool-${messageIndex++}`
|
|
213
|
+
});
|
|
214
|
+
return messages;
|
|
215
|
+
};
|
|
216
|
+
const convertToolResultOutput = (output) => {
|
|
217
|
+
switch (output.type) {
|
|
218
|
+
case "text":
|
|
219
|
+
case "error-text": return output.value;
|
|
220
|
+
case "json":
|
|
221
|
+
case "error-json": return JSON.stringify(output.value);
|
|
222
|
+
case "content": return output.value.map((p) => p.type === "text" ? p.text : "").filter(Boolean).join("\n");
|
|
223
|
+
default: return "";
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
//#endregion
|
|
228
|
+
//#region src/stream-transformers/compose-stream-part-transformers.ts
|
|
229
|
+
/**
|
|
230
|
+
* Compose an arbitrary number of `DatabricksStreamPartTransformer`s.
|
|
231
|
+
*
|
|
232
|
+
* The returned function has the exact same signature as a normal transformer,
|
|
233
|
+
* but its `out`‑element type is inferred from the **last** transformer you pass
|
|
234
|
+
* in.
|
|
235
|
+
*
|
|
236
|
+
* Runtime behaviour:
|
|
237
|
+
* 1️⃣ Call the first transformer with the supplied `parts` and the
|
|
238
|
+
* caller‑provided `last` (usually `null`).
|
|
239
|
+
* 2️⃣ Take its `out` and `last` and feed them to the next transformer.
|
|
240
|
+
* 3️⃣ …repeat until the last transformer runs.
|
|
241
|
+
* 4️⃣ Return the `out`/`last` of that final transformer.
|
|
242
|
+
*/
|
|
243
|
+
function composeDatabricksStreamPartTransformers(...transformers) {
|
|
244
|
+
return (initialParts, last = null) => {
|
|
245
|
+
let currentParts = initialParts;
|
|
246
|
+
for (const fn of transformers) {
|
|
247
|
+
const result = fn(currentParts, last);
|
|
248
|
+
currentParts = result.out;
|
|
249
|
+
}
|
|
250
|
+
return { out: currentParts };
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
//#endregion
|
|
255
|
+
//#region src/stream-transformers/databricks-delta-boundary.ts
|
|
256
|
+
/**
|
|
257
|
+
* Injects start/end deltas for sequential streams.
|
|
258
|
+
*/
|
|
259
|
+
const applyDeltaBoundaryTransform = (parts, last) => {
|
|
260
|
+
const out = [];
|
|
261
|
+
const lastDeltaType = maybeGetDeltaType(last);
|
|
262
|
+
for (const incoming of parts) {
|
|
263
|
+
const incomingDeltaType = maybeGetDeltaType(incoming);
|
|
264
|
+
const incomingId = getPartId$1(incoming);
|
|
265
|
+
const lastId = getPartId$1(last);
|
|
266
|
+
const incomingMatchesLast = Boolean(isDeltaPart(last) && isDeltaPart(incoming)) && Boolean(lastDeltaType && incomingDeltaType) && Boolean(lastDeltaType === incomingDeltaType) && Boolean(incomingId && lastId && incomingId === lastId);
|
|
267
|
+
if (incomingMatchesLast) {
|
|
268
|
+
out.push(incoming);
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
if (isDeltaPart(last)) out.push({
|
|
272
|
+
type: `${getDeltaType(last)}-end`,
|
|
273
|
+
id: last.id
|
|
274
|
+
});
|
|
275
|
+
if (isDeltaPart(incoming)) {
|
|
276
|
+
out.push({
|
|
277
|
+
type: `${getDeltaType(incoming)}-start`,
|
|
278
|
+
id: incoming.id
|
|
279
|
+
}, incoming);
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
out.push(incoming);
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
return { out };
|
|
286
|
+
};
|
|
287
|
+
const isDeltaIsh = (part) => part?.type.startsWith("text-") || part?.type.startsWith("reasoning-") || false;
|
|
288
|
+
const maybeGetDeltaType = (part) => {
|
|
289
|
+
if (!isDeltaIsh(part)) return null;
|
|
290
|
+
if (part.type.startsWith("text-")) return "text";
|
|
291
|
+
if (part.type.startsWith("reasoning-")) return "reasoning";
|
|
292
|
+
return null;
|
|
293
|
+
};
|
|
294
|
+
const getDeltaType = (part) => {
|
|
295
|
+
if (part.type.startsWith("text-")) return "text";
|
|
296
|
+
if (part.type.startsWith("reasoning-")) return "reasoning";
|
|
297
|
+
throw new Error(`Unknown delta type: ${part.type}`);
|
|
298
|
+
};
|
|
299
|
+
const isDeltaPart = (part) => part?.type === "text-delta" || part?.type === "reasoning-delta";
|
|
300
|
+
const getPartId$1 = (part) => {
|
|
301
|
+
if (part && "id" in part) return part.id;
|
|
302
|
+
return void 0;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region src/stream-transformers/databricks-stream-transformer.ts
|
|
307
|
+
/**
|
|
308
|
+
* Allows stream transformations to be composed together.
|
|
309
|
+
*
|
|
310
|
+
* Currently only used to automatically inject start/end
|
|
311
|
+
* deltas since the APIs does not supply the necessary events.
|
|
312
|
+
*/
|
|
313
|
+
const getDatabricksLanguageModelTransformStream = () => {
|
|
314
|
+
let lastChunk = null;
|
|
315
|
+
const deltaEndByTypeAndId = new Set();
|
|
316
|
+
const transformerStreamParts = composeDatabricksStreamPartTransformers(applyDeltaBoundaryTransform);
|
|
317
|
+
return new TransformStream({
|
|
318
|
+
transform(chunk, controller) {
|
|
319
|
+
const { out } = transformerStreamParts([chunk], lastChunk);
|
|
320
|
+
out.forEach((transformedChunk) => {
|
|
321
|
+
const group = getDeltaGroup(transformedChunk.type);
|
|
322
|
+
const endKey = makeEndKey(getPartId(transformedChunk), group);
|
|
323
|
+
if (endKey && deltaEndByTypeAndId.has(endKey)) return;
|
|
324
|
+
if (transformedChunk.type === "text-end" || transformedChunk.type === "reasoning-end") {
|
|
325
|
+
/**
|
|
326
|
+
* We register when a delta ends.
|
|
327
|
+
* We rely on response.output_item.done chunks to display non streamed data
|
|
328
|
+
* so we need to deduplicate them with their corresponding delta chunks.
|
|
329
|
+
*/
|
|
330
|
+
const endGroup = getDeltaGroup(transformedChunk.type);
|
|
331
|
+
const key = makeEndKey(getPartId(transformedChunk), endGroup);
|
|
332
|
+
if (key) deltaEndByTypeAndId.add(key);
|
|
333
|
+
}
|
|
334
|
+
controller.enqueue(transformedChunk);
|
|
335
|
+
});
|
|
336
|
+
lastChunk = out[out.length - 1] ?? lastChunk;
|
|
337
|
+
},
|
|
338
|
+
flush(controller) {
|
|
339
|
+
if (lastChunk?.type === "text-delta") controller.enqueue({
|
|
340
|
+
type: "text-end",
|
|
341
|
+
id: lastChunk.id
|
|
342
|
+
});
|
|
343
|
+
if (lastChunk?.type === "reasoning-delta") controller.enqueue({
|
|
344
|
+
type: "reasoning-end",
|
|
345
|
+
id: lastChunk.id
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
};
|
|
350
|
+
const getDeltaGroup = (type) => {
|
|
351
|
+
if (type.startsWith("text-")) return "text";
|
|
352
|
+
if (type.startsWith("reasoning-")) return "reasoning";
|
|
353
|
+
return null;
|
|
354
|
+
};
|
|
355
|
+
const getPartId = (part) => {
|
|
356
|
+
if ("id" in part) return part.id;
|
|
357
|
+
return void 0;
|
|
358
|
+
};
|
|
359
|
+
const makeEndKey = (id, group) => id && group ? `${group}:${id}` : null;
|
|
360
|
+
|
|
361
|
+
//#endregion
|
|
362
|
+
//#region src/chat-agent-language-model/chat-agent-language-model.ts
|
|
363
|
+
var DatabricksChatAgentLanguageModel = class {
|
|
364
|
+
specificationVersion = "v2";
|
|
365
|
+
modelId;
|
|
366
|
+
config;
|
|
367
|
+
constructor(modelId, config) {
|
|
368
|
+
this.modelId = modelId;
|
|
369
|
+
this.config = config;
|
|
370
|
+
}
|
|
371
|
+
get provider() {
|
|
372
|
+
return this.config.provider;
|
|
373
|
+
}
|
|
374
|
+
supportedUrls = {};
|
|
375
|
+
async doGenerate(options) {
|
|
376
|
+
const networkArgs = this.getArgs({
|
|
377
|
+
config: this.config,
|
|
378
|
+
options,
|
|
379
|
+
stream: false,
|
|
380
|
+
modelId: this.modelId
|
|
381
|
+
});
|
|
382
|
+
const { value: response } = await (0, __ai_sdk_provider_utils.postJsonToApi)({
|
|
383
|
+
...networkArgs,
|
|
384
|
+
successfulResponseHandler: (0, __ai_sdk_provider_utils.createJsonResponseHandler)(chatAgentResponseSchema),
|
|
385
|
+
failedResponseHandler: (0, __ai_sdk_provider_utils.createJsonErrorResponseHandler)({
|
|
386
|
+
errorSchema: zod_v4.z.any(),
|
|
387
|
+
errorToMessage: (error) => JSON.stringify(error),
|
|
388
|
+
isRetryable: () => false
|
|
389
|
+
})
|
|
390
|
+
});
|
|
391
|
+
return {
|
|
392
|
+
content: convertChatAgentResponseToMessagePart(response),
|
|
393
|
+
finishReason: "stop",
|
|
394
|
+
usage: {
|
|
395
|
+
inputTokens: 0,
|
|
396
|
+
outputTokens: 0,
|
|
397
|
+
totalTokens: 0
|
|
398
|
+
},
|
|
399
|
+
warnings: []
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
async doStream(options) {
|
|
403
|
+
const networkArgs = this.getArgs({
|
|
404
|
+
config: this.config,
|
|
405
|
+
options,
|
|
406
|
+
stream: true,
|
|
407
|
+
modelId: this.modelId
|
|
408
|
+
});
|
|
409
|
+
const { responseHeaders, value: response } = await (0, __ai_sdk_provider_utils.postJsonToApi)({
|
|
410
|
+
...networkArgs,
|
|
411
|
+
failedResponseHandler: (0, __ai_sdk_provider_utils.createJsonErrorResponseHandler)({
|
|
412
|
+
errorSchema: zod_v4.z.any(),
|
|
413
|
+
errorToMessage: (error) => JSON.stringify(error),
|
|
414
|
+
isRetryable: () => false
|
|
415
|
+
}),
|
|
416
|
+
successfulResponseHandler: (0, __ai_sdk_provider_utils.createEventSourceResponseHandler)(chatAgentChunkSchema)
|
|
417
|
+
});
|
|
418
|
+
let finishReason = "unknown";
|
|
419
|
+
return {
|
|
420
|
+
stream: response.pipeThrough(new TransformStream({
|
|
421
|
+
start(controller) {
|
|
422
|
+
controller.enqueue({
|
|
423
|
+
type: "stream-start",
|
|
424
|
+
warnings: []
|
|
425
|
+
});
|
|
426
|
+
},
|
|
427
|
+
transform(chunk, controller) {
|
|
428
|
+
if (options.includeRawChunks) controller.enqueue({
|
|
429
|
+
type: "raw",
|
|
430
|
+
rawValue: chunk.rawValue
|
|
431
|
+
});
|
|
432
|
+
if (!chunk.success) {
|
|
433
|
+
finishReason = "error";
|
|
434
|
+
controller.enqueue({
|
|
435
|
+
type: "error",
|
|
436
|
+
error: chunk.error
|
|
437
|
+
});
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const parts = convertChatAgentChunkToMessagePart(chunk.value);
|
|
441
|
+
for (const part of parts) controller.enqueue(part);
|
|
442
|
+
},
|
|
443
|
+
flush(controller) {
|
|
444
|
+
controller.enqueue({
|
|
445
|
+
type: "finish",
|
|
446
|
+
finishReason,
|
|
447
|
+
usage: {
|
|
448
|
+
inputTokens: 0,
|
|
449
|
+
outputTokens: 0,
|
|
450
|
+
totalTokens: 0
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
})).pipeThrough(getDatabricksLanguageModelTransformStream()),
|
|
455
|
+
request: { body: networkArgs.body },
|
|
456
|
+
response: { headers: responseHeaders }
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
getArgs({ config, options, stream, modelId }) {
|
|
460
|
+
return {
|
|
461
|
+
body: {
|
|
462
|
+
model: modelId,
|
|
463
|
+
stream,
|
|
464
|
+
messages: convertLanguageModelV2PromptToChatAgentResponse(options.prompt)
|
|
465
|
+
},
|
|
466
|
+
url: config.url({ path: "/completions" }),
|
|
467
|
+
headers: (0, __ai_sdk_provider_utils.combineHeaders)(config.headers(), options.headers),
|
|
468
|
+
fetch: config.fetch,
|
|
469
|
+
abortSignal: options.abortSignal
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/responses-agent-language-model/responses-agent-schema.ts
|
|
476
|
+
/**
|
|
477
|
+
* Response schema
|
|
478
|
+
*/
|
|
479
|
+
const responsesAgentMessageSchema = zod_v4.z.object({
|
|
480
|
+
type: zod_v4.z.literal("message"),
|
|
481
|
+
role: zod_v4.z.literal("assistant"),
|
|
482
|
+
id: zod_v4.z.string(),
|
|
483
|
+
content: zod_v4.z.array(zod_v4.z.object({
|
|
484
|
+
type: zod_v4.z.literal("output_text"),
|
|
485
|
+
text: zod_v4.z.string(),
|
|
486
|
+
logprobs: zod_v4.z.unknown().nullish(),
|
|
487
|
+
annotations: zod_v4.z.array(zod_v4.z.discriminatedUnion("type", [zod_v4.z.object({
|
|
488
|
+
type: zod_v4.z.literal("url_citation"),
|
|
489
|
+
start_index: zod_v4.z.number(),
|
|
490
|
+
end_index: zod_v4.z.number(),
|
|
491
|
+
url: zod_v4.z.string(),
|
|
492
|
+
title: zod_v4.z.string()
|
|
493
|
+
})]))
|
|
494
|
+
}))
|
|
495
|
+
});
|
|
496
|
+
const responsesAgentFunctionCallSchema = zod_v4.z.object({
|
|
497
|
+
type: zod_v4.z.literal("function_call"),
|
|
498
|
+
call_id: zod_v4.z.string(),
|
|
499
|
+
name: zod_v4.z.string(),
|
|
500
|
+
arguments: zod_v4.z.string(),
|
|
501
|
+
id: zod_v4.z.string()
|
|
502
|
+
});
|
|
503
|
+
const responsesAgentReasoningSchema = zod_v4.z.object({
|
|
504
|
+
type: zod_v4.z.literal("reasoning"),
|
|
505
|
+
id: zod_v4.z.string(),
|
|
506
|
+
encrypted_content: zod_v4.z.string().nullish(),
|
|
507
|
+
summary: zod_v4.z.array(zod_v4.z.object({
|
|
508
|
+
type: zod_v4.z.literal("summary_text"),
|
|
509
|
+
text: zod_v4.z.string()
|
|
510
|
+
}))
|
|
511
|
+
});
|
|
512
|
+
const responsesAgentFunctionCallOutputSchema = zod_v4.z.object({
|
|
513
|
+
type: zod_v4.z.literal("function_call_output"),
|
|
514
|
+
call_id: zod_v4.z.string(),
|
|
515
|
+
output: zod_v4.z.any()
|
|
516
|
+
});
|
|
517
|
+
const responsesAgentMcpApprovalRequestSchema = zod_v4.z.object({
|
|
518
|
+
type: zod_v4.z.literal("mcp_approval_request"),
|
|
519
|
+
id: zod_v4.z.string(),
|
|
520
|
+
name: zod_v4.z.string(),
|
|
521
|
+
arguments: zod_v4.z.string(),
|
|
522
|
+
server_label: zod_v4.z.string()
|
|
523
|
+
});
|
|
524
|
+
const responsesAgentMcpApprovalResponseSchema = zod_v4.z.object({
|
|
525
|
+
type: zod_v4.z.literal("mcp_approval_response"),
|
|
526
|
+
id: zod_v4.z.string().optional(),
|
|
527
|
+
approval_request_id: zod_v4.z.string(),
|
|
528
|
+
approve: zod_v4.z.boolean(),
|
|
529
|
+
reason: zod_v4.z.string().nullish()
|
|
530
|
+
});
|
|
531
|
+
const responsesAgentOutputItem = zod_v4.z.discriminatedUnion("type", [
|
|
532
|
+
responsesAgentMessageSchema,
|
|
533
|
+
responsesAgentFunctionCallSchema,
|
|
534
|
+
responsesAgentReasoningSchema,
|
|
535
|
+
responsesAgentFunctionCallOutputSchema,
|
|
536
|
+
responsesAgentMcpApprovalRequestSchema,
|
|
537
|
+
responsesAgentMcpApprovalResponseSchema
|
|
538
|
+
]);
|
|
539
|
+
const responsesAgentResponseSchema = zod_v4.z.object({
|
|
540
|
+
id: zod_v4.z.string().optional(),
|
|
541
|
+
created_at: zod_v4.z.number().optional(),
|
|
542
|
+
error: zod_v4.z.object({
|
|
543
|
+
code: zod_v4.z.string(),
|
|
544
|
+
message: zod_v4.z.string()
|
|
545
|
+
}).nullish(),
|
|
546
|
+
model: zod_v4.z.string().optional(),
|
|
547
|
+
output: zod_v4.z.array(responsesAgentOutputItem),
|
|
548
|
+
incomplete_details: zod_v4.z.object({ reason: zod_v4.z.enum(["max_output_tokens", "content_filter"]).optional() }).nullish(),
|
|
549
|
+
usage: zod_v4.z.object({
|
|
550
|
+
input_tokens: zod_v4.z.number(),
|
|
551
|
+
output_tokens: zod_v4.z.number(),
|
|
552
|
+
total_tokens: zod_v4.z.number()
|
|
553
|
+
}).optional()
|
|
554
|
+
});
|
|
555
|
+
/**
|
|
556
|
+
* Chunk schema
|
|
557
|
+
*/
|
|
558
|
+
const textDeltaChunkSchema = zod_v4.z.object({
|
|
559
|
+
type: zod_v4.z.literal("response.output_text.delta"),
|
|
560
|
+
item_id: zod_v4.z.string(),
|
|
561
|
+
delta: zod_v4.z.string(),
|
|
562
|
+
logprobs: zod_v4.z.unknown().nullish()
|
|
563
|
+
});
|
|
564
|
+
const errorChunkSchema = zod_v4.z.object({
|
|
565
|
+
type: zod_v4.z.literal("error"),
|
|
566
|
+
code: zod_v4.z.string(),
|
|
567
|
+
message: zod_v4.z.string(),
|
|
568
|
+
param: zod_v4.z.string().nullish(),
|
|
569
|
+
sequence_number: zod_v4.z.number()
|
|
570
|
+
});
|
|
571
|
+
const simpleErrorChunkSchema = zod_v4.z.object({
|
|
572
|
+
type: zod_v4.z.undefined().optional(),
|
|
573
|
+
error: zod_v4.z.string()
|
|
574
|
+
});
|
|
575
|
+
const responseOutputItemDoneSchema = zod_v4.z.object({
|
|
576
|
+
type: zod_v4.z.literal("response.output_item.done"),
|
|
577
|
+
output_index: zod_v4.z.number(),
|
|
578
|
+
item: responsesAgentOutputItem
|
|
579
|
+
});
|
|
580
|
+
const responseAnnotationAddedSchema = zod_v4.z.object({
|
|
581
|
+
type: zod_v4.z.literal("response.output_text.annotation.added"),
|
|
582
|
+
annotation: zod_v4.z.discriminatedUnion("type", [zod_v4.z.object({
|
|
583
|
+
type: zod_v4.z.literal("url_citation"),
|
|
584
|
+
url: zod_v4.z.string(),
|
|
585
|
+
title: zod_v4.z.string()
|
|
586
|
+
})])
|
|
587
|
+
});
|
|
588
|
+
const responseReasoningSummaryTextDeltaSchema = zod_v4.z.object({
|
|
589
|
+
type: zod_v4.z.literal("response.reasoning_summary_text.delta"),
|
|
590
|
+
item_id: zod_v4.z.string(),
|
|
591
|
+
summary_index: zod_v4.z.number(),
|
|
592
|
+
delta: zod_v4.z.string()
|
|
593
|
+
});
|
|
594
|
+
const responseFunctionCallArgumentsDeltaSchema = zod_v4.z.object({
|
|
595
|
+
type: zod_v4.z.literal("response.function_call_arguments.delta"),
|
|
596
|
+
item_id: zod_v4.z.string(),
|
|
597
|
+
delta: zod_v4.z.string(),
|
|
598
|
+
output_index: zod_v4.z.number(),
|
|
599
|
+
sequence_number: zod_v4.z.number()
|
|
600
|
+
});
|
|
601
|
+
const functionCallOutputChunkSchema = zod_v4.z.object({
|
|
602
|
+
type: zod_v4.z.literal("function_call_output"),
|
|
603
|
+
call_id: zod_v4.z.string(),
|
|
604
|
+
output: zod_v4.z.any()
|
|
605
|
+
});
|
|
606
|
+
const responsesCompletedSchema = zod_v4.z.object({
|
|
607
|
+
type: zod_v4.z.literal("responses.completed"),
|
|
608
|
+
response: zod_v4.z.object({
|
|
609
|
+
id: zod_v4.z.string(),
|
|
610
|
+
status: zod_v4.z.enum([
|
|
611
|
+
"completed",
|
|
612
|
+
"failed",
|
|
613
|
+
"in_progress",
|
|
614
|
+
"cancelled",
|
|
615
|
+
"queued",
|
|
616
|
+
"incomplete"
|
|
617
|
+
]).optional(),
|
|
618
|
+
incomplete_details: zod_v4.z.object({ reason: zod_v4.z.enum(["max_output_tokens", "content_filter"]).optional() }).nullish(),
|
|
619
|
+
usage: zod_v4.z.object({
|
|
620
|
+
input_tokens: zod_v4.z.number(),
|
|
621
|
+
output_tokens: zod_v4.z.number(),
|
|
622
|
+
total_tokens: zod_v4.z.number()
|
|
623
|
+
})
|
|
624
|
+
})
|
|
625
|
+
});
|
|
626
|
+
const responsesAgentChunkSchema = zod_v4.z.union([
|
|
627
|
+
textDeltaChunkSchema,
|
|
628
|
+
responseOutputItemDoneSchema,
|
|
629
|
+
responseAnnotationAddedSchema,
|
|
630
|
+
responseReasoningSummaryTextDeltaSchema,
|
|
631
|
+
responseFunctionCallArgumentsDeltaSchema,
|
|
632
|
+
functionCallOutputChunkSchema,
|
|
633
|
+
errorChunkSchema,
|
|
634
|
+
responsesCompletedSchema,
|
|
635
|
+
simpleErrorChunkSchema
|
|
636
|
+
]);
|
|
637
|
+
/**
|
|
638
|
+
* We use a loose schema for response validation to handle unknown chunks.
|
|
639
|
+
*/
|
|
640
|
+
const looseResponseAgentChunkSchema = zod_v4.z.union([responsesAgentChunkSchema, zod_v4.z.object({ type: zod_v4.z.string() }).loose()]);
|
|
641
|
+
|
|
642
|
+
//#endregion
|
|
643
|
+
//#region src/mcp.ts
|
|
644
|
+
/**
|
|
645
|
+
* MCP Approval Utility Functions
|
|
646
|
+
*
|
|
647
|
+
* Shared utilities for handling MCP (Model Context Protocol) approval requests
|
|
648
|
+
* and responses across client and server code.
|
|
649
|
+
*/
|
|
650
|
+
/** Key used in tool output to indicate approval status */
|
|
651
|
+
const MCP_APPROVAL_STATUS_KEY = "__approvalStatus__";
|
|
652
|
+
/** Type string for MCP approval requests in provider metadata */
|
|
653
|
+
const MCP_APPROVAL_REQUEST_TYPE = "mcp_approval_request";
|
|
654
|
+
/** Type string for MCP approval responses in provider metadata */
|
|
655
|
+
const MCP_APPROVAL_RESPONSE_TYPE = "mcp_approval_response";
|
|
656
|
+
/**
|
|
657
|
+
* Check if output contains an approval status marker.
|
|
658
|
+
*
|
|
659
|
+
* @example
|
|
660
|
+
* if (isApprovalStatusOutput(output)) {
|
|
661
|
+
* console.log(output.__approvalStatus__); // TypeScript knows this is boolean
|
|
662
|
+
* }
|
|
663
|
+
*/
|
|
664
|
+
function isApprovalStatusOutput(output) {
|
|
665
|
+
return typeof output === "object" && output !== null && MCP_APPROVAL_STATUS_KEY in output && typeof output[MCP_APPROVAL_STATUS_KEY] === "boolean";
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Check if provider metadata indicates an MCP approval request.
|
|
669
|
+
*
|
|
670
|
+
* @example
|
|
671
|
+
* const metadata = extractDatabricksMetadata(part);
|
|
672
|
+
* if (isMcpApprovalRequest(metadata)) {
|
|
673
|
+
* // Handle MCP approval request
|
|
674
|
+
* }
|
|
675
|
+
*/
|
|
676
|
+
function isMcpApprovalRequest(metadata) {
|
|
677
|
+
return metadata?.type?.toString() === MCP_APPROVAL_REQUEST_TYPE;
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Check if provider metadata indicates an MCP approval response.
|
|
681
|
+
*/
|
|
682
|
+
function isMcpApprovalResponse(metadata) {
|
|
683
|
+
return metadata?.type?.toString() === MCP_APPROVAL_RESPONSE_TYPE;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Extract Databricks metadata from a tool call part's callProviderMetadata.
|
|
687
|
+
*
|
|
688
|
+
* @example
|
|
689
|
+
* const metadata = extractDatabricksMetadata(part);
|
|
690
|
+
* const toolName = metadata?.toolName;
|
|
691
|
+
* const isMcp = isMcpApprovalRequest(metadata);
|
|
692
|
+
*/
|
|
693
|
+
function extractDatabricksMetadata(part) {
|
|
694
|
+
if ("callProviderMetadata" in part && part.callProviderMetadata?.databricks) return part.callProviderMetadata.databricks;
|
|
695
|
+
return void 0;
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Extract the approval status boolean from an output object.
|
|
699
|
+
*
|
|
700
|
+
* @returns `true` if approved, `false` if denied, `undefined` if not an approval output
|
|
701
|
+
*
|
|
702
|
+
* @example
|
|
703
|
+
* const status = extractApprovalStatus(output);
|
|
704
|
+
* if (status !== undefined) {
|
|
705
|
+
* console.log(status ? 'Approved' : 'Denied');
|
|
706
|
+
* }
|
|
707
|
+
*/
|
|
708
|
+
function extractApprovalStatus(output) {
|
|
709
|
+
if (isApprovalStatusOutput(output)) return output[MCP_APPROVAL_STATUS_KEY];
|
|
710
|
+
return void 0;
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Extract approval status from a tool result's output value.
|
|
714
|
+
* Handles the nested structure where output.type === 'json' and value contains the status.
|
|
715
|
+
*
|
|
716
|
+
* @example
|
|
717
|
+
* const status = extractApprovalStatusFromToolResult(toolResult.output);
|
|
718
|
+
*/
|
|
719
|
+
function extractApprovalStatusFromToolResult(output) {
|
|
720
|
+
if (output.type === "json" && output.value && typeof output.value === "object" && MCP_APPROVAL_STATUS_KEY in output.value) {
|
|
721
|
+
const value = output.value[MCP_APPROVAL_STATUS_KEY];
|
|
722
|
+
if (typeof value === "boolean") return value;
|
|
723
|
+
}
|
|
724
|
+
return void 0;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Create an approval status output object.
|
|
728
|
+
*
|
|
729
|
+
* @example
|
|
730
|
+
* await addToolResult({
|
|
731
|
+
* toolCallId,
|
|
732
|
+
* output: createApprovalStatusOutput(true), // Approve
|
|
733
|
+
* });
|
|
734
|
+
*/
|
|
735
|
+
function createApprovalStatusOutput(approve) {
|
|
736
|
+
return { [MCP_APPROVAL_STATUS_KEY]: approve };
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Determine the MCP approval state from a tool output.
|
|
740
|
+
*
|
|
741
|
+
* Logic:
|
|
742
|
+
* - No output → 'awaiting-approval' (user hasn't responded yet)
|
|
743
|
+
* - Output with __approvalStatus__: true → 'approved'
|
|
744
|
+
* - Output with __approvalStatus__: false → 'denied'
|
|
745
|
+
* - Output without __approvalStatus__ → 'approved' (tool executed, so it was approved)
|
|
746
|
+
*
|
|
747
|
+
* @example
|
|
748
|
+
* const approvalState = getMcpApprovalState(part.output);
|
|
749
|
+
* // 'awaiting-approval' | 'approved' | 'denied'
|
|
750
|
+
*/
|
|
751
|
+
function getMcpApprovalState(output) {
|
|
752
|
+
if (!output) return "awaiting-approval";
|
|
753
|
+
const status = extractApprovalStatus(output);
|
|
754
|
+
if (status === void 0) return "approved";
|
|
755
|
+
return status ? "approved" : "denied";
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
//#endregion
|
|
759
|
+
//#region src/responses-agent-language-model/responses-convert-to-message-parts.ts
|
|
760
|
+
const convertResponsesAgentChunkToMessagePart = (chunk) => {
|
|
761
|
+
const parts = [];
|
|
762
|
+
if ("error" in chunk) {
|
|
763
|
+
parts.push({
|
|
764
|
+
type: "error",
|
|
765
|
+
error: chunk.error
|
|
766
|
+
});
|
|
767
|
+
return parts;
|
|
768
|
+
}
|
|
769
|
+
switch (chunk.type) {
|
|
770
|
+
case "response.output_text.delta":
|
|
771
|
+
parts.push({
|
|
772
|
+
type: "text-delta",
|
|
773
|
+
id: chunk.item_id,
|
|
774
|
+
delta: chunk.delta,
|
|
775
|
+
providerMetadata: { databricks: { itemId: chunk.item_id } }
|
|
776
|
+
});
|
|
777
|
+
break;
|
|
778
|
+
case "response.reasoning_summary_text.delta":
|
|
779
|
+
parts.push({
|
|
780
|
+
type: "reasoning-delta",
|
|
781
|
+
id: chunk.item_id,
|
|
782
|
+
delta: chunk.delta,
|
|
783
|
+
providerMetadata: { databricks: { itemId: chunk.item_id } }
|
|
784
|
+
});
|
|
785
|
+
break;
|
|
786
|
+
case "function_call_output":
|
|
787
|
+
parts.push({
|
|
788
|
+
type: "tool-result",
|
|
789
|
+
toolCallId: chunk.call_id,
|
|
790
|
+
result: chunk.output,
|
|
791
|
+
toolName: DATABRICKS_TOOL_CALL_ID
|
|
792
|
+
});
|
|
793
|
+
break;
|
|
794
|
+
case "response.output_item.done":
|
|
795
|
+
parts.push(...convertOutputItemDone(chunk.item));
|
|
796
|
+
break;
|
|
797
|
+
case "response.output_text.annotation.added":
|
|
798
|
+
parts.push({
|
|
799
|
+
type: "source",
|
|
800
|
+
url: chunk.annotation.url,
|
|
801
|
+
title: chunk.annotation.title,
|
|
802
|
+
id: (0, node_crypto.randomUUID)(),
|
|
803
|
+
sourceType: "url"
|
|
804
|
+
});
|
|
805
|
+
break;
|
|
806
|
+
case "error":
|
|
807
|
+
parts.push({
|
|
808
|
+
type: "error",
|
|
809
|
+
error: chunk
|
|
810
|
+
});
|
|
811
|
+
break;
|
|
812
|
+
default: break;
|
|
813
|
+
}
|
|
814
|
+
return parts;
|
|
815
|
+
};
|
|
816
|
+
const convertOutputItemDone = (item) => {
|
|
817
|
+
switch (item.type) {
|
|
818
|
+
case "message": {
|
|
819
|
+
const firstContent = item.content[0];
|
|
820
|
+
if (!firstContent) return [];
|
|
821
|
+
return [{
|
|
822
|
+
type: "text-delta",
|
|
823
|
+
id: item.id,
|
|
824
|
+
delta: firstContent.text,
|
|
825
|
+
providerMetadata: { databricks: {
|
|
826
|
+
itemId: item.id,
|
|
827
|
+
itemType: "response.output_item.done"
|
|
828
|
+
} }
|
|
829
|
+
}];
|
|
830
|
+
}
|
|
831
|
+
case "function_call": return [{
|
|
832
|
+
type: "tool-call",
|
|
833
|
+
toolCallId: item.call_id,
|
|
834
|
+
toolName: DATABRICKS_TOOL_CALL_ID,
|
|
835
|
+
input: item.arguments,
|
|
836
|
+
providerMetadata: { databricks: {
|
|
837
|
+
toolName: item.name,
|
|
838
|
+
itemId: item.id
|
|
839
|
+
} }
|
|
840
|
+
}];
|
|
841
|
+
case "function_call_output": return [{
|
|
842
|
+
type: "tool-result",
|
|
843
|
+
toolCallId: item.call_id,
|
|
844
|
+
result: item.output,
|
|
845
|
+
toolName: DATABRICKS_TOOL_CALL_ID
|
|
846
|
+
}];
|
|
847
|
+
case "reasoning": {
|
|
848
|
+
const firstSummary = item.summary[0];
|
|
849
|
+
if (!firstSummary) return [];
|
|
850
|
+
return [
|
|
851
|
+
{
|
|
852
|
+
type: "reasoning-start",
|
|
853
|
+
id: item.id
|
|
854
|
+
},
|
|
855
|
+
{
|
|
856
|
+
type: "reasoning-delta",
|
|
857
|
+
id: item.id,
|
|
858
|
+
delta: firstSummary.text,
|
|
859
|
+
providerMetadata: { databricks: { itemId: item.id } }
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
type: "reasoning-end",
|
|
863
|
+
id: item.id
|
|
864
|
+
}
|
|
865
|
+
];
|
|
866
|
+
}
|
|
867
|
+
case "mcp_approval_request": return [{
|
|
868
|
+
type: "tool-call",
|
|
869
|
+
toolCallId: item.id,
|
|
870
|
+
toolName: DATABRICKS_TOOL_CALL_ID,
|
|
871
|
+
input: item.arguments,
|
|
872
|
+
providerMetadata: { databricks: {
|
|
873
|
+
type: MCP_APPROVAL_REQUEST_TYPE,
|
|
874
|
+
toolName: item.name,
|
|
875
|
+
itemId: item.id,
|
|
876
|
+
serverLabel: item.server_label
|
|
877
|
+
} }
|
|
878
|
+
}];
|
|
879
|
+
case "mcp_approval_response": return [{
|
|
880
|
+
type: "tool-result",
|
|
881
|
+
toolCallId: item.approval_request_id,
|
|
882
|
+
toolName: DATABRICKS_TOOL_CALL_ID,
|
|
883
|
+
result: createApprovalStatusOutput(item.approve),
|
|
884
|
+
providerMetadata: { databricks: {
|
|
885
|
+
type: MCP_APPROVAL_RESPONSE_TYPE,
|
|
886
|
+
...item.id != null && { itemId: item.id }
|
|
887
|
+
} }
|
|
888
|
+
}];
|
|
889
|
+
default: return [];
|
|
890
|
+
}
|
|
891
|
+
};
|
|
892
|
+
const convertResponsesAgentResponseToMessagePart = (response) => {
|
|
893
|
+
const parts = [];
|
|
894
|
+
for (const output of response.output) switch (output.type) {
|
|
895
|
+
case "message": {
|
|
896
|
+
for (const content of output.content) if (content.type === "output_text") parts.push({
|
|
897
|
+
type: "text",
|
|
898
|
+
text: content.text,
|
|
899
|
+
providerMetadata: { databricks: { itemId: output.id } }
|
|
900
|
+
});
|
|
901
|
+
break;
|
|
902
|
+
}
|
|
903
|
+
case "function_call":
|
|
904
|
+
parts.push({
|
|
905
|
+
type: "tool-call",
|
|
906
|
+
toolCallId: output.call_id,
|
|
907
|
+
toolName: output.name,
|
|
908
|
+
input: output.arguments,
|
|
909
|
+
providerMetadata: { databricks: { itemId: output.id } }
|
|
910
|
+
});
|
|
911
|
+
break;
|
|
912
|
+
case "reasoning":
|
|
913
|
+
for (const summary of output.summary) if (summary.type === "summary_text") parts.push({
|
|
914
|
+
type: "reasoning",
|
|
915
|
+
text: summary.text,
|
|
916
|
+
providerMetadata: { databricks: { itemId: output.id } }
|
|
917
|
+
});
|
|
918
|
+
break;
|
|
919
|
+
case "function_call_output":
|
|
920
|
+
parts.push({
|
|
921
|
+
type: "tool-result",
|
|
922
|
+
result: output.output,
|
|
923
|
+
toolCallId: output.call_id,
|
|
924
|
+
toolName: DATABRICKS_TOOL_CALL_ID
|
|
925
|
+
});
|
|
926
|
+
break;
|
|
927
|
+
case "mcp_approval_request":
|
|
928
|
+
parts.push({
|
|
929
|
+
type: "tool-call",
|
|
930
|
+
toolCallId: output.id,
|
|
931
|
+
toolName: DATABRICKS_TOOL_CALL_ID,
|
|
932
|
+
input: output.arguments,
|
|
933
|
+
providerMetadata: { databricks: {
|
|
934
|
+
type: MCP_APPROVAL_REQUEST_TYPE,
|
|
935
|
+
toolName: output.name,
|
|
936
|
+
itemId: output.id,
|
|
937
|
+
serverLabel: output.server_label
|
|
938
|
+
} }
|
|
939
|
+
});
|
|
940
|
+
break;
|
|
941
|
+
case "mcp_approval_response":
|
|
942
|
+
parts.push({
|
|
943
|
+
type: "tool-result",
|
|
944
|
+
toolCallId: output.approval_request_id,
|
|
945
|
+
toolName: DATABRICKS_TOOL_CALL_ID,
|
|
946
|
+
result: createApprovalStatusOutput(output.approve),
|
|
947
|
+
providerMetadata: { databricks: {
|
|
948
|
+
type: MCP_APPROVAL_RESPONSE_TYPE,
|
|
949
|
+
...output.id != null && { itemId: output.id }
|
|
950
|
+
} }
|
|
951
|
+
});
|
|
952
|
+
break;
|
|
953
|
+
default: break;
|
|
954
|
+
}
|
|
955
|
+
return parts;
|
|
956
|
+
};
|
|
957
|
+
|
|
958
|
+
//#endregion
|
|
959
|
+
//#region src/responses-agent-language-model/responses-convert-to-input.ts
|
|
960
|
+
async function convertToResponsesInput({ prompt, systemMessageMode }) {
|
|
961
|
+
const input = [];
|
|
962
|
+
const warnings = [];
|
|
963
|
+
const toolCallResultsByToolCallId = prompt.filter((p) => p.role === "tool").flatMap((p) => p.content).reduce((reduction, toolCallResult) => {
|
|
964
|
+
if (toolCallResult.type === "tool-result") reduction[toolCallResult.toolCallId] = toolCallResult;
|
|
965
|
+
return reduction;
|
|
966
|
+
}, {});
|
|
967
|
+
for (const { role, content } of prompt) switch (role) {
|
|
968
|
+
case "system": {
|
|
969
|
+
switch (systemMessageMode) {
|
|
970
|
+
case "system":
|
|
971
|
+
input.push({
|
|
972
|
+
role: "system",
|
|
973
|
+
content
|
|
974
|
+
});
|
|
975
|
+
break;
|
|
976
|
+
case "developer":
|
|
977
|
+
input.push({
|
|
978
|
+
role: "developer",
|
|
979
|
+
content
|
|
980
|
+
});
|
|
981
|
+
break;
|
|
982
|
+
case "remove":
|
|
983
|
+
warnings.push({
|
|
984
|
+
type: "other",
|
|
985
|
+
message: "system messages are removed for this model"
|
|
986
|
+
});
|
|
987
|
+
break;
|
|
988
|
+
default: {
|
|
989
|
+
const _exhaustiveCheck = systemMessageMode;
|
|
990
|
+
throw new Error(`Unsupported system message mode: ${String(_exhaustiveCheck)}`);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
break;
|
|
994
|
+
}
|
|
995
|
+
case "user":
|
|
996
|
+
input.push({
|
|
997
|
+
role: "user",
|
|
998
|
+
content: content.map((part) => {
|
|
999
|
+
switch (part.type) {
|
|
1000
|
+
case "text": return {
|
|
1001
|
+
type: "input_text",
|
|
1002
|
+
text: part.text
|
|
1003
|
+
};
|
|
1004
|
+
default: throw new __ai_sdk_provider.UnsupportedFunctionalityError({ functionality: `part ${JSON.stringify(part)}` });
|
|
1005
|
+
}
|
|
1006
|
+
})
|
|
1007
|
+
});
|
|
1008
|
+
break;
|
|
1009
|
+
case "assistant":
|
|
1010
|
+
for (const part of content) {
|
|
1011
|
+
const providerOptions = await (0, __ai_sdk_provider_utils.parseProviderOptions)({
|
|
1012
|
+
provider: "databricks",
|
|
1013
|
+
providerOptions: part.providerOptions,
|
|
1014
|
+
schema: ProviderOptionsSchema
|
|
1015
|
+
});
|
|
1016
|
+
const itemId = providerOptions?.itemId ?? void 0;
|
|
1017
|
+
switch (part.type) {
|
|
1018
|
+
case "text": {
|
|
1019
|
+
input.push({
|
|
1020
|
+
role: "assistant",
|
|
1021
|
+
content: [{
|
|
1022
|
+
type: "output_text",
|
|
1023
|
+
text: part.text
|
|
1024
|
+
}],
|
|
1025
|
+
id: itemId
|
|
1026
|
+
});
|
|
1027
|
+
break;
|
|
1028
|
+
}
|
|
1029
|
+
case "tool-call": {
|
|
1030
|
+
const toolName = providerOptions?.toolName ?? part.toolName;
|
|
1031
|
+
if (providerOptions?.type === MCP_APPROVAL_REQUEST_TYPE) {
|
|
1032
|
+
const serverLabel = providerOptions?.serverLabel ?? "";
|
|
1033
|
+
const argumentsString = JSON.stringify(part.input);
|
|
1034
|
+
const id = part.toolCallId;
|
|
1035
|
+
input.push({
|
|
1036
|
+
type: MCP_APPROVAL_REQUEST_TYPE,
|
|
1037
|
+
id,
|
|
1038
|
+
name: toolName,
|
|
1039
|
+
arguments: argumentsString,
|
|
1040
|
+
server_label: serverLabel
|
|
1041
|
+
});
|
|
1042
|
+
const toolResult = toolCallResultsByToolCallId[part.toolCallId];
|
|
1043
|
+
if (toolResult) {
|
|
1044
|
+
/**
|
|
1045
|
+
* The tool call result is either the approval status or the actual output from the tool call.
|
|
1046
|
+
* If it's the approval status, we need to add an approval response part.
|
|
1047
|
+
* If it's the tool call output, we don't include the approval response part but we do include the tool call output part.
|
|
1048
|
+
*/
|
|
1049
|
+
const approvalStatus = extractApprovalStatusFromToolResult(toolResult.output);
|
|
1050
|
+
if (approvalStatus !== void 0) input.push({
|
|
1051
|
+
type: MCP_APPROVAL_RESPONSE_TYPE,
|
|
1052
|
+
id: toolResult.toolCallId,
|
|
1053
|
+
approval_request_id: toolResult.toolCallId,
|
|
1054
|
+
approve: approvalStatus
|
|
1055
|
+
});
|
|
1056
|
+
else input.push({
|
|
1057
|
+
type: "function_call_output",
|
|
1058
|
+
call_id: toolResult.toolCallId,
|
|
1059
|
+
output: convertToolResultOutputToString(toolResult.output)
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
break;
|
|
1063
|
+
}
|
|
1064
|
+
input.push({
|
|
1065
|
+
type: "function_call",
|
|
1066
|
+
call_id: part.toolCallId,
|
|
1067
|
+
name: toolName,
|
|
1068
|
+
arguments: JSON.stringify(part.input),
|
|
1069
|
+
id: itemId
|
|
1070
|
+
});
|
|
1071
|
+
const toolCallResult = toolCallResultsByToolCallId[part.toolCallId];
|
|
1072
|
+
if (toolCallResult) input.push({
|
|
1073
|
+
type: "function_call_output",
|
|
1074
|
+
call_id: part.toolCallId,
|
|
1075
|
+
output: convertToolResultOutputToString(toolCallResult.output)
|
|
1076
|
+
});
|
|
1077
|
+
break;
|
|
1078
|
+
}
|
|
1079
|
+
case "tool-result": {
|
|
1080
|
+
if (providerOptions?.type === MCP_APPROVAL_RESPONSE_TYPE) {
|
|
1081
|
+
const approvalRequestId = providerOptions?.approvalRequestId ?? part.toolCallId;
|
|
1082
|
+
const approve = providerOptions?.approve ?? false;
|
|
1083
|
+
const reason = providerOptions?.reason ?? "";
|
|
1084
|
+
input.push({
|
|
1085
|
+
type: MCP_APPROVAL_RESPONSE_TYPE,
|
|
1086
|
+
id: approvalRequestId,
|
|
1087
|
+
approval_request_id: approvalRequestId,
|
|
1088
|
+
approve,
|
|
1089
|
+
reason
|
|
1090
|
+
});
|
|
1091
|
+
break;
|
|
1092
|
+
}
|
|
1093
|
+
input.push({
|
|
1094
|
+
type: "function_call_output",
|
|
1095
|
+
call_id: part.toolCallId,
|
|
1096
|
+
output: convertToolResultOutputToString(part.output)
|
|
1097
|
+
});
|
|
1098
|
+
break;
|
|
1099
|
+
}
|
|
1100
|
+
case "reasoning": {
|
|
1101
|
+
if (!itemId) break;
|
|
1102
|
+
input.push({
|
|
1103
|
+
type: "reasoning",
|
|
1104
|
+
summary: [{
|
|
1105
|
+
type: "summary_text",
|
|
1106
|
+
text: part.text
|
|
1107
|
+
}],
|
|
1108
|
+
id: itemId
|
|
1109
|
+
});
|
|
1110
|
+
break;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
break;
|
|
1115
|
+
case "tool": break;
|
|
1116
|
+
default: {
|
|
1117
|
+
const _exhaustiveCheck = role;
|
|
1118
|
+
throw new Error(`Unsupported role: ${String(_exhaustiveCheck)}`);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return {
|
|
1122
|
+
input,
|
|
1123
|
+
warnings
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
const ProviderOptionsSchema = zod_v4.z.object({
|
|
1127
|
+
itemId: zod_v4.z.string().nullish(),
|
|
1128
|
+
toolName: zod_v4.z.string().nullish(),
|
|
1129
|
+
type: zod_v4.z.enum(["mcp_approval_request", "mcp_approval_response"]).nullish(),
|
|
1130
|
+
serverLabel: zod_v4.z.string().nullish(),
|
|
1131
|
+
approvalRequestId: zod_v4.z.string().nullish(),
|
|
1132
|
+
approve: zod_v4.z.boolean().nullish(),
|
|
1133
|
+
reason: zod_v4.z.string().nullish()
|
|
1134
|
+
});
|
|
1135
|
+
const convertToolResultOutputToString = (output) => {
|
|
1136
|
+
switch (output.type) {
|
|
1137
|
+
case "text":
|
|
1138
|
+
case "error-text": return output.value;
|
|
1139
|
+
default: return JSON.stringify(output.value);
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
|
|
1143
|
+
//#endregion
|
|
1144
|
+
//#region src/responses-agent-language-model/responses-agent-language-model.ts
|
|
1145
|
+
function mapResponsesFinishReason({ finishReason, hasToolCalls }) {
|
|
1146
|
+
switch (finishReason) {
|
|
1147
|
+
case void 0:
|
|
1148
|
+
case null: return hasToolCalls ? "tool-calls" : "stop";
|
|
1149
|
+
case "max_output_tokens": return "length";
|
|
1150
|
+
case "content_filter": return "content-filter";
|
|
1151
|
+
default: return hasToolCalls ? "tool-calls" : "other";
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
var DatabricksResponsesAgentLanguageModel = class {
|
|
1155
|
+
specificationVersion = "v2";
|
|
1156
|
+
modelId;
|
|
1157
|
+
config;
|
|
1158
|
+
constructor(modelId, config) {
|
|
1159
|
+
this.modelId = modelId;
|
|
1160
|
+
this.config = config;
|
|
1161
|
+
}
|
|
1162
|
+
get provider() {
|
|
1163
|
+
return this.config.provider;
|
|
1164
|
+
}
|
|
1165
|
+
supportedUrls = {};
|
|
1166
|
+
async doGenerate(options) {
|
|
1167
|
+
const networkArgs = await this.getArgs({
|
|
1168
|
+
config: this.config,
|
|
1169
|
+
options,
|
|
1170
|
+
stream: false,
|
|
1171
|
+
modelId: this.modelId
|
|
1172
|
+
});
|
|
1173
|
+
const { value: response } = await (0, __ai_sdk_provider_utils.postJsonToApi)({
|
|
1174
|
+
...networkArgs,
|
|
1175
|
+
successfulResponseHandler: (0, __ai_sdk_provider_utils.createJsonResponseHandler)(responsesAgentResponseSchema),
|
|
1176
|
+
failedResponseHandler: (0, __ai_sdk_provider_utils.createJsonErrorResponseHandler)({
|
|
1177
|
+
errorSchema: zod_v4.z.any(),
|
|
1178
|
+
errorToMessage: (error) => JSON.stringify(error),
|
|
1179
|
+
isRetryable: () => false
|
|
1180
|
+
})
|
|
1181
|
+
});
|
|
1182
|
+
const content = convertResponsesAgentResponseToMessagePart(response);
|
|
1183
|
+
const hasToolCalls = content.some((p) => p.type === "tool-call");
|
|
1184
|
+
return {
|
|
1185
|
+
content,
|
|
1186
|
+
finishReason: mapResponsesFinishReason({
|
|
1187
|
+
finishReason: response.incomplete_details?.reason,
|
|
1188
|
+
hasToolCalls
|
|
1189
|
+
}),
|
|
1190
|
+
usage: {
|
|
1191
|
+
inputTokens: response.usage?.input_tokens ?? 0,
|
|
1192
|
+
outputTokens: response.usage?.output_tokens ?? 0,
|
|
1193
|
+
totalTokens: response.usage?.total_tokens ?? 0
|
|
1194
|
+
},
|
|
1195
|
+
warnings: []
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
async doStream(options) {
|
|
1199
|
+
const networkArgs = await this.getArgs({
|
|
1200
|
+
config: this.config,
|
|
1201
|
+
options,
|
|
1202
|
+
stream: true,
|
|
1203
|
+
modelId: this.modelId
|
|
1204
|
+
});
|
|
1205
|
+
const { responseHeaders, value: response } = await (0, __ai_sdk_provider_utils.postJsonToApi)({
|
|
1206
|
+
...networkArgs,
|
|
1207
|
+
failedResponseHandler: (0, __ai_sdk_provider_utils.createJsonErrorResponseHandler)({
|
|
1208
|
+
errorSchema: zod_v4.z.any(),
|
|
1209
|
+
errorToMessage: (error) => JSON.stringify(error),
|
|
1210
|
+
isRetryable: () => false
|
|
1211
|
+
}),
|
|
1212
|
+
successfulResponseHandler: (0, __ai_sdk_provider_utils.createEventSourceResponseHandler)(looseResponseAgentChunkSchema),
|
|
1213
|
+
abortSignal: options.abortSignal
|
|
1214
|
+
});
|
|
1215
|
+
let finishReason = "unknown";
|
|
1216
|
+
const usage = {
|
|
1217
|
+
inputTokens: 0,
|
|
1218
|
+
outputTokens: 0,
|
|
1219
|
+
totalTokens: 0
|
|
1220
|
+
};
|
|
1221
|
+
const allParts = [];
|
|
1222
|
+
return {
|
|
1223
|
+
stream: response.pipeThrough(new TransformStream({
|
|
1224
|
+
start(controller) {
|
|
1225
|
+
controller.enqueue({
|
|
1226
|
+
type: "stream-start",
|
|
1227
|
+
warnings: []
|
|
1228
|
+
});
|
|
1229
|
+
},
|
|
1230
|
+
transform(chunk, controller) {
|
|
1231
|
+
if (options.includeRawChunks) controller.enqueue({
|
|
1232
|
+
type: "raw",
|
|
1233
|
+
rawValue: chunk.rawValue
|
|
1234
|
+
});
|
|
1235
|
+
if (!chunk.success) {
|
|
1236
|
+
finishReason = "error";
|
|
1237
|
+
controller.enqueue({
|
|
1238
|
+
type: "error",
|
|
1239
|
+
error: chunk.error
|
|
1240
|
+
});
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
if (chunk.value.type === "responses.completed") {
|
|
1244
|
+
const hasToolCalls = allParts.some((p) => p.type === "tool-call");
|
|
1245
|
+
finishReason = mapResponsesFinishReason({
|
|
1246
|
+
finishReason: chunk.value.response.incomplete_details?.reason,
|
|
1247
|
+
hasToolCalls
|
|
1248
|
+
});
|
|
1249
|
+
usage.inputTokens = chunk.value.response.usage.input_tokens;
|
|
1250
|
+
usage.outputTokens = chunk.value.response.usage.output_tokens;
|
|
1251
|
+
usage.totalTokens = chunk.value.response.usage.total_tokens;
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
const parts = convertResponsesAgentChunkToMessagePart(chunk.value);
|
|
1255
|
+
allParts.push(...parts);
|
|
1256
|
+
/**
|
|
1257
|
+
* Check if the last chunk was a tool result without a tool call
|
|
1258
|
+
* This is a special case for MCP approval requests where the tool result
|
|
1259
|
+
* is sent in a separate call after the tool call was approved/denied.
|
|
1260
|
+
*/
|
|
1261
|
+
if (parts.length === 0) return;
|
|
1262
|
+
const part = parts[0];
|
|
1263
|
+
if (part.type === "tool-result") {
|
|
1264
|
+
const matchingToolCallInParts = parts.find((c) => c.type === "tool-call" && c.toolCallId === part.toolCallId);
|
|
1265
|
+
const matchingToolCallInStream = allParts.find((c) => c.type === "tool-call" && c.toolCallId === part.toolCallId);
|
|
1266
|
+
if (!matchingToolCallInParts && !matchingToolCallInStream) {
|
|
1267
|
+
const toolCallFromPreviousMessages = options.prompt.flatMap((message) => {
|
|
1268
|
+
if (typeof message.content === "string") return [];
|
|
1269
|
+
return message.content;
|
|
1270
|
+
}).find((p) => p.type === "tool-call" && p.toolCallId === part.toolCallId);
|
|
1271
|
+
if (!toolCallFromPreviousMessages) throw new Error("No matching tool call found in previous message");
|
|
1272
|
+
if (toolCallFromPreviousMessages.type === "tool-call") controller.enqueue({
|
|
1273
|
+
...toolCallFromPreviousMessages,
|
|
1274
|
+
input: JSON.stringify(toolCallFromPreviousMessages.input)
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
if (shouldDedupeOutputItemDone(parts, allParts.slice(0, -parts.length))) return;
|
|
1279
|
+
for (const part$1 of parts) controller.enqueue(part$1);
|
|
1280
|
+
},
|
|
1281
|
+
flush(controller) {
|
|
1282
|
+
controller.enqueue({
|
|
1283
|
+
type: "finish",
|
|
1284
|
+
finishReason,
|
|
1285
|
+
usage
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
})).pipeThrough(getDatabricksLanguageModelTransformStream()),
|
|
1289
|
+
request: { body: networkArgs.body },
|
|
1290
|
+
response: { headers: responseHeaders }
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
async getArgs({ config, options, stream, modelId }) {
|
|
1294
|
+
const { input } = await convertToResponsesInput({
|
|
1295
|
+
prompt: options.prompt,
|
|
1296
|
+
systemMessageMode: "system"
|
|
1297
|
+
});
|
|
1298
|
+
return {
|
|
1299
|
+
url: config.url({ path: "/responses" }),
|
|
1300
|
+
headers: (0, __ai_sdk_provider_utils.combineHeaders)(config.headers(), options.headers),
|
|
1301
|
+
body: {
|
|
1302
|
+
model: modelId,
|
|
1303
|
+
input,
|
|
1304
|
+
stream
|
|
1305
|
+
},
|
|
1306
|
+
fetch: config.fetch
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
};
|
|
1310
|
+
function shouldDedupeOutputItemDone(incomingParts, previousParts) {
|
|
1311
|
+
const doneTextDelta = incomingParts.find((p) => p.type === "text-delta" && p.providerMetadata?.databricks?.itemType === "response.output_item.done");
|
|
1312
|
+
if (!doneTextDelta || doneTextDelta.type !== "text-delta" || !doneTextDelta.id) return false;
|
|
1313
|
+
/**
|
|
1314
|
+
* To determine if the text in response.output_item.done is a duplicate, we need to reconstruct the text from the
|
|
1315
|
+
* previous consecutive text-deltas and check if the .done text is already present in what we've streamed.
|
|
1316
|
+
*
|
|
1317
|
+
* The caveat is that the response.output_item.done text uses GFM footnote syntax, where as the streamed content
|
|
1318
|
+
* uses response.output_text.delta and response.output_text.annotation.added events. So we reconstruct all the
|
|
1319
|
+
* delta text and check if the .done text is contained in it (meaning we've already streamed it).
|
|
1320
|
+
*
|
|
1321
|
+
* We only consider text-deltas that came AFTER the last response.output_item.done event, since each .done
|
|
1322
|
+
* corresponds to a specific message and we should only compare against text streamed for that message.
|
|
1323
|
+
*/
|
|
1324
|
+
const lastDoneIndex = previousParts.findLastIndex((part) => part.type === "text-delta" && part.providerMetadata?.databricks?.itemType === "response.output_item.done");
|
|
1325
|
+
const partsAfterLastDone = previousParts.slice(lastDoneIndex + 1);
|
|
1326
|
+
const { texts: reconstructuredTexts, current } = partsAfterLastDone.reduce((acc, part) => {
|
|
1327
|
+
if (part.type === "text-delta") return {
|
|
1328
|
+
...acc,
|
|
1329
|
+
current: acc.current + part.delta
|
|
1330
|
+
};
|
|
1331
|
+
else if (acc.current.trim().length > 0) return {
|
|
1332
|
+
texts: [...acc.texts, acc.current.trim()],
|
|
1333
|
+
current: ""
|
|
1334
|
+
};
|
|
1335
|
+
return acc;
|
|
1336
|
+
}, {
|
|
1337
|
+
texts: [],
|
|
1338
|
+
current: ""
|
|
1339
|
+
});
|
|
1340
|
+
reconstructuredTexts.push(current);
|
|
1341
|
+
if (reconstructuredTexts.length === 0) return false;
|
|
1342
|
+
const allTextsFoundInOrder = reconstructuredTexts.reduce((acc, text) => {
|
|
1343
|
+
if (!acc.found) return acc;
|
|
1344
|
+
const index = doneTextDelta.delta.indexOf(text, acc.lastIndex);
|
|
1345
|
+
if (index === -1) return {
|
|
1346
|
+
found: false,
|
|
1347
|
+
lastIndex: acc.lastIndex
|
|
1348
|
+
};
|
|
1349
|
+
return {
|
|
1350
|
+
found: true,
|
|
1351
|
+
lastIndex: index + text.length
|
|
1352
|
+
};
|
|
1353
|
+
}, {
|
|
1354
|
+
found: true,
|
|
1355
|
+
lastIndex: 0
|
|
1356
|
+
});
|
|
1357
|
+
return allTextsFoundInOrder.found;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
//#endregion
|
|
1361
|
+
//#region src/fmapi-language-model/fmapi-schema.ts
|
|
1362
|
+
const toolCallSchema = zod_v4.z.object({
|
|
1363
|
+
id: zod_v4.z.string(),
|
|
1364
|
+
type: zod_v4.z.literal("function"),
|
|
1365
|
+
function: zod_v4.z.object({
|
|
1366
|
+
name: zod_v4.z.string(),
|
|
1367
|
+
arguments: zod_v4.z.string()
|
|
1368
|
+
})
|
|
1369
|
+
});
|
|
1370
|
+
const reasoningSummarySchema = zod_v4.z.discriminatedUnion("type", [zod_v4.z.object({
|
|
1371
|
+
type: zod_v4.z.literal("summary_text"),
|
|
1372
|
+
text: zod_v4.z.string(),
|
|
1373
|
+
signature: zod_v4.z.string().optional()
|
|
1374
|
+
}), zod_v4.z.object({
|
|
1375
|
+
type: zod_v4.z.literal("summary_encrypted_text"),
|
|
1376
|
+
data: zod_v4.z.string()
|
|
1377
|
+
})]);
|
|
1378
|
+
const contentItemSchema = zod_v4.z.discriminatedUnion("type", [
|
|
1379
|
+
zod_v4.z.object({
|
|
1380
|
+
type: zod_v4.z.literal("text"),
|
|
1381
|
+
text: zod_v4.z.string(),
|
|
1382
|
+
citation: zod_v4.z.unknown().optional()
|
|
1383
|
+
}),
|
|
1384
|
+
zod_v4.z.object({
|
|
1385
|
+
type: zod_v4.z.literal("image"),
|
|
1386
|
+
image_url: zod_v4.z.string()
|
|
1387
|
+
}),
|
|
1388
|
+
zod_v4.z.object({
|
|
1389
|
+
type: zod_v4.z.literal("reasoning"),
|
|
1390
|
+
summary: zod_v4.z.array(reasoningSummarySchema)
|
|
1391
|
+
})
|
|
1392
|
+
]);
|
|
1393
|
+
const toolCallDeltaSchema = zod_v4.z.object({
|
|
1394
|
+
index: zod_v4.z.number(),
|
|
1395
|
+
id: zod_v4.z.string().optional(),
|
|
1396
|
+
type: zod_v4.z.literal("function").optional(),
|
|
1397
|
+
function: zod_v4.z.object({
|
|
1398
|
+
name: zod_v4.z.string().optional(),
|
|
1399
|
+
arguments: zod_v4.z.string().optional()
|
|
1400
|
+
}).optional()
|
|
1401
|
+
});
|
|
1402
|
+
const fmapiChunkSchema = zod_v4.z.object({
|
|
1403
|
+
id: zod_v4.z.string(),
|
|
1404
|
+
created: zod_v4.z.number(),
|
|
1405
|
+
model: zod_v4.z.string(),
|
|
1406
|
+
usage: zod_v4.z.object({
|
|
1407
|
+
prompt_tokens: zod_v4.z.number(),
|
|
1408
|
+
completion_tokens: zod_v4.z.number(),
|
|
1409
|
+
total_tokens: zod_v4.z.number()
|
|
1410
|
+
}).nullable().optional(),
|
|
1411
|
+
object: zod_v4.z.literal("chat.completion.chunk"),
|
|
1412
|
+
choices: zod_v4.z.array(zod_v4.z.object({
|
|
1413
|
+
index: zod_v4.z.number(),
|
|
1414
|
+
delta: zod_v4.z.object({
|
|
1415
|
+
role: zod_v4.z.union([
|
|
1416
|
+
zod_v4.z.literal("assistant"),
|
|
1417
|
+
zod_v4.z.null(),
|
|
1418
|
+
zod_v4.z.undefined()
|
|
1419
|
+
]).optional(),
|
|
1420
|
+
content: zod_v4.z.union([
|
|
1421
|
+
zod_v4.z.string(),
|
|
1422
|
+
zod_v4.z.array(contentItemSchema),
|
|
1423
|
+
zod_v4.z.null()
|
|
1424
|
+
]).optional(),
|
|
1425
|
+
tool_calls: zod_v4.z.array(toolCallDeltaSchema).optional()
|
|
1426
|
+
}),
|
|
1427
|
+
finish_reason: zod_v4.z.union([
|
|
1428
|
+
zod_v4.z.literal("stop"),
|
|
1429
|
+
zod_v4.z.literal("tool_calls"),
|
|
1430
|
+
zod_v4.z.null()
|
|
1431
|
+
]).optional()
|
|
1432
|
+
}))
|
|
1433
|
+
});
|
|
1434
|
+
const fmapiResponseSchema = zod_v4.z.object({
|
|
1435
|
+
id: zod_v4.z.string(),
|
|
1436
|
+
created: zod_v4.z.number(),
|
|
1437
|
+
model: zod_v4.z.string(),
|
|
1438
|
+
usage: zod_v4.z.object({
|
|
1439
|
+
prompt_tokens: zod_v4.z.number(),
|
|
1440
|
+
completion_tokens: zod_v4.z.number(),
|
|
1441
|
+
total_tokens: zod_v4.z.number()
|
|
1442
|
+
}).nullable().optional(),
|
|
1443
|
+
choices: zod_v4.z.array(zod_v4.z.object({
|
|
1444
|
+
message: zod_v4.z.object({
|
|
1445
|
+
role: zod_v4.z.union([
|
|
1446
|
+
zod_v4.z.literal("assistant"),
|
|
1447
|
+
zod_v4.z.literal("user"),
|
|
1448
|
+
zod_v4.z.literal("tool")
|
|
1449
|
+
]),
|
|
1450
|
+
content: zod_v4.z.union([
|
|
1451
|
+
zod_v4.z.string(),
|
|
1452
|
+
zod_v4.z.array(contentItemSchema),
|
|
1453
|
+
zod_v4.z.null()
|
|
1454
|
+
]).optional(),
|
|
1455
|
+
tool_calls: zod_v4.z.array(toolCallSchema).optional()
|
|
1456
|
+
}),
|
|
1457
|
+
finish_reason: zod_v4.z.union([
|
|
1458
|
+
zod_v4.z.literal("stop"),
|
|
1459
|
+
zod_v4.z.literal("tool_calls"),
|
|
1460
|
+
zod_v4.z.null()
|
|
1461
|
+
]).optional()
|
|
1462
|
+
}))
|
|
1463
|
+
});
|
|
1464
|
+
|
|
1465
|
+
//#endregion
|
|
1466
|
+
//#region src/fmapi-language-model/fmapi-tags.ts
|
|
1467
|
+
const TAGS = {
|
|
1468
|
+
LEGACY_CALL_OPEN: "<uc_function_call>",
|
|
1469
|
+
LEGACY_CALL_CLOSE: "</uc_function_call>",
|
|
1470
|
+
LEGACY_RESULT_OPEN: "<uc_function_result>",
|
|
1471
|
+
LEGACY_RESULT_CLOSE: "</uc_function_result>",
|
|
1472
|
+
CALL_OPEN: "<tool_call>",
|
|
1473
|
+
CALL_CLOSE: "</tool_call>",
|
|
1474
|
+
RESULT_OPEN: "<tool_call_result>",
|
|
1475
|
+
RESULT_CLOSE: "</tool_call_result>"
|
|
1476
|
+
};
|
|
1477
|
+
const tagSplitRegex = new RegExp(`(${escapeRegex(TAGS.LEGACY_CALL_OPEN)}.*?${escapeRegex(TAGS.LEGACY_CALL_CLOSE)}|${escapeRegex(TAGS.LEGACY_RESULT_OPEN)}.*?${escapeRegex(TAGS.LEGACY_RESULT_CLOSE)}|${escapeRegex(TAGS.CALL_OPEN)}.*?${escapeRegex(TAGS.CALL_CLOSE)}|${escapeRegex(TAGS.RESULT_OPEN)}.*?${escapeRegex(TAGS.RESULT_CLOSE)})`, "g");
|
|
1478
|
+
function parseTaggedToolCall(text) {
|
|
1479
|
+
const inner = stripEnclosingTag(text, TAGS.LEGACY_CALL_OPEN, TAGS.LEGACY_CALL_CLOSE) ?? stripEnclosingTag(text, TAGS.CALL_OPEN, TAGS.CALL_CLOSE);
|
|
1480
|
+
if (inner == null) return null;
|
|
1481
|
+
try {
|
|
1482
|
+
const parsed = JSON.parse(inner);
|
|
1483
|
+
if (parsed && typeof parsed === "object" && "id" in parsed && "name" in parsed) return {
|
|
1484
|
+
id: String(parsed.id),
|
|
1485
|
+
name: String(parsed.name),
|
|
1486
|
+
arguments: parsed.arguments
|
|
1487
|
+
};
|
|
1488
|
+
} catch {}
|
|
1489
|
+
return null;
|
|
1490
|
+
}
|
|
1491
|
+
function parseTaggedToolResult(text) {
|
|
1492
|
+
const inner = stripEnclosingTag(text, TAGS.LEGACY_RESULT_OPEN, TAGS.LEGACY_RESULT_CLOSE) ?? stripEnclosingTag(text, TAGS.RESULT_OPEN, TAGS.RESULT_CLOSE);
|
|
1493
|
+
if (inner == null) return null;
|
|
1494
|
+
try {
|
|
1495
|
+
const parsed = JSON.parse(inner);
|
|
1496
|
+
if (parsed && typeof parsed === "object" && "id" in parsed) return {
|
|
1497
|
+
id: String(parsed.id),
|
|
1498
|
+
content: parsed.content
|
|
1499
|
+
};
|
|
1500
|
+
} catch {}
|
|
1501
|
+
return null;
|
|
1502
|
+
}
|
|
1503
|
+
function serializeToolCall(value) {
|
|
1504
|
+
const payload = JSON.stringify({
|
|
1505
|
+
id: value.id,
|
|
1506
|
+
name: value.name,
|
|
1507
|
+
arguments: value.arguments
|
|
1508
|
+
});
|
|
1509
|
+
return `${TAGS.CALL_OPEN}${payload}${TAGS.CALL_CLOSE}`;
|
|
1510
|
+
}
|
|
1511
|
+
function serializeToolResult(value) {
|
|
1512
|
+
const payload = JSON.stringify({
|
|
1513
|
+
id: value.id,
|
|
1514
|
+
content: value.content
|
|
1515
|
+
});
|
|
1516
|
+
return `${TAGS.RESULT_OPEN}${payload}${TAGS.RESULT_CLOSE}`;
|
|
1517
|
+
}
|
|
1518
|
+
function stripEnclosingTag(text, open, close) {
|
|
1519
|
+
const trimmed = text.trim();
|
|
1520
|
+
if (trimmed.startsWith(open) && trimmed.endsWith(close)) return trimmed.slice(open.length, trimmed.length - close.length);
|
|
1521
|
+
return null;
|
|
1522
|
+
}
|
|
1523
|
+
function escapeRegex(str) {
|
|
1524
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
//#endregion
|
|
1528
|
+
//#region src/fmapi-language-model/fmapi-convert-to-message-parts.ts
|
|
1529
|
+
const convertFmapiChunkToMessagePart = (chunk, toolCallIdsByIndex) => {
|
|
1530
|
+
const parts = [];
|
|
1531
|
+
if (chunk.choices.length === 0) return parts;
|
|
1532
|
+
const choice = chunk.choices[0];
|
|
1533
|
+
if (choice.delta.tool_calls && choice.delta.tool_calls.length > 0) for (const toolCallDelta of choice.delta.tool_calls) {
|
|
1534
|
+
const index = toolCallDelta.index;
|
|
1535
|
+
if (toolCallDelta.id && toolCallDelta.function?.name) {
|
|
1536
|
+
toolCallIdsByIndex?.set(index, toolCallDelta.id);
|
|
1537
|
+
parts.push({
|
|
1538
|
+
type: "tool-input-start",
|
|
1539
|
+
id: toolCallDelta.id,
|
|
1540
|
+
toolName: toolCallDelta.function.name
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
if (toolCallDelta.function?.arguments) {
|
|
1544
|
+
const id = toolCallDelta.id ?? toolCallIdsByIndex?.get(index) ?? `tool-call-${index}`;
|
|
1545
|
+
parts.push({
|
|
1546
|
+
type: "tool-input-delta",
|
|
1547
|
+
id,
|
|
1548
|
+
delta: toolCallDelta.function.arguments
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
if (typeof choice.delta.content === "string") {
|
|
1553
|
+
const extracted = extractPartsFromTextCompletion(choice.delta.content);
|
|
1554
|
+
for (const part of extracted) if (part.type === "text") parts.push({
|
|
1555
|
+
type: "text-delta",
|
|
1556
|
+
id: chunk.id,
|
|
1557
|
+
delta: part.text
|
|
1558
|
+
});
|
|
1559
|
+
else parts.push(part);
|
|
1560
|
+
} else if (Array.isArray(choice.delta.content)) parts.push(...mapContentItemsToStreamParts(choice.delta.content, chunk.id));
|
|
1561
|
+
return parts;
|
|
1562
|
+
};
|
|
1563
|
+
const convertFmapiResponseToMessagePart = (response) => {
|
|
1564
|
+
const parts = [];
|
|
1565
|
+
if (response.choices.length === 0) return parts;
|
|
1566
|
+
const choice = response.choices[0];
|
|
1567
|
+
if (choice.message.tool_calls && choice.message.tool_calls.length > 0) {
|
|
1568
|
+
for (const toolCall of choice.message.tool_calls) parts.push(convertToolCallToContent(toolCall));
|
|
1569
|
+
if (typeof choice.message.content === "string" && choice.message.content) parts.push({
|
|
1570
|
+
type: "text",
|
|
1571
|
+
text: choice.message.content
|
|
1572
|
+
});
|
|
1573
|
+
return parts;
|
|
1574
|
+
}
|
|
1575
|
+
if (typeof choice.message.content === "string") {
|
|
1576
|
+
const extracted = extractToolPartsFromText(choice.message.content);
|
|
1577
|
+
if (extracted) for (const part of extracted) parts.push(part);
|
|
1578
|
+
else parts.push({
|
|
1579
|
+
type: "text",
|
|
1580
|
+
text: choice.message.content
|
|
1581
|
+
});
|
|
1582
|
+
} else parts.push(...mapContentItemsToProviderContent(choice.message.content ?? []));
|
|
1583
|
+
return parts;
|
|
1584
|
+
};
|
|
1585
|
+
const convertToolCallToContent = (toolCall) => {
|
|
1586
|
+
return {
|
|
1587
|
+
type: "tool-call",
|
|
1588
|
+
toolCallId: toolCall.id,
|
|
1589
|
+
toolName: toolCall.function.name,
|
|
1590
|
+
input: toolCall.function.arguments
|
|
1591
|
+
};
|
|
1592
|
+
};
|
|
1593
|
+
const extractPartsFromTextCompletion = (text) => {
|
|
1594
|
+
const parts = text.split(tagSplitRegex);
|
|
1595
|
+
const accumulated = [];
|
|
1596
|
+
for (const segment of parts.filter((p) => p !== "")) {
|
|
1597
|
+
const toolParts = extractToolPartsFromText(segment);
|
|
1598
|
+
if (toolParts) accumulated.push(...toolParts);
|
|
1599
|
+
else accumulated.push({
|
|
1600
|
+
type: "text",
|
|
1601
|
+
text: segment
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
return accumulated;
|
|
1605
|
+
};
|
|
1606
|
+
const extractToolPartsFromText = (text) => {
|
|
1607
|
+
const trimmed = text.trim();
|
|
1608
|
+
const call = parseTaggedToolCall(trimmed);
|
|
1609
|
+
if (call) return [{
|
|
1610
|
+
type: "tool-call",
|
|
1611
|
+
input: typeof call.arguments === "string" ? call.arguments : JSON.stringify(call.arguments),
|
|
1612
|
+
toolName: call.name,
|
|
1613
|
+
toolCallId: call.id,
|
|
1614
|
+
providerExecuted: true
|
|
1615
|
+
}];
|
|
1616
|
+
const result = parseTaggedToolResult(trimmed);
|
|
1617
|
+
if (result) return [{
|
|
1618
|
+
type: "tool-result",
|
|
1619
|
+
result: result.content,
|
|
1620
|
+
toolCallId: result.id,
|
|
1621
|
+
toolName: DATABRICKS_TOOL_CALL_ID
|
|
1622
|
+
}];
|
|
1623
|
+
return null;
|
|
1624
|
+
};
|
|
1625
|
+
const mapContentItemsToStreamParts = (items, id) => {
|
|
1626
|
+
const parts = [];
|
|
1627
|
+
for (const item of items) switch (item.type) {
|
|
1628
|
+
case "text":
|
|
1629
|
+
parts.push({
|
|
1630
|
+
type: "text-delta",
|
|
1631
|
+
id,
|
|
1632
|
+
delta: item.text
|
|
1633
|
+
});
|
|
1634
|
+
break;
|
|
1635
|
+
case "image": break;
|
|
1636
|
+
case "reasoning": {
|
|
1637
|
+
for (const summary of item.summary.filter((s) => s.type === "summary_text")) parts.push({
|
|
1638
|
+
type: "reasoning-delta",
|
|
1639
|
+
id,
|
|
1640
|
+
delta: summary.text
|
|
1641
|
+
});
|
|
1642
|
+
break;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
return parts;
|
|
1646
|
+
};
|
|
1647
|
+
const mapContentItemsToProviderContent = (items) => {
|
|
1648
|
+
const parts = [];
|
|
1649
|
+
for (const item of items) switch (item.type) {
|
|
1650
|
+
case "text":
|
|
1651
|
+
parts.push({
|
|
1652
|
+
type: "text",
|
|
1653
|
+
text: item.text
|
|
1654
|
+
});
|
|
1655
|
+
break;
|
|
1656
|
+
case "image": break;
|
|
1657
|
+
case "reasoning": {
|
|
1658
|
+
for (const summary of item.summary.filter((s) => s.type === "summary_text")) parts.push({
|
|
1659
|
+
type: "reasoning",
|
|
1660
|
+
text: summary.text
|
|
1661
|
+
});
|
|
1662
|
+
break;
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
return parts;
|
|
1666
|
+
};
|
|
1667
|
+
|
|
1668
|
+
//#endregion
|
|
1669
|
+
//#region src/fmapi-language-model/fmapi-convert-to-input.ts
|
|
1670
|
+
const convertPromptToFmapiMessages = (prompt) => {
|
|
1671
|
+
const messages = prompt.map((message) => {
|
|
1672
|
+
const role = message.role === "system" ? "user" : message.role;
|
|
1673
|
+
let contentItems = [];
|
|
1674
|
+
switch (message.role) {
|
|
1675
|
+
case "system":
|
|
1676
|
+
contentItems = convertSystemContent(message);
|
|
1677
|
+
break;
|
|
1678
|
+
case "user":
|
|
1679
|
+
contentItems = convertUserContent(message);
|
|
1680
|
+
break;
|
|
1681
|
+
case "assistant":
|
|
1682
|
+
contentItems = convertAssistantContent(message);
|
|
1683
|
+
break;
|
|
1684
|
+
case "tool":
|
|
1685
|
+
contentItems = convertToolContent(message);
|
|
1686
|
+
break;
|
|
1687
|
+
}
|
|
1688
|
+
const content = contentItems.length === 0 ? "" : contentItems;
|
|
1689
|
+
return {
|
|
1690
|
+
role,
|
|
1691
|
+
content
|
|
1692
|
+
};
|
|
1693
|
+
});
|
|
1694
|
+
return { messages };
|
|
1695
|
+
};
|
|
1696
|
+
const convertSystemContent = (message) => {
|
|
1697
|
+
return [{
|
|
1698
|
+
type: "text",
|
|
1699
|
+
text: message.content
|
|
1700
|
+
}];
|
|
1701
|
+
};
|
|
1702
|
+
const convertUserContent = (message) => {
|
|
1703
|
+
const items = [];
|
|
1704
|
+
for (const part of message.content) switch (part.type) {
|
|
1705
|
+
case "text":
|
|
1706
|
+
items.push({
|
|
1707
|
+
type: "text",
|
|
1708
|
+
text: part.text
|
|
1709
|
+
});
|
|
1710
|
+
break;
|
|
1711
|
+
case "file":
|
|
1712
|
+
if (part.mediaType.startsWith("image/")) {
|
|
1713
|
+
const url = toHttpUrlString(part.data);
|
|
1714
|
+
if (url) items.push({
|
|
1715
|
+
type: "image",
|
|
1716
|
+
image_url: url
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1719
|
+
break;
|
|
1720
|
+
}
|
|
1721
|
+
return items;
|
|
1722
|
+
};
|
|
1723
|
+
const convertAssistantContent = (message) => {
|
|
1724
|
+
const items = [];
|
|
1725
|
+
for (const part of message.content) switch (part.type) {
|
|
1726
|
+
case "text":
|
|
1727
|
+
items.push({
|
|
1728
|
+
type: "text",
|
|
1729
|
+
text: part.text
|
|
1730
|
+
});
|
|
1731
|
+
break;
|
|
1732
|
+
case "file":
|
|
1733
|
+
if (part.mediaType.startsWith("image/")) {
|
|
1734
|
+
const url = toHttpUrlString(part.data);
|
|
1735
|
+
if (url) items.push({
|
|
1736
|
+
type: "image",
|
|
1737
|
+
image_url: url
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
break;
|
|
1741
|
+
case "reasoning":
|
|
1742
|
+
items.push({
|
|
1743
|
+
type: "reasoning",
|
|
1744
|
+
summary: [{
|
|
1745
|
+
type: "summary_text",
|
|
1746
|
+
text: part.text
|
|
1747
|
+
}]
|
|
1748
|
+
});
|
|
1749
|
+
break;
|
|
1750
|
+
case "tool-call":
|
|
1751
|
+
items.push({
|
|
1752
|
+
type: "text",
|
|
1753
|
+
text: serializeToolCall({
|
|
1754
|
+
id: part.toolCallId,
|
|
1755
|
+
name: part.toolName,
|
|
1756
|
+
arguments: part.input
|
|
1757
|
+
})
|
|
1758
|
+
});
|
|
1759
|
+
break;
|
|
1760
|
+
case "tool-result":
|
|
1761
|
+
items.push({
|
|
1762
|
+
type: "text",
|
|
1763
|
+
text: serializeToolResult({
|
|
1764
|
+
id: part.toolCallId,
|
|
1765
|
+
content: convertToolResultOutputToContentValue(part.output)
|
|
1766
|
+
})
|
|
1767
|
+
});
|
|
1768
|
+
break;
|
|
1769
|
+
}
|
|
1770
|
+
return items;
|
|
1771
|
+
};
|
|
1772
|
+
const convertToolContent = (message) => {
|
|
1773
|
+
const items = [];
|
|
1774
|
+
for (const part of message.content) if (part.type === "tool-result") items.push({
|
|
1775
|
+
type: "text",
|
|
1776
|
+
text: serializeToolResult({
|
|
1777
|
+
id: part.toolCallId,
|
|
1778
|
+
content: convertToolResultOutputToContentValue(part.output)
|
|
1779
|
+
})
|
|
1780
|
+
});
|
|
1781
|
+
return items;
|
|
1782
|
+
};
|
|
1783
|
+
const toHttpUrlString = (data) => {
|
|
1784
|
+
if (data instanceof URL) return data.toString();
|
|
1785
|
+
if (typeof data === "string") {
|
|
1786
|
+
if (data.startsWith("http://") || data.startsWith("https://")) return data;
|
|
1787
|
+
}
|
|
1788
|
+
return null;
|
|
1789
|
+
};
|
|
1790
|
+
const convertToolResultOutputToContentValue = (output) => {
|
|
1791
|
+
switch (output.type) {
|
|
1792
|
+
case "text":
|
|
1793
|
+
case "error-text": return output.value;
|
|
1794
|
+
case "json":
|
|
1795
|
+
case "error-json": return output.value;
|
|
1796
|
+
case "content": return output.value;
|
|
1797
|
+
default: return null;
|
|
1798
|
+
}
|
|
1799
|
+
};
|
|
1800
|
+
|
|
1801
|
+
//#endregion
|
|
1802
|
+
//#region src/fmapi-language-model/fmapi-language-model.ts
|
|
1803
|
+
var DatabricksFmapiLanguageModel = class {
|
|
1804
|
+
specificationVersion = "v2";
|
|
1805
|
+
modelId;
|
|
1806
|
+
config;
|
|
1807
|
+
constructor(modelId, config) {
|
|
1808
|
+
this.modelId = modelId;
|
|
1809
|
+
this.config = config;
|
|
1810
|
+
}
|
|
1811
|
+
get provider() {
|
|
1812
|
+
return this.config.provider;
|
|
1813
|
+
}
|
|
1814
|
+
supportedUrls = {};
|
|
1815
|
+
async doGenerate(options) {
|
|
1816
|
+
const networkArgs = this.getArgs({
|
|
1817
|
+
config: this.config,
|
|
1818
|
+
options,
|
|
1819
|
+
stream: false,
|
|
1820
|
+
modelId: this.modelId
|
|
1821
|
+
});
|
|
1822
|
+
const { value: response } = await (0, __ai_sdk_provider_utils.postJsonToApi)({
|
|
1823
|
+
...networkArgs,
|
|
1824
|
+
successfulResponseHandler: (0, __ai_sdk_provider_utils.createJsonResponseHandler)(fmapiResponseSchema),
|
|
1825
|
+
failedResponseHandler: (0, __ai_sdk_provider_utils.createJsonErrorResponseHandler)({
|
|
1826
|
+
errorSchema: zod_v4.z.any(),
|
|
1827
|
+
errorToMessage: (error) => JSON.stringify(error),
|
|
1828
|
+
isRetryable: () => false
|
|
1829
|
+
})
|
|
1830
|
+
});
|
|
1831
|
+
const choice = response.choices[0];
|
|
1832
|
+
let finishReason = "stop";
|
|
1833
|
+
if (choice?.finish_reason === "tool_calls") finishReason = "tool-calls";
|
|
1834
|
+
return {
|
|
1835
|
+
content: convertFmapiResponseToMessagePart(response),
|
|
1836
|
+
finishReason,
|
|
1837
|
+
usage: {
|
|
1838
|
+
inputTokens: response.usage?.prompt_tokens ?? 0,
|
|
1839
|
+
outputTokens: response.usage?.completion_tokens ?? 0,
|
|
1840
|
+
totalTokens: response.usage?.total_tokens ?? 0
|
|
1841
|
+
},
|
|
1842
|
+
warnings: []
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
async doStream(options) {
|
|
1846
|
+
const networkArgs = this.getArgs({
|
|
1847
|
+
config: this.config,
|
|
1848
|
+
options,
|
|
1849
|
+
stream: true,
|
|
1850
|
+
modelId: this.modelId
|
|
1851
|
+
});
|
|
1852
|
+
const { responseHeaders, value: response } = await (0, __ai_sdk_provider_utils.postJsonToApi)({
|
|
1853
|
+
...networkArgs,
|
|
1854
|
+
failedResponseHandler: (0, __ai_sdk_provider_utils.createJsonErrorResponseHandler)({
|
|
1855
|
+
errorSchema: zod_v4.z.any(),
|
|
1856
|
+
errorToMessage: (error) => JSON.stringify(error),
|
|
1857
|
+
isRetryable: () => false
|
|
1858
|
+
}),
|
|
1859
|
+
successfulResponseHandler: (0, __ai_sdk_provider_utils.createEventSourceResponseHandler)(fmapiChunkSchema),
|
|
1860
|
+
abortSignal: options.abortSignal
|
|
1861
|
+
});
|
|
1862
|
+
let finishReason = "unknown";
|
|
1863
|
+
let usage = {
|
|
1864
|
+
inputTokens: 0,
|
|
1865
|
+
outputTokens: 0,
|
|
1866
|
+
totalTokens: 0
|
|
1867
|
+
};
|
|
1868
|
+
const toolCallIdsByIndex = new Map();
|
|
1869
|
+
const toolCallNamesById = new Map();
|
|
1870
|
+
const toolCallInputsById = new Map();
|
|
1871
|
+
return {
|
|
1872
|
+
stream: response.pipeThrough(new TransformStream({
|
|
1873
|
+
start(controller) {
|
|
1874
|
+
controller.enqueue({
|
|
1875
|
+
type: "stream-start",
|
|
1876
|
+
warnings: []
|
|
1877
|
+
});
|
|
1878
|
+
},
|
|
1879
|
+
transform(chunk, controller) {
|
|
1880
|
+
if (options.includeRawChunks) controller.enqueue({
|
|
1881
|
+
type: "raw",
|
|
1882
|
+
rawValue: chunk.rawValue
|
|
1883
|
+
});
|
|
1884
|
+
if (!chunk.success) {
|
|
1885
|
+
finishReason = "error";
|
|
1886
|
+
controller.enqueue({
|
|
1887
|
+
type: "error",
|
|
1888
|
+
error: chunk.error
|
|
1889
|
+
});
|
|
1890
|
+
return;
|
|
1891
|
+
}
|
|
1892
|
+
const choice = chunk.value.choices[0];
|
|
1893
|
+
if (choice?.finish_reason === "stop") finishReason = "stop";
|
|
1894
|
+
else if (choice?.finish_reason === "tool_calls") finishReason = "tool-calls";
|
|
1895
|
+
if (chunk.value.usage) usage = {
|
|
1896
|
+
inputTokens: chunk.value.usage.prompt_tokens,
|
|
1897
|
+
outputTokens: chunk.value.usage.completion_tokens,
|
|
1898
|
+
totalTokens: chunk.value.usage.total_tokens
|
|
1899
|
+
};
|
|
1900
|
+
const parts = convertFmapiChunkToMessagePart(chunk.value, toolCallIdsByIndex);
|
|
1901
|
+
for (const part of parts) {
|
|
1902
|
+
if (part.type === "tool-input-start") {
|
|
1903
|
+
toolCallNamesById.set(part.id, part.toolName);
|
|
1904
|
+
toolCallInputsById.set(part.id, "");
|
|
1905
|
+
} else if (part.type === "tool-input-delta") {
|
|
1906
|
+
const current = toolCallInputsById.get(part.id) ?? "";
|
|
1907
|
+
toolCallInputsById.set(part.id, current + part.delta);
|
|
1908
|
+
}
|
|
1909
|
+
controller.enqueue(part);
|
|
1910
|
+
}
|
|
1911
|
+
},
|
|
1912
|
+
flush(controller) {
|
|
1913
|
+
for (const [toolCallId, inputText] of toolCallInputsById) {
|
|
1914
|
+
const toolName = toolCallNamesById.get(toolCallId);
|
|
1915
|
+
if (toolName) {
|
|
1916
|
+
controller.enqueue({
|
|
1917
|
+
type: "tool-input-end",
|
|
1918
|
+
id: toolCallId
|
|
1919
|
+
});
|
|
1920
|
+
controller.enqueue({
|
|
1921
|
+
type: "tool-call",
|
|
1922
|
+
toolCallId,
|
|
1923
|
+
toolName,
|
|
1924
|
+
input: inputText
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
controller.enqueue({
|
|
1929
|
+
type: "finish",
|
|
1930
|
+
finishReason,
|
|
1931
|
+
usage
|
|
1932
|
+
});
|
|
1933
|
+
}
|
|
1934
|
+
})).pipeThrough(getDatabricksLanguageModelTransformStream()),
|
|
1935
|
+
request: { body: networkArgs.body },
|
|
1936
|
+
response: { headers: responseHeaders }
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
getArgs({ config, options, stream, modelId }) {
|
|
1940
|
+
const tools = options.tools?.map((tool) => convertToolToOpenAIFormat(tool)).filter((tool) => tool !== void 0);
|
|
1941
|
+
const toolChoice = options.toolChoice ? convertToolChoiceToOpenAIFormat(options.toolChoice) : void 0;
|
|
1942
|
+
return {
|
|
1943
|
+
url: config.url({ path: "/chat/completions" }),
|
|
1944
|
+
headers: (0, __ai_sdk_provider_utils.combineHeaders)(config.headers(), options.headers),
|
|
1945
|
+
body: {
|
|
1946
|
+
messages: convertPromptToFmapiMessages(options.prompt).messages,
|
|
1947
|
+
stream,
|
|
1948
|
+
model: modelId,
|
|
1949
|
+
...tools && tools.length > 0 ? { tools } : {},
|
|
1950
|
+
...toolChoice && tools && tools.length > 0 ? { tool_choice: toolChoice } : {},
|
|
1951
|
+
...options.temperature !== void 0 ? { temperature: options.temperature } : {},
|
|
1952
|
+
...options.maxOutputTokens !== void 0 ? { max_tokens: options.maxOutputTokens } : {},
|
|
1953
|
+
...options.stopSequences && options.stopSequences.length > 0 ? { stop: options.stopSequences } : {}
|
|
1954
|
+
},
|
|
1955
|
+
fetch: config.fetch
|
|
1956
|
+
};
|
|
1957
|
+
}
|
|
1958
|
+
};
|
|
1959
|
+
/**
|
|
1960
|
+
* Convert AI SDK tool to OpenAI format
|
|
1961
|
+
*/
|
|
1962
|
+
function convertToolToOpenAIFormat(tool) {
|
|
1963
|
+
if (tool.type === "provider-defined" || tool.name === DATABRICKS_TOOL_CALL_ID) return void 0;
|
|
1964
|
+
return {
|
|
1965
|
+
type: "function",
|
|
1966
|
+
function: {
|
|
1967
|
+
name: tool.name,
|
|
1968
|
+
description: tool.description,
|
|
1969
|
+
parameters: tool.inputSchema
|
|
1970
|
+
}
|
|
1971
|
+
};
|
|
1972
|
+
}
|
|
1973
|
+
/**
|
|
1974
|
+
* Convert AI SDK tool choice to OpenAI format
|
|
1975
|
+
*/
|
|
1976
|
+
function convertToolChoiceToOpenAIFormat(toolChoice) {
|
|
1977
|
+
if (toolChoice.type === "auto") return "auto";
|
|
1978
|
+
if (toolChoice.type === "none") return "none";
|
|
1979
|
+
if (toolChoice.type === "required") return "required";
|
|
1980
|
+
if (toolChoice.type === "tool") return {
|
|
1981
|
+
type: "function",
|
|
1982
|
+
function: { name: toolChoice.toolName }
|
|
1983
|
+
};
|
|
1984
|
+
return "auto";
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
//#endregion
|
|
1988
|
+
//#region src/databricks-provider.ts
|
|
1989
|
+
const createDatabricksProvider = (settings) => {
|
|
1990
|
+
const baseUrl = (0, __ai_sdk_provider_utils.withoutTrailingSlash)(settings.baseURL);
|
|
1991
|
+
const getHeaders = () => (0, __ai_sdk_provider_utils.combineHeaders)(settings.headers);
|
|
1992
|
+
const fetch = settings.fetch;
|
|
1993
|
+
const provider = settings.provider ?? "databricks";
|
|
1994
|
+
const formatUrl = ({ path }) => settings.formatUrl?.({
|
|
1995
|
+
baseUrl,
|
|
1996
|
+
path
|
|
1997
|
+
}) ?? `${baseUrl}${path}`;
|
|
1998
|
+
const createChatAgent = (modelId) => new DatabricksChatAgentLanguageModel(modelId, {
|
|
1999
|
+
url: formatUrl,
|
|
2000
|
+
headers: getHeaders,
|
|
2001
|
+
fetch,
|
|
2002
|
+
provider
|
|
2003
|
+
});
|
|
2004
|
+
const createResponsesAgent = (modelId) => new DatabricksResponsesAgentLanguageModel(modelId, {
|
|
2005
|
+
url: formatUrl,
|
|
2006
|
+
headers: getHeaders,
|
|
2007
|
+
fetch,
|
|
2008
|
+
provider
|
|
2009
|
+
});
|
|
2010
|
+
const createFmapi = (modelId) => new DatabricksFmapiLanguageModel(modelId, {
|
|
2011
|
+
url: formatUrl,
|
|
2012
|
+
headers: getHeaders,
|
|
2013
|
+
fetch,
|
|
2014
|
+
provider
|
|
2015
|
+
});
|
|
2016
|
+
const notImplemented = (name) => {
|
|
2017
|
+
return () => {
|
|
2018
|
+
throw new Error(`${name} is not supported yet`);
|
|
2019
|
+
};
|
|
2020
|
+
};
|
|
2021
|
+
return {
|
|
2022
|
+
chatAgent: createChatAgent,
|
|
2023
|
+
responsesAgent: createResponsesAgent,
|
|
2024
|
+
fmapi: createFmapi,
|
|
2025
|
+
imageModel: notImplemented("ImageModel"),
|
|
2026
|
+
textEmbeddingModel: notImplemented("TextEmbeddingModel"),
|
|
2027
|
+
languageModel: notImplemented("LanguageModel")
|
|
2028
|
+
};
|
|
2029
|
+
};
|
|
2030
|
+
|
|
2031
|
+
//#endregion
|
|
2032
|
+
exports.DATABRICKS_TOOL_CALL_ID = DATABRICKS_TOOL_CALL_ID
|
|
2033
|
+
exports.DATABRICKS_TOOL_DEFINITION = DATABRICKS_TOOL_DEFINITION
|
|
2034
|
+
exports.MCP_APPROVAL_REQUEST_TYPE = MCP_APPROVAL_REQUEST_TYPE
|
|
2035
|
+
exports.MCP_APPROVAL_RESPONSE_TYPE = MCP_APPROVAL_RESPONSE_TYPE
|
|
2036
|
+
exports.MCP_APPROVAL_STATUS_KEY = MCP_APPROVAL_STATUS_KEY
|
|
2037
|
+
exports.createApprovalStatusOutput = createApprovalStatusOutput
|
|
2038
|
+
exports.createDatabricksProvider = createDatabricksProvider
|
|
2039
|
+
exports.extractApprovalStatus = extractApprovalStatus
|
|
2040
|
+
exports.extractApprovalStatusFromToolResult = extractApprovalStatusFromToolResult
|
|
2041
|
+
exports.extractDatabricksMetadata = extractDatabricksMetadata
|
|
2042
|
+
exports.getMcpApprovalState = getMcpApprovalState
|
|
2043
|
+
exports.isApprovalStatusOutput = isApprovalStatusOutput
|
|
2044
|
+
exports.isMcpApprovalRequest = isMcpApprovalRequest
|
|
2045
|
+
exports.isMcpApprovalResponse = isMcpApprovalResponse
|
|
2046
|
+
//# sourceMappingURL=index.cjs.map
|