From 4ad3fd44bb69a9a39c86545dc7660aebee2ad246 Mon Sep 17 00:00:00 2001 From: Dimitri Kennedy Date: Tue, 2 Jan 2024 19:23:22 -0500 Subject: [PATCH] initial stream handling --- .github/workflows/npm-publish.yml | 22 ---------- bun.lockb | Bin 219721 -> 220459 bytes examples/extract_user copy/index.ts | 31 ++++++++++++++ examples/extract_user_stream/index.ts | 53 ++++++++++++++++++++++++ package.json | 1 + src/instructor.ts | 56 +++++++++++++++++--------- src/oai/parser.ts | 23 +++++++---- src/oai/stream.ts | 51 +++++++++++++++++++++++ tests/functions.test.ts | 1 + 9 files changed, 190 insertions(+), 48 deletions(-) delete mode 100644 .github/workflows/npm-publish.yml create mode 100644 examples/extract_user copy/index.ts create mode 100644 examples/extract_user_stream/index.ts create mode 100644 src/oai/stream.ts diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml deleted file mode 100644 index d7784252..00000000 --- a/.github/workflows/npm-publish.yml +++ /dev/null @@ -1,22 +0,0 @@ -# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created -# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages - -name: Node.js Package - -on: - release: - types: [created] - -jobs: - publish-npm: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 16 - registry-url: https://registry.npmjs.org/ - - run: npm ci - - run: npm publish - env: - NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} diff --git a/bun.lockb b/bun.lockb index f15e43b9afccbdf7f405e1d8902627c86e5d408e..50750dbd2697f1f43bb5538902fde888abb2fe51 100755 GIT binary patch delta 41681 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 Pu* name.includes(" "), { + message: "Name must contain a space" + }) +}) + +type User = z.infer + +const oai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY ?? undefined, + organization: process.env.OPENAI_ORG_ID ?? undefined +}) + +const client = Instructor({ + client: oai, + mode: "FUNCTIONS" +}) + +const user: User = await client.chat.completions.create({ + messages: [{ role: "user", content: "Jason Liu is 30 years old" }], + model: "gpt-3.5-turbo", + response_model: UserSchema, + max_retries: 3 +}) + +console.log(user) diff --git a/examples/extract_user_stream/index.ts b/examples/extract_user_stream/index.ts new file mode 100644 index 00000000..66378599 --- /dev/null +++ b/examples/extract_user_stream/index.ts @@ -0,0 +1,53 @@ +import Instructor from "@/instructor" +import OpenAI from "openai" +import { z } from "zod" + +const UserSchema = z.object({ + age: z.number(), + name: z.string() +}) + +type User = z.infer + +const oai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY ?? undefined, + organization: process.env.OPENAI_ORG_ID ?? undefined +}) + +const client = Instructor({ + client: oai, + mode: "FUNCTIONS" +}) + +const userStream = await client.chat.completions.create({ + messages: [{ role: "user", content: "Jason Liu is 30 years old" }], + model: "gpt-3.5-turbo", + response_model: UserSchema, + max_retries: 3, + stream: true +}) + +const reader = userStream.readable.getReader() +const decoder = new TextDecoder() + +let result = {} +let done = false + +while (!done) { + try { + const { value, done: doneReading } = await reader.read() + done = doneReading + + if (done) { + break + } + + const chunkValue = decoder.decode(value) + result = JSON.parse(chunkValue) + console.log(result) + } catch (e) { + done = true + console.log(e) + break + } +} diff --git a/package.json b/package.json index 7b10ff1f..ac93a84c 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ }, "homepage": "https://github.com/jxnl/instructor-js#readme", "dependencies": { + "schema-stream": "^1.1.0", "zod-to-json-schema": "^3.22.3" }, "peerDependencies": { diff --git a/src/instructor.ts b/src/instructor.ts index f59d2516..8be00890 100644 --- a/src/instructor.ts +++ b/src/instructor.ts @@ -8,9 +8,11 @@ import { OAIResponseJSONStringParser, OAIResponseToolArgsParser } from "@/oai/parser" +import { OAIStream } from "@/oai/stream" import OpenAI from "openai" -import { ChatCompletion, ChatCompletionCreateParamsNonStreaming } from "openai/resources/index.mjs" -import { ZodObject } from "zod" +import { ChatCompletion, ChatCompletionCreateParams } from "openai/resources/index.mjs" +import { SchemaStream } from "schema-stream" +import { z, ZodObject } from "zod" import zodToJsonSchema from "zod-to-json-schema" import { MODE } from "@/constants/modes" @@ -31,7 +33,7 @@ const MODE_TO_PARAMS = { [MODE.JSON_SCHEMA]: OAIBuildMessageBasedParams } -type PatchedChatCompletionCreateParams = ChatCompletionCreateParamsNonStreaming & { +type PatchedChatCompletionCreateParams = ChatCompletionCreateParams & { //eslint-disable-next-line @typescript-eslint/no-explicit-any response_model?: ZodObject max_retries?: number @@ -84,9 +86,14 @@ class Instructor { } const completion = await this.client.chat.completions.create(resolvedParams) - const response = this.parseOAIResponse(completion) + const parser = MODE_TO_PARSER[this.mode] - return response + if ("choices" in completion) { + const parsedCompletion = parser(completion) + return JSON.parse(parsedCompletion) + } else { + return OAIStream({ res: completion, parser }) + } } catch (error) { throw error } @@ -95,6 +102,15 @@ class Instructor { const makeCompletionCallWithRetries = async () => { try { const data = await makeCompletionCall() + + //short circuit if this is a stream for now + if (params.stream) { + return this.partialStreamResponse({ + stream: data, + schema: params.response_model + }) + } + const validation = params.response_model.safeParse(data) if (!validation.success) { @@ -125,15 +141,30 @@ class Instructor { return await makeCompletionCallWithRetries() } + private async partialStreamResponse({ stream, schema }) { + const streamParser = new SchemaStream(schema, { + onKeyComplete: console.log + }) + + const parser = streamParser.parse({ + stringStreaming: true, + handleUnescapedNewLines: true + }) + + stream.pipeThrough(parser) + + return parser + } + /** * Builds the chat completion parameters. * @param {PatchedChatCompletionCreateParams} params - The parameters for chat completion. - * @returns {ChatCompletionCreateParamsNonStreaming} The chat completion parameters. + * @returns {ChatCompletionCreateParams} The chat completion parameters. */ private buildChatCompletionParams = ({ response_model, ...params - }: PatchedChatCompletionCreateParams): ChatCompletionCreateParamsNonStreaming => { + }: PatchedChatCompletionCreateParams): ChatCompletionCreateParams => { const jsonSchema = zodToJsonSchema(response_model, "response_model") const definition = { @@ -149,17 +180,6 @@ class Instructor { } } - /** - * Parses the OAI response. - * @param {ChatCompletion} response - The response from the chat completion. - * @returns {any} The parsed response. - */ - private parseOAIResponse = (response: ChatCompletion) => { - const parser = MODE_TO_PARSER[this.mode] - - return parser(response) - } - /** * Public chat interface. */ diff --git a/src/oai/parser.ts b/src/oai/parser.ts index e5a4276d..4753a575 100644 --- a/src/oai/parser.ts +++ b/src/oai/parser.ts @@ -16,9 +16,9 @@ export function OAIResponseTextParser( ) { const parsedData = typeof data === "string" ? JSON.parse(data) : data - const text = parsedData?.choices[0]?.message?.content ?? "{}" + const text = parsedData?.choices[0]?.message?.content ?? null - return JSON.parse(text) + return text } /** @@ -36,9 +36,12 @@ export function OAIResponseFnArgsParser( ) { const parsedData = typeof data === "string" ? JSON.parse(data) : data - const text = parsedData.choices?.[0]?.message?.function_call?.arguments ?? "{}" + const text = + parsedData.choices?.[0].delta?.function_call?.arguments ?? + parsedData.choices?.[0]?.message?.function_call?.arguments ?? + null - return JSON.parse(text) + return text } /** @@ -56,9 +59,12 @@ export function OAIResponseToolArgsParser( ) { const parsedData = typeof data === "string" ? JSON.parse(data) : data - const text = parsedData.choices?.[0]?.message?.tool_call?.function?.arguments ?? "{}" + const text = + parsedData.choices?.[0].delta?.tool_call?.function?.arguments ?? + parsedData.choices?.[0]?.message?.tool_call?.function?.arguments ?? + null - return JSON.parse(text) + return text } /** @@ -75,7 +81,8 @@ export function OAIResponseJSONStringParser( | OpenAI.Chat.Completions.ChatCompletion ) { const parsedData = typeof data === "string" ? JSON.parse(data) : data - const text = parsedData?.choices[0]?.message?.content ?? "{}" + const text = + parsedData.choices?.[0].delta?.content ?? parsedData?.choices[0]?.message?.content ?? null - return JSON.parse(text) + return text } diff --git a/src/oai/stream.ts b/src/oai/stream.ts new file mode 100644 index 00000000..d22ee62f --- /dev/null +++ b/src/oai/stream.ts @@ -0,0 +1,51 @@ +import OpenAI from "openai" +import { Stream } from "openai/streaming" + +interface OaiStreamArgs { + res: Stream + parser +} + +/** + * `OaiStream` creates a ReadableStream that parses the SSE response from OAI + * and returns a parsed string from the response. + * + * @param {OaiStreamArgs} args - The arguments for the function. + * @returns {ReadableStream} - The created ReadableStream. + */ +export function OAIStream({ res, parser }: OaiStreamArgs): ReadableStream { + const encoder = new TextEncoder() + let cancelGenerator: () => void + + async function* generateStream(res): AsyncGenerator { + cancelGenerator = () => { + return + } + + for await (const part of res) { + if (part?.choices?.[0]?.finish_reason === "stop") { + cancelGenerator() + break + } + + yield parser(part) + } + } + + const generator = generateStream(res) + + return new ReadableStream({ + async start(controller) { + for await (const parsedData of generator) { + controller.enqueue(encoder.encode(parsedData)) + } + + controller.close() + }, + cancel() { + if (cancelGenerator) { + cancelGenerator() + } + } + }) +} diff --git a/tests/functions.test.ts b/tests/functions.test.ts index a606cc3c..e3c4c7ab 100644 --- a/tests/functions.test.ts +++ b/tests/functions.test.ts @@ -26,6 +26,7 @@ async function extractUser() { const user: User = await client.chat.completions.create({ messages: [{ role: "user", content: "Jason Liu is 30 years old" }], model: "gpt-3.5-turbo", + response_model: UserSchema, max_retries: 3 })