From a98877bdcaec0408475bddd57cb57430f9ec05b0 Mon Sep 17 00:00:00 2001 From: Joe McIlvain Date: Mon, 9 Dec 2024 09:40:59 -0800 Subject: [PATCH] feat: add support for schema-constrained tokens in `KurtOpenAI` This commit also updates the set of supported models to include the newer model snapshots. We also update the tests to allow for specifying different models per test, and to mostly use the newer models. --- examples/basic/package.json | 2 +- packages/kurt-open-ai/package.json | 2 +- .../spec/generateNaturalLanguage.spec.ts | 7 +- .../spec/generateStructuredData.spec.ts | 62 +++- .../spec/generateWithOptionalTools.spec.ts | 26 +- packages/kurt-open-ai/spec/snapshots.ts | 8 +- ...nAI_generateStructuredData_says_hello.yaml | 43 ++- ..._hello_with_schema_constrained_tokens.yaml | 116 ++++++++ ...redData_says_hello_with_system_prompt.yaml | 149 ++++++++++ ...idate_error_from_an_impossible_schema.yaml | 39 ++- ...alculator_(after_parallel_tool_calls).yaml | 170 +++++------ ...nalTools_calculator_(after_tool_call).yaml | 57 ++-- ...calculator_(with_parallel_tool_calls).yaml | 86 +++--- ...ls_calculator_(with_strict_tool_call).yaml | 273 ++++++++++++++++++ ...onalTools_calculator_(with_tool_call).yaml | 47 +-- packages/kurt-open-ai/src/KurtOpenAI.ts | 146 +++++++++- pnpm-lock.yaml | 14 +- 17 files changed, 1019 insertions(+), 228 deletions(-) create mode 100644 packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_says_hello_with_schema_constrained_tokens.yaml create mode 100644 packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateStructuredData_says_hello_with_system_prompt.yaml create mode 100644 packages/kurt-open-ai/spec/snapshots/KurtOpenAI_generateWithOptionalTools_calculator_(with_strict_tool_call).yaml 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