@databricks/appkit 0.77.0 → 0.79.0

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.
@@ -32,6 +32,12 @@ interface RawFetchAdapterOptions {
32
32
  authenticate: () => Promise<Record<string, string>>;
33
33
  maxSteps?: number;
34
34
  maxTokens?: number;
35
+ /**
36
+ * When set, sent in the request body as `model`. Required by the AI Gateway,
37
+ * which routes by the body's `model` (not the URL). Serving-endpoint paths
38
+ * name the model in the URL and leave this unset.
39
+ */
40
+ model?: string;
35
41
  /** Optional generation params forwarded to the serving request body. */
36
42
  generationParams?: GenerationParams;
37
43
  /** Max length of one SSE line (including an incomplete tail in the buffer). */
@@ -51,6 +57,8 @@ interface StreamBodyAdapterOptions {
51
57
  streamBody: StreamBody;
52
58
  maxSteps?: number;
53
59
  maxTokens?: number;
60
+ /** See {@link RawFetchAdapterOptions.model}. Set by {@link DatabricksAdapter.fromAiGateway}. */
61
+ model?: string;
54
62
  generationParams?: GenerationParams;
55
63
  maxSseLineChars?: number;
56
64
  maxStreamTextChars?: number;
@@ -88,6 +96,26 @@ interface ModelServingOptions {
88
96
  maxStreamTextChars?: number;
89
97
  maxToolArgumentsChars?: number;
90
98
  }
99
+ interface AiGatewayOptions {
100
+ /**
101
+ * Model to name in the request body, e.g. `"system.ai.claude-opus-5-5"`.
102
+ * The gateway routes by this value; there is no per-endpoint URL.
103
+ */
104
+ model: string;
105
+ /**
106
+ * Pre-built WorkspaceClient (or structural equivalent). When omitted, one
107
+ * is created from the ambient client options (SDK credential chain). It is
108
+ * captured once and reused across requests — do not pass a per-request OBO
109
+ * client (it would leak the first request's identity into later ones).
110
+ */
111
+ workspaceClient?: WorkspaceClientLike;
112
+ maxSteps?: number;
113
+ maxTokens?: number;
114
+ generationParams?: GenerationParams;
115
+ maxSseLineChars?: number;
116
+ maxStreamTextChars?: number;
117
+ maxToolArgumentsChars?: number;
118
+ }
91
119
  /**
92
120
  * Adapter that talks directly to Databricks Model Serving `/invocations` endpoint.
93
121
  *
@@ -134,6 +162,8 @@ declare class DatabricksAdapter implements AgentAdapter {
134
162
  private streamBody;
135
163
  private maxSteps;
136
164
  private maxTokens;
165
+ /** UC model name sent in the request body (AI Gateway); unset for serving endpoints. */
166
+ private model?;
137
167
  private generationParams;
138
168
  private maxSseLineChars;
139
169
  private maxStreamTextChars;
@@ -169,6 +199,41 @@ declare class DatabricksAdapter implements AgentAdapter {
169
199
  * ```
170
200
  */
171
201
  static fromModelServing(endpointName?: string, options?: ModelServingOptions): Promise<DatabricksAdapter>;
202
+ /**
203
+ * Creates a DatabricksAdapter that talks to the Databricks AI Gateway
204
+ * Chat Completions endpoint (`/ai-gateway/mlflow/v1/chat/completions`).
205
+ *
206
+ * Unlike {@link fromModelServing}, the target model is named in the request
207
+ * body (`model`, e.g. `"system.ai.claude-opus-5-5"`) rather than in the URL:
208
+ * the gateway is a single fixed path that routes by the body's `model`. Auth
209
+ * and transport reuse the SDK's `apiClient.request`, same as the serving
210
+ * path, so no bespoke `fetch()` + token handling. The request/response wire
211
+ * format and tool-calling loop are identical to the serving path.
212
+ *
213
+ * @example
214
+ * ```ts
215
+ * import { createApp, createAgent } from "@databricks/appkit";
216
+ * import { agents, DatabricksAdapter } from "@databricks/appkit/beta";
217
+ *
218
+ * const adapter = await DatabricksAdapter.fromAiGateway({
219
+ * model: "system.ai.claude-opus-5-5",
220
+ * });
221
+ *
222
+ * await createApp({
223
+ * plugins: [
224
+ * agents({
225
+ * agents: {
226
+ * assistant: createAgent({
227
+ * instructions: "You are a helpful assistant.",
228
+ * model: adapter,
229
+ * }),
230
+ * },
231
+ * }),
232
+ * ],
233
+ * });
234
+ * ```
235
+ */
236
+ static fromAiGateway(options: AiGatewayOptions): Promise<DatabricksAdapter>;
172
237
  /**
173
238
  * Discoverability shim for the Supervisor API adapter. Returns an
174
239
  * {@link AgentAdapter} (a `SupervisorApiAdapter` at runtime), NOT a
@@ -1 +1 @@
1
- {"version":3,"file":"databricks.d.ts","names":[],"sources":["../../src/agents/databricks.ts"],"mappings":";;;;;;;;;;AAwCA;;UAAiB,gBAAA;EAAgB;EAE/B,WAAA;EAEA;EAAA,KAAA;EAIA;EAFA,IAAA;EAIgB;EAFhB,iBAAA;EA6EQ;EA3ER,gBAAA;AAAA;;;;;;UA2EQ,sBAAA;EACR,WAAA;EACA,YAAA,QAAoB,OAAA,CAAQ,MAAA;EAC5B,QAAA;EACA,SAAA;EAAA;EAEA,gBAAA,GAAmB,gBAAA;EAAA;EAEnB,eAAA;EAEA;EAAA,kBAAA;EAEqB;EAArB,qBAAA;AAAA;;;;;;;UASQ,wBAAA;EACR,UAAA,EAAY,UAAA;EACZ,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;AAAA,KAGG,wBAAA,GACD,sBAAA,GACA,wBAAA;;;;AAAwB;;;;UAelB,mBAAA;EACR,SAAA;IACE,OAAA,CAAQ,OAAA,EAAS,MAAA,oBAA0B,OAAA;EAAA;AAAA;AAAA,UAIrC,sBAAA;EACR,eAAA,EAAiB,mBAAA;EACjB,YAAA;EACA,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;AAAA,UAGQ,mBAAA;EACR,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA,GAAkB,mBAAA;EAClB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;;;;;;;;;;;;;;;AAuFF;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,iBAAA,YAA6B,YAAA;EAAA,QAChC,UAAA;EAAA,QACA,QAAA;EAAA,QACA,SAAA;EAAA,QACA,gBAAA;EAAA,QACA,eAAA;EAAA,QACA,kBAAA;EAAA,QACA,qBAAA;cAEI,OAAA,EAAS,wBAAA;EAuGnB;;;;;;;;EAAA,OAtDW,mBAAA,CACX,OAAA,EAAS,sBAAA,GACR,OAAA,CAAQ,iBAAA;EA4GA;;;;;;;;;;;;;;;AAoZb;;;;;EApZa,OAzDE,gBAAA,CACX,YAAA,WACA,OAAA,GAAU,mBAAA,GACT,OAAA,CAAQ,iBAAA;EA4cF;;;;;;;;;;;;;;;;;;;;;EAAA,OAxZI,iBAAA,CACX,OAAA,EArDQ,2BAAA,GAsDP,OAAA,CAAQ,YAAA;EAKJ,GAAA,CACL,KAAA,EAAO,UAAA,EACP,OAAA,EAAS,eAAA,GACR,cAAA,CAAe,UAAA;;UAuDH,iBAAA;EAAA,QA8CA,gBAAA;EAAA,QAyLA,gBAAA;;;;;;UAoCP,aAAA;EAAA,QAgDA,UAAA;AAAA;;;;;;;;iBA0BM,kBAAA,CACd,IAAA,WACC,KAAA;EAAQ,IAAA;EAAc,IAAA;AAAA"}
1
+ {"version":3,"file":"databricks.d.ts","names":[],"sources":["../../src/agents/databricks.ts"],"mappings":";;;;;;;;;;AAyCA;;UAAiB,gBAAA;EAAgB;EAE/B,WAAA;EAEA;EAAA,KAAA;EAIA;EAFA,IAAA;EAIgB;EAFhB,iBAAA;EA6EQ;EA3ER,gBAAA;AAAA;;;;;;UA2EQ,sBAAA;EACR,WAAA;EACA,YAAA,QAAoB,OAAA,CAAQ,MAAA;EAC5B,QAAA;EACA,SAAA;EAAA;;;;;EAMA,KAAA;EAQA;EANA,gBAAA,GAAmB,gBAAA;EAME;EAJrB,eAAA;EAagC;EAXhC,kBAAA;EAiBmC;EAfnC,qBAAA;AAAA;;;;;;;UASQ,wBAAA;EACR,UAAA,EAAY,UAAA;EACZ,QAAA;EACA,SAAA;EASG;EAPH,KAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;AAAA,KAGG,wBAAA,GACD,sBAAA,GACA,wBAAA;;;;;;;;UAeM,mBAAA;EACR,SAAA;IACE,OAAA,CAAQ,OAAA,EAAS,MAAA,oBAA0B,OAAA;EAAA;AAAA;AAAA,UAIrC,sBAAA;EACR,eAAA,EAAiB,mBAAA;EACjB,YAAA;EACA,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;AAAA,UAGQ,mBAAA;EACR,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA,GAAkB,mBAAA;EAClB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;AAAA,UAGQ,gBAAA;EANR;;;;EAWA,KAAA;EARqB;;AAAA;;;;EAerB,eAAA,GAAkB,mBAAA;EAClB,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;;;;;AAuFF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,iBAAA,YAA6B,YAAA;EAAA,QAChC,UAAA;EAAA,QACA,QAAA;EAAA,QACA,SAAA;EAiHI;EAAA,QA/GJ,KAAA;EAAA,QACA,gBAAA;EAAA,QACA,eAAA;EAAA,QACA,kBAAA;EAAA,QACA,qBAAA;cAEI,OAAA,EAAS,wBAAA;EA6KlB;;;;;;;;EAAA,OA3HU,mBAAA,CACX,OAAA,EAAS,sBAAA,GACR,OAAA,CAAQ,iBAAA;EA4LT;;;;;;;;;;;;AAsbJ;;;;;;;;EAtbI,OAzIW,gBAAA,CACX,YAAA,WACA,OAAA,GAAU,mBAAA,GACT,OAAA,CAAQ,iBAAA;EA8jBgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAA,OA7fd,aAAA,CACX,OAAA,EAAS,gBAAA,GACR,OAAA,CAAQ,iBAAA;;;;;;;;;;;;;;;;;;;;;;SA2DE,iBAAA,CACX,OAAA,EA5DQ,2BAAA,GA6DP,OAAA,CAAQ,YAAA;EAKJ,GAAA,CACL,KAAA,EAAO,UAAA,EACP,OAAA,EAAS,eAAA,GACR,cAAA,CAAe,UAAA;;UAuDH,iBAAA;EAAA,QA8CA,gBAAA;EAAA,QA6LA,gBAAA;;;;;;UAoCP,aAAA;EAAA,QAgDA,UAAA;AAAA;;;;;;;;iBA8DM,kBAAA,CACd,IAAA,WACC,KAAA;EAAQ,IAAA;EAAc,IAAA;AAAA"}
@@ -1,6 +1,6 @@
1
1
  import { createWorkspaceClient } from "../shared/src/workspace-client/factory.js";
2
2
  import { APPKIT_USER_AGENT, getClientOptions } from "../context/client-options.js";
3
- import { stream } from "../connectors/serving/client.js";
3
+ import { stream, streamAiGateway } from "../connectors/serving/client.js";
4
4
 
5
5
  //#region src/agents/databricks.ts
6
6
  /** Default cap for a single incomplete SSE line tail (DoS guard). */
@@ -111,6 +111,8 @@ var DatabricksAdapter = class DatabricksAdapter {
111
111
  streamBody;
112
112
  maxSteps;
113
113
  maxTokens;
114
+ /** UC model name sent in the request body (AI Gateway); unset for serving endpoints. */
115
+ model;
114
116
  generationParams;
115
117
  maxSseLineChars;
116
118
  maxStreamTextChars;
@@ -118,6 +120,7 @@ var DatabricksAdapter = class DatabricksAdapter {
118
120
  constructor(options) {
119
121
  this.maxSteps = options.maxSteps ?? 10;
120
122
  this.maxTokens = options.maxTokens ?? 4096;
123
+ this.model = options.model;
121
124
  this.generationParams = options.generationParams ?? {};
122
125
  this.maxSseLineChars = options.maxSseLineChars ?? DEFAULT_MAX_SSE_LINE_CHARS;
123
126
  this.maxStreamTextChars = options.maxStreamTextChars ?? DEFAULT_MAX_STREAM_TEXT_CHARS;
@@ -204,6 +207,54 @@ var DatabricksAdapter = class DatabricksAdapter {
204
207
  });
205
208
  }
206
209
  /**
210
+ * Creates a DatabricksAdapter that talks to the Databricks AI Gateway
211
+ * Chat Completions endpoint (`/ai-gateway/mlflow/v1/chat/completions`).
212
+ *
213
+ * Unlike {@link fromModelServing}, the target model is named in the request
214
+ * body (`model`, e.g. `"system.ai.claude-opus-5-5"`) rather than in the URL:
215
+ * the gateway is a single fixed path that routes by the body's `model`. Auth
216
+ * and transport reuse the SDK's `apiClient.request`, same as the serving
217
+ * path, so no bespoke `fetch()` + token handling. The request/response wire
218
+ * format and tool-calling loop are identical to the serving path.
219
+ *
220
+ * @example
221
+ * ```ts
222
+ * import { createApp, createAgent } from "@databricks/appkit";
223
+ * import { agents, DatabricksAdapter } from "@databricks/appkit/beta";
224
+ *
225
+ * const adapter = await DatabricksAdapter.fromAiGateway({
226
+ * model: "system.ai.claude-opus-5-5",
227
+ * });
228
+ *
229
+ * await createApp({
230
+ * plugins: [
231
+ * agents({
232
+ * agents: {
233
+ * assistant: createAgent({
234
+ * instructions: "You are a helpful assistant.",
235
+ * model: adapter,
236
+ * }),
237
+ * },
238
+ * }),
239
+ * ],
240
+ * });
241
+ * ```
242
+ */
243
+ static async fromAiGateway(options) {
244
+ const { model, workspaceClient, maxSteps, maxTokens, generationParams, maxSseLineChars, maxStreamTextChars, maxToolArgumentsChars } = options;
245
+ const client = workspaceClient ?? createWorkspaceClient({ clientOptions: getClientOptions() });
246
+ return new DatabricksAdapter({
247
+ streamBody: (body, signal) => streamAiGateway(client, body, signal),
248
+ model,
249
+ maxSteps,
250
+ maxTokens,
251
+ generationParams,
252
+ maxSseLineChars,
253
+ maxStreamTextChars,
254
+ maxToolArgumentsChars
255
+ });
256
+ }
257
+ /**
207
258
  * Discoverability shim for the Supervisor API adapter. Returns an
208
259
  * {@link AgentAdapter} (a `SupervisorApiAdapter` at runtime), NOT a
209
260
  * {@link DatabricksAdapter} — the two are separate classes (different
@@ -314,6 +365,7 @@ var DatabricksAdapter = class DatabricksAdapter {
314
365
  stream: true,
315
366
  max_tokens: this.maxTokens
316
367
  };
368
+ if (this.model) body.model = this.model;
317
369
  applyGenerationParams(body, this.generationParams);
318
370
  if (tools.length > 0) body.tools = tools;
319
371
  let responseBody;
@@ -490,6 +542,29 @@ var DatabricksAdapter = class DatabricksAdapter {
490
542
  }
491
543
  };
492
544
  /**
545
+ * Resolves a string `model` to a {@link DatabricksAdapter}, routing by name:
546
+ *
547
+ * - UC model names (`system.*`, e.g. `"system.ai.claude-opus-5-5"`) → AI
548
+ * Gateway ({@link DatabricksAdapter.fromAiGateway}); the model is named in
549
+ * the request body.
550
+ * - Everything else — serving-endpoint names like
551
+ * `"databricks-claude-sonnet-4-5"` or a custom endpoint → Model Serving
552
+ * ({@link DatabricksAdapter.fromModelServing}); the name goes in the URL.
553
+ *
554
+ * Serving-endpoint names are `[a-zA-Z0-9_-]` (no dots), so the `system.`
555
+ * prefix cleanly separates the two namespaces and no existing endpoint name
556
+ * changes routing. This is the single decision point shared by the agents
557
+ * plugin and standalone `runAgent`, so the two never drift. To force a
558
+ * `system.*` name onto a serving endpoint instead, pass a pre-built adapter
559
+ * (`DatabricksAdapter.fromServingEndpoint(...)`) as the agent's `model`.
560
+ */
561
+ function adapterFromModelString(model, options) {
562
+ return model.startsWith("system.") ? DatabricksAdapter.fromAiGateway({
563
+ model,
564
+ ...options
565
+ }) : DatabricksAdapter.fromModelServing(model, options);
566
+ }
567
+ /**
493
568
  * Parses text-based tool calls from model output.
494
569
  *
495
570
  * Handles two formats:
@@ -543,5 +618,5 @@ function tryParsePythonStyleToolCalls(text) {
543
618
  }
544
619
 
545
620
  //#endregion
546
- export { DatabricksAdapter, parseTextToolCalls };
621
+ export { DatabricksAdapter, adapterFromModelString, parseTextToolCalls };
547
622
  //# sourceMappingURL=databricks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"databricks.js","names":["servingStream"],"sources":["../../src/agents/databricks.ts"],"sourcesContent":["import type {\n AgentAdapter,\n AgentEvent,\n AgentInput,\n AgentRunContext,\n AgentToolDefinition,\n} from \"shared\";\n\nimport {\n type StreamBody,\n stream as servingStream,\n} from \"../connectors/serving/client\";\nimport { APPKIT_USER_AGENT, getClientOptions } from \"../context/client-options\";\nimport { createWorkspaceClient } from \"../workspace-client\";\n\n/** Default cap for a single incomplete SSE line tail (DoS guard). */\nconst DEFAULT_MAX_SSE_LINE_CHARS = 1024 * 1024;\n\n/** Default cap for accumulated assistant text from `delta.content`. */\nconst DEFAULT_MAX_STREAM_TEXT_CHARS = 4 * 1024 * 1024;\n\n/** Default cap for accumulated JSON arguments per streamed tool call index. */\nconst DEFAULT_MAX_TOOL_ARGUMENT_CHARS = 2 * 1024 * 1024;\n\n/** Cap text length before running Python-style tool-call regex (ReDoS guard). */\nconst PYTHON_STYLE_TOOL_PARSE_MAX_INPUT = 64 * 1024;\n\n/** Fallback HTTP timeout when the raw fetch adapter path receives no AbortSignal from the runner. */\nconst RAW_FETCH_DEFAULT_TIMEOUT_MS = 120_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * Optional generation parameters forwarded to the OpenAI-compatible serving\n * request body. Names match the serving API wire keys. Only keys that are set\n * are sent — undefined values are omitted so the endpoint applies its own\n * defaults. Ranges are not validated here; the serving endpoint validates.\n */\nexport interface GenerationParams {\n /** Sampling temperature. */\n temperature?: number;\n /** Nucleus sampling probability mass (`top_p`). */\n top_p?: number;\n /** Stop sequence(s) that end generation. */\n stop?: string | string[];\n /** Penalize tokens by frequency. */\n frequency_penalty?: number;\n /** Penalize tokens by prior presence. */\n presence_penalty?: number;\n}\n\nconst GENERATION_PARAM_KEYS = [\n \"temperature\",\n \"top_p\",\n \"stop\",\n \"frequency_penalty\",\n \"presence_penalty\",\n] as const satisfies readonly (keyof GenerationParams)[];\n\n/** Copy only the set generation params onto the request body. */\nfunction applyGenerationParams(\n body: Record<string, unknown>,\n params: GenerationParams,\n): void {\n for (const key of GENERATION_PARAM_KEYS) {\n const value = params[key];\n if (value !== undefined) body[key] = value;\n }\n}\n\nfunction extractLlamaToolJsonSlice(text: string): string | undefined {\n const start = text.indexOf(\"[{\");\n if (start < 0) return undefined;\n const endBracket = text.lastIndexOf(\"}]\");\n if (endBracket < start) return undefined;\n return text.slice(start, endBracket + 2);\n}\n\n/** OpenAI SSE payload: `{ choices: [{ delta }] }`. */\nfunction openAiChoicesDelta(parsed: unknown): unknown {\n if (!isRecord(parsed)) return undefined;\n const choices = parsed.choices;\n if (!Array.isArray(choices) || choices.length < 1) return undefined;\n const first = choices[0];\n if (!isRecord(first)) return undefined;\n return first.delta;\n}\n\nfunction isStreamingDeltaToolCall(value: unknown): value is DeltaToolCall {\n if (!isRecord(value)) return false;\n return typeof value.index === \"number\";\n}\n\nfunction throwIfExceedsStreamLimit(\n label: string,\n currentLength: number,\n chunk: string,\n max: number,\n): void {\n if (currentLength + chunk.length > max) {\n throw new Error(\n `DatabricksAdapter: ${label} exceeds configured limit (${max} UTF-16 code units)`,\n );\n }\n}\n\n/** Text of a harmony `reasoning` part: `summary[].text` plus an optional `text`. */\nfunction reasoningPartText(part: Record<string, unknown>): string {\n let text = \"\";\n if (Array.isArray(part.summary)) {\n for (const s of part.summary) {\n if (isRecord(s) && typeof s.text === \"string\") text += s.text;\n }\n }\n if (typeof part.text === \"string\") text += part.text;\n return text;\n}\n\n/**\n * Escape-hatch options: provide an `endpointUrl` + `authenticate()` and the\n * adapter uses a bare `fetch()` to call it. Useful for tests and for pointing\n * the adapter at non-workspace endpoints (reverse proxies, mocks).\n */\ninterface RawFetchAdapterOptions {\n endpointUrl: string;\n authenticate: () => Promise<Record<string, string>>;\n maxSteps?: number;\n maxTokens?: number;\n /** Optional generation params forwarded to the serving request body. */\n generationParams?: GenerationParams;\n /** Max length of one SSE line (including an incomplete tail in the buffer). */\n maxSseLineChars?: number;\n /** Max total length of assistant `delta.content` across the stream. */\n maxStreamTextChars?: number;\n /** Max length of streamed `function.arguments` per tool call index. */\n maxToolArgumentsChars?: number;\n}\n\n/**\n * Preferred options: caller provides the transport function directly.\n * The `fromServingEndpoint` / `fromModelServing` factories use this to route\n * through `connectors/serving/stream`, which centralises URL encoding, auth\n * via the SDK's `apiClient.request`, and any future retries/telemetry.\n */\ninterface StreamBodyAdapterOptions {\n streamBody: StreamBody;\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ntype DatabricksAdapterOptions =\n | RawFetchAdapterOptions\n | StreamBodyAdapterOptions;\n\nfunction isStreamBodyOptions(\n o: DatabricksAdapterOptions,\n): o is StreamBodyAdapterOptions {\n return \"streamBody\" in o;\n}\n\n/**\n * Duck-typed subset of the Databricks SDK `WorkspaceClient`. Callers of\n * `fromServingEndpoint` and `fromModelServing` pass a real `WorkspaceClient`,\n * but we only need the `apiClient.request` surface — so we declare the minimal\n * interface rather than importing the SDK type directly. This keeps the adapter\n * free of a hard compile-time dependency on `@databricks/sdk-experimental`.\n */\ninterface WorkspaceClientLike {\n apiClient: {\n request(options: Record<string, unknown>): Promise<unknown>;\n };\n}\n\ninterface ServingEndpointOptions {\n workspaceClient: WorkspaceClientLike;\n endpointName: string;\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ninterface ModelServingOptions {\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n workspaceClient?: WorkspaceClientLike;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ninterface OpenAIMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | null;\n tool_calls?: OpenAIToolCall[];\n tool_call_id?: string;\n}\n\ninterface OpenAIToolCall {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n /**\n * Opaque Vertex/Gemini \"thought signature\" blob the request must echo\n * back verbatim on the next turn. Vertex's OpenAI-compat proxy emits\n * this as `thoughtSignature` (camelCase) at the top level of the\n * tool_call delta (verified against `gemini-3.1-flash-lite-preview`),\n * and accepts the same spelling back on outbound. Non-Gemini endpoints\n * (Claude on Databricks, external OpenAI-compat models, Llama, etc.)\n * leave this undefined and the serializer omits the key.\n * See https://docs.cloud.google.com/vertex-ai/generative-ai/docs/thought-signatures\n */\n thoughtSignature?: string;\n}\n\ninterface OpenAITool {\n type: \"function\";\n function: {\n name: string;\n description: string;\n parameters: unknown;\n };\n}\n\ninterface DeltaToolCall {\n index: number;\n id?: string;\n type?: string;\n function?: { name?: string; arguments?: string };\n /** See {@link OpenAIToolCall.thoughtSignature}. */\n thoughtSignature?: string;\n}\n\n/**\n * Adapter that talks directly to Databricks Model Serving `/invocations` endpoint.\n *\n * No dependency on the Vercel AI SDK or LangChain. Uses raw `fetch()` to POST\n * OpenAI-compatible payloads and parses the SSE stream itself. Calls\n * `authenticate()` per-request so tokens are always fresh.\n *\n * Handles both structured `tool_calls` responses and text-based tool call\n * fallback parsing for models that output tool calls as text.\n *\n * @example Using the factory (recommended)\n * ```ts\n * import { createApp, createAgent, agents, createWorkspaceClient } from \"@databricks/appkit\";\n * import { DatabricksAdapter } from \"@databricks/appkit/beta\";\n *\n * const adapter = DatabricksAdapter.fromServingEndpoint({\n * workspaceClient: createWorkspaceClient(),\n * endpointName: \"my-endpoint\",\n * });\n *\n * await createApp({\n * plugins: [\n * agents({\n * agents: {\n * assistant: createAgent({\n * instructions: \"You are a helpful assistant.\",\n * model: adapter,\n * }),\n * },\n * }),\n * ],\n * });\n * ```\n *\n * @example Using the raw constructor\n * ```ts\n * const adapter = new DatabricksAdapter({\n * endpointUrl: \"https://host/serving-endpoints/my-endpoint/invocations\",\n * authenticate: async () => ({ Authorization: `Bearer ${token}` }),\n * });\n * ```\n */\nexport class DatabricksAdapter implements AgentAdapter {\n private streamBody: StreamBody;\n private maxSteps: number;\n private maxTokens: number;\n private generationParams: GenerationParams;\n private maxSseLineChars: number;\n private maxStreamTextChars: number;\n private maxToolArgumentsChars: number;\n\n constructor(options: DatabricksAdapterOptions) {\n this.maxSteps = options.maxSteps ?? 10;\n this.maxTokens = options.maxTokens ?? 4096;\n this.generationParams = options.generationParams ?? {};\n this.maxSseLineChars =\n options.maxSseLineChars ?? DEFAULT_MAX_SSE_LINE_CHARS;\n this.maxStreamTextChars =\n options.maxStreamTextChars ?? DEFAULT_MAX_STREAM_TEXT_CHARS;\n this.maxToolArgumentsChars =\n options.maxToolArgumentsChars ?? DEFAULT_MAX_TOOL_ARGUMENT_CHARS;\n\n if (isStreamBodyOptions(options)) {\n this.streamBody = options.streamBody;\n } else {\n const { endpointUrl, authenticate } = options;\n this.streamBody = async (body, signal) => {\n const fetchSignal =\n signal ?? AbortSignal.timeout(RAW_FETCH_DEFAULT_TIMEOUT_MS);\n const authHeaders = await authenticate();\n const response = await fetch(endpointUrl, {\n method: \"POST\",\n headers: {\n \"User-Agent\": APPKIT_USER_AGENT,\n \"Content-Type\": \"application/json\",\n ...authHeaders,\n },\n body: JSON.stringify(body),\n signal: fetchSignal,\n });\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"Unknown error\");\n throw new Error(\n `Databricks API error (${response.status}): ${errorText}`,\n );\n }\n if (!response.body) throw new Error(\"No response body\");\n return response.body;\n };\n }\n }\n\n /**\n * Creates a DatabricksAdapter for a Databricks Model Serving endpoint.\n *\n * Routes through the shared `connectors/serving/stream` helper, which\n * delegates to the SDK's `apiClient.request({ raw: true })`. That gives the\n * adapter centralised URL encoding + authentication with the rest of the\n * serving surface — no bespoke `fetch()` + `authenticate()` plumbing.\n */\n static async fromServingEndpoint(\n options: ServingEndpointOptions,\n ): Promise<DatabricksAdapter> {\n const {\n workspaceClient,\n endpointName,\n maxSteps,\n maxTokens,\n generationParams,\n maxSseLineChars,\n maxStreamTextChars,\n maxToolArgumentsChars,\n } = options;\n return new DatabricksAdapter({\n streamBody: (body, signal) =>\n // Cast through the structural shape: the connector types\n // `workspaceClient` as the SDK's concrete `WorkspaceClient`, but we\n // only need `apiClient.request`.\n servingStream(\n workspaceClient as unknown as Parameters<typeof servingStream>[0],\n endpointName,\n body,\n signal,\n ),\n maxSteps,\n maxTokens,\n generationParams,\n maxSseLineChars,\n maxStreamTextChars,\n maxToolArgumentsChars,\n });\n }\n\n /**\n * Creates a DatabricksAdapter from a Model Serving endpoint name.\n * Auto-creates a WorkspaceClient internally. Reads the endpoint name\n * from the argument or the `DATABRICKS_SERVING_ENDPOINT_NAME` env var.\n *\n * @example\n * ```ts\n * // Reads endpoint from DATABRICKS_SERVING_ENDPOINT_NAME env var\n * const adapter = await DatabricksAdapter.fromModelServing();\n *\n * // Explicit endpoint\n * const adapter = await DatabricksAdapter.fromModelServing(\"my-endpoint\");\n *\n * // With options\n * const adapter = await DatabricksAdapter.fromModelServing(\"my-endpoint\", {\n * maxSteps: 5,\n * maxTokens: 2048,\n * });\n * ```\n */\n static async fromModelServing(\n endpointName?: string,\n options?: ModelServingOptions,\n ): Promise<DatabricksAdapter> {\n const resolvedEndpoint =\n endpointName ?? process.env.DATABRICKS_SERVING_ENDPOINT_NAME;\n\n if (!resolvedEndpoint) {\n throw new Error(\n \"No endpoint name provided and DATABRICKS_SERVING_ENDPOINT_NAME env var is not set. \" +\n \"Pass an endpoint name or set DATABRICKS_SERVING_ENDPOINT_NAME.\",\n );\n }\n\n let workspaceClient: WorkspaceClientLike | undefined =\n options?.workspaceClient;\n if (!workspaceClient) {\n workspaceClient = createWorkspaceClient({\n clientOptions: getClientOptions(),\n }) as unknown as WorkspaceClientLike;\n }\n\n return DatabricksAdapter.fromServingEndpoint({\n workspaceClient,\n endpointName: resolvedEndpoint,\n maxSteps: options?.maxSteps,\n maxTokens: options?.maxTokens,\n generationParams: options?.generationParams,\n maxSseLineChars: options?.maxSseLineChars,\n maxStreamTextChars: options?.maxStreamTextChars,\n maxToolArgumentsChars: options?.maxToolArgumentsChars,\n });\n }\n\n /**\n * Discoverability shim for the Supervisor API adapter. Returns an\n * {@link AgentAdapter} (a `SupervisorApiAdapter` at runtime), NOT a\n * {@link DatabricksAdapter} — the two are separate classes (different\n * wire formats, different lifecycle). The return type is the\n * {@link AgentAdapter} interface so callers aren't bound to the concrete\n * class. Surfaced here so application developers see a single\n * `DatabricksAdapter.from*` autocomplete root.\n *\n * Dynamic-imports `./supervisor-api` to avoid forming a load-time cycle:\n * both files share `connectors/serving/client.ts`.\n *\n * @example\n * ```ts\n * import { DatabricksAdapter } from \"@databricks/appkit/beta\";\n *\n * const model = await DatabricksAdapter.fromSupervisorApi({\n * model: \"databricks-claude-sonnet-4-5\",\n * });\n * ```\n */\n static async fromSupervisorApi(\n options: import(\"./supervisor-api\").SupervisorApiAdapterOptions,\n ): Promise<AgentAdapter> {\n const { fromSupervisorApi } = await import(\"./supervisor-api\");\n return fromSupervisorApi(options);\n }\n\n async *run(\n input: AgentInput,\n context: AgentRunContext,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n // Databricks API requires tool names to match [a-zA-Z0-9_-].\n // Our tool names use dots (e.g. \"analytics.query\"), so we swap dots\n // for double-underscores in the wire format and map back on receipt.\n const nameToWire = new Map<string, string>();\n const wireToName = new Map<string, string>();\n for (const tool of input.tools) {\n const wire = tool.name.replace(/\\./g, \"__\");\n if (wireToName.has(wire) && wireToName.get(wire) !== tool.name) {\n throw new Error(\n `Tool name collision: '${tool.name}' and '${wireToName.get(wire)}' both map to wire name '${wire}'`,\n );\n }\n nameToWire.set(tool.name, wire);\n wireToName.set(wire, tool.name);\n }\n\n const tools = this.buildTools(input.tools, nameToWire);\n const messages = this.buildMessages(input.messages, nameToWire);\n\n yield { type: \"status\", status: \"running\" };\n\n for (let step = 0; step < this.maxSteps; step++) {\n if (context.signal?.aborted) break;\n\n const { text, toolCalls } = yield* this.streamCompletion(\n messages,\n tools,\n context,\n );\n\n if (toolCalls.length === 0) {\n const parsed = parseTextToolCalls(text);\n if (parsed.length > 0) {\n yield* this.executeToolCalls(parsed, messages, context, nameToWire);\n continue;\n }\n break;\n }\n\n messages.push({\n role: \"assistant\",\n content: text || null,\n tool_calls: toolCalls,\n });\n\n for (const tc of toolCalls) {\n const wireName = tc.function.name;\n const originalName = wireToName.get(wireName) ?? wireName;\n yield* this.executeSingleTool(tc, originalName, messages, context);\n }\n }\n }\n\n /** Parse wire arguments, emit tool_call / tool_result, append tool messages. */\n private async *executeSingleTool(\n tc: OpenAIToolCall,\n originalName: string,\n messages: OpenAIMessage[],\n context: AgentRunContext,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n let args: unknown;\n try {\n args = JSON.parse(tc.function.arguments);\n } catch {\n args = {};\n }\n\n yield { type: \"tool_call\", callId: tc.id, name: originalName, args };\n\n try {\n const result = await context.executeTool(originalName, args);\n const resultStr =\n typeof result === \"string\" ? result : JSON.stringify(result);\n\n yield { type: \"tool_result\", callId: tc.id, result };\n\n messages.push({\n role: \"tool\",\n content: resultStr,\n tool_call_id: tc.id,\n });\n } catch (error) {\n const errMsg =\n error instanceof Error ? error.message : \"Tool execution failed\";\n\n yield {\n type: \"tool_result\",\n callId: tc.id,\n result: null,\n error: errMsg,\n };\n\n messages.push({\n role: \"tool\",\n content: JSON.stringify({ error: errMsg }),\n tool_call_id: tc.id,\n });\n }\n }\n\n private async *streamCompletion(\n messages: OpenAIMessage[],\n tools: OpenAITool[],\n context: AgentRunContext,\n ): AsyncGenerator<\n AgentEvent,\n { text: string; toolCalls: OpenAIToolCall[] },\n unknown\n > {\n const body: Record<string, unknown> = {\n messages,\n stream: true,\n max_tokens: this.maxTokens,\n };\n\n applyGenerationParams(body, this.generationParams);\n\n if (tools.length > 0) {\n body.tools = tools;\n }\n\n let responseBody: ReadableStream<Uint8Array>;\n try {\n responseBody = await this.streamBody(body, context.signal);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Stream request failed\";\n yield { type: \"status\", status: \"error\", error: msg };\n throw err;\n }\n\n const reader = responseBody.getReader();\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let fullText = \"\";\n const toolCallAccumulator = new Map<\n number,\n {\n id: string;\n name: string;\n arguments: string;\n thoughtSignature?: string;\n }\n >();\n\n try {\n while (true) {\n if (context.signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n if (buffer.length > this.maxSseLineChars) {\n throw new Error(\n `DatabricksAdapter: SSE line buffer exceeds configured limit (${this.maxSseLineChars} UTF-16 code units)`,\n );\n }\n\n for (const line of lines) {\n if (line.length > this.maxSseLineChars) {\n throw new Error(\n `DatabricksAdapter: SSE line exceeds configured limit (${this.maxSseLineChars} UTF-16 code units)`,\n );\n }\n\n const trimmed = line.trim();\n if (!trimmed.startsWith(\"data: \")) continue;\n const data = trimmed.slice(6);\n if (data === \"[DONE]\") continue;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch (parseErr) {\n console.debug(\n \"[DatabricksAdapter] malformed SSE data line JSON\",\n { line: `${data.slice(0, 256)}${data.length > 256 ? \"…\" : \"\"}` },\n parseErr,\n );\n continue;\n }\n\n const deltaUnknown = openAiChoicesDelta(parsed);\n if (!isRecord(deltaUnknown)) continue;\n\n // gpt-oss (harmony) sends delta.content as an array of parts\n // (text = answer, reasoning = chain-of-thought), not a string.\n const contentUnknown = deltaUnknown.content;\n const parts =\n typeof contentUnknown === \"string\"\n ? [{ type: \"text\", text: contentUnknown }]\n : Array.isArray(contentUnknown)\n ? contentUnknown\n : [];\n\n for (const part of parts) {\n if (!isRecord(part)) continue;\n if (part.type === \"text\" && typeof part.text === \"string\") {\n const content = part.text;\n throwIfExceedsStreamLimit(\n \"streamed assistant text\",\n fullText.length,\n content,\n this.maxStreamTextChars,\n );\n fullText += content;\n yield { type: \"message_delta\" as const, content };\n } else if (part.type === \"reasoning\") {\n const content = reasoningPartText(part);\n if (content) yield { type: \"thinking\" as const, content };\n }\n }\n\n const toolCallsRaw = deltaUnknown.tool_calls;\n if (!Array.isArray(toolCallsRaw)) continue;\n\n for (const tc of toolCallsRaw) {\n if (!isStreamingDeltaToolCall(tc)) continue;\n const sig = tc.thoughtSignature;\n const existing = toolCallAccumulator.get(tc.index);\n if (existing) {\n if (tc.function?.arguments) {\n throwIfExceedsStreamLimit(\n \"tool call arguments\",\n existing.arguments.length,\n tc.function.arguments,\n this.maxToolArgumentsChars,\n );\n existing.arguments += tc.function.arguments;\n }\n if (sig && !existing.thoughtSignature) {\n existing.thoughtSignature = sig;\n }\n } else {\n const initial = tc.function?.arguments ?? \"\";\n if (initial.length > this.maxToolArgumentsChars) {\n throw new Error(\n `DatabricksAdapter: tool call arguments exceed configured limit (${this.maxToolArgumentsChars} UTF-16 code units)`,\n );\n }\n toolCallAccumulator.set(tc.index, {\n id: tc.id ?? `call_${tc.index}`,\n name: tc.function?.name ?? \"\",\n arguments: initial,\n ...(sig ? { thoughtSignature: sig } : {}),\n });\n }\n }\n }\n }\n } finally {\n try {\n await reader.cancel();\n } catch (cancelErr) {\n console.debug(\n \"[DatabricksAdapter] reader.cancel() failed during teardown\",\n cancelErr,\n );\n }\n try {\n reader.releaseLock();\n } catch (unlockErr) {\n console.debug(\n \"[DatabricksAdapter] reader.releaseLock() failed during teardown\",\n unlockErr,\n );\n }\n }\n\n const toolCalls: OpenAIToolCall[] = Array.from(\n toolCallAccumulator.values(),\n ).map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments || \"{}\" },\n ...(tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}),\n }));\n\n return { text: fullText, toolCalls };\n }\n\n private async *executeToolCalls(\n calls: Array<{ name: string; args: unknown }>,\n messages: OpenAIMessage[],\n context: AgentRunContext,\n nameToWire: Map<string, string>,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n const wireToolName = (name: string) =>\n nameToWire.get(name) ?? name.replace(/\\./g, \"__\");\n\n const toolCallObjs: OpenAIToolCall[] = calls.map((c, i) => ({\n id: `text_call_${i}`,\n type: \"function\" as const,\n function: {\n name: wireToolName(c.name),\n arguments: JSON.stringify(c.args),\n },\n }));\n\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: toolCallObjs,\n });\n\n for (let i = 0; i < toolCallObjs.length; i++) {\n const tc = toolCallObjs[i];\n const originalName = calls[i]?.name ?? tc.function.name;\n yield* this.executeSingleTool(tc, originalName, messages, context);\n }\n }\n\n /**\n * Maps AppKit {@link AgentInput} messages into OpenAI-compatible wire messages.\n * Preserves multi-turn tool state (`toolCalls` → `tool_calls`, `toolCallId` →\n * `tool_call_id`) so resumed threads and hydrated history reach the model.\n */\n private buildMessages(\n messages: AgentInput[\"messages\"],\n nameToWire: Map<string, string>,\n ): OpenAIMessage[] {\n const wireToolName = (name: string) =>\n nameToWire.get(name) ?? name.replace(/\\./g, \"__\");\n\n return messages.map((m) => {\n let content: string | null = m.content;\n if (\n m.role === \"assistant\" &&\n m.toolCalls &&\n m.toolCalls.length > 0 &&\n (!m.content || m.content.trim() === \"\")\n ) {\n content = null;\n }\n\n const out: OpenAIMessage = {\n role: m.role as OpenAIMessage[\"role\"],\n content,\n };\n\n if (m.toolCallId) {\n out.tool_call_id = m.toolCallId;\n }\n\n if (m.toolCalls && m.toolCalls.length > 0) {\n out.tool_calls = m.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: wireToolName(tc.name),\n arguments:\n typeof tc.args === \"string\"\n ? tc.args\n : JSON.stringify(tc.args ?? {}),\n },\n ...(tc.thoughtSignature\n ? { thoughtSignature: tc.thoughtSignature }\n : {}),\n }));\n }\n\n return out;\n });\n }\n\n private buildTools(\n definitions: AgentToolDefinition[],\n nameToWire: Map<string, string>,\n ): OpenAITool[] {\n return definitions.map((def) => ({\n type: \"function\" as const,\n function: {\n name: nameToWire.get(def.name) ?? def.name,\n description: def.description,\n parameters: def.parameters,\n },\n }));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Text-based tool call parsing (fallback)\n// ---------------------------------------------------------------------------\n\n/**\n * Parses text-based tool calls from model output.\n *\n * Handles two formats:\n * 1. Llama native: `[{\"name\": \"tool_name\", \"parameters\": {\"arg\": \"val\"}}]`\n * 2. Python-style: `[tool_name(arg1='val1', arg2='val2')]`\n */\nexport function parseTextToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n const trimmed = text.trim();\n\n const jsonResult = tryParseLlamaJsonToolCalls(trimmed);\n if (jsonResult.length > 0) return jsonResult;\n\n const pyResult = tryParsePythonStyleToolCalls(trimmed);\n if (pyResult.length > 0) return pyResult;\n\n return [];\n}\n\nfunction isLlamaToolJsonItem(value: unknown): value is Record<\n string,\n unknown\n> & {\n name: string;\n} {\n if (!isRecord(value)) return false;\n return typeof value.name === \"string\";\n}\n\nfunction tryParseLlamaJsonToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n const slice = extractLlamaToolJsonSlice(text);\n if (!slice) return [];\n\n try {\n const parsed: unknown = JSON.parse(slice);\n if (!Array.isArray(parsed)) return [];\n\n return parsed.filter(isLlamaToolJsonItem).map((item) => ({\n name: item.name,\n args: item.parameters ?? item.arguments ?? item.args ?? {},\n }));\n } catch {\n return [];\n }\n}\n\nfunction tryParsePythonStyleToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n if (text.length > PYTHON_STYLE_TOOL_PARSE_MAX_INPUT) {\n return [];\n }\n\n const pattern = /\\[?([a-zA-Z_][\\w.]*)\\(([^)]*)\\)\\]?/g;\n const results: Array<{ name: string; args: unknown }> = [];\n\n for (const match of text.matchAll(pattern)) {\n const name = match[1];\n const argsStr = match[2];\n\n const args: Record<string, unknown> = {};\n const argPattern = /(\\w+)\\s*=\\s*(?:'([^']*)'|\"([^\"]*)\"|(\\S+))/g;\n for (const argMatch of argsStr.matchAll(argPattern)) {\n const key = argMatch[1];\n const value = argMatch[2] ?? argMatch[3] ?? argMatch[4];\n args[key] = value;\n }\n\n results.push({ name, args });\n }\n\n return results;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"databricks.js","names":["servingStream"],"sources":["../../src/agents/databricks.ts"],"sourcesContent":["import type {\n AgentAdapter,\n AgentEvent,\n AgentInput,\n AgentRunContext,\n AgentToolDefinition,\n} from \"shared\";\n\nimport {\n type StreamBody,\n stream as servingStream,\n streamAiGateway,\n} from \"../connectors/serving/client\";\nimport { APPKIT_USER_AGENT, getClientOptions } from \"../context/client-options\";\nimport { createWorkspaceClient } from \"../workspace-client\";\n\n/** Default cap for a single incomplete SSE line tail (DoS guard). */\nconst DEFAULT_MAX_SSE_LINE_CHARS = 1024 * 1024;\n\n/** Default cap for accumulated assistant text from `delta.content`. */\nconst DEFAULT_MAX_STREAM_TEXT_CHARS = 4 * 1024 * 1024;\n\n/** Default cap for accumulated JSON arguments per streamed tool call index. */\nconst DEFAULT_MAX_TOOL_ARGUMENT_CHARS = 2 * 1024 * 1024;\n\n/** Cap text length before running Python-style tool-call regex (ReDoS guard). */\nconst PYTHON_STYLE_TOOL_PARSE_MAX_INPUT = 64 * 1024;\n\n/** Fallback HTTP timeout when the raw fetch adapter path receives no AbortSignal from the runner. */\nconst RAW_FETCH_DEFAULT_TIMEOUT_MS = 120_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * Optional generation parameters forwarded to the OpenAI-compatible serving\n * request body. Names match the serving API wire keys. Only keys that are set\n * are sent — undefined values are omitted so the endpoint applies its own\n * defaults. Ranges are not validated here; the serving endpoint validates.\n */\nexport interface GenerationParams {\n /** Sampling temperature. */\n temperature?: number;\n /** Nucleus sampling probability mass (`top_p`). */\n top_p?: number;\n /** Stop sequence(s) that end generation. */\n stop?: string | string[];\n /** Penalize tokens by frequency. */\n frequency_penalty?: number;\n /** Penalize tokens by prior presence. */\n presence_penalty?: number;\n}\n\nconst GENERATION_PARAM_KEYS = [\n \"temperature\",\n \"top_p\",\n \"stop\",\n \"frequency_penalty\",\n \"presence_penalty\",\n] as const satisfies readonly (keyof GenerationParams)[];\n\n/** Copy only the set generation params onto the request body. */\nfunction applyGenerationParams(\n body: Record<string, unknown>,\n params: GenerationParams,\n): void {\n for (const key of GENERATION_PARAM_KEYS) {\n const value = params[key];\n if (value !== undefined) body[key] = value;\n }\n}\n\nfunction extractLlamaToolJsonSlice(text: string): string | undefined {\n const start = text.indexOf(\"[{\");\n if (start < 0) return undefined;\n const endBracket = text.lastIndexOf(\"}]\");\n if (endBracket < start) return undefined;\n return text.slice(start, endBracket + 2);\n}\n\n/** OpenAI SSE payload: `{ choices: [{ delta }] }`. */\nfunction openAiChoicesDelta(parsed: unknown): unknown {\n if (!isRecord(parsed)) return undefined;\n const choices = parsed.choices;\n if (!Array.isArray(choices) || choices.length < 1) return undefined;\n const first = choices[0];\n if (!isRecord(first)) return undefined;\n return first.delta;\n}\n\nfunction isStreamingDeltaToolCall(value: unknown): value is DeltaToolCall {\n if (!isRecord(value)) return false;\n return typeof value.index === \"number\";\n}\n\nfunction throwIfExceedsStreamLimit(\n label: string,\n currentLength: number,\n chunk: string,\n max: number,\n): void {\n if (currentLength + chunk.length > max) {\n throw new Error(\n `DatabricksAdapter: ${label} exceeds configured limit (${max} UTF-16 code units)`,\n );\n }\n}\n\n/** Text of a harmony `reasoning` part: `summary[].text` plus an optional `text`. */\nfunction reasoningPartText(part: Record<string, unknown>): string {\n let text = \"\";\n if (Array.isArray(part.summary)) {\n for (const s of part.summary) {\n if (isRecord(s) && typeof s.text === \"string\") text += s.text;\n }\n }\n if (typeof part.text === \"string\") text += part.text;\n return text;\n}\n\n/**\n * Escape-hatch options: provide an `endpointUrl` + `authenticate()` and the\n * adapter uses a bare `fetch()` to call it. Useful for tests and for pointing\n * the adapter at non-workspace endpoints (reverse proxies, mocks).\n */\ninterface RawFetchAdapterOptions {\n endpointUrl: string;\n authenticate: () => Promise<Record<string, string>>;\n maxSteps?: number;\n maxTokens?: number;\n /**\n * When set, sent in the request body as `model`. Required by the AI Gateway,\n * which routes by the body's `model` (not the URL). Serving-endpoint paths\n * name the model in the URL and leave this unset.\n */\n model?: string;\n /** Optional generation params forwarded to the serving request body. */\n generationParams?: GenerationParams;\n /** Max length of one SSE line (including an incomplete tail in the buffer). */\n maxSseLineChars?: number;\n /** Max total length of assistant `delta.content` across the stream. */\n maxStreamTextChars?: number;\n /** Max length of streamed `function.arguments` per tool call index. */\n maxToolArgumentsChars?: number;\n}\n\n/**\n * Preferred options: caller provides the transport function directly.\n * The `fromServingEndpoint` / `fromModelServing` factories use this to route\n * through `connectors/serving/stream`, which centralises URL encoding, auth\n * via the SDK's `apiClient.request`, and any future retries/telemetry.\n */\ninterface StreamBodyAdapterOptions {\n streamBody: StreamBody;\n maxSteps?: number;\n maxTokens?: number;\n /** See {@link RawFetchAdapterOptions.model}. Set by {@link DatabricksAdapter.fromAiGateway}. */\n model?: string;\n generationParams?: GenerationParams;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ntype DatabricksAdapterOptions =\n | RawFetchAdapterOptions\n | StreamBodyAdapterOptions;\n\nfunction isStreamBodyOptions(\n o: DatabricksAdapterOptions,\n): o is StreamBodyAdapterOptions {\n return \"streamBody\" in o;\n}\n\n/**\n * Duck-typed subset of the Databricks SDK `WorkspaceClient`. Callers of\n * `fromServingEndpoint` and `fromModelServing` pass a real `WorkspaceClient`,\n * but we only need the `apiClient.request` surface — so we declare the minimal\n * interface rather than importing the SDK type directly. This keeps the adapter\n * free of a hard compile-time dependency on `@databricks/sdk-experimental`.\n */\ninterface WorkspaceClientLike {\n apiClient: {\n request(options: Record<string, unknown>): Promise<unknown>;\n };\n}\n\ninterface ServingEndpointOptions {\n workspaceClient: WorkspaceClientLike;\n endpointName: string;\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ninterface ModelServingOptions {\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n workspaceClient?: WorkspaceClientLike;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ninterface AiGatewayOptions {\n /**\n * Model to name in the request body, e.g. `\"system.ai.claude-opus-5-5\"`.\n * The gateway routes by this value; there is no per-endpoint URL.\n */\n model: string;\n /**\n * Pre-built WorkspaceClient (or structural equivalent). When omitted, one\n * is created from the ambient client options (SDK credential chain). It is\n * captured once and reused across requests — do not pass a per-request OBO\n * client (it would leak the first request's identity into later ones).\n */\n workspaceClient?: WorkspaceClientLike;\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ninterface OpenAIMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | null;\n tool_calls?: OpenAIToolCall[];\n tool_call_id?: string;\n}\n\ninterface OpenAIToolCall {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n /**\n * Opaque Vertex/Gemini \"thought signature\" blob the request must echo\n * back verbatim on the next turn. Vertex's OpenAI-compat proxy emits\n * this as `thoughtSignature` (camelCase) at the top level of the\n * tool_call delta (verified against `gemini-3.1-flash-lite-preview`),\n * and accepts the same spelling back on outbound. Non-Gemini endpoints\n * (Claude on Databricks, external OpenAI-compat models, Llama, etc.)\n * leave this undefined and the serializer omits the key.\n * See https://docs.cloud.google.com/vertex-ai/generative-ai/docs/thought-signatures\n */\n thoughtSignature?: string;\n}\n\ninterface OpenAITool {\n type: \"function\";\n function: {\n name: string;\n description: string;\n parameters: unknown;\n };\n}\n\ninterface DeltaToolCall {\n index: number;\n id?: string;\n type?: string;\n function?: { name?: string; arguments?: string };\n /** See {@link OpenAIToolCall.thoughtSignature}. */\n thoughtSignature?: string;\n}\n\n/**\n * Adapter that talks directly to Databricks Model Serving `/invocations` endpoint.\n *\n * No dependency on the Vercel AI SDK or LangChain. Uses raw `fetch()` to POST\n * OpenAI-compatible payloads and parses the SSE stream itself. Calls\n * `authenticate()` per-request so tokens are always fresh.\n *\n * Handles both structured `tool_calls` responses and text-based tool call\n * fallback parsing for models that output tool calls as text.\n *\n * @example Using the factory (recommended)\n * ```ts\n * import { createApp, createAgent, agents, createWorkspaceClient } from \"@databricks/appkit\";\n * import { DatabricksAdapter } from \"@databricks/appkit/beta\";\n *\n * const adapter = DatabricksAdapter.fromServingEndpoint({\n * workspaceClient: createWorkspaceClient(),\n * endpointName: \"my-endpoint\",\n * });\n *\n * await createApp({\n * plugins: [\n * agents({\n * agents: {\n * assistant: createAgent({\n * instructions: \"You are a helpful assistant.\",\n * model: adapter,\n * }),\n * },\n * }),\n * ],\n * });\n * ```\n *\n * @example Using the raw constructor\n * ```ts\n * const adapter = new DatabricksAdapter({\n * endpointUrl: \"https://host/serving-endpoints/my-endpoint/invocations\",\n * authenticate: async () => ({ Authorization: `Bearer ${token}` }),\n * });\n * ```\n */\nexport class DatabricksAdapter implements AgentAdapter {\n private streamBody: StreamBody;\n private maxSteps: number;\n private maxTokens: number;\n /** UC model name sent in the request body (AI Gateway); unset for serving endpoints. */\n private model?: string;\n private generationParams: GenerationParams;\n private maxSseLineChars: number;\n private maxStreamTextChars: number;\n private maxToolArgumentsChars: number;\n\n constructor(options: DatabricksAdapterOptions) {\n this.maxSteps = options.maxSteps ?? 10;\n this.maxTokens = options.maxTokens ?? 4096;\n this.model = options.model;\n this.generationParams = options.generationParams ?? {};\n this.maxSseLineChars =\n options.maxSseLineChars ?? DEFAULT_MAX_SSE_LINE_CHARS;\n this.maxStreamTextChars =\n options.maxStreamTextChars ?? DEFAULT_MAX_STREAM_TEXT_CHARS;\n this.maxToolArgumentsChars =\n options.maxToolArgumentsChars ?? DEFAULT_MAX_TOOL_ARGUMENT_CHARS;\n\n if (isStreamBodyOptions(options)) {\n this.streamBody = options.streamBody;\n } else {\n const { endpointUrl, authenticate } = options;\n this.streamBody = async (body, signal) => {\n const fetchSignal =\n signal ?? AbortSignal.timeout(RAW_FETCH_DEFAULT_TIMEOUT_MS);\n const authHeaders = await authenticate();\n const response = await fetch(endpointUrl, {\n method: \"POST\",\n headers: {\n \"User-Agent\": APPKIT_USER_AGENT,\n \"Content-Type\": \"application/json\",\n ...authHeaders,\n },\n body: JSON.stringify(body),\n signal: fetchSignal,\n });\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"Unknown error\");\n throw new Error(\n `Databricks API error (${response.status}): ${errorText}`,\n );\n }\n if (!response.body) throw new Error(\"No response body\");\n return response.body;\n };\n }\n }\n\n /**\n * Creates a DatabricksAdapter for a Databricks Model Serving endpoint.\n *\n * Routes through the shared `connectors/serving/stream` helper, which\n * delegates to the SDK's `apiClient.request({ raw: true })`. That gives the\n * adapter centralised URL encoding + authentication with the rest of the\n * serving surface — no bespoke `fetch()` + `authenticate()` plumbing.\n */\n static async fromServingEndpoint(\n options: ServingEndpointOptions,\n ): Promise<DatabricksAdapter> {\n const {\n workspaceClient,\n endpointName,\n maxSteps,\n maxTokens,\n generationParams,\n maxSseLineChars,\n maxStreamTextChars,\n maxToolArgumentsChars,\n } = options;\n return new DatabricksAdapter({\n streamBody: (body, signal) =>\n // Cast through the structural shape: the connector types\n // `workspaceClient` as the SDK's concrete `WorkspaceClient`, but we\n // only need `apiClient.request`.\n servingStream(\n workspaceClient as unknown as Parameters<typeof servingStream>[0],\n endpointName,\n body,\n signal,\n ),\n maxSteps,\n maxTokens,\n generationParams,\n maxSseLineChars,\n maxStreamTextChars,\n maxToolArgumentsChars,\n });\n }\n\n /**\n * Creates a DatabricksAdapter from a Model Serving endpoint name.\n * Auto-creates a WorkspaceClient internally. Reads the endpoint name\n * from the argument or the `DATABRICKS_SERVING_ENDPOINT_NAME` env var.\n *\n * @example\n * ```ts\n * // Reads endpoint from DATABRICKS_SERVING_ENDPOINT_NAME env var\n * const adapter = await DatabricksAdapter.fromModelServing();\n *\n * // Explicit endpoint\n * const adapter = await DatabricksAdapter.fromModelServing(\"my-endpoint\");\n *\n * // With options\n * const adapter = await DatabricksAdapter.fromModelServing(\"my-endpoint\", {\n * maxSteps: 5,\n * maxTokens: 2048,\n * });\n * ```\n */\n static async fromModelServing(\n endpointName?: string,\n options?: ModelServingOptions,\n ): Promise<DatabricksAdapter> {\n const resolvedEndpoint =\n endpointName ?? process.env.DATABRICKS_SERVING_ENDPOINT_NAME;\n\n if (!resolvedEndpoint) {\n throw new Error(\n \"No endpoint name provided and DATABRICKS_SERVING_ENDPOINT_NAME env var is not set. \" +\n \"Pass an endpoint name or set DATABRICKS_SERVING_ENDPOINT_NAME.\",\n );\n }\n\n let workspaceClient: WorkspaceClientLike | undefined =\n options?.workspaceClient;\n if (!workspaceClient) {\n workspaceClient = createWorkspaceClient({\n clientOptions: getClientOptions(),\n }) as unknown as WorkspaceClientLike;\n }\n\n return DatabricksAdapter.fromServingEndpoint({\n workspaceClient,\n endpointName: resolvedEndpoint,\n maxSteps: options?.maxSteps,\n maxTokens: options?.maxTokens,\n generationParams: options?.generationParams,\n maxSseLineChars: options?.maxSseLineChars,\n maxStreamTextChars: options?.maxStreamTextChars,\n maxToolArgumentsChars: options?.maxToolArgumentsChars,\n });\n }\n\n /**\n * Creates a DatabricksAdapter that talks to the Databricks AI Gateway\n * Chat Completions endpoint (`/ai-gateway/mlflow/v1/chat/completions`).\n *\n * Unlike {@link fromModelServing}, the target model is named in the request\n * body (`model`, e.g. `\"system.ai.claude-opus-5-5\"`) rather than in the URL:\n * the gateway is a single fixed path that routes by the body's `model`. Auth\n * and transport reuse the SDK's `apiClient.request`, same as the serving\n * path, so no bespoke `fetch()` + token handling. The request/response wire\n * format and tool-calling loop are identical to the serving path.\n *\n * @example\n * ```ts\n * import { createApp, createAgent } from \"@databricks/appkit\";\n * import { agents, DatabricksAdapter } from \"@databricks/appkit/beta\";\n *\n * const adapter = await DatabricksAdapter.fromAiGateway({\n * model: \"system.ai.claude-opus-5-5\",\n * });\n *\n * await createApp({\n * plugins: [\n * agents({\n * agents: {\n * assistant: createAgent({\n * instructions: \"You are a helpful assistant.\",\n * model: adapter,\n * }),\n * },\n * }),\n * ],\n * });\n * ```\n */\n static async fromAiGateway(\n options: AiGatewayOptions,\n ): Promise<DatabricksAdapter> {\n const {\n model,\n workspaceClient,\n maxSteps,\n maxTokens,\n generationParams,\n maxSseLineChars,\n maxStreamTextChars,\n maxToolArgumentsChars,\n } = options;\n\n const client =\n workspaceClient ??\n (createWorkspaceClient({\n clientOptions: getClientOptions(),\n }) as unknown as WorkspaceClientLike);\n\n return new DatabricksAdapter({\n streamBody: (body, signal) =>\n // Same structural cast as `fromServingEndpoint`: the connector types\n // the client as the SDK's `WorkspaceClient`, but we only need\n // `apiClient.request`.\n streamAiGateway(\n client as unknown as Parameters<typeof streamAiGateway>[0],\n body,\n signal,\n ),\n model,\n maxSteps,\n maxTokens,\n generationParams,\n maxSseLineChars,\n maxStreamTextChars,\n maxToolArgumentsChars,\n });\n }\n\n /**\n * Discoverability shim for the Supervisor API adapter. Returns an\n * {@link AgentAdapter} (a `SupervisorApiAdapter` at runtime), NOT a\n * {@link DatabricksAdapter} — the two are separate classes (different\n * wire formats, different lifecycle). The return type is the\n * {@link AgentAdapter} interface so callers aren't bound to the concrete\n * class. Surfaced here so application developers see a single\n * `DatabricksAdapter.from*` autocomplete root.\n *\n * Dynamic-imports `./supervisor-api` to avoid forming a load-time cycle:\n * both files share `connectors/serving/client.ts`.\n *\n * @example\n * ```ts\n * import { DatabricksAdapter } from \"@databricks/appkit/beta\";\n *\n * const model = await DatabricksAdapter.fromSupervisorApi({\n * model: \"databricks-claude-sonnet-4-5\",\n * });\n * ```\n */\n static async fromSupervisorApi(\n options: import(\"./supervisor-api\").SupervisorApiAdapterOptions,\n ): Promise<AgentAdapter> {\n const { fromSupervisorApi } = await import(\"./supervisor-api\");\n return fromSupervisorApi(options);\n }\n\n async *run(\n input: AgentInput,\n context: AgentRunContext,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n // Databricks API requires tool names to match [a-zA-Z0-9_-].\n // Our tool names use dots (e.g. \"analytics.query\"), so we swap dots\n // for double-underscores in the wire format and map back on receipt.\n const nameToWire = new Map<string, string>();\n const wireToName = new Map<string, string>();\n for (const tool of input.tools) {\n const wire = tool.name.replace(/\\./g, \"__\");\n if (wireToName.has(wire) && wireToName.get(wire) !== tool.name) {\n throw new Error(\n `Tool name collision: '${tool.name}' and '${wireToName.get(wire)}' both map to wire name '${wire}'`,\n );\n }\n nameToWire.set(tool.name, wire);\n wireToName.set(wire, tool.name);\n }\n\n const tools = this.buildTools(input.tools, nameToWire);\n const messages = this.buildMessages(input.messages, nameToWire);\n\n yield { type: \"status\", status: \"running\" };\n\n for (let step = 0; step < this.maxSteps; step++) {\n if (context.signal?.aborted) break;\n\n const { text, toolCalls } = yield* this.streamCompletion(\n messages,\n tools,\n context,\n );\n\n if (toolCalls.length === 0) {\n const parsed = parseTextToolCalls(text);\n if (parsed.length > 0) {\n yield* this.executeToolCalls(parsed, messages, context, nameToWire);\n continue;\n }\n break;\n }\n\n messages.push({\n role: \"assistant\",\n content: text || null,\n tool_calls: toolCalls,\n });\n\n for (const tc of toolCalls) {\n const wireName = tc.function.name;\n const originalName = wireToName.get(wireName) ?? wireName;\n yield* this.executeSingleTool(tc, originalName, messages, context);\n }\n }\n }\n\n /** Parse wire arguments, emit tool_call / tool_result, append tool messages. */\n private async *executeSingleTool(\n tc: OpenAIToolCall,\n originalName: string,\n messages: OpenAIMessage[],\n context: AgentRunContext,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n let args: unknown;\n try {\n args = JSON.parse(tc.function.arguments);\n } catch {\n args = {};\n }\n\n yield { type: \"tool_call\", callId: tc.id, name: originalName, args };\n\n try {\n const result = await context.executeTool(originalName, args);\n const resultStr =\n typeof result === \"string\" ? result : JSON.stringify(result);\n\n yield { type: \"tool_result\", callId: tc.id, result };\n\n messages.push({\n role: \"tool\",\n content: resultStr,\n tool_call_id: tc.id,\n });\n } catch (error) {\n const errMsg =\n error instanceof Error ? error.message : \"Tool execution failed\";\n\n yield {\n type: \"tool_result\",\n callId: tc.id,\n result: null,\n error: errMsg,\n };\n\n messages.push({\n role: \"tool\",\n content: JSON.stringify({ error: errMsg }),\n tool_call_id: tc.id,\n });\n }\n }\n\n private async *streamCompletion(\n messages: OpenAIMessage[],\n tools: OpenAITool[],\n context: AgentRunContext,\n ): AsyncGenerator<\n AgentEvent,\n { text: string; toolCalls: OpenAIToolCall[] },\n unknown\n > {\n const body: Record<string, unknown> = {\n messages,\n stream: true,\n max_tokens: this.maxTokens,\n };\n\n // AI Gateway routes by the body's `model`; serving endpoints name it in\n // the URL and leave this unset.\n if (this.model) body.model = this.model;\n\n applyGenerationParams(body, this.generationParams);\n\n if (tools.length > 0) {\n body.tools = tools;\n }\n\n let responseBody: ReadableStream<Uint8Array>;\n try {\n responseBody = await this.streamBody(body, context.signal);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Stream request failed\";\n yield { type: \"status\", status: \"error\", error: msg };\n throw err;\n }\n\n const reader = responseBody.getReader();\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let fullText = \"\";\n const toolCallAccumulator = new Map<\n number,\n {\n id: string;\n name: string;\n arguments: string;\n thoughtSignature?: string;\n }\n >();\n\n try {\n while (true) {\n if (context.signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n if (buffer.length > this.maxSseLineChars) {\n throw new Error(\n `DatabricksAdapter: SSE line buffer exceeds configured limit (${this.maxSseLineChars} UTF-16 code units)`,\n );\n }\n\n for (const line of lines) {\n if (line.length > this.maxSseLineChars) {\n throw new Error(\n `DatabricksAdapter: SSE line exceeds configured limit (${this.maxSseLineChars} UTF-16 code units)`,\n );\n }\n\n const trimmed = line.trim();\n if (!trimmed.startsWith(\"data: \")) continue;\n const data = trimmed.slice(6);\n if (data === \"[DONE]\") continue;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch (parseErr) {\n console.debug(\n \"[DatabricksAdapter] malformed SSE data line JSON\",\n { line: `${data.slice(0, 256)}${data.length > 256 ? \"…\" : \"\"}` },\n parseErr,\n );\n continue;\n }\n\n const deltaUnknown = openAiChoicesDelta(parsed);\n if (!isRecord(deltaUnknown)) continue;\n\n // gpt-oss (harmony) sends delta.content as an array of parts\n // (text = answer, reasoning = chain-of-thought), not a string.\n const contentUnknown = deltaUnknown.content;\n const parts =\n typeof contentUnknown === \"string\"\n ? [{ type: \"text\", text: contentUnknown }]\n : Array.isArray(contentUnknown)\n ? contentUnknown\n : [];\n\n for (const part of parts) {\n if (!isRecord(part)) continue;\n if (part.type === \"text\" && typeof part.text === \"string\") {\n const content = part.text;\n throwIfExceedsStreamLimit(\n \"streamed assistant text\",\n fullText.length,\n content,\n this.maxStreamTextChars,\n );\n fullText += content;\n yield { type: \"message_delta\" as const, content };\n } else if (part.type === \"reasoning\") {\n const content = reasoningPartText(part);\n if (content) yield { type: \"thinking\" as const, content };\n }\n }\n\n const toolCallsRaw = deltaUnknown.tool_calls;\n if (!Array.isArray(toolCallsRaw)) continue;\n\n for (const tc of toolCallsRaw) {\n if (!isStreamingDeltaToolCall(tc)) continue;\n const sig = tc.thoughtSignature;\n const existing = toolCallAccumulator.get(tc.index);\n if (existing) {\n if (tc.function?.arguments) {\n throwIfExceedsStreamLimit(\n \"tool call arguments\",\n existing.arguments.length,\n tc.function.arguments,\n this.maxToolArgumentsChars,\n );\n existing.arguments += tc.function.arguments;\n }\n if (sig && !existing.thoughtSignature) {\n existing.thoughtSignature = sig;\n }\n } else {\n const initial = tc.function?.arguments ?? \"\";\n if (initial.length > this.maxToolArgumentsChars) {\n throw new Error(\n `DatabricksAdapter: tool call arguments exceed configured limit (${this.maxToolArgumentsChars} UTF-16 code units)`,\n );\n }\n toolCallAccumulator.set(tc.index, {\n id: tc.id ?? `call_${tc.index}`,\n name: tc.function?.name ?? \"\",\n arguments: initial,\n ...(sig ? { thoughtSignature: sig } : {}),\n });\n }\n }\n }\n }\n } finally {\n try {\n await reader.cancel();\n } catch (cancelErr) {\n console.debug(\n \"[DatabricksAdapter] reader.cancel() failed during teardown\",\n cancelErr,\n );\n }\n try {\n reader.releaseLock();\n } catch (unlockErr) {\n console.debug(\n \"[DatabricksAdapter] reader.releaseLock() failed during teardown\",\n unlockErr,\n );\n }\n }\n\n const toolCalls: OpenAIToolCall[] = Array.from(\n toolCallAccumulator.values(),\n ).map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments || \"{}\" },\n ...(tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}),\n }));\n\n return { text: fullText, toolCalls };\n }\n\n private async *executeToolCalls(\n calls: Array<{ name: string; args: unknown }>,\n messages: OpenAIMessage[],\n context: AgentRunContext,\n nameToWire: Map<string, string>,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n const wireToolName = (name: string) =>\n nameToWire.get(name) ?? name.replace(/\\./g, \"__\");\n\n const toolCallObjs: OpenAIToolCall[] = calls.map((c, i) => ({\n id: `text_call_${i}`,\n type: \"function\" as const,\n function: {\n name: wireToolName(c.name),\n arguments: JSON.stringify(c.args),\n },\n }));\n\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: toolCallObjs,\n });\n\n for (let i = 0; i < toolCallObjs.length; i++) {\n const tc = toolCallObjs[i];\n const originalName = calls[i]?.name ?? tc.function.name;\n yield* this.executeSingleTool(tc, originalName, messages, context);\n }\n }\n\n /**\n * Maps AppKit {@link AgentInput} messages into OpenAI-compatible wire messages.\n * Preserves multi-turn tool state (`toolCalls` → `tool_calls`, `toolCallId` →\n * `tool_call_id`) so resumed threads and hydrated history reach the model.\n */\n private buildMessages(\n messages: AgentInput[\"messages\"],\n nameToWire: Map<string, string>,\n ): OpenAIMessage[] {\n const wireToolName = (name: string) =>\n nameToWire.get(name) ?? name.replace(/\\./g, \"__\");\n\n return messages.map((m) => {\n let content: string | null = m.content;\n if (\n m.role === \"assistant\" &&\n m.toolCalls &&\n m.toolCalls.length > 0 &&\n (!m.content || m.content.trim() === \"\")\n ) {\n content = null;\n }\n\n const out: OpenAIMessage = {\n role: m.role as OpenAIMessage[\"role\"],\n content,\n };\n\n if (m.toolCallId) {\n out.tool_call_id = m.toolCallId;\n }\n\n if (m.toolCalls && m.toolCalls.length > 0) {\n out.tool_calls = m.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: wireToolName(tc.name),\n arguments:\n typeof tc.args === \"string\"\n ? tc.args\n : JSON.stringify(tc.args ?? {}),\n },\n ...(tc.thoughtSignature\n ? { thoughtSignature: tc.thoughtSignature }\n : {}),\n }));\n }\n\n return out;\n });\n }\n\n private buildTools(\n definitions: AgentToolDefinition[],\n nameToWire: Map<string, string>,\n ): OpenAITool[] {\n return definitions.map((def) => ({\n type: \"function\" as const,\n function: {\n name: nameToWire.get(def.name) ?? def.name,\n description: def.description,\n parameters: def.parameters,\n },\n }));\n }\n}\n\n/**\n * Adapter knobs an agent definition contributes to string-model resolution.\n * A subset of {@link AiGatewayOptions} / {@link ModelServingOptions} — the two\n * factories accept the same fields, so one object routes to either.\n */\ntype ModelStringOptions = Pick<\n AiGatewayOptions,\n \"maxSteps\" | \"maxTokens\" | \"generationParams\"\n>;\n\n/**\n * Resolves a string `model` to a {@link DatabricksAdapter}, routing by name:\n *\n * - UC model names (`system.*`, e.g. `\"system.ai.claude-opus-5-5\"`) → AI\n * Gateway ({@link DatabricksAdapter.fromAiGateway}); the model is named in\n * the request body.\n * - Everything else — serving-endpoint names like\n * `\"databricks-claude-sonnet-4-5\"` or a custom endpoint → Model Serving\n * ({@link DatabricksAdapter.fromModelServing}); the name goes in the URL.\n *\n * Serving-endpoint names are `[a-zA-Z0-9_-]` (no dots), so the `system.`\n * prefix cleanly separates the two namespaces and no existing endpoint name\n * changes routing. This is the single decision point shared by the agents\n * plugin and standalone `runAgent`, so the two never drift. To force a\n * `system.*` name onto a serving endpoint instead, pass a pre-built adapter\n * (`DatabricksAdapter.fromServingEndpoint(...)`) as the agent's `model`.\n */\nexport function adapterFromModelString(\n model: string,\n options?: ModelStringOptions,\n): Promise<DatabricksAdapter> {\n return model.startsWith(\"system.\")\n ? DatabricksAdapter.fromAiGateway({ model, ...options })\n : DatabricksAdapter.fromModelServing(model, options);\n}\n\n// ---------------------------------------------------------------------------\n// Text-based tool call parsing (fallback)\n// ---------------------------------------------------------------------------\n\n/**\n * Parses text-based tool calls from model output.\n *\n * Handles two formats:\n * 1. Llama native: `[{\"name\": \"tool_name\", \"parameters\": {\"arg\": \"val\"}}]`\n * 2. Python-style: `[tool_name(arg1='val1', arg2='val2')]`\n */\nexport function parseTextToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n const trimmed = text.trim();\n\n const jsonResult = tryParseLlamaJsonToolCalls(trimmed);\n if (jsonResult.length > 0) return jsonResult;\n\n const pyResult = tryParsePythonStyleToolCalls(trimmed);\n if (pyResult.length > 0) return pyResult;\n\n return [];\n}\n\nfunction isLlamaToolJsonItem(value: unknown): value is Record<\n string,\n unknown\n> & {\n name: string;\n} {\n if (!isRecord(value)) return false;\n return typeof value.name === \"string\";\n}\n\nfunction tryParseLlamaJsonToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n const slice = extractLlamaToolJsonSlice(text);\n if (!slice) return [];\n\n try {\n const parsed: unknown = JSON.parse(slice);\n if (!Array.isArray(parsed)) return [];\n\n return parsed.filter(isLlamaToolJsonItem).map((item) => ({\n name: item.name,\n args: item.parameters ?? item.arguments ?? item.args ?? {},\n }));\n } catch {\n return [];\n }\n}\n\nfunction tryParsePythonStyleToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n if (text.length > PYTHON_STYLE_TOOL_PARSE_MAX_INPUT) {\n return [];\n }\n\n const pattern = /\\[?([a-zA-Z_][\\w.]*)\\(([^)]*)\\)\\]?/g;\n const results: Array<{ name: string; args: unknown }> = [];\n\n for (const match of text.matchAll(pattern)) {\n const name = match[1];\n const argsStr = match[2];\n\n const args: Record<string, unknown> = {};\n const argPattern = /(\\w+)\\s*=\\s*(?:'([^']*)'|\"([^\"]*)\"|(\\S+))/g;\n for (const argMatch of argsStr.matchAll(argPattern)) {\n const key = argMatch[1];\n const value = argMatch[2] ?? argMatch[3] ?? argMatch[4];\n args[key] = value;\n }\n\n results.push({ name, args });\n }\n\n return results;\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  //#region package.json
2
2
  var name = "@databricks/appkit";
3
- var version = "0.77.0";
3
+ var version = "0.79.0";
4
4
 
5
5
  //#endregion
6
6
  export { name, version };
@@ -63,7 +63,23 @@ async function stream(client, endpointName, body, signal) {
63
63
  stream: true
64
64
  }, signal);
65
65
  }
66
+ /**
67
+ * Returns the raw SSE byte stream from the Databricks AI Gateway Chat
68
+ * Completions endpoint. Thin wrapper over {@link streamPath} that hard-codes
69
+ * the gateway path and forces `stream: true`.
70
+ *
71
+ * Unlike a serving endpoint, the target model is named in the request body
72
+ * (`body.model`, e.g. `"system.ai.claude-opus-5-5"`) — the gateway is a single
73
+ * fixed path that routes by the body's `model`, so the caller sets it there.
74
+ */
75
+ async function streamAiGateway(client, body, signal) {
76
+ const { stream: _stream, ...cleanBody } = body;
77
+ return streamPath(client, "/ai-gateway/mlflow/v1/chat/completions", {
78
+ ...cleanBody,
79
+ stream: true
80
+ }, signal);
81
+ }
66
82
 
67
83
  //#endregion
68
- export { invoke, stream, streamPath };
84
+ export { invoke, stream, streamAiGateway, streamPath };
69
85
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":[],"sources":["../../../src/connectors/serving/client.ts"],"sourcesContent":["import { createLogger } from \"../../logging/logger\";\nimport type { serving, WorkspaceClient } from \"../../workspace-client\";\nimport { contextFromAbortSignal } from \"../context\";\n\nconst logger = createLogger(\"connectors:serving\");\n\n/**\n * Structural shape of a Databricks SDK client we need for the low-level\n * `apiClient.request` call. Lets `streamPath` be reused by adapters that\n * don't want a hard dependency on the concrete `WorkspaceClient` type.\n */\nexport interface ApiClientLike {\n apiClient: {\n request(\n options: Record<string, unknown>,\n context?: unknown,\n ): Promise<unknown>;\n };\n}\n\n/**\n * Transport shim shared by the agent adapters: given a request body, returns\n * the raw SSE byte stream from a serving / AI-gateway endpoint. Injected at\n * adapter construction time so callers can swap in the workspace SDK (the\n * factory paths via {@link streamPath}), a bare `fetch` (a reverse proxy /\n * mock), or a test fake.\n */\nexport type StreamBody = (\n body: Record<string, unknown>,\n signal?: AbortSignal,\n) => Promise<ReadableStream<Uint8Array>>;\n\n/**\n * Invokes a serving endpoint using the SDK's high-level query API.\n * Returns a typed QueryEndpointResponse.\n */\nexport async function invoke(\n client: WorkspaceClient,\n endpointName: string,\n body: Record<string, unknown>,\n): Promise<serving.QueryEndpointResponse> {\n // Strip `stream` from the body — the connector controls this\n const { stream: _stream, ...cleanBody } = body;\n\n logger.debug(\"Invoking endpoint %s\", endpointName);\n\n return client.servingEndpoints.query({\n name: endpointName,\n ...cleanBody,\n } as serving.QueryEndpointInput);\n}\n\n/**\n * POSTs `body` as JSON to an arbitrary workspace API path and returns the raw\n * SSE byte stream. No parsing is performed — bytes are passed through as-is.\n *\n * Uses the SDK's low-level `apiClient.request({ raw: true })` so callers\n * inherit URL resolution, the SDK credential chain (PAT/OAuth/OIDC), and\n * any future retries/telemetry baked into the SDK transport.\n *\n * When `signal` is provided it is bridged to the SDK's `Context` /\n * `CancellationToken` so aborts cancel the outbound HTTP request.\n *\n * @internal\n *\n * Not part of the public AppKit surface. `path` is passed through to the\n * SDK without any allowlist — exposing this to user-controlled input would\n * turn it into workspace-credentialled SSRF (CWE-918). Internal callers\n * must hard-code the path (or build it from a closed enum). New callers\n * inside the package: keep this constraint, and do not re-export from\n * `beta.ts` or any other entry point.\n */\nexport async function streamPath(\n client: ApiClientLike,\n path: string,\n body: Record<string, unknown>,\n signal?: AbortSignal,\n): Promise<ReadableStream<Uint8Array>> {\n logger.debug(\"Streaming from path %s\", path);\n\n const context = contextFromAbortSignal(signal);\n\n const response = (await client.apiClient.request(\n {\n path,\n method: \"POST\",\n headers: new Headers({\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n }),\n payload: body,\n raw: true,\n },\n context,\n )) as { contents: ReadableStream<Uint8Array> | null };\n\n if (!response.contents) {\n throw new Error(\"Response body is null — streaming not supported\");\n }\n\n return response.contents;\n}\n\n/**\n * Returns the raw SSE byte stream from a serving endpoint. Thin wrapper over\n * {@link streamPath} that handles serving-specific URL encoding and forces\n * `stream: true` in the payload.\n */\nexport async function stream(\n client: WorkspaceClient,\n endpointName: string,\n body: Record<string, unknown>,\n signal?: AbortSignal,\n): Promise<ReadableStream<Uint8Array>> {\n const { stream: _stream, ...cleanBody } = body;\n return streamPath(\n client as unknown as ApiClientLike,\n `/serving-endpoints/${encodeURIComponent(endpointName)}/invocations`,\n { ...cleanBody, stream: true },\n signal,\n );\n}\n"],"mappings":";;;;AAIA,MAAM,SAAS,aAAa,qBAAqB;;;;;AAgCjD,eAAsB,OACpB,QACA,cACA,MACwC;CAExC,MAAM,EAAE,QAAQ,SAAS,GAAG,cAAc;AAE1C,QAAO,MAAM,wBAAwB,aAAa;AAElD,QAAO,OAAO,iBAAiB,MAAM;EACnC,MAAM;EACN,GAAG;EACJ,CAA+B;;;;;;;;;;;;;;;;;;;;;;AAuBlC,eAAsB,WACpB,QACA,MACA,MACA,QACqC;AACrC,QAAO,MAAM,0BAA0B,KAAK;CAE5C,MAAM,UAAU,uBAAuB,OAAO;CAE9C,MAAM,WAAY,MAAM,OAAO,UAAU,QACvC;EACE;EACA,QAAQ;EACR,SAAS,IAAI,QAAQ;GACnB,gBAAgB;GAChB,QAAQ;GACT,CAAC;EACF,SAAS;EACT,KAAK;EACN,EACD,QACD;AAED,KAAI,CAAC,SAAS,SACZ,OAAM,IAAI,MAAM,kDAAkD;AAGpE,QAAO,SAAS;;;;;;;AAQlB,eAAsB,OACpB,QACA,cACA,MACA,QACqC;CACrC,MAAM,EAAE,QAAQ,SAAS,GAAG,cAAc;AAC1C,QAAO,WACL,QACA,sBAAsB,mBAAmB,aAAa,CAAC,eACvD;EAAE,GAAG;EAAW,QAAQ;EAAM,EAC9B,OACD"}
1
+ {"version":3,"file":"client.js","names":[],"sources":["../../../src/connectors/serving/client.ts"],"sourcesContent":["import { createLogger } from \"../../logging/logger\";\nimport type { serving, WorkspaceClient } from \"../../workspace-client\";\nimport { contextFromAbortSignal } from \"../context\";\n\nconst logger = createLogger(\"connectors:serving\");\n\n/**\n * Structural shape of a Databricks SDK client we need for the low-level\n * `apiClient.request` call. Lets `streamPath` be reused by adapters that\n * don't want a hard dependency on the concrete `WorkspaceClient` type.\n */\nexport interface ApiClientLike {\n apiClient: {\n request(\n options: Record<string, unknown>,\n context?: unknown,\n ): Promise<unknown>;\n };\n}\n\n/**\n * Transport shim shared by the agent adapters: given a request body, returns\n * the raw SSE byte stream from a serving / AI-gateway endpoint. Injected at\n * adapter construction time so callers can swap in the workspace SDK (the\n * factory paths via {@link streamPath}), a bare `fetch` (a reverse proxy /\n * mock), or a test fake.\n */\nexport type StreamBody = (\n body: Record<string, unknown>,\n signal?: AbortSignal,\n) => Promise<ReadableStream<Uint8Array>>;\n\n/**\n * Invokes a serving endpoint using the SDK's high-level query API.\n * Returns a typed QueryEndpointResponse.\n */\nexport async function invoke(\n client: WorkspaceClient,\n endpointName: string,\n body: Record<string, unknown>,\n): Promise<serving.QueryEndpointResponse> {\n // Strip `stream` from the body — the connector controls this\n const { stream: _stream, ...cleanBody } = body;\n\n logger.debug(\"Invoking endpoint %s\", endpointName);\n\n return client.servingEndpoints.query({\n name: endpointName,\n ...cleanBody,\n } as serving.QueryEndpointInput);\n}\n\n/**\n * POSTs `body` as JSON to an arbitrary workspace API path and returns the raw\n * SSE byte stream. No parsing is performed — bytes are passed through as-is.\n *\n * Uses the SDK's low-level `apiClient.request({ raw: true })` so callers\n * inherit URL resolution, the SDK credential chain (PAT/OAuth/OIDC), and\n * any future retries/telemetry baked into the SDK transport.\n *\n * When `signal` is provided it is bridged to the SDK's `Context` /\n * `CancellationToken` so aborts cancel the outbound HTTP request.\n *\n * @internal\n *\n * Not part of the public AppKit surface. `path` is passed through to the\n * SDK without any allowlist — exposing this to user-controlled input would\n * turn it into workspace-credentialled SSRF (CWE-918). Internal callers\n * must hard-code the path (or build it from a closed enum). New callers\n * inside the package: keep this constraint, and do not re-export from\n * `beta.ts` or any other entry point.\n */\nexport async function streamPath(\n client: ApiClientLike,\n path: string,\n body: Record<string, unknown>,\n signal?: AbortSignal,\n): Promise<ReadableStream<Uint8Array>> {\n logger.debug(\"Streaming from path %s\", path);\n\n const context = contextFromAbortSignal(signal);\n\n const response = (await client.apiClient.request(\n {\n path,\n method: \"POST\",\n headers: new Headers({\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n }),\n payload: body,\n raw: true,\n },\n context,\n )) as { contents: ReadableStream<Uint8Array> | null };\n\n if (!response.contents) {\n throw new Error(\"Response body is null — streaming not supported\");\n }\n\n return response.contents;\n}\n\n/**\n * Returns the raw SSE byte stream from a serving endpoint. Thin wrapper over\n * {@link streamPath} that handles serving-specific URL encoding and forces\n * `stream: true` in the payload.\n */\nexport async function stream(\n client: WorkspaceClient,\n endpointName: string,\n body: Record<string, unknown>,\n signal?: AbortSignal,\n): Promise<ReadableStream<Uint8Array>> {\n const { stream: _stream, ...cleanBody } = body;\n return streamPath(\n client as unknown as ApiClientLike,\n `/serving-endpoints/${encodeURIComponent(endpointName)}/invocations`,\n { ...cleanBody, stream: true },\n signal,\n );\n}\n\n/**\n * Returns the raw SSE byte stream from the Databricks AI Gateway Chat\n * Completions endpoint. Thin wrapper over {@link streamPath} that hard-codes\n * the gateway path and forces `stream: true`.\n *\n * Unlike a serving endpoint, the target model is named in the request body\n * (`body.model`, e.g. `\"system.ai.claude-opus-5-5\"`) — the gateway is a single\n * fixed path that routes by the body's `model`, so the caller sets it there.\n */\nexport async function streamAiGateway(\n client: ApiClientLike,\n body: Record<string, unknown>,\n signal?: AbortSignal,\n): Promise<ReadableStream<Uint8Array>> {\n const { stream: _stream, ...cleanBody } = body;\n return streamPath(\n client,\n \"/ai-gateway/mlflow/v1/chat/completions\",\n { ...cleanBody, stream: true },\n signal,\n );\n}\n"],"mappings":";;;;AAIA,MAAM,SAAS,aAAa,qBAAqB;;;;;AAgCjD,eAAsB,OACpB,QACA,cACA,MACwC;CAExC,MAAM,EAAE,QAAQ,SAAS,GAAG,cAAc;AAE1C,QAAO,MAAM,wBAAwB,aAAa;AAElD,QAAO,OAAO,iBAAiB,MAAM;EACnC,MAAM;EACN,GAAG;EACJ,CAA+B;;;;;;;;;;;;;;;;;;;;;;AAuBlC,eAAsB,WACpB,QACA,MACA,MACA,QACqC;AACrC,QAAO,MAAM,0BAA0B,KAAK;CAE5C,MAAM,UAAU,uBAAuB,OAAO;CAE9C,MAAM,WAAY,MAAM,OAAO,UAAU,QACvC;EACE;EACA,QAAQ;EACR,SAAS,IAAI,QAAQ;GACnB,gBAAgB;GAChB,QAAQ;GACT,CAAC;EACF,SAAS;EACT,KAAK;EACN,EACD,QACD;AAED,KAAI,CAAC,SAAS,SACZ,OAAM,IAAI,MAAM,kDAAkD;AAGpE,QAAO,SAAS;;;;;;;AAQlB,eAAsB,OACpB,QACA,cACA,MACA,QACqC;CACrC,MAAM,EAAE,QAAQ,SAAS,GAAG,cAAc;AAC1C,QAAO,WACL,QACA,sBAAsB,mBAAmB,aAAa,CAAC,eACvD;EAAE,GAAG;EAAW,QAAQ;EAAM,EAC9B,OACD;;;;;;;;;;;AAYH,eAAsB,gBACpB,QACA,MACA,QACqC;CACrC,MAAM,EAAE,QAAQ,SAAS,GAAG,cAAc;AAC1C,QAAO,WACL,QACA,0CACA;EAAE,GAAG;EAAW,QAAQ;EAAM,EAC9B,OACD"}
@@ -121,16 +121,16 @@ async function initStandalonePlugins(plugins, cache) {
121
121
  }
122
122
  }
123
123
  async function resolveAdapter(def) {
124
- const { model } = def;
125
- if (!model) {
124
+ const source = def.model ?? process.env.DATABRICKS_SERVING_ENDPOINT_NAME;
125
+ if (!source) {
126
126
  const { DatabricksAdapter } = await import("../../agents/databricks.js");
127
127
  return DatabricksAdapter.fromModelServing();
128
128
  }
129
- if (typeof model === "string") {
130
- const { DatabricksAdapter } = await import("../../agents/databricks.js");
131
- return DatabricksAdapter.fromModelServing(model);
129
+ if (typeof source === "string") {
130
+ const { adapterFromModelString } = await import("../../agents/databricks.js");
131
+ return adapterFromModelString(source);
132
132
  }
133
- return await model;
133
+ return await source;
134
134
  }
135
135
  function normalizeMessages(input, instructions) {
136
136
  const systemMessage = {