diff --git a/examples/basic/package.json b/examples/basic/package.json index 05fdd69..65a48ea 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -28,7 +28,7 @@ "@formula-monks/kurt-open-ai": "workspace:*", "@formula-monks/kurt-vertex-ai": "workspace:*", "@google-cloud/vertexai": "1.1.0", - "openai": "4.66.1", + "openai": "^4.76.0", "zod": "^3.23.8" } } diff --git a/packages/kurt-open-ai/package.json b/packages/kurt-open-ai/package.json index 7b5b674..77066f7 100644 --- a/packages/kurt-open-ai/package.json +++ b/packages/kurt-open-ai/package.json @@ -28,7 +28,7 @@ }, "dependencies": { "@formula-monks/kurt": "^1.4.0", - "openai": "4.66.1", + "openai": "4.76.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.3" }, diff --git a/packages/kurt-open-ai/spec/generateNaturalLanguage.spec.ts b/packages/kurt-open-ai/spec/generateNaturalLanguage.spec.ts index d1bde5f..3b76366 100644 --- a/packages/kurt-open-ai/spec/generateNaturalLanguage.spec.ts +++ b/packages/kurt-open-ai/spec/generateNaturalLanguage.spec.ts @@ -4,7 +4,7 @@ import { KurtResultLimitError } from "@formula-monks/kurt" describe("KurtOpenAI generateNaturalLanguage", () => { test("says hello", async () => { - const result = await snapshotAndMock((kurt) => + const result = await snapshotAndMock("gpt-4o-2024-05-13", (kurt) => kurt.generateNaturalLanguage({ prompt: "Say hello!", }) @@ -13,7 +13,7 @@ describe("KurtOpenAI generateNaturalLanguage", () => { }) test("writes a haiku with high temperature", async () => { - const result = await snapshotAndMock((kurt) => + const result = await snapshotAndMock("gpt-4o-2024-05-13", (kurt) => kurt.generateNaturalLanguage({ prompt: "Compose a haiku about a mountain stream at night.", sampling: { @@ -34,6 +34,7 @@ describe("KurtOpenAI generateNaturalLanguage", () => { test("throws a limit error", async () => { await snapshotAndMockWithError( + "gpt-4o-2024-05-13", (kurt) => kurt.generateNaturalLanguage({ prompt: "Compose a haiku about content length limitations.", @@ -50,7 +51,7 @@ describe("KurtOpenAI generateNaturalLanguage", () => { }) test("describes a base64-encoded image", async () => { - const result = await snapshotAndMock((kurt) => + const result = await snapshotAndMock("gpt-4o-2024-05-13", (kurt) => kurt.generateNaturalLanguage({ prompt: "Describe this emoji, in two words.", extraMessages: [ diff --git a/packages/kurt-open-ai/spec/generateStructuredData.spec.ts b/packages/kurt-open-ai/spec/generateStructuredData.spec.ts index e79a16f..c4a3d48 100644 --- a/packages/kurt-open-ai/spec/generateStructuredData.spec.ts +++ b/packages/kurt-open-ai/spec/generateStructuredData.spec.ts @@ -1,11 +1,14 @@ import { describe, test, expect } from "@jest/globals" import { z } from "zod" import { snapshotAndMock, snapshotAndMockWithError } from "./snapshots" -import { KurtResultValidateError } from "@formula-monks/kurt" +import { + KurtCapabilityError, + KurtResultValidateError, +} from "@formula-monks/kurt" describe("KurtOpenAI generateStructuredData", () => { test("says hello", async () => { - const result = await snapshotAndMock((kurt) => + const result = await snapshotAndMock("gpt-4o-mini-2024-07-18", (kurt) => kurt.generateStructuredData({ prompt: "Say hello!", schema: z @@ -18,8 +21,63 @@ describe("KurtOpenAI generateStructuredData", () => { expect(result.data).toEqual({ say: "hello" }) }) + test("says hello with system prompt", async () => { + const result = await snapshotAndMock("gpt-4o-mini-2024-07-18", (kurt) => + kurt.generateStructuredData({ + systemPrompt: "Be nice.", + prompt: "Say hello!", + schema: z + .object({ + say: z.string().describe("A single word to say"), + }) + .describe("Say a word"), + }) + ) + expect(result.data).toEqual({ say: "hello" }) + }) + + test("says hello with schema constrained tokens", async () => { + const result = await snapshotAndMock("gpt-4o-mini-2024-07-18", (kurt) => + kurt.generateStructuredData({ + prompt: "Say hello!", + schema: z + .object({ + say: z.string().describe("A single word to say"), + }) + .describe("Say a word"), + sampling: { forceSchemaConstrainedTokens: true }, + }) + ) + expect(result.data).toEqual({ say: "hello" }) + }) + + test("throws a capability error for schema constrained tokens in an older model", async () => { + await snapshotAndMockWithError( + "gpt-4o-2024-05-13", + (kurt) => + kurt.generateStructuredData({ + prompt: "Say hello!", + schema: z + .object({ + say: z.string().describe("A single word to say"), + }) + .describe("Say a word"), + sampling: { forceSchemaConstrainedTokens: true }, + }), + (errorAny) => { + expect(errorAny).toBeInstanceOf(KurtCapabilityError) + const error = errorAny as KurtCapabilityError + expect(error.missingCapability).toEqual( + "forceSchemaConstrainedTokens is not available for older models, including gpt-4o-2024-05-13" + ) + expect(error.message).toContain(error.missingCapability) + } + ) + }) + test("throws a validate error from an impossible schema", async () => { await snapshotAndMockWithError( + "gpt-4o-mini-2024-07-18", (kurt) => kurt.generateStructuredData({ prompt: "Say hello!", diff --git a/packages/kurt-open-ai/spec/generateWithOptionalTools.spec.ts b/packages/kurt-open-ai/spec/generateWithOptionalTools.spec.ts index 68aa381..0bb9860 100644 --- a/packages/kurt-open-ai/spec/generateWithOptionalTools.spec.ts +++ b/packages/kurt-open-ai/spec/generateWithOptionalTools.spec.ts @@ -20,7 +20,7 @@ const calculatorTools = { describe("KurtOpenAI generateWithOptionalTools", () => { test("calculator (with tool call)", async () => { - const result = await snapshotAndMock((kurt) => + const result = await snapshotAndMock("gpt-4o-2024-05-13", (kurt) => kurt.generateWithOptionalTools({ prompt: "What's 9876356 divided by 30487, rounded to the nearest integer?", @@ -34,8 +34,24 @@ describe("KurtOpenAI generateWithOptionalTools", () => { expect(result.additionalData).toBeUndefined() // no parallel tool calls }) + test("calculator (with strict tool call)", async () => { + const result = await snapshotAndMock("gpt-4o-mini-2024-07-18", (kurt) => + kurt.generateWithOptionalTools({ + prompt: + "What's 9876356 divided by 30487, rounded to the nearest integer?", + tools: calculatorTools, + sampling: { forceSchemaConstrainedTokens: true }, + }) + ) + expect(result.data).toEqual({ + name: "divide", + args: { dividend: 9876356, divisor: 30487 }, + }) + expect(result.additionalData).toBeUndefined() // no parallel tool calls + }) + test("calculator (after tool call)", async () => { - const result = await snapshotAndMock((kurt) => + const result = await snapshotAndMock("gpt-4o-2024-05-13", (kurt) => kurt.generateWithOptionalTools({ prompt: "What's 9876356 divided by 30487, rounded to the nearest integer?", @@ -58,7 +74,7 @@ describe("KurtOpenAI generateWithOptionalTools", () => { }) test("calculator (with parallel tool calls)", async () => { - const result = await snapshotAndMock((kurt) => + const result = await snapshotAndMock("gpt-4o-2024-05-13", (kurt) => kurt.generateWithOptionalTools({ prompt: [ "Calculate each of the following:", @@ -86,7 +102,7 @@ describe("KurtOpenAI generateWithOptionalTools", () => { }) test("calculator (after parallel tool calls)", async () => { - const result = await snapshotAndMock((kurt) => + const result = await snapshotAndMock("gpt-4o-2024-05-13", (kurt) => kurt.generateWithOptionalTools({ prompt: [ "Calculate each of the following:", @@ -125,7 +141,7 @@ describe("KurtOpenAI generateWithOptionalTools", () => { ) expect(result.text).toEqual( [ - "Here are the results of the calculations:", + "Here are the results:", "", "1. 8026256882 divided by 3402398 is 2359.", "2. 1185835515 divided by 348263 is 3405.", diff --git a/packages/kurt-open-ai/spec/snapshots.ts b/packages/kurt-open-ai/spec/snapshots.ts index dd51a2b..bac60cd 100644 --- a/packages/kurt-open-ai/spec/snapshots.ts +++ b/packages/kurt-open-ai/spec/snapshots.ts @@ -13,7 +13,7 @@ import type { OpenAIResponse, OpenAIResponseChunk, } from "../src/OpenAI.types" -import { KurtOpenAI } from "../src/KurtOpenAI" +import { KurtOpenAI, type KurtOpenAISupportedModel } from "../src/KurtOpenAI" function snapshotFilenameFor(testName: string | undefined) { return `${__dirname}/snapshots/${testName?.replace(/ /g, "_")}.yaml` @@ -29,6 +29,7 @@ function dumpYaml(filename: string, data: object) { } export async function snapshotAndMock( + model: KurtOpenAISupportedModel, testCaseFn: (kurt: Kurt) => KurtStream ) { // Here's the data structure we will use to snapshot a request/response cycle. @@ -91,7 +92,7 @@ export async function snapshotAndMock( } as unknown as OpenAI // Run the test case function with a new instance of Kurt. - const kurt = new Kurt(new KurtOpenAI({ openAI, model: "gpt-4o-2024-05-13" })) + const kurt = new Kurt(new KurtOpenAI({ openAI, model })) const stream = testCaseFn(kurt) // Save the final stream of Kurt events. @@ -114,11 +115,12 @@ export async function snapshotAndMock( } export async function snapshotAndMockWithError( + model: KurtOpenAISupportedModel, testCaseFn: (kurt: Kurt) => KurtStream, errorCheckFn: (error: Error) => void ) { try { - await snapshotAndMock(testCaseFn) + await snapshotAndMock(model, testCaseFn) expectedErrorToBeThrownBeforeThisPoint() } catch (error: unknown) { if ( diff --git a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_says_hello.yaml b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_says_hello.yaml index 9bd7f8c..9c1415f 100644 --- a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_says_hello.yaml +++ b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_says_hello.yaml @@ -2,11 +2,15 @@ step1Request: stream: true stream_options: include_usage: true - model: gpt-4o-2024-05-13 + model: gpt-4o-mini-2024-07-18 max_tokens: 4096 temperature: 0.5 top_p: 0.95 messages: + - role: system + content: + - type: text + text: Respond with JSON. - role: user content: - type: text @@ -31,6 +35,8 @@ step1Request: type: function function: name: structured_data + response_format: + type: json_object step2RawChunks: - choices: - index: 0 @@ -39,14 +45,15 @@ step2RawChunks: content: null tool_calls: - index: 0 - id: call_oZj1FnPJSZCVbFYtpNYPAm7P + id: call_9x7qX8eO6DgWYP8h1xc5kHsl type: function function: name: structured_data arguments: "" + refusal: null logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_bba3c8e70b usage: null - choices: - index: 0 @@ -57,7 +64,7 @@ step2RawChunks: arguments: '{"' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_bba3c8e70b usage: null - choices: - index: 0 @@ -68,7 +75,7 @@ step2RawChunks: arguments: say logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_bba3c8e70b usage: null - choices: - index: 0 @@ -79,7 +86,7 @@ step2RawChunks: arguments: '":"' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_bba3c8e70b usage: null - choices: - index: 0 @@ -90,7 +97,7 @@ step2RawChunks: arguments: hello logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_bba3c8e70b usage: null - choices: - index: 0 @@ -101,21 +108,29 @@ step2RawChunks: arguments: '"}' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_bba3c8e70b usage: null - choices: - index: 0 delta: {} logprobs: null finish_reason: stop - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_bba3c8e70b usage: null - choices: [] - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_bba3c8e70b usage: - prompt_tokens: 66 + prompt_tokens: 70 completion_tokens: 5 - total_tokens: 71 + total_tokens: 75 + prompt_tokens_details: + cached_tokens: 0 + audio_tokens: 0 + completion_tokens_details: + reasoning_tokens: 0 + audio_tokens: 0 + accepted_prediction_tokens: 0 + rejected_prediction_tokens: 0 step3KurtEvents: - chunk: '{"' - chunk: say @@ -127,6 +142,6 @@ step3KurtEvents: data: say: hello metadata: - totalInputTokens: 66 + totalInputTokens: 70 totalOutputTokens: 5 - systemFingerprint: fp_5bf7397cd3 + systemFingerprint: fp_bba3c8e70b diff --git a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_says_hello_with_schema_constrained_tokens.yaml b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_says_hello_with_schema_constrained_tokens.yaml new file mode 100644 index 0000000..4a2b659 --- /dev/null +++ b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_says_hello_with_schema_constrained_tokens.yaml @@ -0,0 +1,116 @@ +step1Request: + stream: true + stream_options: + include_usage: true + model: gpt-4o-mini-2024-07-18 + max_tokens: 4096 + temperature: 0.5 + top_p: 0.95 + messages: + - role: user + content: + - type: text + text: Say hello! + response_format: + type: json_schema + json_schema: + strict: true + name: structured_data + description: Say a word + schema: + type: object + properties: + say: + type: string + description: A single word to say + required: + - say + additionalProperties: false + description: Say a word + $schema: http://json-schema.org/draft-07/schema# +step2RawChunks: + - choices: + - index: 0 + delta: + role: assistant + content: "" + refusal: null + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + content: '{"' + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + content: say + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + content: '":"' + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + content: hello + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + content: '"}' + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: {} + logprobs: null + finish_reason: stop + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: [] + system_fingerprint: fp_bba3c8e70b + usage: + prompt_tokens: 74 + completion_tokens: 5 + total_tokens: 79 + prompt_tokens_details: + cached_tokens: 0 + audio_tokens: 0 + completion_tokens_details: + reasoning_tokens: 0 + audio_tokens: 0 + accepted_prediction_tokens: 0 + rejected_prediction_tokens: 0 +step3KurtEvents: + - chunk: '{"' + - chunk: say + - chunk: '":"' + - chunk: hello + - chunk: '"}' + - finished: true + text: '{"say":"hello"}' + data: + say: hello + metadata: + totalInputTokens: 74 + totalOutputTokens: 5 + systemFingerprint: fp_bba3c8e70b diff --git a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_says_hello_with_system_prompt.yaml b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_says_hello_with_system_prompt.yaml new file mode 100644 index 0000000..0d4bbea --- /dev/null +++ b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_says_hello_with_system_prompt.yaml @@ -0,0 +1,149 @@ +step1Request: + stream: true + stream_options: + include_usage: true + model: gpt-4o-mini-2024-07-18 + max_tokens: 4096 + temperature: 0.5 + top_p: 0.95 + messages: + - role: system + content: + - type: text + text: |- + Be nice. + Respond with JSON. + - role: user + content: + - type: text + text: Say hello! + tools: + - type: function + function: + name: structured_data + description: Say a word + parameters: + type: object + properties: + say: + type: string + description: A single word to say + required: + - say + additionalProperties: false + description: Say a word + $schema: http://json-schema.org/draft-07/schema# + tool_choice: + type: function + function: + name: structured_data + response_format: + type: json_object +step2RawChunks: + - choices: + - index: 0 + delta: + role: assistant + content: null + tool_calls: + - index: 0 + id: call_0Ac3aIIJfKuDY7IopXG4rF2C + type: function + function: + name: structured_data + arguments: "" + refusal: null + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: '{"' + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: say + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: '":"' + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: hello + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: '"}' + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: {} + logprobs: null + finish_reason: stop + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: [] + system_fingerprint: fp_bba3c8e70b + usage: + prompt_tokens: 73 + completion_tokens: 5 + total_tokens: 78 + prompt_tokens_details: + cached_tokens: 0 + audio_tokens: 0 + completion_tokens_details: + reasoning_tokens: 0 + audio_tokens: 0 + accepted_prediction_tokens: 0 + rejected_prediction_tokens: 0 +step3KurtEvents: + - chunk: '{"' + - chunk: say + - chunk: '":"' + - chunk: hello + - chunk: '"}' + - finished: true + text: '{"say":"hello"}' + data: + say: hello + metadata: + totalInputTokens: 73 + totalOutputTokens: 5 + systemFingerprint: fp_bba3c8e70b diff --git a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_throws_a_validate_error_from_an_impossible_schema.yaml b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_throws_a_validate_error_from_an_impossible_schema.yaml index 35d25cc..25cb9b3 100644 --- a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_throws_a_validate_error_from_an_impossible_schema.yaml +++ b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_throws_a_validate_error_from_an_impossible_schema.yaml @@ -2,11 +2,15 @@ step1Request: stream: true stream_options: include_usage: true - model: gpt-4o-2024-05-13 + model: gpt-4o-mini-2024-07-18 max_tokens: 4096 temperature: 0.5 top_p: 0.95 messages: + - role: system + content: + - type: text + text: Respond with JSON. - role: user content: - type: text @@ -32,6 +36,8 @@ step1Request: type: function function: name: structured_data + response_format: + type: json_object step2RawChunks: - choices: - index: 0 @@ -40,14 +46,15 @@ step2RawChunks: content: null tool_calls: - index: 0 - id: call_6d3Q5nFbbCcgMrihn9EBquVq + id: call_o9V4SGLznZr6NCnsU2QjOdeg type: function function: name: structured_data arguments: "" + refusal: null logprobs: null finish_reason: null - system_fingerprint: fp_d33f7b429e + system_fingerprint: fp_bba3c8e70b usage: null - choices: - index: 0 @@ -58,7 +65,7 @@ step2RawChunks: arguments: '{"' logprobs: null finish_reason: null - system_fingerprint: fp_d33f7b429e + system_fingerprint: fp_bba3c8e70b usage: null - choices: - index: 0 @@ -69,7 +76,7 @@ step2RawChunks: arguments: say logprobs: null finish_reason: null - system_fingerprint: fp_d33f7b429e + system_fingerprint: fp_bba3c8e70b usage: null - choices: - index: 0 @@ -80,7 +87,7 @@ step2RawChunks: arguments: '":"' logprobs: null finish_reason: null - system_fingerprint: fp_d33f7b429e + system_fingerprint: fp_bba3c8e70b usage: null - choices: - index: 0 @@ -91,7 +98,7 @@ step2RawChunks: arguments: hello logprobs: null finish_reason: null - system_fingerprint: fp_d33f7b429e + system_fingerprint: fp_bba3c8e70b usage: null - choices: - index: 0 @@ -102,21 +109,29 @@ step2RawChunks: arguments: '"}' logprobs: null finish_reason: null - system_fingerprint: fp_d33f7b429e + system_fingerprint: fp_bba3c8e70b usage: null - choices: - index: 0 delta: {} logprobs: null finish_reason: stop - system_fingerprint: fp_d33f7b429e + system_fingerprint: fp_bba3c8e70b usage: null - choices: [] - system_fingerprint: fp_d33f7b429e + system_fingerprint: fp_bba3c8e70b usage: - prompt_tokens: 78 + prompt_tokens: 82 completion_tokens: 5 - total_tokens: 83 + total_tokens: 87 + prompt_tokens_details: + cached_tokens: 0 + audio_tokens: 0 + completion_tokens_details: + reasoning_tokens: 0 + audio_tokens: 0 + accepted_prediction_tokens: 0 + rejected_prediction_tokens: 0 step3KurtEvents: - chunk: '{"' - chunk: say diff --git a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(after_parallel_tool_calls).yaml b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(after_parallel_tool_calls).yaml index 42f9067..58378e3 100644 --- a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(after_parallel_tool_calls).yaml +++ b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(after_parallel_tool_calls).yaml @@ -90,9 +90,10 @@ step2RawChunks: delta: role: assistant content: "" + refusal: null logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -100,7 +101,7 @@ step2RawChunks: content: Here logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -108,7 +109,7 @@ step2RawChunks: content: " are" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -116,7 +117,7 @@ step2RawChunks: content: " the" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -124,31 +125,7 @@ step2RawChunks: content: " results" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 - usage: null - - choices: - - index: 0 - delta: - content: " of" - logprobs: null - finish_reason: null - system_fingerprint: fp_5bf7397cd3 - usage: null - - choices: - - index: 0 - delta: - content: " the" - logprobs: null - finish_reason: null - system_fingerprint: fp_5bf7397cd3 - usage: null - - choices: - - index: 0 - delta: - content: " calculations" - logprobs: null - finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -158,7 +135,7 @@ step2RawChunks: logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -166,7 +143,7 @@ step2RawChunks: content: "1" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -174,7 +151,7 @@ step2RawChunks: content: . logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -182,7 +159,7 @@ step2RawChunks: content: " " logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -190,7 +167,7 @@ step2RawChunks: content: "802" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -198,7 +175,7 @@ step2RawChunks: content: "625" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -206,7 +183,7 @@ step2RawChunks: content: "688" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -214,7 +191,7 @@ step2RawChunks: content: "2" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -222,7 +199,7 @@ step2RawChunks: content: " divided" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -230,7 +207,7 @@ step2RawChunks: content: " by" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -238,7 +215,7 @@ step2RawChunks: content: " " logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -246,7 +223,7 @@ step2RawChunks: content: "340" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -254,7 +231,7 @@ step2RawChunks: content: "239" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -262,7 +239,7 @@ step2RawChunks: content: "8" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -270,7 +247,7 @@ step2RawChunks: content: " is" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -278,7 +255,7 @@ step2RawChunks: content: " " logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -286,7 +263,7 @@ step2RawChunks: content: "235" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -294,7 +271,7 @@ step2RawChunks: content: "9" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -303,7 +280,7 @@ step2RawChunks: . logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -311,7 +288,7 @@ step2RawChunks: content: "2" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -319,7 +296,7 @@ step2RawChunks: content: . logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -327,7 +304,7 @@ step2RawChunks: content: " " logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -335,7 +312,7 @@ step2RawChunks: content: "118" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -343,7 +320,7 @@ step2RawChunks: content: "583" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -351,7 +328,7 @@ step2RawChunks: content: "551" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -359,7 +336,7 @@ step2RawChunks: content: "5" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -367,7 +344,7 @@ step2RawChunks: content: " divided" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -375,7 +352,7 @@ step2RawChunks: content: " by" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -383,7 +360,7 @@ step2RawChunks: content: " " logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -391,7 +368,7 @@ step2RawChunks: content: "348" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -399,7 +376,7 @@ step2RawChunks: content: "263" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -407,7 +384,7 @@ step2RawChunks: content: " is" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -415,7 +392,7 @@ step2RawChunks: content: " " logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -423,7 +400,7 @@ step2RawChunks: content: "340" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -431,7 +408,7 @@ step2RawChunks: content: "5" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -440,7 +417,7 @@ step2RawChunks: . logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -448,7 +425,7 @@ step2RawChunks: content: "3" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -456,7 +433,7 @@ step2RawChunks: content: . logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -464,7 +441,7 @@ step2RawChunks: content: " " logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -472,7 +449,7 @@ step2RawChunks: content: "901" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -480,7 +457,7 @@ step2RawChunks: content: "350" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -488,7 +465,7 @@ step2RawChunks: content: "944" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -496,7 +473,7 @@ step2RawChunks: content: "95" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -504,7 +481,7 @@ step2RawChunks: content: " minus" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -512,7 +489,7 @@ step2RawChunks: content: " " logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -520,7 +497,7 @@ step2RawChunks: content: "899" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -528,7 +505,7 @@ step2RawChunks: content: "449" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -536,7 +513,7 @@ step2RawChunks: content: "543" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -544,7 +521,7 @@ step2RawChunks: content: "50" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -552,7 +529,7 @@ step2RawChunks: content: " is" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -560,7 +537,7 @@ step2RawChunks: content: " " logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -568,7 +545,7 @@ step2RawChunks: content: "190" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -576,7 +553,7 @@ step2RawChunks: content: "140" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -584,7 +561,7 @@ step2RawChunks: content: "145" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -592,29 +569,34 @@ step2RawChunks: content: . logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 delta: {} logprobs: null finish_reason: stop - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: [] - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: prompt_tokens: 272 - completion_tokens: 63 - total_tokens: 335 + completion_tokens: 60 + total_tokens: 332 + prompt_tokens_details: + cached_tokens: 0 + audio_tokens: 0 + completion_tokens_details: + reasoning_tokens: 0 + audio_tokens: 0 + accepted_prediction_tokens: 0 + rejected_prediction_tokens: 0 step3KurtEvents: - chunk: Here - chunk: " are" - chunk: " the" - chunk: " results" - - chunk: " of" - - chunk: " the" - - chunk: " calculations" - chunk: |+ : @@ -676,12 +658,12 @@ step3KurtEvents: - chunk: . - finished: true text: |- - Here are the results of the calculations: + Here are the results: 1. 8026256882 divided by 3402398 is 2359. 2. 1185835515 divided by 348263 is 3405. 3. 90135094495 minus 89944954350 is 190140145. metadata: totalInputTokens: 272 - totalOutputTokens: 63 - systemFingerprint: fp_5bf7397cd3 + totalOutputTokens: 60 + systemFingerprint: fp_e37d8a480e diff --git a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(after_tool_call).yaml b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(after_tool_call).yaml index fe102d6..30a3572 100644 --- a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(after_tool_call).yaml +++ b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(after_tool_call).yaml @@ -66,9 +66,10 @@ step2RawChunks: delta: role: assistant content: "" + refusal: null logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -76,7 +77,7 @@ step2RawChunks: content: "987" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -84,7 +85,7 @@ step2RawChunks: content: "635" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -92,7 +93,7 @@ step2RawChunks: content: "6" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -100,7 +101,7 @@ step2RawChunks: content: " divided" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -108,7 +109,7 @@ step2RawChunks: content: " by" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -116,7 +117,7 @@ step2RawChunks: content: " " logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -124,7 +125,7 @@ step2RawChunks: content: "304" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -132,7 +133,7 @@ step2RawChunks: content: "87" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -140,7 +141,7 @@ step2RawChunks: content: "," logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -148,7 +149,7 @@ step2RawChunks: content: " rounded" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -156,7 +157,7 @@ step2RawChunks: content: " to" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -164,7 +165,7 @@ step2RawChunks: content: " the" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -172,7 +173,7 @@ step2RawChunks: content: " nearest" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -180,7 +181,7 @@ step2RawChunks: content: " integer" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -188,7 +189,7 @@ step2RawChunks: content: "," logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -196,7 +197,7 @@ step2RawChunks: content: " is" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -204,7 +205,7 @@ step2RawChunks: content: " approximately" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -212,7 +213,7 @@ step2RawChunks: content: " " logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -220,7 +221,7 @@ step2RawChunks: content: "324" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -228,21 +229,29 @@ step2RawChunks: content: . logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 delta: {} logprobs: null finish_reason: stop - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: [] - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: prompt_tokens: 166 completion_tokens: 21 total_tokens: 187 + prompt_tokens_details: + cached_tokens: 0 + audio_tokens: 0 + completion_tokens_details: + reasoning_tokens: 0 + audio_tokens: 0 + accepted_prediction_tokens: 0 + rejected_prediction_tokens: 0 step3KurtEvents: - chunk: "987" - chunk: "635" @@ -270,4 +279,4 @@ step3KurtEvents: metadata: totalInputTokens: 166 totalOutputTokens: 21 - systemFingerprint: fp_5bf7397cd3 + systemFingerprint: fp_e37d8a480e diff --git a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(with_parallel_tool_calls).yaml b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(with_parallel_tool_calls).yaml index bc5b1f1..92db011 100644 --- a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(with_parallel_tool_calls).yaml +++ b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(with_parallel_tool_calls).yaml @@ -62,21 +62,21 @@ step2RawChunks: content: null logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 delta: tool_calls: - index: 0 - id: call_ktAoc80YJV28c2LbGLnuCS5d + id: call_PGf2wPkvmAag3xtEsDnS9B0F type: function function: name: divide arguments: "" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -87,7 +87,7 @@ step2RawChunks: arguments: '{"di' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -98,7 +98,7 @@ step2RawChunks: arguments: viden logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -109,7 +109,7 @@ step2RawChunks: arguments: 'd": 80' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -120,7 +120,7 @@ step2RawChunks: arguments: "2625" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -131,7 +131,7 @@ step2RawChunks: arguments: 6882, logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -142,7 +142,7 @@ step2RawChunks: arguments: ' "divi' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -153,7 +153,7 @@ step2RawChunks: arguments: sor" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -164,7 +164,7 @@ step2RawChunks: arguments: ": 340" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -175,21 +175,21 @@ step2RawChunks: arguments: 2398} logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 delta: tool_calls: - index: 1 - id: call_GL9pg4tobHyUhUrxzlt8BsXk + id: call_vBgrIZCwG8YMhcrxJCEbdq1b type: function function: name: divide arguments: "" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -200,7 +200,7 @@ step2RawChunks: arguments: '{"di' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -211,7 +211,7 @@ step2RawChunks: arguments: viden logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -222,7 +222,7 @@ step2RawChunks: arguments: 'd": 11' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -233,7 +233,7 @@ step2RawChunks: arguments: "8583" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -244,7 +244,7 @@ step2RawChunks: arguments: 5515, logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -255,7 +255,7 @@ step2RawChunks: arguments: ' "divi' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -266,7 +266,7 @@ step2RawChunks: arguments: sor" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -277,7 +277,7 @@ step2RawChunks: arguments: ": 348" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -288,21 +288,21 @@ step2RawChunks: arguments: 263} logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 delta: tool_calls: - index: 2 - id: call_uRCrAfrGiGUByuE2ShmPdM9L + id: call_uvfAmM0tr46k5CQnffssjiSa type: function function: name: subtract arguments: "" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -313,7 +313,7 @@ step2RawChunks: arguments: '{"mi' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -324,7 +324,7 @@ step2RawChunks: arguments: nuend logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -335,7 +335,7 @@ step2RawChunks: arguments: '": 901' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -346,7 +346,7 @@ step2RawChunks: arguments: "3509" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -357,7 +357,7 @@ step2RawChunks: arguments: 4495, logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -368,7 +368,7 @@ step2RawChunks: arguments: ' "subt' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -379,7 +379,7 @@ step2RawChunks: arguments: rahe logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -390,7 +390,7 @@ step2RawChunks: arguments: 'nd": ' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -401,7 +401,7 @@ step2RawChunks: arguments: "899449" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -412,7 +412,7 @@ step2RawChunks: arguments: "5435" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -423,21 +423,29 @@ step2RawChunks: arguments: 0} logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 delta: {} logprobs: null finish_reason: tool_calls - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: [] - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: prompt_tokens: 154 completion_tokens: 91 total_tokens: 245 + prompt_tokens_details: + cached_tokens: 0 + audio_tokens: 0 + completion_tokens_details: + reasoning_tokens: 0 + audio_tokens: 0 + accepted_prediction_tokens: 0 + rejected_prediction_tokens: 0 step3KurtEvents: - chunk: '{"di' - chunk: viden @@ -492,4 +500,4 @@ step3KurtEvents: metadata: totalInputTokens: 154 totalOutputTokens: 91 - systemFingerprint: fp_5bf7397cd3 + systemFingerprint: fp_e37d8a480e diff --git a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(with_strict_tool_call).yaml b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(with_strict_tool_call).yaml new file mode 100644 index 0000000..78cfd44 --- /dev/null +++ b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(with_strict_tool_call).yaml @@ -0,0 +1,273 @@ +step1Request: + stream: true + stream_options: + include_usage: true + model: gpt-4o-mini-2024-07-18 + max_tokens: 4096 + temperature: 0.5 + top_p: 0.95 + messages: + - role: user + content: + - type: text + text: What's 9876356 divided by 30487, rounded to the nearest integer? + tools: + - type: function + function: + name: subtract + description: Calculate a subtraction + parameters: + type: object + properties: + minuend: + type: number + description: The number to subtract from + subtrahend: + type: number + description: The number to subtract by + required: + - minuend + - subtrahend + additionalProperties: false + description: Calculate a subtraction + $schema: http://json-schema.org/draft-07/schema# + strict: true + - type: function + function: + name: divide + description: Calculate a division + parameters: + type: object + properties: + dividend: + type: number + description: The number to be divided + divisor: + type: number + description: The number to divide by + required: + - dividend + - divisor + additionalProperties: false + description: Calculate a division + $schema: http://json-schema.org/draft-07/schema# + strict: true +step2RawChunks: + - choices: + - index: 0 + delta: + role: assistant + content: null + tool_calls: + - index: 0 + id: call_JSjy9XbYc4xyg5prbtds4JqN + type: function + function: + name: divide + arguments: "" + refusal: null + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: '{"' + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: div + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: idend + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: '":' + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: "987" + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: "635" + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: "6" + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: ',"' + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: div + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: isor + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: '":' + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: "304" + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: "87" + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: + tool_calls: + - index: 0 + function: + arguments: "}" + logprobs: null + finish_reason: null + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: + - index: 0 + delta: {} + logprobs: null + finish_reason: tool_calls + system_fingerprint: fp_bba3c8e70b + usage: null + - choices: [] + system_fingerprint: fp_bba3c8e70b + usage: + prompt_tokens: 125 + completion_tokens: 22 + total_tokens: 147 + prompt_tokens_details: + cached_tokens: 0 + audio_tokens: 0 + completion_tokens_details: + reasoning_tokens: 0 + audio_tokens: 0 + accepted_prediction_tokens: 0 + rejected_prediction_tokens: 0 +step3KurtEvents: + - chunk: '{"' + - chunk: div + - chunk: idend + - chunk: '":' + - chunk: "987" + - chunk: "635" + - chunk: "6" + - chunk: ',"' + - chunk: div + - chunk: isor + - chunk: '":' + - chunk: "304" + - chunk: "87" + - chunk: "}" + - finished: true + text: '{"dividend":9876356,"divisor":30487}' + data: + name: divide + args: + dividend: 9876356 + divisor: 30487 + metadata: + totalInputTokens: 125 + totalOutputTokens: 22 + systemFingerprint: fp_bba3c8e70b diff --git a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(with_tool_call).yaml b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(with_tool_call).yaml index 1f2215b..3d41833 100644 --- a/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(with_tool_call).yaml +++ b/packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(with_tool_call).yaml @@ -58,14 +58,15 @@ step2RawChunks: content: null tool_calls: - index: 0 - id: call_SJChZ90kT3b6xTA0BOfgW8tF + id: call_AbpblIPQVkJoagXGB6NsGD5V type: function function: name: divide arguments: "" + refusal: null logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -76,7 +77,7 @@ step2RawChunks: arguments: '{"' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -87,7 +88,7 @@ step2RawChunks: arguments: div logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -98,7 +99,7 @@ step2RawChunks: arguments: idend logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -109,7 +110,7 @@ step2RawChunks: arguments: '":' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -120,7 +121,7 @@ step2RawChunks: arguments: "987" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -131,7 +132,7 @@ step2RawChunks: arguments: "635" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -142,7 +143,7 @@ step2RawChunks: arguments: "6" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -153,7 +154,7 @@ step2RawChunks: arguments: ',"' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -164,7 +165,7 @@ step2RawChunks: arguments: div logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -175,7 +176,7 @@ step2RawChunks: arguments: isor logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -186,7 +187,7 @@ step2RawChunks: arguments: '":' logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -197,7 +198,7 @@ step2RawChunks: arguments: "304" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -208,7 +209,7 @@ step2RawChunks: arguments: "87" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 @@ -219,21 +220,29 @@ step2RawChunks: arguments: "}" logprobs: null finish_reason: null - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: - index: 0 delta: {} logprobs: null finish_reason: tool_calls - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: null - choices: [] - system_fingerprint: fp_5bf7397cd3 + system_fingerprint: fp_e37d8a480e usage: prompt_tokens: 125 completion_tokens: 22 total_tokens: 147 + prompt_tokens_details: + cached_tokens: 0 + audio_tokens: 0 + completion_tokens_details: + reasoning_tokens: 0 + audio_tokens: 0 + accepted_prediction_tokens: 0 + rejected_prediction_tokens: 0 step3KurtEvents: - chunk: '{"' - chunk: div @@ -259,4 +268,4 @@ step3KurtEvents: metadata: totalInputTokens: 125 totalOutputTokens: 22 - systemFingerprint: fp_5bf7397cd3 + systemFingerprint: fp_e37d8a480e diff --git a/packages/kurt-open-ai/src/KurtOpenAI.ts b/packages/kurt-open-ai/src/KurtOpenAI.ts index fe9e3de..ba43d8b 100644 --- a/packages/kurt-open-ai/src/KurtOpenAI.ts +++ b/packages/kurt-open-ai/src/KurtOpenAI.ts @@ -18,6 +18,7 @@ import { KurtResultBlockedError, KurtResultParseError, KurtResultValidateError, + KurtCapabilityError, } from "@formula-monks/kurt" import type { OpenAI, @@ -32,6 +33,8 @@ import { ZodError } from "zod" // These models support function calling. const COMPATIBLE_MODELS = [ "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", "gpt-4o-2024-05-13", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", @@ -82,6 +85,34 @@ export class KurtOpenAI return COMPATIBLE_MODELS.includes(model as KurtOpenAISupportedModel) } + static isSupportedModelForSchemaConstrainedTokens(model: string): boolean { + if (!KurtOpenAI.isSupportedModel(model)) return false + // gpt-4-* models do NOT support schema-constrained tokens + if (model.startsWith("gpt-4-")) return false + // gpt-3.5-* models do NOT support schema-constrained tokens + if (model.startsWith("gpt-3.5-")) return false + // gpt-4o-2024-05-13 does NOT support schema-constrained tokens + if (model === "gpt-4o-2024-05-13") return false + + // Other supported models do support schema-constrained tokens. + return true + } + + /** + * Throw an error if the model doesn't support schema constrained tokens. + */ + private checkModelForSchemaConstrainedTokens(model: string) { + if (!KurtOpenAI.isSupportedModelForSchemaConstrainedTokens(model)) + throw new KurtCapabilityError( + this, + [ + "forceSchemaConstrainedTokens", + "is not available for older models, including", + model, + ].join(" ") + ) + } + transformToRawMessages = toOpenAIMessages transformToRawSchema = zodToJsonSchema @@ -109,11 +140,53 @@ export class KurtOpenAI const tools = Object.values(options.tools) if (tools.length > 0) req.tools = tools - if (options.forceTool) - req.tool_choice = { - type: "function", - function: { name: options.forceTool }, + if (options.sampling.forceSchemaConstrainedTokens) { + this.checkModelForSchemaConstrainedTokens(this.options.model) + + // Mark all tools as strict, enabling schema-constrained token sampling. + for (const tool of tools) tool.function.strict = true + } + + if (options.forceTool) { + const toolFunction = options.tools[options.forceTool]?.function + if (!toolFunction) + throw new Error( + `The tool ${options.forceTool} wasn't found in the tools list` + ) + + if (options.sampling.forceSchemaConstrainedTokens) { + // Forcing a particular tool with schema-constrained tokens is treated + // as a special case of the response format, instead of a tool call. + req.tools = undefined + req.response_format = { + type: "json_schema", + json_schema: { + strict: true, + name: toolFunction.name, + description: toolFunction.description, + schema: toolFunction.parameters, + }, + } + } else { + req.tool_choice = { + type: "function", + function: { name: options.forceTool }, + } + // If we're not using schema-constrained tokens, we can at least use + // JSON-constrained token sampling to force the tool call as valid JSON. + req.response_format = { type: "json_object" } + + // OpenAI requires us to use the word JSON at least once in the prompt + // messages when forcing JSON-constrained token sampling. + // Therefore we may need to add it here. + if (!doesMessageListContainSubstring(options.messages, "JSON")) { + req.messages = withInjectedSystemPromptLine( + options.messages, + "Respond with JSON." + ) + } } + } const promise = this.options.openAI.chat.completions.create(req) @@ -450,3 +523,68 @@ function convertMetadata(rawEvent: OpenAIResponseChunk) { } return metadata } + +/** + * Return true if any of the messages contain the given substring. + */ +function doesMessageListContainSubstring( + messages: OpenAIMessage[], + substring: string +) { + for (const message of messages) { + const content = message.content + if (!content) continue + + if (typeof content === "string") { + if (content.includes(substring)) return true + continue + } + + for (const part of content) { + if ("text" in part && part.text?.includes(substring)) return true + } + } + + return false +} + +/** + * Return a new list of messages with the given system prompt line injected. + */ +function withInjectedSystemPromptLine( + messages: readonly OpenAIMessage[], + systemPromptLine: string +): OpenAIMessage[] { + // Find an existing system prompt to modify. + const existingSystemPrompt = messages.find( + (message) => message.role === "system" + ) + + // If there is no existing system prompt, we'll simply add one + // that contains only the new desired line. + if (!existingSystemPrompt) { + return [ + { role: "system", content: [{ type: "text", text: systemPromptLine }] }, + ...messages, + ] + } + + // If there is an existing system prompt, we'll modify it to include + // the new desired line as part of the last content part. + const existingParts: { type: "text"; text: string }[] = + typeof existingSystemPrompt.content === "string" + ? [{ type: "text", text: existingSystemPrompt.content }] + : (existingSystemPrompt.content as { type: "text"; text: string }[]) + const lastPart = existingParts[existingParts.length - 1] ?? { text: "" } + const priorParts = existingParts.slice(0, -1) + const newParts: { type: "text"; text: string }[] = [ + ...priorParts, + { type: "text", text: `${lastPart?.text}\n${systemPromptLine}` }, + ] + return messages.map( + (message): OpenAIMessage => + message === existingSystemPrompt + ? { ...message, content: newParts } + : message + ) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e771ed1..02bc197 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -51,8 +51,8 @@ importers: specifier: 1.1.0 version: 1.1.0 openai: - specifier: 4.66.1 - version: 4.66.1(zod@3.23.8) + specifier: ^4.76.0 + version: 4.76.0(zod@3.23.8) zod: specifier: ^3.23.8 version: 3.23.8 @@ -156,8 +156,8 @@ importers: specifier: workspace:^ version: link:../kurt openai: - specifier: 4.66.1 - version: 4.66.1(zod@3.23.8) + specifier: 4.76.0 + version: 4.76.0(zod@3.23.8) zod: specifier: ^3.23.8 version: 3.23.8 @@ -2519,8 +2519,8 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} - openai@4.66.1: - resolution: {integrity: sha512-+sSyV6VtGHerPb6Kfi76hrEOVt+wayvuw7GX/ky7rAR11kN6JVs4dgbtoaxzDXvAxc5dcQLuXeco54mBfOMoQQ==} + openai@4.76.0: + resolution: {integrity: sha512-QBGIetjX1C9xDp5XGa/3mPnfKI9BgAe2xHQX6PmO98wuW9qQaurBaumcYptQWc9LHZZq7cH/Y1Rjnsr6uUDdVw==} hasBin: true peerDependencies: zod: ^3.23.8 @@ -6063,7 +6063,7 @@ snapshots: dependencies: mimic-fn: 4.0.0 - openai@4.66.1(zod@3.23.8): + openai@4.76.0(zod@3.23.8): dependencies: '@types/node': 18.19.32 '@types/node-fetch': 2.6.11