From accc4700403c8eb289be27f623478cb601650326 Mon Sep 17 00:00:00 2001 From: Donnie Adams Date: Mon, 20 May 2024 12:55:32 -0400 Subject: [PATCH] feat: add ability to export chat state --- src/gptscript.ts | 21 +++++++++++----- tests/fixtures/global-tools.gpt | 2 +- tests/gptscript.test.ts | 43 +++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/gptscript.ts b/src/gptscript.ts index 0da1cbf..716e04d 100644 --- a/src/gptscript.ts +++ b/src/gptscript.ts @@ -9,6 +9,7 @@ export interface RunOpts { chdir?: string subTool?: string workspace?: string + chatState?: string } function toArgs(opts: RunOpts): string[] { @@ -174,7 +175,7 @@ export class Run { private req?: any private stderr?: string private callbacks: Record void)[]> = {} - private chatState: string | undefined + private chatState?: string constructor(subCommand: string, path: string, content: string, opts: RunOpts, bin?: string, gptscriptURL?: string) { this.id = randomId("run-") @@ -199,13 +200,17 @@ export class Run { run = new (this.constructor as any)(this.requestPath, this.filePath, this.content, this.opts, this.bin, this.gptscriptURL) } - run.chatState = this.chatState + if (this.chatState) { + run.chatState = this.chatState + } else if (this.opts.chatState) { + run.chatState = this.opts.chatState + } run.opts.input = input if (run.gptscriptURL) { if (run.content !== "") { - run.request({content: this.content, chatState: JSON.stringify(run.chatState)}) + run.request({content: this.content, chatState: run.chatState}) } else { - run.request({file: this.filePath, chatState: JSON.stringify(run.chatState)}) + run.request({file: this.filePath, chatState: run.chatState}) } } else { run.exec() @@ -216,7 +221,7 @@ export class Run { exec(extraArgs: string[] = [], env: NodeJS.Dict = process.env) { extraArgs.push(...toArgs(this.opts)) - extraArgs.push("--chat-state=" + (this.chatState ? JSON.stringify(this.chatState) : "null")) + extraArgs.push("--chat-state=" + (this.chatState ? this.chatState : "null")) this.chatState = undefined if (this.filePath) { @@ -353,7 +358,7 @@ export class Run { const out = data as ChatState if (out.done !== undefined && !out.done) { - this.chatState = out.state + this.chatState = JSON.stringify(out.state) this.state = RunState.Continue } else { this.state = RunState.Finished @@ -637,6 +642,10 @@ export class Run { return JSON.parse(await this.text()) } + public currentChatState(): string | undefined { + return this.chatState + } + public close(): void { if (this.process) { if (this.process.exitCode === null) { diff --git a/tests/fixtures/global-tools.gpt b/tests/fixtures/global-tools.gpt index f38e599..0e5d0f6 100644 --- a/tests/fixtures/global-tools.gpt +++ b/tests/fixtures/global-tools.gpt @@ -4,7 +4,7 @@ Runbook 3 --- Name: tool_1 -Global Tools: sys.workspace.ls, sys.workspace.read, sys.workspace.write, github.com/gptscript-ai/knowledge, github.com/drpebcak/duckdb, github.com/gptscript-ai/browser, github.com/gptscript-ai/browser-search/google, github.com/gptscript-ai/browser-search/google-question-answerer +Global Tools: sys.read, sys.write, github.com/gptscript-ai/knowledge, github.com/drpebcak/duckdb, github.com/gptscript-ai/browser, github.com/gptscript-ai/browser-search/google, github.com/gptscript-ai/browser-search/google-question-answerer Say "Hello!" diff --git a/tests/gptscript.test.ts b/tests/gptscript.test.ts index 57e9776..d367e08 100644 --- a/tests/gptscript.test.ts +++ b/tests/gptscript.test.ts @@ -381,4 +381,47 @@ describe("gptscript module", () => { expect(run.state).toEqual(gptscript.RunState.Finished) expect(err).toEqual("") }, 60000) + + test("nextChat on file providing chat state", async () => { + let run = client.run(path.join(__dirname, "fixtures", "chat.gpt"), {disableCache: true}) + + run = run.nextChat("List the 3 largest of the Great Lakes by volume.") + expect(await run.text()).toContain("Lake Superior") + expect(run.err).toEqual("") + expect(run.state).toEqual(gptscript.RunState.Continue) + + run = client.run(path.join(__dirname, "fixtures", "chat.gpt"), { + disableCache: true, + input: "What is the total area of the third one in square miles?", + chatState: run.currentChatState() + }) + + expect(await run.text()).toContain("Lake Huron") + expect(run.err).toEqual("") + expect(run.state).toEqual(gptscript.RunState.Continue) + }, 10000) + + test("nextChat on tool providing chat state", async () => { + const t = { + chat: true, + instructions: "You are a chat bot. Don't finish the conversation until I say 'bye'.", + tools: ["sys.chat.finish"] + } + let run = client.evaluate(t as any, {disableCache: true}) + + run = run.nextChat("List the three largest states in the United States by area.") + expect(await run.text()).toContain("California") + expect(run.err).toEqual("") + expect(run.state).toEqual(gptscript.RunState.Continue) + + run = client.evaluate(t as any, { + disableCache: true, + input: "What is the capital of the second one?", + chatState: run.currentChatState() + }) + + expect(await run.text()).toContain("Austin") + expect(run.err).toEqual("") + expect(run.state).toEqual(gptscript.RunState.Continue) + }, 10000) })