|
1 | 1 | import type { ChatCompletionChunk } from "openai/resources/chat/completions.js"; |
2 | 2 | import { describe, expect, it, vi } from "vitest"; |
3 | 3 | import { SYSTEM_PROMPT_CACHE_BOUNDARY } from "../../agents/system-prompt-cache-boundary.js"; |
4 | | -import type { Context, Model } from "../types.js"; |
| 4 | +import type { Context, Model, Tool } from "../types.js"; |
5 | 5 |
|
6 | 6 | type DeepPartial<T> = { [P in keyof T]?: DeepPartial<T[P]> }; |
7 | 7 | type OpenAICompatibleDelta = DeepPartial<ChatCompletionChunk["choices"][number]["delta"]> & { |
@@ -117,6 +117,60 @@ function makeFinishChunk( |
117 | 117 | } |
118 | 118 |
|
119 | 119 | describe("OpenAI-compatible completions params", () => { |
| 120 | + it("skips unreadable tool schemas while preserving healthy tools", async () => { |
| 121 | + const unreadableSchemaTool = { |
| 122 | + name: "bad_schema", |
| 123 | + description: "Bad schema", |
| 124 | + get parameters(): never { |
| 125 | + throw new Error("parameters getter exploded"); |
| 126 | + }, |
| 127 | + } satisfies Tool; |
| 128 | + const healthyTool = { |
| 129 | + name: "lookup", |
| 130 | + description: "Lookup", |
| 131 | + parameters: { |
| 132 | + type: "object", |
| 133 | + properties: { query: { type: "string" } }, |
| 134 | + required: ["query"], |
| 135 | + }, |
| 136 | + } satisfies Tool; |
| 137 | + let capturedTools: unknown; |
| 138 | + |
| 139 | + const stream = streamOpenAICompletions( |
| 140 | + createModel(32_000), |
| 141 | + { |
| 142 | + messages: [{ role: "user", content: "lookup", timestamp: 1 }], |
| 143 | + tools: [unreadableSchemaTool, healthyTool], |
| 144 | + } satisfies Context, |
| 145 | + { |
| 146 | + apiKey: "sk-test", |
| 147 | + onPayload(payload) { |
| 148 | + capturedTools = (payload as { tools?: unknown }).tools; |
| 149 | + throw new Error("stop before network"); |
| 150 | + }, |
| 151 | + }, |
| 152 | + ); |
| 153 | + |
| 154 | + const result = await stream.result(); |
| 155 | + |
| 156 | + expect(result.stopReason).toBe("error"); |
| 157 | + expect(capturedTools).toEqual([ |
| 158 | + { |
| 159 | + type: "function", |
| 160 | + function: { |
| 161 | + name: "lookup", |
| 162 | + description: "Lookup", |
| 163 | + strict: false, |
| 164 | + parameters: { |
| 165 | + type: "object", |
| 166 | + properties: { query: { type: "string" } }, |
| 167 | + required: ["query"], |
| 168 | + }, |
| 169 | + }, |
| 170 | + }, |
| 171 | + ]); |
| 172 | + }); |
| 173 | + |
120 | 174 | it("clamps requested max tokens to the model output cap", async () => { |
121 | 175 | let capturedMaxTokens: unknown; |
122 | 176 | const stream = streamOpenAICompletions(createModel(32_000), context, { |
|
0 commit comments