@dbx-tools/shared-model 0.3.44 → 0.4.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.
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Wire-format types for the model toolkit: the model class taxonomy,
3
+ * the Foundation Model API score profile, the minimal serving-endpoint
4
+ * descriptor, and the model-lookup request / ranked-result contract.
5
+ * Pure types and zod schemas - no Node-only imports - so a browser
6
+ * client (or an agent tool's `inputSchema`) can validate a lookup
7
+ * request and type a ranked response without dragging in server
8
+ * dependencies.
9
+ *
10
+ * @module
11
+ */
12
+ import { z } from "zod";
13
+ /**
14
+ * Class of a Databricks Model Serving endpoint - its modality plus,
15
+ * for chat models, a capability band. Chat endpoints split by measured
16
+ * `quality` into a three-rung ladder; embeddings are a separate
17
+ * modality, not a rung on that ladder.
18
+ *
19
+ * String enum so the value is the slug used in cache keys, logs, query
20
+ * strings, and serialized configs - and is what a client sends to
21
+ * request a class.
22
+ */
23
+ export declare enum ModelClass {
24
+ ChatThinking = "chat-thinking",
25
+ ChatBalanced = "chat-balanced",
26
+ ChatFast = "chat-fast",
27
+ Embedding = "embedding"
28
+ }
29
+ export declare const ModelClassSchema: z.ZodEnum<typeof ModelClass>;
30
+ /**
31
+ * Foundation Model API quality / speed / cost scores Databricks
32
+ * publishes per pay-per-token endpoint (the bars shown in the AI
33
+ * Playground). Surfaced from the serving list under
34
+ * `config.served_entities[].foundation_model.ai_gateway_model_profile`
35
+ * so the class classifier can rank models from live, workspace-local
36
+ * numbers instead of a hand-maintained table. Scales are relative and
37
+ * unitless; all axes are absent for custom or brand-new endpoints that
38
+ * have no profile yet.
39
+ */
40
+ export declare const ModelProfileSchema: z.ZodObject<{
41
+ quality: z.ZodOptional<z.ZodNumber>;
42
+ speed: z.ZodOptional<z.ZodNumber>;
43
+ cost: z.ZodOptional<z.ZodNumber>;
44
+ }, z.core.$strip>;
45
+ export type ModelProfile = z.infer<typeof ModelProfileSchema>;
46
+ /**
47
+ * Minimal descriptor for a Databricks Model Serving endpoint - a
48
+ * stable subset of the SDK type so cache hits and `/models` responses
49
+ * never expose SDK internals.
50
+ */
51
+ export declare const ServingEndpointSummarySchema: z.ZodObject<{
52
+ name: z.ZodString;
53
+ displayName: z.ZodOptional<z.ZodString>;
54
+ task: z.ZodOptional<z.ZodString>;
55
+ state: z.ZodOptional<z.ZodString>;
56
+ description: z.ZodOptional<z.ZodString>;
57
+ profile: z.ZodOptional<z.ZodObject<{
58
+ quality: z.ZodOptional<z.ZodNumber>;
59
+ speed: z.ZodOptional<z.ZodNumber>;
60
+ cost: z.ZodOptional<z.ZodNumber>;
61
+ }, z.core.$strip>>;
62
+ class: z.ZodOptional<z.ZodEnum<typeof ModelClass>>;
63
+ dimension: z.ZodOptional<z.ZodNumber>;
64
+ }, z.core.$strip>;
65
+ export type ServingEndpointSummary = z.infer<typeof ServingEndpointSummarySchema>;
66
+ /**
67
+ * A model-lookup request: search string, capability ceiling, both, or
68
+ * nothing. This is the caller-facing query the `@dbx-tools/model`
69
+ * ranker reads - shaped as a zod schema (with per-field descriptions)
70
+ * so an agent tool can adopt it directly as its `inputSchema` and a
71
+ * browser client can validate args before calling the model service.
72
+ *
73
+ * Every field is optional: an empty query ranks the live catalogue by
74
+ * class alone. The "this class and below" ceiling semantics and the
75
+ * match-then-class ordering live in the service, not here - this
76
+ * surface is declarative.
77
+ */
78
+ export declare const ModelQuerySchema: z.ZodObject<{
79
+ search: z.ZodOptional<z.ZodString>;
80
+ modelClass: z.ZodOptional<z.ZodEnum<typeof ModelClass>>;
81
+ limit: z.ZodOptional<z.ZodNumber>;
82
+ threshold: z.ZodOptional<z.ZodNumber>;
83
+ }, z.core.$strip>;
84
+ export type ModelQuery = z.infer<typeof ModelQuerySchema>;
85
+ /**
86
+ * One ranked lookup result. Backs an agent tool's `outputSchema` and a
87
+ * `/models` ranked response.
88
+ */
89
+ export declare const RankedModelSchema: z.ZodObject<{
90
+ endpoint: z.ZodObject<{
91
+ name: z.ZodString;
92
+ displayName: z.ZodOptional<z.ZodString>;
93
+ task: z.ZodOptional<z.ZodString>;
94
+ state: z.ZodOptional<z.ZodString>;
95
+ description: z.ZodOptional<z.ZodString>;
96
+ profile: z.ZodOptional<z.ZodObject<{
97
+ quality: z.ZodOptional<z.ZodNumber>;
98
+ speed: z.ZodOptional<z.ZodNumber>;
99
+ cost: z.ZodOptional<z.ZodNumber>;
100
+ }, z.core.$strip>>;
101
+ class: z.ZodOptional<z.ZodEnum<typeof ModelClass>>;
102
+ dimension: z.ZodOptional<z.ZodNumber>;
103
+ }, z.core.$strip>;
104
+ modelClass: z.ZodEnum<typeof ModelClass>;
105
+ score: z.ZodOptional<z.ZodNumber>;
106
+ }, z.core.$strip>;
107
+ export type RankedModel = z.infer<typeof RankedModelSchema>;
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Wire-format types for the model toolkit: the model class taxonomy,
3
+ * the Foundation Model API score profile, the minimal serving-endpoint
4
+ * descriptor, and the model-lookup request / ranked-result contract.
5
+ * Pure types and zod schemas - no Node-only imports - so a browser
6
+ * client (or an agent tool's `inputSchema`) can validate a lookup
7
+ * request and type a ranked response without dragging in server
8
+ * dependencies.
9
+ *
10
+ * @module
11
+ */
12
+ import { z } from "zod";
13
+ /**
14
+ * Class of a Databricks Model Serving endpoint - its modality plus,
15
+ * for chat models, a capability band. Chat endpoints split by measured
16
+ * `quality` into a three-rung ladder; embeddings are a separate
17
+ * modality, not a rung on that ladder.
18
+ *
19
+ * String enum so the value is the slug used in cache keys, logs, query
20
+ * strings, and serialized configs - and is what a client sends to
21
+ * request a class.
22
+ */
23
+ export var ModelClass;
24
+ (function (ModelClass) {
25
+ ModelClass["ChatThinking"] = "chat-thinking";
26
+ ModelClass["ChatBalanced"] = "chat-balanced";
27
+ ModelClass["ChatFast"] = "chat-fast";
28
+ ModelClass["Embedding"] = "embedding";
29
+ })(ModelClass || (ModelClass = {}));
30
+ const ModelClassDescriptions = {
31
+ [ModelClass.ChatThinking]: "Most capable reasoning for hard, multi-step tasks; slowest and most expensive.",
32
+ [ModelClass.ChatBalanced]: "Good default for most chat and agent work; balances quality, speed, and cost.",
33
+ [ModelClass.ChatFast]: "Fastest and cheapest; best for simple tasks like classification, routing, and short summaries.",
34
+ [ModelClass.Embedding]: "Produces text embeddings (vectors), not chat replies; not interchangeable with chat models.",
35
+ };
36
+ export const ModelClassSchema = z.enum(ModelClass).describe(`Endpoint class slug: ${Object.values(ModelClass)
37
+ .map((modelClass) => {
38
+ return `'${modelClass}' (${ModelClassDescriptions[modelClass]})`;
39
+ })
40
+ .join(", ")}.`);
41
+ /**
42
+ * Foundation Model API quality / speed / cost scores Databricks
43
+ * publishes per pay-per-token endpoint (the bars shown in the AI
44
+ * Playground). Surfaced from the serving list under
45
+ * `config.served_entities[].foundation_model.ai_gateway_model_profile`
46
+ * so the class classifier can rank models from live, workspace-local
47
+ * numbers instead of a hand-maintained table. Scales are relative and
48
+ * unitless; all axes are absent for custom or brand-new endpoints that
49
+ * have no profile yet.
50
+ */
51
+ export const ModelProfileSchema = z.object({
52
+ quality: z.number().optional().describe("Relative quality score; higher is better."),
53
+ speed: z.number().optional().describe("Relative throughput score; higher is faster."),
54
+ cost: z.number().optional().describe("Relative price score; lower is cheaper."),
55
+ });
56
+ /**
57
+ * Minimal descriptor for a Databricks Model Serving endpoint - a
58
+ * stable subset of the SDK type so cache hits and `/models` responses
59
+ * never expose SDK internals.
60
+ */
61
+ export const ServingEndpointSummarySchema = z.object({
62
+ name: z
63
+ .string()
64
+ .describe("Endpoint name as listed by the Model Serving REST API; the id used to invoke the model."),
65
+ displayName: z
66
+ .string()
67
+ .optional()
68
+ .describe("Human-readable label for the endpoint: a Databricks-provided name when present (a display-name tag or external-model name), else a title-cased rendering of `name` with vendor prefixes stripped. `name` remains the invoke id."),
69
+ task: z.string().optional().describe("Task hint, e.g. 'llm/v1/chat' or 'llm/v1/embeddings'."),
70
+ state: z.string().optional().describe("Deployment state, e.g. ready, updating, or failed."),
71
+ description: z.string().optional().describe("Free-form endpoint description; informational."),
72
+ profile: ModelProfileSchema.optional().describe("Foundation Model API quality/speed/cost scores when the endpoint is a scored pay-per-token model; absent otherwise."),
73
+ class: ModelClassSchema.optional().describe("Class the endpoint was classified into; absent when the classifier doesn't recognize it."),
74
+ dimension: z
75
+ .number()
76
+ .optional()
77
+ .describe("Embedding vector length, measured by a best-effort server-side ping. Only set for embedding endpoints, and only when the probe succeeded."),
78
+ });
79
+ /**
80
+ * A model-lookup request: search string, capability ceiling, both, or
81
+ * nothing. This is the caller-facing query the `@dbx-tools/model`
82
+ * ranker reads - shaped as a zod schema (with per-field descriptions)
83
+ * so an agent tool can adopt it directly as its `inputSchema` and a
84
+ * browser client can validate args before calling the model service.
85
+ *
86
+ * Every field is optional: an empty query ranks the live catalogue by
87
+ * class alone. The "this class and below" ceiling semantics and the
88
+ * match-then-class ordering live in the service, not here - this
89
+ * surface is declarative.
90
+ */
91
+ export const ModelQuerySchema = z.object({
92
+ search: z
93
+ .string()
94
+ .optional()
95
+ .describe("Loose name fuzzy-matched against endpoint names, e.g. 'claude sonnet'. Omit to rank by class alone."),
96
+ modelClass: ModelClassSchema.optional().describe("Capability ceiling: a chat class restricts to that band and the less-capable chat bands below it; 'embedding' restricts to embedding endpoints. Omit to consider every chat band."),
97
+ limit: z
98
+ .number()
99
+ .int()
100
+ .positive()
101
+ .max(50)
102
+ .optional()
103
+ .describe("Maximum number of ranked results to return."),
104
+ threshold: z
105
+ .number()
106
+ .min(0)
107
+ .max(1)
108
+ .optional()
109
+ .describe("Fuzzy-match distance cutoff (0 = exact, 1 = match anything). Default 0.4."),
110
+ });
111
+ /**
112
+ * One ranked lookup result. Backs an agent tool's `outputSchema` and a
113
+ * `/models` ranked response.
114
+ */
115
+ export const RankedModelSchema = z.object({
116
+ endpoint: ServingEndpointSummarySchema.describe("The matched serving endpoint."),
117
+ modelClass: ModelClassSchema.describe("Class the endpoint was classified into."),
118
+ score: z
119
+ .number()
120
+ .optional()
121
+ .describe("Fuzzy-match distance (0 = exact .. 1 = worst) when the query carried a search; absent for class-only ranking."),
122
+ });
123
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kZWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbW9kZWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7R0FVRztBQUVILE9BQU8sRUFBRSxDQUFDLEVBQUUsTUFBTSxLQUFLLENBQUM7QUFFeEI7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxDQUFOLElBQVksVUFLWDtBQUxELFdBQVksVUFBVTtJQUNwQiw0Q0FBOEIsQ0FBQTtJQUM5Qiw0Q0FBOEIsQ0FBQTtJQUM5QixvQ0FBc0IsQ0FBQTtJQUN0QixxQ0FBdUIsQ0FBQTtBQUN6QixDQUFDLEVBTFcsVUFBVSxLQUFWLFVBQVUsUUFLckI7QUFFRCxNQUFNLHNCQUFzQixHQUErQjtJQUN6RCxDQUFDLFVBQVUsQ0FBQyxZQUFZLENBQUMsRUFDdkIsZ0ZBQWdGO0lBQ2xGLENBQUMsVUFBVSxDQUFDLFlBQVksQ0FBQyxFQUN2QiwrRUFBK0U7SUFDakYsQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLEVBQ25CLGdHQUFnRztJQUNsRyxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsRUFDcEIsNkZBQTZGO0NBQ2hHLENBQUM7QUFFRixNQUFNLENBQUMsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDLFFBQVEsQ0FDekQsd0JBQXlCLE1BQU0sQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFrQjtLQUNoRSxHQUFHLENBQUMsQ0FBQyxVQUFVLEVBQUUsRUFBRTtJQUNsQixPQUFPLElBQUksVUFBVSxNQUFNLHNCQUFzQixDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUM7QUFDbkUsQ0FBQyxDQUFDO0tBQ0QsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQ2pCLENBQUM7QUFFRjs7Ozs7Ozs7O0dBU0c7QUFDSCxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQ3pDLE9BQU8sRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFLENBQUMsUUFBUSxDQUFDLDJDQUEyQyxDQUFDO0lBQ3BGLEtBQUssRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFLENBQUMsUUFBUSxDQUFDLDhDQUE4QyxDQUFDO0lBQ3JGLElBQUksRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFLENBQUMsUUFBUSxDQUFDLHlDQUF5QyxDQUFDO0NBQ2hGLENBQUMsQ0FBQztBQUdIOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSw0QkFBNEIsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQ25ELElBQUksRUFBRSxDQUFDO1NBQ0osTUFBTSxFQUFFO1NBQ1IsUUFBUSxDQUNQLHlGQUF5RixDQUMxRjtJQUNILFdBQVcsRUFBRSxDQUFDO1NBQ1gsTUFBTSxFQUFFO1NBQ1IsUUFBUSxFQUFFO1NBQ1YsUUFBUSxDQUNQLGlPQUFpTyxDQUNsTztJQUNILElBQUksRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFLENBQUMsUUFBUSxDQUFDLHVEQUF1RCxDQUFDO0lBQzdGLEtBQUssRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFLENBQUMsUUFBUSxDQUFDLG9EQUFvRCxDQUFDO0lBQzNGLFdBQVcsRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFLENBQUMsUUFBUSxDQUFDLGdEQUFnRCxDQUFDO0lBQzdGLE9BQU8sRUFBRSxrQkFBa0IsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQzdDLHFIQUFxSCxDQUN0SDtJQUNELEtBQUssRUFBRSxnQkFBZ0IsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQ3pDLDBGQUEwRixDQUMzRjtJQUNELFNBQVMsRUFBRSxDQUFDO1NBQ1QsTUFBTSxFQUFFO1NBQ1IsUUFBUSxFQUFFO1NBQ1YsUUFBUSxDQUNQLDJJQUEySSxDQUM1STtDQUNKLENBQUMsQ0FBQztBQUdIOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sZ0JBQWdCLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQztJQUN2QyxNQUFNLEVBQUUsQ0FBQztTQUNOLE1BQU0sRUFBRTtTQUNSLFFBQVEsRUFBRTtTQUNWLFFBQVEsQ0FDUCxxR0FBcUcsQ0FDdEc7SUFDSCxVQUFVLEVBQUUsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLENBQUMsUUFBUSxDQUM5QyxtTEFBbUwsQ0FDcEw7SUFDRCxLQUFLLEVBQUUsQ0FBQztTQUNMLE1BQU0sRUFBRTtTQUNSLEdBQUcsRUFBRTtTQUNMLFFBQVEsRUFBRTtTQUNWLEdBQUcsQ0FBQyxFQUFFLENBQUM7U0FDUCxRQUFRLEVBQUU7U0FDVixRQUFRLENBQUMsNkNBQTZDLENBQUM7SUFDMUQsU0FBUyxFQUFFLENBQUM7U0FDVCxNQUFNLEVBQUU7U0FDUixHQUFHLENBQUMsQ0FBQyxDQUFDO1NBQ04sR0FBRyxDQUFDLENBQUMsQ0FBQztTQUNOLFFBQVEsRUFBRTtTQUNWLFFBQVEsQ0FBQywyRUFBMkUsQ0FBQztDQUN6RixDQUFDLENBQUM7QUFHSDs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQ3hDLFFBQVEsRUFBRSw0QkFBNEIsQ0FBQyxRQUFRLENBQUMsK0JBQStCLENBQUM7SUFDaEYsVUFBVSxFQUFFLGdCQUFnQixDQUFDLFFBQVEsQ0FBQyx5Q0FBeUMsQ0FBQztJQUNoRixLQUFLLEVBQUUsQ0FBQztTQUNMLE1BQU0sRUFBRTtTQUNSLFFBQVEsRUFBRTtTQUNWLFFBQVEsQ0FDUCwrR0FBK0csQ0FDaEg7Q0FDSixDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFdpcmUtZm9ybWF0IHR5cGVzIGZvciB0aGUgbW9kZWwgdG9vbGtpdDogdGhlIG1vZGVsIGNsYXNzIHRheG9ub215LFxuICogdGhlIEZvdW5kYXRpb24gTW9kZWwgQVBJIHNjb3JlIHByb2ZpbGUsIHRoZSBtaW5pbWFsIHNlcnZpbmctZW5kcG9pbnRcbiAqIGRlc2NyaXB0b3IsIGFuZCB0aGUgbW9kZWwtbG9va3VwIHJlcXVlc3QgLyByYW5rZWQtcmVzdWx0IGNvbnRyYWN0LlxuICogUHVyZSB0eXBlcyBhbmQgem9kIHNjaGVtYXMgLSBubyBOb2RlLW9ubHkgaW1wb3J0cyAtIHNvIGEgYnJvd3NlclxuICogY2xpZW50IChvciBhbiBhZ2VudCB0b29sJ3MgYGlucHV0U2NoZW1hYCkgY2FuIHZhbGlkYXRlIGEgbG9va3VwXG4gKiByZXF1ZXN0IGFuZCB0eXBlIGEgcmFua2VkIHJlc3BvbnNlIHdpdGhvdXQgZHJhZ2dpbmcgaW4gc2VydmVyXG4gKiBkZXBlbmRlbmNpZXMuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IHogfSBmcm9tIFwiem9kXCI7XG5cbi8qKlxuICogQ2xhc3Mgb2YgYSBEYXRhYnJpY2tzIE1vZGVsIFNlcnZpbmcgZW5kcG9pbnQgLSBpdHMgbW9kYWxpdHkgcGx1cyxcbiAqIGZvciBjaGF0IG1vZGVscywgYSBjYXBhYmlsaXR5IGJhbmQuIENoYXQgZW5kcG9pbnRzIHNwbGl0IGJ5IG1lYXN1cmVkXG4gKiBgcXVhbGl0eWAgaW50byBhIHRocmVlLXJ1bmcgbGFkZGVyOyBlbWJlZGRpbmdzIGFyZSBhIHNlcGFyYXRlXG4gKiBtb2RhbGl0eSwgbm90IGEgcnVuZyBvbiB0aGF0IGxhZGRlci5cbiAqXG4gKiBTdHJpbmcgZW51bSBzbyB0aGUgdmFsdWUgaXMgdGhlIHNsdWcgdXNlZCBpbiBjYWNoZSBrZXlzLCBsb2dzLCBxdWVyeVxuICogc3RyaW5ncywgYW5kIHNlcmlhbGl6ZWQgY29uZmlncyAtIGFuZCBpcyB3aGF0IGEgY2xpZW50IHNlbmRzIHRvXG4gKiByZXF1ZXN0IGEgY2xhc3MuXG4gKi9cbmV4cG9ydCBlbnVtIE1vZGVsQ2xhc3Mge1xuICBDaGF0VGhpbmtpbmcgPSBcImNoYXQtdGhpbmtpbmdcIixcbiAgQ2hhdEJhbGFuY2VkID0gXCJjaGF0LWJhbGFuY2VkXCIsXG4gIENoYXRGYXN0ID0gXCJjaGF0LWZhc3RcIixcbiAgRW1iZWRkaW5nID0gXCJlbWJlZGRpbmdcIixcbn1cblxuY29uc3QgTW9kZWxDbGFzc0Rlc2NyaXB0aW9uczogUmVjb3JkPE1vZGVsQ2xhc3MsIHN0cmluZz4gPSB7XG4gIFtNb2RlbENsYXNzLkNoYXRUaGlua2luZ106XG4gICAgXCJNb3N0IGNhcGFibGUgcmVhc29uaW5nIGZvciBoYXJkLCBtdWx0aS1zdGVwIHRhc2tzOyBzbG93ZXN0IGFuZCBtb3N0IGV4cGVuc2l2ZS5cIixcbiAgW01vZGVsQ2xhc3MuQ2hhdEJhbGFuY2VkXTpcbiAgICBcIkdvb2QgZGVmYXVsdCBmb3IgbW9zdCBjaGF0IGFuZCBhZ2VudCB3b3JrOyBiYWxhbmNlcyBxdWFsaXR5LCBzcGVlZCwgYW5kIGNvc3QuXCIsXG4gIFtNb2RlbENsYXNzLkNoYXRGYXN0XTpcbiAgICBcIkZhc3Rlc3QgYW5kIGNoZWFwZXN0OyBiZXN0IGZvciBzaW1wbGUgdGFza3MgbGlrZSBjbGFzc2lmaWNhdGlvbiwgcm91dGluZywgYW5kIHNob3J0IHN1bW1hcmllcy5cIixcbiAgW01vZGVsQ2xhc3MuRW1iZWRkaW5nXTpcbiAgICBcIlByb2R1Y2VzIHRleHQgZW1iZWRkaW5ncyAodmVjdG9ycyksIG5vdCBjaGF0IHJlcGxpZXM7IG5vdCBpbnRlcmNoYW5nZWFibGUgd2l0aCBjaGF0IG1vZGVscy5cIixcbn07XG5cbmV4cG9ydCBjb25zdCBNb2RlbENsYXNzU2NoZW1hID0gei5lbnVtKE1vZGVsQ2xhc3MpLmRlc2NyaWJlKFxuICBgRW5kcG9pbnQgY2xhc3Mgc2x1ZzogJHsoT2JqZWN0LnZhbHVlcyhNb2RlbENsYXNzKSBhcyBNb2RlbENsYXNzW10pXG4gICAgLm1hcCgobW9kZWxDbGFzcykgPT4ge1xuICAgICAgcmV0dXJuIGAnJHttb2RlbENsYXNzfScgKCR7TW9kZWxDbGFzc0Rlc2NyaXB0aW9uc1ttb2RlbENsYXNzXX0pYDtcbiAgICB9KVxuICAgIC5qb2luKFwiLCBcIil9LmAsXG4pO1xuXG4vKipcbiAqIEZvdW5kYXRpb24gTW9kZWwgQVBJIHF1YWxpdHkgLyBzcGVlZCAvIGNvc3Qgc2NvcmVzIERhdGFicmlja3NcbiAqIHB1Ymxpc2hlcyBwZXIgcGF5LXBlci10b2tlbiBlbmRwb2ludCAodGhlIGJhcnMgc2hvd24gaW4gdGhlIEFJXG4gKiBQbGF5Z3JvdW5kKS4gU3VyZmFjZWQgZnJvbSB0aGUgc2VydmluZyBsaXN0IHVuZGVyXG4gKiBgY29uZmlnLnNlcnZlZF9lbnRpdGllc1tdLmZvdW5kYXRpb25fbW9kZWwuYWlfZ2F0ZXdheV9tb2RlbF9wcm9maWxlYFxuICogc28gdGhlIGNsYXNzIGNsYXNzaWZpZXIgY2FuIHJhbmsgbW9kZWxzIGZyb20gbGl2ZSwgd29ya3NwYWNlLWxvY2FsXG4gKiBudW1iZXJzIGluc3RlYWQgb2YgYSBoYW5kLW1haW50YWluZWQgdGFibGUuIFNjYWxlcyBhcmUgcmVsYXRpdmUgYW5kXG4gKiB1bml0bGVzczsgYWxsIGF4ZXMgYXJlIGFic2VudCBmb3IgY3VzdG9tIG9yIGJyYW5kLW5ldyBlbmRwb2ludHMgdGhhdFxuICogaGF2ZSBubyBwcm9maWxlIHlldC5cbiAqL1xuZXhwb3J0IGNvbnN0IE1vZGVsUHJvZmlsZVNjaGVtYSA9IHoub2JqZWN0KHtcbiAgcXVhbGl0eTogei5udW1iZXIoKS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiUmVsYXRpdmUgcXVhbGl0eSBzY29yZTsgaGlnaGVyIGlzIGJldHRlci5cIiksXG4gIHNwZWVkOiB6Lm51bWJlcigpLm9wdGlvbmFsKCkuZGVzY3JpYmUoXCJSZWxhdGl2ZSB0aHJvdWdocHV0IHNjb3JlOyBoaWdoZXIgaXMgZmFzdGVyLlwiKSxcbiAgY29zdDogei5udW1iZXIoKS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiUmVsYXRpdmUgcHJpY2Ugc2NvcmU7IGxvd2VyIGlzIGNoZWFwZXIuXCIpLFxufSk7XG5leHBvcnQgdHlwZSBNb2RlbFByb2ZpbGUgPSB6LmluZmVyPHR5cGVvZiBNb2RlbFByb2ZpbGVTY2hlbWE+O1xuXG4vKipcbiAqIE1pbmltYWwgZGVzY3JpcHRvciBmb3IgYSBEYXRhYnJpY2tzIE1vZGVsIFNlcnZpbmcgZW5kcG9pbnQgLSBhXG4gKiBzdGFibGUgc3Vic2V0IG9mIHRoZSBTREsgdHlwZSBzbyBjYWNoZSBoaXRzIGFuZCBgL21vZGVsc2AgcmVzcG9uc2VzXG4gKiBuZXZlciBleHBvc2UgU0RLIGludGVybmFscy5cbiAqL1xuZXhwb3J0IGNvbnN0IFNlcnZpbmdFbmRwb2ludFN1bW1hcnlTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIG5hbWU6IHpcbiAgICAuc3RyaW5nKClcbiAgICAuZGVzY3JpYmUoXG4gICAgICBcIkVuZHBvaW50IG5hbWUgYXMgbGlzdGVkIGJ5IHRoZSBNb2RlbCBTZXJ2aW5nIFJFU1QgQVBJOyB0aGUgaWQgdXNlZCB0byBpbnZva2UgdGhlIG1vZGVsLlwiLFxuICAgICksXG4gIGRpc3BsYXlOYW1lOiB6XG4gICAgLnN0cmluZygpXG4gICAgLm9wdGlvbmFsKClcbiAgICAuZGVzY3JpYmUoXG4gICAgICBcIkh1bWFuLXJlYWRhYmxlIGxhYmVsIGZvciB0aGUgZW5kcG9pbnQ6IGEgRGF0YWJyaWNrcy1wcm92aWRlZCBuYW1lIHdoZW4gcHJlc2VudCAoYSBkaXNwbGF5LW5hbWUgdGFnIG9yIGV4dGVybmFsLW1vZGVsIG5hbWUpLCBlbHNlIGEgdGl0bGUtY2FzZWQgcmVuZGVyaW5nIG9mIGBuYW1lYCB3aXRoIHZlbmRvciBwcmVmaXhlcyBzdHJpcHBlZC4gYG5hbWVgIHJlbWFpbnMgdGhlIGludm9rZSBpZC5cIixcbiAgICApLFxuICB0YXNrOiB6LnN0cmluZygpLm9wdGlvbmFsKCkuZGVzY3JpYmUoXCJUYXNrIGhpbnQsIGUuZy4gJ2xsbS92MS9jaGF0JyBvciAnbGxtL3YxL2VtYmVkZGluZ3MnLlwiKSxcbiAgc3RhdGU6IHouc3RyaW5nKCkub3B0aW9uYWwoKS5kZXNjcmliZShcIkRlcGxveW1lbnQgc3RhdGUsIGUuZy4gcmVhZHksIHVwZGF0aW5nLCBvciBmYWlsZWQuXCIpLFxuICBkZXNjcmlwdGlvbjogei5zdHJpbmcoKS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiRnJlZS1mb3JtIGVuZHBvaW50IGRlc2NyaXB0aW9uOyBpbmZvcm1hdGlvbmFsLlwiKSxcbiAgcHJvZmlsZTogTW9kZWxQcm9maWxlU2NoZW1hLm9wdGlvbmFsKCkuZGVzY3JpYmUoXG4gICAgXCJGb3VuZGF0aW9uIE1vZGVsIEFQSSBxdWFsaXR5L3NwZWVkL2Nvc3Qgc2NvcmVzIHdoZW4gdGhlIGVuZHBvaW50IGlzIGEgc2NvcmVkIHBheS1wZXItdG9rZW4gbW9kZWw7IGFic2VudCBvdGhlcndpc2UuXCIsXG4gICksXG4gIGNsYXNzOiBNb2RlbENsYXNzU2NoZW1hLm9wdGlvbmFsKCkuZGVzY3JpYmUoXG4gICAgXCJDbGFzcyB0aGUgZW5kcG9pbnQgd2FzIGNsYXNzaWZpZWQgaW50bzsgYWJzZW50IHdoZW4gdGhlIGNsYXNzaWZpZXIgZG9lc24ndCByZWNvZ25pemUgaXQuXCIsXG4gICksXG4gIGRpbWVuc2lvbjogelxuICAgIC5udW1iZXIoKVxuICAgIC5vcHRpb25hbCgpXG4gICAgLmRlc2NyaWJlKFxuICAgICAgXCJFbWJlZGRpbmcgdmVjdG9yIGxlbmd0aCwgbWVhc3VyZWQgYnkgYSBiZXN0LWVmZm9ydCBzZXJ2ZXItc2lkZSBwaW5nLiBPbmx5IHNldCBmb3IgZW1iZWRkaW5nIGVuZHBvaW50cywgYW5kIG9ubHkgd2hlbiB0aGUgcHJvYmUgc3VjY2VlZGVkLlwiLFxuICAgICksXG59KTtcbmV4cG9ydCB0eXBlIFNlcnZpbmdFbmRwb2ludFN1bW1hcnkgPSB6LmluZmVyPHR5cGVvZiBTZXJ2aW5nRW5kcG9pbnRTdW1tYXJ5U2NoZW1hPjtcblxuLyoqXG4gKiBBIG1vZGVsLWxvb2t1cCByZXF1ZXN0OiBzZWFyY2ggc3RyaW5nLCBjYXBhYmlsaXR5IGNlaWxpbmcsIGJvdGgsIG9yXG4gKiBub3RoaW5nLiBUaGlzIGlzIHRoZSBjYWxsZXItZmFjaW5nIHF1ZXJ5IHRoZSBgQGRieC10b29scy9tb2RlbGBcbiAqIHJhbmtlciByZWFkcyAtIHNoYXBlZCBhcyBhIHpvZCBzY2hlbWEgKHdpdGggcGVyLWZpZWxkIGRlc2NyaXB0aW9ucylcbiAqIHNvIGFuIGFnZW50IHRvb2wgY2FuIGFkb3B0IGl0IGRpcmVjdGx5IGFzIGl0cyBgaW5wdXRTY2hlbWFgIGFuZCBhXG4gKiBicm93c2VyIGNsaWVudCBjYW4gdmFsaWRhdGUgYXJncyBiZWZvcmUgY2FsbGluZyB0aGUgbW9kZWwgc2VydmljZS5cbiAqXG4gKiBFdmVyeSBmaWVsZCBpcyBvcHRpb25hbDogYW4gZW1wdHkgcXVlcnkgcmFua3MgdGhlIGxpdmUgY2F0YWxvZ3VlIGJ5XG4gKiBjbGFzcyBhbG9uZS4gVGhlIFwidGhpcyBjbGFzcyBhbmQgYmVsb3dcIiBjZWlsaW5nIHNlbWFudGljcyBhbmQgdGhlXG4gKiBtYXRjaC10aGVuLWNsYXNzIG9yZGVyaW5nIGxpdmUgaW4gdGhlIHNlcnZpY2UsIG5vdCBoZXJlIC0gdGhpc1xuICogc3VyZmFjZSBpcyBkZWNsYXJhdGl2ZS5cbiAqL1xuZXhwb3J0IGNvbnN0IE1vZGVsUXVlcnlTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIHNlYXJjaDogelxuICAgIC5zdHJpbmcoKVxuICAgIC5vcHRpb25hbCgpXG4gICAgLmRlc2NyaWJlKFxuICAgICAgXCJMb29zZSBuYW1lIGZ1enp5LW1hdGNoZWQgYWdhaW5zdCBlbmRwb2ludCBuYW1lcywgZS5nLiAnY2xhdWRlIHNvbm5ldCcuIE9taXQgdG8gcmFuayBieSBjbGFzcyBhbG9uZS5cIixcbiAgICApLFxuICBtb2RlbENsYXNzOiBNb2RlbENsYXNzU2NoZW1hLm9wdGlvbmFsKCkuZGVzY3JpYmUoXG4gICAgXCJDYXBhYmlsaXR5IGNlaWxpbmc6IGEgY2hhdCBjbGFzcyByZXN0cmljdHMgdG8gdGhhdCBiYW5kIGFuZCB0aGUgbGVzcy1jYXBhYmxlIGNoYXQgYmFuZHMgYmVsb3cgaXQ7ICdlbWJlZGRpbmcnIHJlc3RyaWN0cyB0byBlbWJlZGRpbmcgZW5kcG9pbnRzLiBPbWl0IHRvIGNvbnNpZGVyIGV2ZXJ5IGNoYXQgYmFuZC5cIixcbiAgKSxcbiAgbGltaXQ6IHpcbiAgICAubnVtYmVyKClcbiAgICAuaW50KClcbiAgICAucG9zaXRpdmUoKVxuICAgIC5tYXgoNTApXG4gICAgLm9wdGlvbmFsKClcbiAgICAuZGVzY3JpYmUoXCJNYXhpbXVtIG51bWJlciBvZiByYW5rZWQgcmVzdWx0cyB0byByZXR1cm4uXCIpLFxuICB0aHJlc2hvbGQ6IHpcbiAgICAubnVtYmVyKClcbiAgICAubWluKDApXG4gICAgLm1heCgxKVxuICAgIC5vcHRpb25hbCgpXG4gICAgLmRlc2NyaWJlKFwiRnV6enktbWF0Y2ggZGlzdGFuY2UgY3V0b2ZmICgwID0gZXhhY3QsIDEgPSBtYXRjaCBhbnl0aGluZykuIERlZmF1bHQgMC40LlwiKSxcbn0pO1xuZXhwb3J0IHR5cGUgTW9kZWxRdWVyeSA9IHouaW5mZXI8dHlwZW9mIE1vZGVsUXVlcnlTY2hlbWE+O1xuXG4vKipcbiAqIE9uZSByYW5rZWQgbG9va3VwIHJlc3VsdC4gQmFja3MgYW4gYWdlbnQgdG9vbCdzIGBvdXRwdXRTY2hlbWFgIGFuZCBhXG4gKiBgL21vZGVsc2AgcmFua2VkIHJlc3BvbnNlLlxuICovXG5leHBvcnQgY29uc3QgUmFua2VkTW9kZWxTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIGVuZHBvaW50OiBTZXJ2aW5nRW5kcG9pbnRTdW1tYXJ5U2NoZW1hLmRlc2NyaWJlKFwiVGhlIG1hdGNoZWQgc2VydmluZyBlbmRwb2ludC5cIiksXG4gIG1vZGVsQ2xhc3M6IE1vZGVsQ2xhc3NTY2hlbWEuZGVzY3JpYmUoXCJDbGFzcyB0aGUgZW5kcG9pbnQgd2FzIGNsYXNzaWZpZWQgaW50by5cIiksXG4gIHNjb3JlOiB6XG4gICAgLm51bWJlcigpXG4gICAgLm9wdGlvbmFsKClcbiAgICAuZGVzY3JpYmUoXG4gICAgICBcIkZ1enp5LW1hdGNoIGRpc3RhbmNlICgwID0gZXhhY3QgLi4gMSA9IHdvcnN0KSB3aGVuIHRoZSBxdWVyeSBjYXJyaWVkIGEgc2VhcmNoOyBhYnNlbnQgZm9yIGNsYXNzLW9ubHkgcmFua2luZy5cIixcbiAgICApLFxufSk7XG5leHBvcnQgdHlwZSBSYW5rZWRNb2RlbCA9IHouaW5mZXI8dHlwZW9mIFJhbmtlZE1vZGVsU2NoZW1hPjtcbiJdfQ==
@@ -0,0 +1,108 @@
1
+ /**
2
+ * OpenAI Chat Completions wire shapes, as Databricks Model Serving speaks them.
3
+ *
4
+ * These are the request/reply payload types for `/chat/completions` and a
5
+ * serving endpoint's `invocations` URL. They are deliberately declared here
6
+ * rather than imported from the OpenAI or AI SDK packages: both keep these
7
+ * fields under internal namespaces that are not part of their public API,
8
+ * whereas the wire payload itself is the stable contract every caller in this
9
+ * repo actually codes against.
10
+ *
11
+ * Pure types plus one text-flattening helper - no zod, no runtime deps - so a
12
+ * browser client, a Node proxy, and an AppKit plugin can all agree on the shape
13
+ * without any of them pulling in the others' dependencies.
14
+ *
15
+ * @module
16
+ */
17
+ /**
18
+ * The roles OpenAI defines for a chat turn. {@link ChatMessage.role} is typed as
19
+ * a plain string rather than this union, because providers add their own (for
20
+ * example Databricks-hosted Claude replays a `"reasoning"` turn); use this when
21
+ * you want the standard set named.
22
+ */
23
+ export type ChatRole = "system" | "developer" | "user" | "assistant" | "tool";
24
+ /**
25
+ * One entry of a structured `content` array. Only `type` and `text` are read
26
+ * here; the index signature keeps provider-specific parts (images, thinking
27
+ * blocks, cache markers) intact through a parse/serialize round trip.
28
+ */
29
+ export interface ChatContentPart {
30
+ type?: string;
31
+ text?: string;
32
+ [key: string]: unknown;
33
+ }
34
+ /** The function a tool call invokes, with its arguments as a JSON string. */
35
+ export interface ChatToolCallFunction {
36
+ name: string;
37
+ /** JSON-encoded argument object. Streamed in fragments, so assemble before parsing. */
38
+ arguments: string;
39
+ }
40
+ /** One tool call attached to an assistant turn. */
41
+ export interface ChatToolCall {
42
+ id: string;
43
+ /** `"function"` in practice; widened so an unrecognized type round-trips. */
44
+ type: string;
45
+ function: ChatToolCallFunction;
46
+ }
47
+ /**
48
+ * A single chat message. `content` is nullable because an assistant turn that
49
+ * only calls tools carries `content: null`.
50
+ */
51
+ export interface ChatMessage {
52
+ /** See {@link ChatRole} for the standard values; widened for provider extensions. */
53
+ role: string;
54
+ content?: string | ChatContentPart[] | null;
55
+ tool_calls?: ChatToolCall[];
56
+ /** Set on a `tool` turn, keying it back to the call it answers. */
57
+ tool_call_id?: string;
58
+ name?: string;
59
+ }
60
+ /**
61
+ * Top-level request fields an OpenAI client may send that Databricks Model
62
+ * Serving rejects outright, failing the whole turn rather than ignoring them.
63
+ *
64
+ * Databricks validates the chat body strictly, so an unrecognized key comes
65
+ * back as `parallel_tool_calls: Extra inputs are not permitted` (the
66
+ * gateway's pydantic validation) or `json: unknown field "parallel_tool_calls"`
67
+ * (its strict JSON decode), depending on the endpoint. Everything listed here
68
+ * is either OpenAI-platform bookkeeping with no bearing on the completion, or -
69
+ * in the case of `parallel_tool_calls` - a real setting Databricks has no way
70
+ * to accept, so dropping it is the only way the request can succeed at all.
71
+ *
72
+ * Callers that translate a request field-by-field (an allowlist, as
73
+ * `openaiResponses.responsesToChat` does) never need this; it exists for the
74
+ * paths that forward a client body largely as-is.
75
+ */
76
+ export declare const UNSUPPORTED_CHAT_FIELDS: readonly string[];
77
+ /**
78
+ * Delete the fields Databricks rejects from a chat request body, in place.
79
+ * Returns the names actually removed so a caller can log what it dropped.
80
+ *
81
+ * @param body - Parsed chat request body. Mutated.
82
+ * @param extra - Additional field names to drop, for a workspace or endpoint
83
+ * that rejects something not yet in {@link UNSUPPORTED_CHAT_FIELDS}.
84
+ */
85
+ export declare function stripUnsupportedChatFields(body: Record<string, unknown>, extra?: readonly string[]): string[];
86
+ /** Options for {@link chatContentToText}. */
87
+ export interface ChatContentToTextOptions {
88
+ /**
89
+ * Placed between parts. Defaults to `""`, which reassembles a message that was
90
+ * split purely for transport; pass `"\n\n"` when the parts are separate
91
+ * paragraphs that a human will read.
92
+ */
93
+ separator?: string;
94
+ /**
95
+ * Restrict to these `type` values. Defaults to accepting any part carrying
96
+ * `text`, which covers the `input_text` / `output_text` / `text` spellings
97
+ * different APIs use for the same thing.
98
+ */
99
+ types?: readonly string[];
100
+ }
101
+ /**
102
+ * Flatten a message `content` value to plain text. Accepts the string form and
103
+ * the structured-parts form ({@link ChatContentPart}), and yields `""` for
104
+ * anything else (null, a lone image part, a malformed payload). Typed as
105
+ * `unknown` because most callers are reading a just-parsed JSON body, and the
106
+ * point of this helper is that they do not have to pre-check it.
107
+ */
108
+ export declare function chatContentToText(content: unknown, options?: ChatContentToTextOptions): string;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * OpenAI Chat Completions wire shapes, as Databricks Model Serving speaks them.
3
+ *
4
+ * These are the request/reply payload types for `/chat/completions` and a
5
+ * serving endpoint's `invocations` URL. They are deliberately declared here
6
+ * rather than imported from the OpenAI or AI SDK packages: both keep these
7
+ * fields under internal namespaces that are not part of their public API,
8
+ * whereas the wire payload itself is the stable contract every caller in this
9
+ * repo actually codes against.
10
+ *
11
+ * Pure types plus one text-flattening helper - no zod, no runtime deps - so a
12
+ * browser client, a Node proxy, and an AppKit plugin can all agree on the shape
13
+ * without any of them pulling in the others' dependencies.
14
+ *
15
+ * @module
16
+ */
17
+ /**
18
+ * Top-level request fields an OpenAI client may send that Databricks Model
19
+ * Serving rejects outright, failing the whole turn rather than ignoring them.
20
+ *
21
+ * Databricks validates the chat body strictly, so an unrecognized key comes
22
+ * back as `parallel_tool_calls: Extra inputs are not permitted` (the
23
+ * gateway's pydantic validation) or `json: unknown field "parallel_tool_calls"`
24
+ * (its strict JSON decode), depending on the endpoint. Everything listed here
25
+ * is either OpenAI-platform bookkeeping with no bearing on the completion, or -
26
+ * in the case of `parallel_tool_calls` - a real setting Databricks has no way
27
+ * to accept, so dropping it is the only way the request can succeed at all.
28
+ *
29
+ * Callers that translate a request field-by-field (an allowlist, as
30
+ * `openaiResponses.responsesToChat` does) never need this; it exists for the
31
+ * paths that forward a client body largely as-is.
32
+ */
33
+ export const UNSUPPORTED_CHAT_FIELDS = [
34
+ // Tool-calling concurrency hint. Databricks rejects it; the upstream provider
35
+ // decides parallelism itself.
36
+ "parallel_tool_calls",
37
+ // Response-persistence and bookkeeping fields for OpenAI's own platform.
38
+ "store",
39
+ "metadata",
40
+ "service_tier",
41
+ "prompt_cache_key",
42
+ "safety_identifier",
43
+ ];
44
+ /**
45
+ * Delete the fields Databricks rejects from a chat request body, in place.
46
+ * Returns the names actually removed so a caller can log what it dropped.
47
+ *
48
+ * @param body - Parsed chat request body. Mutated.
49
+ * @param extra - Additional field names to drop, for a workspace or endpoint
50
+ * that rejects something not yet in {@link UNSUPPORTED_CHAT_FIELDS}.
51
+ */
52
+ export function stripUnsupportedChatFields(body, extra = []) {
53
+ const dropped = [];
54
+ for (const field of [...UNSUPPORTED_CHAT_FIELDS, ...extra]) {
55
+ if (!(field in body))
56
+ continue;
57
+ delete body[field];
58
+ dropped.push(field);
59
+ }
60
+ return dropped;
61
+ }
62
+ /**
63
+ * Flatten a message `content` value to plain text. Accepts the string form and
64
+ * the structured-parts form ({@link ChatContentPart}), and yields `""` for
65
+ * anything else (null, a lone image part, a malformed payload). Typed as
66
+ * `unknown` because most callers are reading a just-parsed JSON body, and the
67
+ * point of this helper is that they do not have to pre-check it.
68
+ */
69
+ export function chatContentToText(content, options = {}) {
70
+ if (typeof content === "string")
71
+ return content;
72
+ if (!Array.isArray(content))
73
+ return "";
74
+ const { separator = "", types } = options;
75
+ const parts = [];
76
+ for (const raw of content) {
77
+ if (!raw || typeof raw !== "object")
78
+ continue;
79
+ const part = raw;
80
+ if (types && (typeof part.type !== "string" || !types.includes(part.type)))
81
+ continue;
82
+ if (typeof part.text === "string")
83
+ parts.push(part.text);
84
+ }
85
+ return parts.join(separator);
86
+ }
87
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbmFpLWNoYXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3BlbmFpLWNoYXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBa0RIOzs7Ozs7Ozs7Ozs7Ozs7R0FlRztBQUNILE1BQU0sQ0FBQyxNQUFNLHVCQUF1QixHQUFzQjtJQUN4RCw4RUFBOEU7SUFDOUUsOEJBQThCO0lBQzlCLHFCQUFxQjtJQUNyQix5RUFBeUU7SUFDekUsT0FBTztJQUNQLFVBQVU7SUFDVixjQUFjO0lBQ2Qsa0JBQWtCO0lBQ2xCLG1CQUFtQjtDQUNwQixDQUFDO0FBRUY7Ozs7Ozs7R0FPRztBQUNILE1BQU0sVUFBVSwwQkFBMEIsQ0FDeEMsSUFBNkIsRUFDN0IsUUFBMkIsRUFBRTtJQUU3QixNQUFNLE9BQU8sR0FBYSxFQUFFLENBQUM7SUFDN0IsS0FBSyxNQUFNLEtBQUssSUFBSSxDQUFDLEdBQUcsdUJBQXVCLEVBQUUsR0FBRyxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQzNELElBQUksQ0FBQyxDQUFDLEtBQUssSUFBSSxJQUFJLENBQUM7WUFBRSxTQUFTO1FBQy9CLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ25CLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDdEIsQ0FBQztJQUNELE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUM7QUFrQkQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQixDQUMvQixPQUFnQixFQUNoQixVQUFvQyxFQUFFO0lBRXRDLElBQUksT0FBTyxPQUFPLEtBQUssUUFBUTtRQUFFLE9BQU8sT0FBTyxDQUFDO0lBQ2hELElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQztRQUFFLE9BQU8sRUFBRSxDQUFDO0lBQ3ZDLE1BQU0sRUFBRSxTQUFTLEdBQUcsRUFBRSxFQUFFLEtBQUssRUFBRSxHQUFHLE9BQU8sQ0FBQztJQUMxQyxNQUFNLEtBQUssR0FBYSxFQUFFLENBQUM7SUFDM0IsS0FBSyxNQUFNLEdBQUcsSUFBSSxPQUFPLEVBQUUsQ0FBQztRQUMxQixJQUFJLENBQUMsR0FBRyxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVE7WUFBRSxTQUFTO1FBQzlDLE1BQU0sSUFBSSxHQUFHLEdBQXNCLENBQUM7UUFDcEMsSUFBSSxLQUFLLElBQUksQ0FBQyxPQUFPLElBQUksQ0FBQyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7WUFBRSxTQUFTO1FBQ3JGLElBQUksT0FBTyxJQUFJLENBQUMsSUFBSSxLQUFLLFFBQVE7WUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBQ0QsT0FBTyxLQUFLLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO0FBQy9CLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIE9wZW5BSSBDaGF0IENvbXBsZXRpb25zIHdpcmUgc2hhcGVzLCBhcyBEYXRhYnJpY2tzIE1vZGVsIFNlcnZpbmcgc3BlYWtzIHRoZW0uXG4gKlxuICogVGhlc2UgYXJlIHRoZSByZXF1ZXN0L3JlcGx5IHBheWxvYWQgdHlwZXMgZm9yIGAvY2hhdC9jb21wbGV0aW9uc2AgYW5kIGFcbiAqIHNlcnZpbmcgZW5kcG9pbnQncyBgaW52b2NhdGlvbnNgIFVSTC4gVGhleSBhcmUgZGVsaWJlcmF0ZWx5IGRlY2xhcmVkIGhlcmVcbiAqIHJhdGhlciB0aGFuIGltcG9ydGVkIGZyb20gdGhlIE9wZW5BSSBvciBBSSBTREsgcGFja2FnZXM6IGJvdGgga2VlcCB0aGVzZVxuICogZmllbGRzIHVuZGVyIGludGVybmFsIG5hbWVzcGFjZXMgdGhhdCBhcmUgbm90IHBhcnQgb2YgdGhlaXIgcHVibGljIEFQSSxcbiAqIHdoZXJlYXMgdGhlIHdpcmUgcGF5bG9hZCBpdHNlbGYgaXMgdGhlIHN0YWJsZSBjb250cmFjdCBldmVyeSBjYWxsZXIgaW4gdGhpc1xuICogcmVwbyBhY3R1YWxseSBjb2RlcyBhZ2FpbnN0LlxuICpcbiAqIFB1cmUgdHlwZXMgcGx1cyBvbmUgdGV4dC1mbGF0dGVuaW5nIGhlbHBlciAtIG5vIHpvZCwgbm8gcnVudGltZSBkZXBzIC0gc28gYVxuICogYnJvd3NlciBjbGllbnQsIGEgTm9kZSBwcm94eSwgYW5kIGFuIEFwcEtpdCBwbHVnaW4gY2FuIGFsbCBhZ3JlZSBvbiB0aGUgc2hhcGVcbiAqIHdpdGhvdXQgYW55IG9mIHRoZW0gcHVsbGluZyBpbiB0aGUgb3RoZXJzJyBkZXBlbmRlbmNpZXMuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbi8qKlxuICogVGhlIHJvbGVzIE9wZW5BSSBkZWZpbmVzIGZvciBhIGNoYXQgdHVybi4ge0BsaW5rIENoYXRNZXNzYWdlLnJvbGV9IGlzIHR5cGVkIGFzXG4gKiBhIHBsYWluIHN0cmluZyByYXRoZXIgdGhhbiB0aGlzIHVuaW9uLCBiZWNhdXNlIHByb3ZpZGVycyBhZGQgdGhlaXIgb3duIChmb3JcbiAqIGV4YW1wbGUgRGF0YWJyaWNrcy1ob3N0ZWQgQ2xhdWRlIHJlcGxheXMgYSBgXCJyZWFzb25pbmdcImAgdHVybik7IHVzZSB0aGlzIHdoZW5cbiAqIHlvdSB3YW50IHRoZSBzdGFuZGFyZCBzZXQgbmFtZWQuXG4gKi9cbmV4cG9ydCB0eXBlIENoYXRSb2xlID0gXCJzeXN0ZW1cIiB8IFwiZGV2ZWxvcGVyXCIgfCBcInVzZXJcIiB8IFwiYXNzaXN0YW50XCIgfCBcInRvb2xcIjtcblxuLyoqXG4gKiBPbmUgZW50cnkgb2YgYSBzdHJ1Y3R1cmVkIGBjb250ZW50YCBhcnJheS4gT25seSBgdHlwZWAgYW5kIGB0ZXh0YCBhcmUgcmVhZFxuICogaGVyZTsgdGhlIGluZGV4IHNpZ25hdHVyZSBrZWVwcyBwcm92aWRlci1zcGVjaWZpYyBwYXJ0cyAoaW1hZ2VzLCB0aGlua2luZ1xuICogYmxvY2tzLCBjYWNoZSBtYXJrZXJzKSBpbnRhY3QgdGhyb3VnaCBhIHBhcnNlL3NlcmlhbGl6ZSByb3VuZCB0cmlwLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIENoYXRDb250ZW50UGFydCB7XG4gIHR5cGU/OiBzdHJpbmc7XG4gIHRleHQ/OiBzdHJpbmc7XG4gIFtrZXk6IHN0cmluZ106IHVua25vd247XG59XG5cbi8qKiBUaGUgZnVuY3Rpb24gYSB0b29sIGNhbGwgaW52b2tlcywgd2l0aCBpdHMgYXJndW1lbnRzIGFzIGEgSlNPTiBzdHJpbmcuICovXG5leHBvcnQgaW50ZXJmYWNlIENoYXRUb29sQ2FsbEZ1bmN0aW9uIHtcbiAgbmFtZTogc3RyaW5nO1xuICAvKiogSlNPTi1lbmNvZGVkIGFyZ3VtZW50IG9iamVjdC4gU3RyZWFtZWQgaW4gZnJhZ21lbnRzLCBzbyBhc3NlbWJsZSBiZWZvcmUgcGFyc2luZy4gKi9cbiAgYXJndW1lbnRzOiBzdHJpbmc7XG59XG5cbi8qKiBPbmUgdG9vbCBjYWxsIGF0dGFjaGVkIHRvIGFuIGFzc2lzdGFudCB0dXJuLiAqL1xuZXhwb3J0IGludGVyZmFjZSBDaGF0VG9vbENhbGwge1xuICBpZDogc3RyaW5nO1xuICAvKiogYFwiZnVuY3Rpb25cImAgaW4gcHJhY3RpY2U7IHdpZGVuZWQgc28gYW4gdW5yZWNvZ25pemVkIHR5cGUgcm91bmQtdHJpcHMuICovXG4gIHR5cGU6IHN0cmluZztcbiAgZnVuY3Rpb246IENoYXRUb29sQ2FsbEZ1bmN0aW9uO1xufVxuXG4vKipcbiAqIEEgc2luZ2xlIGNoYXQgbWVzc2FnZS4gYGNvbnRlbnRgIGlzIG51bGxhYmxlIGJlY2F1c2UgYW4gYXNzaXN0YW50IHR1cm4gdGhhdFxuICogb25seSBjYWxscyB0b29scyBjYXJyaWVzIGBjb250ZW50OiBudWxsYC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBDaGF0TWVzc2FnZSB7XG4gIC8qKiBTZWUge0BsaW5rIENoYXRSb2xlfSBmb3IgdGhlIHN0YW5kYXJkIHZhbHVlczsgd2lkZW5lZCBmb3IgcHJvdmlkZXIgZXh0ZW5zaW9ucy4gKi9cbiAgcm9sZTogc3RyaW5nO1xuICBjb250ZW50Pzogc3RyaW5nIHwgQ2hhdENvbnRlbnRQYXJ0W10gfCBudWxsO1xuICB0b29sX2NhbGxzPzogQ2hhdFRvb2xDYWxsW107XG4gIC8qKiBTZXQgb24gYSBgdG9vbGAgdHVybiwga2V5aW5nIGl0IGJhY2sgdG8gdGhlIGNhbGwgaXQgYW5zd2Vycy4gKi9cbiAgdG9vbF9jYWxsX2lkPzogc3RyaW5nO1xuICBuYW1lPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIFRvcC1sZXZlbCByZXF1ZXN0IGZpZWxkcyBhbiBPcGVuQUkgY2xpZW50IG1heSBzZW5kIHRoYXQgRGF0YWJyaWNrcyBNb2RlbFxuICogU2VydmluZyByZWplY3RzIG91dHJpZ2h0LCBmYWlsaW5nIHRoZSB3aG9sZSB0dXJuIHJhdGhlciB0aGFuIGlnbm9yaW5nIHRoZW0uXG4gKlxuICogRGF0YWJyaWNrcyB2YWxpZGF0ZXMgdGhlIGNoYXQgYm9keSBzdHJpY3RseSwgc28gYW4gdW5yZWNvZ25pemVkIGtleSBjb21lc1xuICogYmFjayBhcyBgcGFyYWxsZWxfdG9vbF9jYWxsczogRXh0cmEgaW5wdXRzIGFyZSBub3QgcGVybWl0dGVkYCAodGhlXG4gKiBnYXRld2F5J3MgcHlkYW50aWMgdmFsaWRhdGlvbikgb3IgYGpzb246IHVua25vd24gZmllbGQgXCJwYXJhbGxlbF90b29sX2NhbGxzXCJgXG4gKiAoaXRzIHN0cmljdCBKU09OIGRlY29kZSksIGRlcGVuZGluZyBvbiB0aGUgZW5kcG9pbnQuIEV2ZXJ5dGhpbmcgbGlzdGVkIGhlcmVcbiAqIGlzIGVpdGhlciBPcGVuQUktcGxhdGZvcm0gYm9va2tlZXBpbmcgd2l0aCBubyBiZWFyaW5nIG9uIHRoZSBjb21wbGV0aW9uLCBvciAtXG4gKiBpbiB0aGUgY2FzZSBvZiBgcGFyYWxsZWxfdG9vbF9jYWxsc2AgLSBhIHJlYWwgc2V0dGluZyBEYXRhYnJpY2tzIGhhcyBubyB3YXlcbiAqIHRvIGFjY2VwdCwgc28gZHJvcHBpbmcgaXQgaXMgdGhlIG9ubHkgd2F5IHRoZSByZXF1ZXN0IGNhbiBzdWNjZWVkIGF0IGFsbC5cbiAqXG4gKiBDYWxsZXJzIHRoYXQgdHJhbnNsYXRlIGEgcmVxdWVzdCBmaWVsZC1ieS1maWVsZCAoYW4gYWxsb3dsaXN0LCBhc1xuICogYG9wZW5haVJlc3BvbnNlcy5yZXNwb25zZXNUb0NoYXRgIGRvZXMpIG5ldmVyIG5lZWQgdGhpczsgaXQgZXhpc3RzIGZvciB0aGVcbiAqIHBhdGhzIHRoYXQgZm9yd2FyZCBhIGNsaWVudCBib2R5IGxhcmdlbHkgYXMtaXMuXG4gKi9cbmV4cG9ydCBjb25zdCBVTlNVUFBPUlRFRF9DSEFUX0ZJRUxEUzogcmVhZG9ubHkgc3RyaW5nW10gPSBbXG4gIC8vIFRvb2wtY2FsbGluZyBjb25jdXJyZW5jeSBoaW50LiBEYXRhYnJpY2tzIHJlamVjdHMgaXQ7IHRoZSB1cHN0cmVhbSBwcm92aWRlclxuICAvLyBkZWNpZGVzIHBhcmFsbGVsaXNtIGl0c2VsZi5cbiAgXCJwYXJhbGxlbF90b29sX2NhbGxzXCIsXG4gIC8vIFJlc3BvbnNlLXBlcnNpc3RlbmNlIGFuZCBib29ra2VlcGluZyBmaWVsZHMgZm9yIE9wZW5BSSdzIG93biBwbGF0Zm9ybS5cbiAgXCJzdG9yZVwiLFxuICBcIm1ldGFkYXRhXCIsXG4gIFwic2VydmljZV90aWVyXCIsXG4gIFwicHJvbXB0X2NhY2hlX2tleVwiLFxuICBcInNhZmV0eV9pZGVudGlmaWVyXCIsXG5dO1xuXG4vKipcbiAqIERlbGV0ZSB0aGUgZmllbGRzIERhdGFicmlja3MgcmVqZWN0cyBmcm9tIGEgY2hhdCByZXF1ZXN0IGJvZHksIGluIHBsYWNlLlxuICogUmV0dXJucyB0aGUgbmFtZXMgYWN0dWFsbHkgcmVtb3ZlZCBzbyBhIGNhbGxlciBjYW4gbG9nIHdoYXQgaXQgZHJvcHBlZC5cbiAqXG4gKiBAcGFyYW0gYm9keSAtIFBhcnNlZCBjaGF0IHJlcXVlc3QgYm9keS4gTXV0YXRlZC5cbiAqIEBwYXJhbSBleHRyYSAtIEFkZGl0aW9uYWwgZmllbGQgbmFtZXMgdG8gZHJvcCwgZm9yIGEgd29ya3NwYWNlIG9yIGVuZHBvaW50XG4gKiAgIHRoYXQgcmVqZWN0cyBzb21ldGhpbmcgbm90IHlldCBpbiB7QGxpbmsgVU5TVVBQT1JURURfQ0hBVF9GSUVMRFN9LlxuICovXG5leHBvcnQgZnVuY3Rpb24gc3RyaXBVbnN1cHBvcnRlZENoYXRGaWVsZHMoXG4gIGJvZHk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuICBleHRyYTogcmVhZG9ubHkgc3RyaW5nW10gPSBbXSxcbik6IHN0cmluZ1tdIHtcbiAgY29uc3QgZHJvcHBlZDogc3RyaW5nW10gPSBbXTtcbiAgZm9yIChjb25zdCBmaWVsZCBvZiBbLi4uVU5TVVBQT1JURURfQ0hBVF9GSUVMRFMsIC4uLmV4dHJhXSkge1xuICAgIGlmICghKGZpZWxkIGluIGJvZHkpKSBjb250aW51ZTtcbiAgICBkZWxldGUgYm9keVtmaWVsZF07XG4gICAgZHJvcHBlZC5wdXNoKGZpZWxkKTtcbiAgfVxuICByZXR1cm4gZHJvcHBlZDtcbn1cblxuLyoqIE9wdGlvbnMgZm9yIHtAbGluayBjaGF0Q29udGVudFRvVGV4dH0uICovXG5leHBvcnQgaW50ZXJmYWNlIENoYXRDb250ZW50VG9UZXh0T3B0aW9ucyB7XG4gIC8qKlxuICAgKiBQbGFjZWQgYmV0d2VlbiBwYXJ0cy4gRGVmYXVsdHMgdG8gYFwiXCJgLCB3aGljaCByZWFzc2VtYmxlcyBhIG1lc3NhZ2UgdGhhdCB3YXNcbiAgICogc3BsaXQgcHVyZWx5IGZvciB0cmFuc3BvcnQ7IHBhc3MgYFwiXFxuXFxuXCJgIHdoZW4gdGhlIHBhcnRzIGFyZSBzZXBhcmF0ZVxuICAgKiBwYXJhZ3JhcGhzIHRoYXQgYSBodW1hbiB3aWxsIHJlYWQuXG4gICAqL1xuICBzZXBhcmF0b3I/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBSZXN0cmljdCB0byB0aGVzZSBgdHlwZWAgdmFsdWVzLiBEZWZhdWx0cyB0byBhY2NlcHRpbmcgYW55IHBhcnQgY2FycnlpbmdcbiAgICogYHRleHRgLCB3aGljaCBjb3ZlcnMgdGhlIGBpbnB1dF90ZXh0YCAvIGBvdXRwdXRfdGV4dGAgLyBgdGV4dGAgc3BlbGxpbmdzXG4gICAqIGRpZmZlcmVudCBBUElzIHVzZSBmb3IgdGhlIHNhbWUgdGhpbmcuXG4gICAqL1xuICB0eXBlcz86IHJlYWRvbmx5IHN0cmluZ1tdO1xufVxuXG4vKipcbiAqIEZsYXR0ZW4gYSBtZXNzYWdlIGBjb250ZW50YCB2YWx1ZSB0byBwbGFpbiB0ZXh0LiBBY2NlcHRzIHRoZSBzdHJpbmcgZm9ybSBhbmRcbiAqIHRoZSBzdHJ1Y3R1cmVkLXBhcnRzIGZvcm0gKHtAbGluayBDaGF0Q29udGVudFBhcnR9KSwgYW5kIHlpZWxkcyBgXCJcImAgZm9yXG4gKiBhbnl0aGluZyBlbHNlIChudWxsLCBhIGxvbmUgaW1hZ2UgcGFydCwgYSBtYWxmb3JtZWQgcGF5bG9hZCkuIFR5cGVkIGFzXG4gKiBgdW5rbm93bmAgYmVjYXVzZSBtb3N0IGNhbGxlcnMgYXJlIHJlYWRpbmcgYSBqdXN0LXBhcnNlZCBKU09OIGJvZHksIGFuZCB0aGVcbiAqIHBvaW50IG9mIHRoaXMgaGVscGVyIGlzIHRoYXQgdGhleSBkbyBub3QgaGF2ZSB0byBwcmUtY2hlY2sgaXQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjaGF0Q29udGVudFRvVGV4dChcbiAgY29udGVudDogdW5rbm93bixcbiAgb3B0aW9uczogQ2hhdENvbnRlbnRUb1RleHRPcHRpb25zID0ge30sXG4pOiBzdHJpbmcge1xuICBpZiAodHlwZW9mIGNvbnRlbnQgPT09IFwic3RyaW5nXCIpIHJldHVybiBjb250ZW50O1xuICBpZiAoIUFycmF5LmlzQXJyYXkoY29udGVudCkpIHJldHVybiBcIlwiO1xuICBjb25zdCB7IHNlcGFyYXRvciA9IFwiXCIsIHR5cGVzIH0gPSBvcHRpb25zO1xuICBjb25zdCBwYXJ0czogc3RyaW5nW10gPSBbXTtcbiAgZm9yIChjb25zdCByYXcgb2YgY29udGVudCkge1xuICAgIGlmICghcmF3IHx8IHR5cGVvZiByYXcgIT09IFwib2JqZWN0XCIpIGNvbnRpbnVlO1xuICAgIGNvbnN0IHBhcnQgPSByYXcgYXMgQ2hhdENvbnRlbnRQYXJ0O1xuICAgIGlmICh0eXBlcyAmJiAodHlwZW9mIHBhcnQudHlwZSAhPT0gXCJzdHJpbmdcIiB8fCAhdHlwZXMuaW5jbHVkZXMocGFydC50eXBlKSkpIGNvbnRpbnVlO1xuICAgIGlmICh0eXBlb2YgcGFydC50ZXh0ID09PSBcInN0cmluZ1wiKSBwYXJ0cy5wdXNoKHBhcnQudGV4dCk7XG4gIH1cbiAgcmV0dXJuIHBhcnRzLmpvaW4oc2VwYXJhdG9yKTtcbn1cbiJdfQ==
@@ -0,0 +1,175 @@
1
+ /**
2
+ * OpenAI Responses API <-> Chat Completions translation.
3
+ *
4
+ * Some clients speak only the Responses API (`POST /v1/responses`) - the Codex
5
+ * CLI is the motivating one. Databricks now also exposes native Responses
6
+ * surfaces (`/serving-endpoints/responses` and `/serving-endpoints/open-responses`),
7
+ * while most endpoints still speak Chat Completions at `/invocations`. This
8
+ * module bridges the two, in both directions:
9
+ *
10
+ * - {@link responsesToChat} lowers a Responses request body to a chat
11
+ * completions body (instructions -> system message, typed `input` items ->
12
+ * `messages`, `tools`/`tool_choice` carried through, function-call outputs
13
+ * folded back into the transcript).
14
+ * - {@link chatToResponsesRequest} raises a chat-completions request into a
15
+ * Responses request (the inverse of {@link responsesToChat}), for routing a
16
+ * chat client at a Responses-only model like Codex.
17
+ * - {@link chatToResponse} lifts a non-streaming chat completion back into a
18
+ * Responses `response` object.
19
+ * - {@link responseToChatCompletion} lifts a native Responses `response`
20
+ * back into a chat-completions body (the inverse of {@link chatToResponse}).
21
+ * - {@link createResponsesStreamTranslator} lifts a streaming chat completion
22
+ * (an OpenAI SSE `chat.completion.chunk` stream) into the Responses SSE
23
+ * event stream those clients consume (`response.created`,
24
+ * `response.output_text.delta`, function-call argument deltas,
25
+ * `response.completed`).
26
+ * - {@link readResponsesOutput} reads the other direction: pull the answer
27
+ * text and citations out of a `response` object returned by an endpoint
28
+ * that speaks Responses natively (the Databricks native web-search tool).
29
+ * - {@link sanitizeResponsesTools} keeps only `function` tools on a Responses
30
+ * request body (for `/open-responses` / Anthropic, which reject Codex
31
+ * built-ins like `web_search`).
32
+ * - {@link sanitizeOpenResponsesInput} rewrites `output_*` content part types
33
+ * in `input` to `input_*`, and drops Claude thinking / reasoning blocks
34
+ * that Open Responses rejects on replay (`redacted_thinking`, …).
35
+ *
36
+ * Only the surface real clients exercise is translated; unknown fields are
37
+ * ignored rather than rejected, so a newer client degrades instead of breaking.
38
+ *
39
+ * Browser-safe: pure functions over plain JSON, no transport and no Node
40
+ * built-ins, so the same translation runs in a proxy, a server route, or a test.
41
+ *
42
+ * @module
43
+ */
44
+ /**
45
+ * Lower a Responses request body to a chat-completions body. Returns the chat
46
+ * body plus whether the caller asked for streaming (the server needs it to pick
47
+ * the upstream `accept` header and the translation path).
48
+ */
49
+ export declare function responsesToChat(body: Record<string, unknown>): {
50
+ chat: Record<string, unknown>;
51
+ stream: boolean;
52
+ };
53
+ /**
54
+ * Raise a chat-completions request into a Responses request body. Inverse of
55
+ * {@link responsesToChat}: used when a chat client hits a Responses-only
56
+ * Databricks model (e.g. Codex) so the proxy can POST to
57
+ * `/serving-endpoints/responses` without the client knowing.
58
+ */
59
+ export declare function chatToResponsesRequest(body: Record<string, unknown>): {
60
+ responses: Record<string, unknown>;
61
+ stream: boolean;
62
+ };
63
+ /** Lift a non-streaming chat completion JSON into a Responses `response` object. */
64
+ export declare function chatToResponse(chat: Record<string, unknown>, model: string): unknown;
65
+ /**
66
+ * Lift a native Responses `response` object into a chat-completions body.
67
+ * Inverse of {@link chatToResponse}: used after a Responses-only upstream call
68
+ * when the client spoke Chat Completions.
69
+ */
70
+ export declare function responseToChatCompletion(response: Record<string, unknown>, model: string): Record<string, unknown>;
71
+ /**
72
+ * Incremental chat-chunk -> Responses-SSE translator. `feed` and `finish` both
73
+ * return the SSE bytes to forward, or `""` when a chunk produced no event.
74
+ */
75
+ export interface ResponsesStreamTranslator {
76
+ /** Translate one upstream `chat.completion.chunk` object. */
77
+ feed(chunk: Record<string, unknown>): string;
78
+ /** Close any open items and emit `response.completed`. Call once, at end of stream. */
79
+ finish(): string;
80
+ }
81
+ /**
82
+ * Translate an upstream chat-completions SSE stream into the Responses SSE
83
+ * event stream a Responses client consumes.
84
+ *
85
+ * Upstream chunks are `chat.completion.chunk` objects whose `choices[0].delta`
86
+ * carries incremental `content` (assistant text) and/or `tool_calls` (function
87
+ * calls, streamed by `index` with partial `arguments`). We emit the Responses
88
+ * lifecycle around them:
89
+ *
90
+ * response.created
91
+ * -> per text run: output_item.added → output_text.delta* → output_item.done
92
+ * -> per tool call: output_item.added → function_call_arguments.delta*
93
+ * → function_call_arguments.done → output_item.done
94
+ * response.completed (with the assembled final `response` object)
95
+ *
96
+ * The translator is intentionally tolerant: malformed/keepalive lines yield
97
+ * nothing.
98
+ */
99
+ export declare function createResponsesStreamTranslator(model: string, responseId: string): ResponsesStreamTranslator;
100
+ /** A source the model cited, as carried by a Responses content-part annotation. */
101
+ export interface ResponsesCitation {
102
+ url: string;
103
+ title?: string;
104
+ }
105
+ /** What {@link readResponsesOutput} pulls out of a `response` object. */
106
+ export interface ResponsesOutput {
107
+ /** The assistant's answer, with the output items concatenated. */
108
+ text: string;
109
+ /** Cited sources in first-seen order, deduplicated by URL. */
110
+ citations: ResponsesCitation[];
111
+ }
112
+ /**
113
+ * Read the answer text and cited sources out of a Responses `response` payload.
114
+ *
115
+ * This is the inverse of {@link chatToResponse}: it consumes what an endpoint
116
+ * speaking Responses natively returns, rather than producing it. The Databricks
117
+ * native web-search tool answers this way, with each source attached as a
118
+ * `url_citation` annotation on the content part that used it.
119
+ *
120
+ * Prefers the flattened `output_text` when the payload carries it, and falls
121
+ * back to walking `output[].content[].text`. Every field is optional upstream,
122
+ * so a payload missing any of it yields empty results rather than throwing.
123
+ */
124
+ export declare function readResponsesOutput(payload: Record<string, unknown>): ResponsesOutput;
125
+ /**
126
+ * Strip tool types the upstream model doesn't support from a Responses request
127
+ * body, returning a shallow copy safe to forward (the input is not mutated).
128
+ *
129
+ * Clients like the Codex CLI include built-in Responses tools (`web_search`,
130
+ * `local_shell`, `custom`, ...) alongside their `function` tools. Databricks'
131
+ * Open Responses surface for Anthropic/Claude (and other non-OpenAI providers)
132
+ * only accepts `function` tools and hard-errors otherwise:
133
+ * "Anthropic does not support tool type 'web_search'. Only 'function' is supported."
134
+ * Call this before forwarding to `/serving-endpoints/open-responses`. The OpenAI
135
+ * `/serving-endpoints/responses` path should keep built-ins (GPT supports them).
136
+ * If filtering empties the list, `tools` / `tool_choice` / `parallel_tool_calls`
137
+ * are dropped so we never send an empty `tools: []`.
138
+ */
139
+ export declare function sanitizeResponsesTools(body: Record<string, unknown>): Record<string, unknown>;
140
+ /**
141
+ * Content-part / input-item types Claude extended thinking uses on the wire.
142
+ *
143
+ * Shared so both wire surfaces strip the same set: the Responses path here
144
+ * ({@link sanitizeOpenResponsesInput}) and the Chat Completions path in
145
+ * `@dbx-tools/appkit-mastra`'s serving sanitize. Anthropic signs these blocks,
146
+ * so a replay that mutates one is rejected outright - both paths must agree on
147
+ * what counts as a reasoning block.
148
+ */
149
+ export declare const REASONING_TYPES: ReadonlySet<string>;
150
+ /**
151
+ * Rewrite Responses `input` so Open Responses will accept it.
152
+ *
153
+ * Two OpenAI / Codex shapes that Databricks Open Responses (Claude, Gemini, …)
154
+ * reject:
155
+ *
156
+ * 1. Prior assistant turns with `output_text` / `output_*` content parts —
157
+ * Open Responses only allows `input_text`, `input_image`, `input_file`,
158
+ * `input_audio`. Map every `output_<kind>` part to `input_<kind>`.
159
+ * 2. Replayed extended-thinking blocks (`thinking`, `redacted_thinking`,
160
+ * `reasoning` content parts, and top-level `reasoning` input items). Claude
161
+ * signs those; any client round-trip that mutates them (or a cross-provider
162
+ * replay) fails with e.g.
163
+ * "messages.N.content.M: Invalid `data` in `redacted_thinking` block"
164
+ * Strip them — the UI/client already showed the thinking; replay does not
165
+ * need the signed blob. Same policy as appkit-mastra's serving sanitize.
166
+ *
167
+ * Returns a shallow copy when anything changes; otherwise the input body.
168
+ */
169
+ export declare function sanitizeOpenResponsesInput(body: Record<string, unknown>): Record<string, unknown>;
170
+ /**
171
+ * Full Open Responses request sanitizer: strip non-`function` tools, rewrite
172
+ * `output_*` content parts, and drop thinking / reasoning blocks. Safe no-op
173
+ * when nothing needs changing.
174
+ */
175
+ export declare function sanitizeOpenResponsesRequest(body: Record<string, unknown>): Record<string, unknown>;