@dbx-tools/shared-model 0.3.37 → 0.3.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -12,20 +12,24 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "zod": "4.3.6",
15
- "@dbx-tools/shared-core": "0.3.37"
15
+ "@dbx-tools/shared-core": "0.3.39"
16
16
  },
17
17
  "main": "index.ts",
18
18
  "license": "UNLICENSED",
19
19
  "publishConfig": {
20
20
  "access": "public"
21
21
  },
22
- "version": "0.3.37",
22
+ "version": "0.3.39",
23
23
  "types": "index.ts",
24
24
  "type": "module",
25
25
  "exports": {
26
26
  ".": "./index.ts",
27
27
  "./package.json": "./package.json"
28
28
  },
29
+ "files": [
30
+ "index.ts",
31
+ "src"
32
+ ],
29
33
  "dbxToolsConfig": {
30
34
  "tags": [
31
35
  "shared"
@@ -1,211 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { describe, it } from "node:test";
3
- import {
4
- classifyByFamily,
5
- classifyEndpoints,
6
- endpointCapabilities,
7
- versionTuple,
8
- } from "../src/classify";
9
- import { ModelClass, type ModelProfile, type ServingEndpointSummary } from "../src/model";
10
-
11
- const CHAT_TASK = "llm/v1/chat";
12
- const EMBEDDING_TASK = "llm/v1/embeddings";
13
-
14
- /** Build a chat serving endpoint, optionally scored, for classifier fixtures. */
15
- function chat(name: string, profile?: ModelProfile): ServingEndpointSummary {
16
- return { name, task: CHAT_TASK, ...(profile ? { profile } : {}) };
17
- }
18
-
19
- /** Build an embedding serving endpoint for classifier fixtures. */
20
- function embedding(name: string): ServingEndpointSummary {
21
- return { name, task: EMBEDDING_TASK };
22
- }
23
-
24
- /** Names of the endpoints classified into `cls`, in ranked order. */
25
- function classNames(
26
- buckets: Record<ModelClass, ServingEndpointSummary[]>,
27
- cls: ModelClass,
28
- ): string[] {
29
- return buckets[cls].map((e) => e.name);
30
- }
31
-
32
- describe("versionTuple", () => {
33
- it("reads major.minor.patch starting at the first digit", () => {
34
- assert.deepEqual(versionTuple("databricks-claude-opus-4-8"), [4, 8, 0]);
35
- assert.deepEqual(versionTuple("databricks-claude-opus-4-7"), [4, 7, 0]);
36
- assert.deepEqual(versionTuple("databricks-gpt-5-5"), [5, 5, 0]);
37
- });
38
-
39
- it("keeps double-digit components numeric (no decimal collapse)", () => {
40
- assert.deepEqual(versionTuple("databricks-claude-opus-4-10"), [4, 10, 0]);
41
- // 4.10 must sort ABOVE 4.8 - the bug a `major + minor/10` score hit.
42
- const [, tenMinor] = versionTuple("databricks-claude-opus-4-10");
43
- const [, eightMinor] = versionTuple("databricks-claude-opus-4-8");
44
- assert.ok(tenMinor > eightMinor);
45
- });
46
-
47
- it("reads a parameter-size suffix as the patch component", () => {
48
- assert.deepEqual(versionTuple("databricks-meta-llama-3-3-70b-instruct"), [3, 3, 70]);
49
- assert.deepEqual(versionTuple("databricks-gpt-oss-120b"), [120, 0, 0]);
50
- });
51
-
52
- it("returns all-zero for names with no digits", () => {
53
- assert.deepEqual(versionTuple("databricks-bge-large-en"), [0, 0, 0]);
54
- });
55
- });
56
-
57
- describe("endpointCapabilities", () => {
58
- it("reads capability off the task hint", () => {
59
- assert.deepEqual(endpointCapabilities(chat("databricks-gpt-5-5")), {
60
- chat: true,
61
- embedding: false,
62
- tools: true,
63
- });
64
- assert.deepEqual(endpointCapabilities(embedding("databricks-gte-large-en")), {
65
- chat: false,
66
- embedding: true,
67
- tools: false,
68
- });
69
- });
70
-
71
- it("falls back to the classified class when the endpoint carries no task", () => {
72
- assert.equal(
73
- endpointCapabilities({ name: "custom", class: ModelClass.ChatFast }).chat,
74
- true,
75
- );
76
- assert.equal(
77
- endpointCapabilities({ name: "custom", class: ModelClass.Embedding }).embedding,
78
- true,
79
- );
80
- });
81
-
82
- it("keeps an unclassified, untasked endpoint out of both buckets", () => {
83
- assert.deepEqual(endpointCapabilities({ name: "mystery" }), {
84
- chat: false,
85
- embedding: false,
86
- tools: false,
87
- });
88
- });
89
-
90
- it("lets embedding win when task and class disagree", () => {
91
- const conflicted: ServingEndpointSummary = {
92
- name: "databricks-gte-large-en",
93
- task: EMBEDDING_TASK,
94
- class: ModelClass.ChatFast,
95
- };
96
- assert.deepEqual(endpointCapabilities(conflicted), {
97
- chat: false,
98
- embedding: true,
99
- tools: false,
100
- });
101
- });
102
- });
103
-
104
- describe("classifyEndpoints", () => {
105
- it("buckets scored models by quality quantiles, best-first", () => {
106
- // Three clear quality bands (gaps keep the 1/3 and 2/3 quantiles
107
- // between bands), two models per chat band.
108
- const buckets = classifyEndpoints([
109
- chat("databricks-claude-opus-4-8", { quality: 90, cost: 10, speed: 44 }),
110
- chat("databricks-claude-opus-4-7", { quality: 88, cost: 10, speed: 44 }),
111
- chat("databricks-claude-sonnet-4-6", { quality: 50, cost: 18, speed: 55 }),
112
- chat("databricks-gpt-5-5", { quality: 48, cost: 20, speed: 50 }),
113
- chat("databricks-claude-haiku-4-5", { quality: 10, cost: 5, speed: 80 }),
114
- chat("databricks-meta-llama-3-1-8b-instruct", { quality: 8, cost: 3, speed: 90 }),
115
- ]);
116
- assert.deepEqual(classNames(buckets, ModelClass.ChatThinking), [
117
- "databricks-claude-opus-4-8",
118
- "databricks-claude-opus-4-7",
119
- ]);
120
- assert.deepEqual(classNames(buckets, ModelClass.ChatBalanced), [
121
- "databricks-claude-sonnet-4-6",
122
- "databricks-gpt-5-5",
123
- ]);
124
- assert.deepEqual(classNames(buckets, ModelClass.ChatFast), [
125
- "databricks-claude-haiku-4-5",
126
- "databricks-meta-llama-3-1-8b-instruct",
127
- ]);
128
- });
129
-
130
- it("breaks a scored tie (same quality/cost/speed) by parsed version", () => {
131
- // All identical except the trailing version: only versionTuple can
132
- // order them. This is the live `q=46` Opus case.
133
- const buckets = classifyEndpoints([
134
- chat("databricks-claude-opus-4-6", { quality: 90, cost: 10, speed: 44 }),
135
- chat("databricks-claude-opus-4-8", { quality: 90, cost: 10, speed: 44 }),
136
- chat("databricks-claude-opus-4-7", { quality: 90, cost: 10, speed: 44 }),
137
- ]);
138
- assert.deepEqual(classNames(buckets, ModelClass.ChatThinking), [
139
- "databricks-claude-opus-4-8",
140
- "databricks-claude-opus-4-7",
141
- "databricks-claude-opus-4-6",
142
- ]);
143
- });
144
-
145
- it("orders a double-digit point release above a single-digit one", () => {
146
- const buckets = classifyEndpoints([
147
- chat("databricks-claude-opus-4-9", { quality: 90, cost: 10, speed: 44 }),
148
- chat("databricks-claude-opus-4-10", { quality: 90, cost: 10, speed: 44 }),
149
- ]);
150
- assert.deepEqual(classNames(buckets, ModelClass.ChatThinking), [
151
- "databricks-claude-opus-4-10",
152
- "databricks-claude-opus-4-9",
153
- ]);
154
- });
155
-
156
- it("places unscored but recognizable chat endpoints by family heuristic", () => {
157
- const buckets = classifyEndpoints([
158
- chat("databricks-claude-opus-4-1"),
159
- chat("databricks-claude-sonnet-4-0"),
160
- chat("databricks-claude-haiku-3-5"),
161
- ]);
162
- assert.deepEqual(classNames(buckets, ModelClass.ChatThinking), [
163
- "databricks-claude-opus-4-1",
164
- ]);
165
- assert.deepEqual(classNames(buckets, ModelClass.ChatBalanced), [
166
- "databricks-claude-sonnet-4-0",
167
- ]);
168
- assert.deepEqual(classNames(buckets, ModelClass.ChatFast), [
169
- "databricks-claude-haiku-3-5",
170
- ]);
171
- });
172
-
173
- it("buckets embedding endpoints by task, regardless of name or score", () => {
174
- const buckets = classifyEndpoints([
175
- chat("databricks-claude-opus-4-1"),
176
- embedding("databricks-gte-large-en"),
177
- embedding("databricks-bge-large-en"),
178
- ]);
179
- assert.deepEqual(classNames(buckets, ModelClass.Embedding), [
180
- "databricks-gte-large-en",
181
- "databricks-bge-large-en",
182
- ]);
183
- // Embeddings never leak into a chat band.
184
- assert.deepEqual(classNames(buckets, ModelClass.ChatThinking), [
185
- "databricks-claude-opus-4-1",
186
- ]);
187
- });
188
-
189
- it("omits unrecognized unscored chat endpoints from every chat band", () => {
190
- const buckets = classifyEndpoints([
191
- chat("databricks-claude-opus-4-1"),
192
- chat("databricks-some-custom-thing"),
193
- ]);
194
- const chatNames = [
195
- ...buckets[ModelClass.ChatThinking],
196
- ...buckets[ModelClass.ChatBalanced],
197
- ...buckets[ModelClass.ChatFast],
198
- ].map((e) => e.name);
199
- assert.deepEqual(chatNames, ["databricks-claude-opus-4-1"]);
200
- });
201
- });
202
-
203
- describe("classifyByFamily", () => {
204
- it("maps well-known variants to chat classes and returns null for unknowns", () => {
205
- assert.equal(classifyByFamily("databricks-claude-opus-4-8")?.class, ModelClass.ChatThinking);
206
- assert.equal(classifyByFamily("databricks-claude-sonnet-4-6")?.class, ModelClass.ChatBalanced);
207
- assert.equal(classifyByFamily("databricks-claude-haiku-4-5")?.class, ModelClass.ChatFast);
208
- assert.equal(classifyByFamily("databricks-gpt-5-nano")?.class, ModelClass.ChatFast);
209
- assert.equal(classifyByFamily("databricks-some-custom-thing"), null);
210
- });
211
- });
@@ -1,45 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { describe, it } from "node:test";
3
- import { toModelDisplayName } from "../src/display";
4
-
5
- describe("toModelDisplayName", () => {
6
- it("strips a leading databricks prefix and dots the version", () => {
7
- assert.equal(toModelDisplayName("databricks-claude-sonnet-4-6"), "Claude Sonnet 4.6");
8
- });
9
-
10
- it("strips the system.ai namespace and uppercases acronyms", () => {
11
- assert.equal(toModelDisplayName("system.ai.bge_large_en"), "BGE Large En");
12
- });
13
-
14
- it("uppercases GPT/OSS and glues the size unit", () => {
15
- assert.equal(toModelDisplayName("databricks-gpt-oss-120b"), "GPT OSS 120B");
16
- });
17
-
18
- it("restores AI casing", () => {
19
- assert.equal(toModelDisplayName("databricks-ai-router"), "AI Router");
20
- });
21
-
22
- it("joins multi-part version runs with dots", () => {
23
- assert.equal(
24
- toModelDisplayName("databricks-meta-llama-3-3-70b-instruct"),
25
- "Meta Llama 3.3 70B Instruct",
26
- );
27
- });
28
-
29
- it("prefers a Databricks-provided name verbatim", () => {
30
- assert.equal(
31
- toModelDisplayName("databricks-claude-sonnet-4-6", "Claude Sonnet 4.6 (Preview)"),
32
- "Claude Sonnet 4.6 (Preview)",
33
- );
34
- });
35
-
36
- it("ignores a blank provided name and falls back to the derived label", () => {
37
- assert.equal(toModelDisplayName("databricks-gpt-5-5", " "), "GPT 5.5");
38
- });
39
-
40
- it("title-cases a bare vendor token when nothing else remains", () => {
41
- // Only leading vendor prefixes are stripped, and never the last
42
- // remaining token, so a name that is *only* a vendor word stays.
43
- assert.equal(toModelDisplayName("databricks"), "Databricks");
44
- });
45
- });
@@ -1,80 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { describe, it } from "node:test";
3
- import {
4
- UNSUPPORTED_CHAT_FIELDS,
5
- chatContentToText,
6
- stripUnsupportedChatFields,
7
- } from "../src/openai-chat";
8
-
9
- describe("chatContentToText", () => {
10
- it("passes a string through and flattens typed parts", () => {
11
- assert.equal(chatContentToText("hello"), "hello");
12
- assert.equal(
13
- chatContentToText([{ type: "input_text", text: "a" }, { type: "output_text", text: "b" }]),
14
- "ab",
15
- );
16
- });
17
-
18
- it("honors a separator and a type filter", () => {
19
- const content = [
20
- { type: "text", text: "one" },
21
- { type: "thinking", text: "hidden" },
22
- { type: "text", text: "two" },
23
- ];
24
- assert.equal(chatContentToText(content, { separator: "\n\n", types: ["text"] }), "one\n\ntwo");
25
- });
26
-
27
- it("yields empty string for anything it can't read", () => {
28
- assert.equal(chatContentToText(null), "");
29
- assert.equal(chatContentToText(undefined), "");
30
- assert.equal(chatContentToText([{ type: "image_url" }]), "");
31
- });
32
- });
33
-
34
- describe("stripUnsupportedChatFields", () => {
35
- it("drops parallel_tool_calls, the field Databricks rejects outright", () => {
36
- const body: Record<string, unknown> = {
37
- model: "databricks-claude-opus-4-8",
38
- messages: [],
39
- parallel_tool_calls: false,
40
- };
41
- assert.deepEqual(stripUnsupportedChatFields(body), ["parallel_tool_calls"]);
42
- assert.deepEqual(body, { model: "databricks-claude-opus-4-8", messages: [] });
43
- });
44
-
45
- it("drops a field present with a falsy or null value", () => {
46
- const body: Record<string, unknown> = { store: false, metadata: null };
47
- assert.deepEqual(stripUnsupportedChatFields(body).sort(), ["metadata", "store"]);
48
- assert.deepEqual(body, {});
49
- });
50
-
51
- it("leaves a supported body untouched and reports nothing dropped", () => {
52
- const body: Record<string, unknown> = {
53
- model: "m",
54
- messages: [{ role: "user", content: "hi" }],
55
- stream: true,
56
- tools: [],
57
- tool_choice: "auto",
58
- temperature: 0.2,
59
- max_tokens: 64,
60
- };
61
- const before = structuredClone(body);
62
- assert.deepEqual(stripUnsupportedChatFields(body), []);
63
- assert.deepEqual(body, before);
64
- });
65
-
66
- it("drops caller-supplied extra fields alongside the built-in list", () => {
67
- const body: Record<string, unknown> = { store: true, future_field: 1, keep: 2 };
68
- assert.deepEqual(stripUnsupportedChatFields(body, ["future_field"]), [
69
- "store",
70
- "future_field",
71
- ]);
72
- assert.deepEqual(body, { keep: 2 });
73
- });
74
-
75
- it("covers every name it advertises", () => {
76
- const body = Object.fromEntries(UNSUPPORTED_CHAT_FIELDS.map((field) => [field, 1]));
77
- assert.deepEqual(stripUnsupportedChatFields(body), [...UNSUPPORTED_CHAT_FIELDS]);
78
- assert.deepEqual(body, {});
79
- });
80
- });
@@ -1,312 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { describe, it } from "node:test";
3
- import type { ChatMessage } from "../src/openai-chat";
4
- import {
5
- chatToResponse,
6
- createResponsesStreamTranslator,
7
- readResponsesOutput,
8
- responsesToChat,
9
- sanitizeOpenResponsesRequest,
10
- } from "../src/openai-responses";
11
-
12
- /** The `messages` a lowered Responses body produced, typed for assertions. */
13
- function messagesOf(body: Record<string, unknown>): ChatMessage[] {
14
- const { chat } = responsesToChat(body);
15
- return chat.messages as ChatMessage[];
16
- }
17
-
18
- /** Event names, in order, from a block of SSE text. */
19
- function eventNames(sse: string): string[] {
20
- return sse
21
- .split("\n")
22
- .filter((line) => line.startsWith("event: "))
23
- .map((line) => line.slice("event: ".length));
24
- }
25
-
26
- /** Parsed `data:` payloads from a block of SSE text. */
27
- function eventData(sse: string): Record<string, unknown>[] {
28
- return sse
29
- .split("\n")
30
- .filter((line) => line.startsWith("data: "))
31
- .map((line) => JSON.parse(line.slice("data: ".length)) as Record<string, unknown>);
32
- }
33
-
34
- describe("responsesToChat", () => {
35
- it("lowers instructions and message items into a chat transcript", () => {
36
- const messages = messagesOf({
37
- instructions: "be terse",
38
- input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] }],
39
- });
40
- assert.deepEqual(messages, [
41
- { role: "system", content: "be terse" },
42
- { role: "user", content: "hi" },
43
- ]);
44
- });
45
-
46
- it("maps the developer role to system (chat has no developer role)", () => {
47
- const [first] = messagesOf({ input: [{ role: "developer", content: "policy" }] });
48
- assert.equal(first?.role, "system");
49
- });
50
-
51
- it("re-attaches a prior function_call to an assistant turn", () => {
52
- const [first] = messagesOf({
53
- input: [{ type: "function_call", call_id: "c1", name: "lookup", arguments: '{"q":1}' }],
54
- });
55
- assert.equal(first?.role, "assistant");
56
- assert.equal(first?.content, null);
57
- assert.deepEqual(first?.tool_calls, [
58
- { id: "c1", type: "function", function: { name: "lookup", arguments: '{"q":1}' } },
59
- ]);
60
- });
61
-
62
- it("keys a function_call_output back to the call it answers", () => {
63
- const [first] = messagesOf({
64
- input: [{ type: "function_call_output", call_id: "c1", output: "42" }],
65
- });
66
- assert.deepEqual(first, { role: "tool", tool_call_id: "c1", content: "42" });
67
- });
68
-
69
- it("nests flat Responses function tools and drops built-in tool types", () => {
70
- const { chat } = responsesToChat({
71
- input: [],
72
- tools: [
73
- { type: "function", name: "lookup", description: "d", parameters: { type: "object" } },
74
- { type: "local_shell" },
75
- { type: "web_search" },
76
- ],
77
- tool_choice: "auto",
78
- });
79
- assert.deepEqual(chat.tools, [
80
- {
81
- type: "function",
82
- function: { name: "lookup", description: "d", parameters: { type: "object" } },
83
- },
84
- ]);
85
- assert.equal(chat.tool_choice, "auto");
86
- });
87
-
88
- it("never forwards parallel_tool_calls (Databricks rejects unknown fields)", () => {
89
- const { chat } = responsesToChat({
90
- input: [],
91
- parallel_tool_calls: true,
92
- tools: [{ type: "function", name: "lookup" }],
93
- });
94
- assert.ok(!("parallel_tool_calls" in chat));
95
- });
96
-
97
- it("reports the streaming flag", () => {
98
- assert.equal(responsesToChat({ input: [] }).stream, false);
99
- assert.equal(responsesToChat({ input: [], stream: true }).stream, true);
100
- });
101
- });
102
-
103
- describe("chatToResponse", () => {
104
- it("lifts assistant text, tool calls, and usage into a response object", () => {
105
- const response = chatToResponse(
106
- {
107
- id: "chatcmpl-1",
108
- created: 1700000000,
109
- choices: [
110
- {
111
- message: {
112
- content: "done",
113
- tool_calls: [{ id: "c1", function: { name: "lookup", arguments: '{"q":1}' } }],
114
- },
115
- },
116
- ],
117
- usage: { prompt_tokens: 3, completion_tokens: 4, total_tokens: 7 },
118
- },
119
- "databricks-claude-opus-4-8",
120
- ) as Record<string, unknown>;
121
-
122
- assert.equal(response.id, "chatcmpl-1");
123
- assert.equal(response.status, "completed");
124
- assert.equal(response.model, "databricks-claude-opus-4-8");
125
- assert.deepEqual(response.output, [
126
- { type: "message", role: "assistant", content: [{ type: "output_text", text: "done" }] },
127
- { type: "function_call", call_id: "c1", name: "lookup", arguments: '{"q":1}' },
128
- ]);
129
- assert.deepEqual(response.usage, { input_tokens: 3, output_tokens: 4, total_tokens: 7 });
130
- });
131
-
132
- it("omits the message item when the turn was tool calls only", () => {
133
- const response = chatToResponse(
134
- { choices: [{ message: { content: null, tool_calls: [] } }] },
135
- "m",
136
- ) as { output: unknown[] };
137
- assert.deepEqual(response.output, []);
138
- });
139
- });
140
-
141
- describe("createResponsesStreamTranslator", () => {
142
- it("opens on the first chunk and closes the text run at finish", () => {
143
- const translator = createResponsesStreamTranslator("m", "resp-1");
144
- const first = translator.feed({ choices: [{ delta: { content: "he" } }] });
145
- assert.deepEqual(eventNames(first), [
146
- "response.created",
147
- "response.output_item.added",
148
- "response.output_text.delta",
149
- ]);
150
-
151
- const second = translator.feed({ choices: [{ delta: { content: "llo" } }] });
152
- assert.deepEqual(eventNames(second), ["response.output_text.delta"]);
153
-
154
- const done = translator.finish();
155
- assert.deepEqual(eventNames(done), [
156
- "response.output_text.done",
157
- "response.output_item.done",
158
- "response.completed",
159
- ]);
160
- // The buffered text is what the client renders as the final message.
161
- assert.equal(eventData(done)[0]?.text, "hello");
162
- });
163
-
164
- it("assembles argument fragments into one function_call item", () => {
165
- const translator = createResponsesStreamTranslator("m", "resp-1");
166
- translator.feed({
167
- choices: [
168
- { delta: { tool_calls: [{ index: 0, id: "c1", function: { name: "lookup" } }] } },
169
- ],
170
- });
171
- translator.feed({ choices: [{ delta: { tool_calls: [{ index: 0, function: { arguments: '{"q' } }] } }] });
172
- translator.feed({ choices: [{ delta: { tool_calls: [{ index: 0, function: { arguments: '":1}' } }] } }] });
173
-
174
- const done = translator.finish();
175
- const [args] = eventData(done);
176
- assert.equal(args?.arguments, '{"q":1}');
177
- assert.ok(eventNames(done).includes("response.function_call_arguments.done"));
178
- });
179
-
180
- it("closes an open text run before opening a tool call so output stays ordered", () => {
181
- const translator = createResponsesStreamTranslator("m", "resp-1");
182
- translator.feed({ choices: [{ delta: { content: "thinking" } }] });
183
- const withTool = translator.feed({
184
- choices: [{ delta: { tool_calls: [{ index: 0, id: "c1", function: { name: "f" } }] } }],
185
- });
186
- assert.deepEqual(eventNames(withTool), [
187
- "response.output_text.done",
188
- "response.output_item.done",
189
- "response.output_item.added",
190
- ]);
191
- });
192
-
193
- it("emits nothing but the lifecycle for an empty chunk", () => {
194
- const translator = createResponsesStreamTranslator("m", "resp-1");
195
- assert.deepEqual(eventNames(translator.feed({})), ["response.created"]);
196
- assert.deepEqual(eventNames(translator.finish()), ["response.completed"]);
197
- });
198
- });
199
-
200
- describe("readResponsesOutput", () => {
201
- it("prefers the flattened output_text", () => {
202
- const { text } = readResponsesOutput({
203
- output_text: " the answer ",
204
- output: [{ content: [{ text: "ignored" }] }],
205
- });
206
- assert.equal(text, "the answer");
207
- });
208
-
209
- it("falls back to joining the output items", () => {
210
- const { text } = readResponsesOutput({
211
- output: [{ content: [{ text: "one" }, { text: "two" }] }],
212
- });
213
- assert.equal(text, "one\ntwo");
214
- });
215
-
216
- it("collects url_citation annotations, deduplicated by url", () => {
217
- const { citations } = readResponsesOutput({
218
- output: [
219
- {
220
- content: [
221
- {
222
- text: "answer",
223
- annotations: [
224
- { type: "url_citation", url: "https://a.example", title: "A" },
225
- { type: "url_citation", url: "https://a.example", title: "A again" },
226
- { type: "url_citation", url: "https://b.example" },
227
- { type: "file_citation" },
228
- ],
229
- },
230
- ],
231
- },
232
- ],
233
- });
234
- assert.deepEqual(citations, [
235
- { url: "https://a.example", title: "A" },
236
- { url: "https://b.example" },
237
- ]);
238
- });
239
-
240
- it("returns empty results for a payload with no output", () => {
241
- assert.deepEqual(readResponsesOutput({}), { text: "", citations: [] });
242
- });
243
- });
244
-
245
- describe("sanitizeOpenResponsesRequest", () => {
246
- it("rewrites output_text content parts to input_text", () => {
247
- const out = sanitizeOpenResponsesRequest({
248
- input: [
249
- {
250
- type: "message",
251
- role: "assistant",
252
- content: [{ type: "output_text", text: "hi" }],
253
- },
254
- ],
255
- });
256
- assert.deepEqual(out.input, [
257
- {
258
- type: "message",
259
- role: "assistant",
260
- content: [{ type: "input_text", text: "hi" }],
261
- },
262
- ]);
263
- });
264
-
265
- it("strips redacted_thinking / thinking content parts and reasoning items", () => {
266
- const out = sanitizeOpenResponsesRequest({
267
- input: [
268
- { type: "reasoning", summary: [{ type: "summary_text", text: "plan" }] },
269
- {
270
- type: "message",
271
- role: "assistant",
272
- content: [
273
- { type: "redacted_thinking", data: "not-a-valid-blob" },
274
- { type: "thinking", thinking: "secret" },
275
- { type: "output_text", text: "visible" },
276
- ],
277
- },
278
- ],
279
- });
280
- assert.deepEqual(out.input, [
281
- {
282
- type: "message",
283
- role: "assistant",
284
- content: [{ type: "input_text", text: "visible" }],
285
- },
286
- ]);
287
- });
288
-
289
- it("drops assistant turns that only carried thinking", () => {
290
- const out = sanitizeOpenResponsesRequest({
291
- input: [
292
- {
293
- type: "message",
294
- role: "assistant",
295
- content: [{ type: "redacted_thinking", data: "x" }],
296
- },
297
- { type: "message", role: "user", content: [{ type: "input_text", text: "next" }] },
298
- ],
299
- });
300
- assert.deepEqual(out.input, [
301
- { type: "message", role: "user", content: [{ type: "input_text", text: "next" }] },
302
- ]);
303
- });
304
-
305
- it("strips non-function tools", () => {
306
- const out = sanitizeOpenResponsesRequest({
307
- input: [],
308
- tools: [{ type: "web_search" }, { type: "function", name: "f", parameters: {} }],
309
- });
310
- assert.deepEqual(out.tools, [{ type: "function", name: "f", parameters: {} }]);
311
- });
312
- });
@@ -1,14 +0,0 @@
1
- // ~~ Generated by projen. To modify, edit .projenrc.js and run "pnpm exec projen".
2
- {
3
- "extends": "../tsconfig.json",
4
- "compilerOptions": {
5
- "noEmit": true,
6
- "rootDir": ".."
7
- },
8
- "include": [
9
- "**/*.ts"
10
- ],
11
- "exclude": [
12
- "node_modules"
13
- ]
14
- }
package/tsconfig.json DELETED
@@ -1,40 +0,0 @@
1
- // ~~ Generated by projen. To modify, edit .projenrc.js and run "pnpm exec projen".
2
- {
3
- "compilerOptions": {
4
- "rootDir": "src",
5
- "outDir": "lib",
6
- "alwaysStrict": true,
7
- "declaration": true,
8
- "esModuleInterop": true,
9
- "experimentalDecorators": true,
10
- "inlineSourceMap": true,
11
- "inlineSources": true,
12
- "lib": [
13
- "ES2022",
14
- "WebWorker"
15
- ],
16
- "module": "ESNext",
17
- "noEmitOnError": false,
18
- "noFallthroughCasesInSwitch": true,
19
- "noImplicitAny": true,
20
- "noImplicitReturns": true,
21
- "noImplicitThis": true,
22
- "noUnusedLocals": true,
23
- "noUnusedParameters": true,
24
- "resolveJsonModule": true,
25
- "strict": true,
26
- "strictNullChecks": true,
27
- "strictPropertyInitialization": true,
28
- "stripInternal": true,
29
- "target": "ES2022",
30
- "types": [],
31
- "moduleResolution": "bundler",
32
- "skipLibCheck": true
33
- },
34
- "include": [
35
- "src/**/*.ts"
36
- ],
37
- "exclude": [
38
- "node_modules"
39
- ]
40
- }