diff --git a/src/gptscript.ts b/src/gptscript.ts index 393752c..14a8ecb 100644 --- a/src/gptscript.ts +++ b/src/gptscript.ts @@ -73,8 +73,10 @@ export class GPTScript { private ready: boolean + private opts: GlobalOpts constructor(opts?: GlobalOpts) { + this.opts = opts || {} this.ready = false GPTScript.instanceCount++ if (!GPTScript.serverURL) { @@ -82,9 +84,9 @@ export class GPTScript { } if (GPTScript.instanceCount === 1 && process.env.GPTSCRIPT_DISABLE_SERVER !== "true") { let env = process.env - if (opts && opts.Env) { + if (this.opts.Env) { env = {} - for (const v of opts.Env) { + for (const v of this.opts.Env) { const equalIndex = v.indexOf("=") if (equalIndex === -1) { env[v] = "" @@ -94,7 +96,7 @@ export class GPTScript { } } - globalOptsToEnv(env, opts) + globalOptsToEnv(env, this.opts) process.on("exit", (code) => { if (GPTScript.serverProcess) { GPTScript.serverProcess.stdin?.end() @@ -133,20 +135,30 @@ export class GPTScript { return this.runBasicCommand("list-tools") } - listModels(): Promise { - return this.runBasicCommand("list-models") + listModels(providers?: string[], credentialOverrides?: string[]): Promise { + if (this.opts.DefaultModelProvider) { + if (!providers) { + providers = [] + } + providers.push(this.opts.DefaultModelProvider) + } + return this.runBasicCommand("list-models", { + "providers": providers, + "env": this.opts.Env, + "credentialOverrides": credentialOverrides + }) } version(): Promise { return this.runBasicCommand("version") } - async runBasicCommand(cmd: string): Promise { + async runBasicCommand(cmd: string, body?: any): Promise { if (!this.ready) { this.ready = await this.testGPTScriptURL(20) } const r = new RunSubcommand(cmd, "", {}, GPTScript.serverURL) - r.requestNoStream(null) + r.requestNoStream(body) return r.text() } @@ -161,7 +173,8 @@ export class GPTScript { if (!this.ready) { this.ready = await this.testGPTScriptURL(20) } - return (new Run("run", toolName, opts, GPTScript.serverURL)).nextChat(opts.input) + + return (new Run("run", toolName, {...this.opts, ...opts}, GPTScript.serverURL)).nextChat(opts.input) } /** @@ -176,7 +189,7 @@ export class GPTScript { this.ready = await this.testGPTScriptURL(20) } - return (new Run("evaluate", tool, opts, GPTScript.serverURL)).nextChat(opts.input) + return (new Run("evaluate", tool, {...this.opts, ...opts}, GPTScript.serverURL)).nextChat(opts.input) } async parse(fileName: string, disableCache?: boolean): Promise { @@ -265,7 +278,7 @@ export class GPTScript { disableCache?: boolean, subTool?: string ): Promise { - return this._load({ file: fileName, disableCache, subTool }); + return this._load({file: fileName, disableCache, subTool}) } /** @@ -281,7 +294,7 @@ export class GPTScript { disableCache?: boolean, subTool?: string ): Promise { - return this._load({ content, disableCache, subTool }); + return this._load({content, disableCache, subTool}) } /** @@ -297,7 +310,7 @@ export class GPTScript { disableCache?: boolean, subTool?: string ): Promise { - return this._load({ toolDefs, disableCache, subTool }); + return this._load({toolDefs, disableCache, subTool}) } /** @@ -308,12 +321,12 @@ export class GPTScript { */ private async _load(payload: any): Promise { if (!this.ready) { - this.ready = await this.testGPTScriptURL(20); + this.ready = await this.testGPTScriptURL(20) } - const r: Run = new RunSubcommand("load", payload.toolDefs || [], {}, GPTScript.serverURL); + const r: Run = new RunSubcommand("load", payload.toolDefs || [], {}, GPTScript.serverURL) - r.request(payload); - return (await r.json()) as LoadResponse; + r.request(payload) + return (await r.json()) as LoadResponse } private async testGPTScriptURL(count: number): Promise { @@ -511,12 +524,16 @@ export class Run { const options = this.requestOptions(this.gptscriptURL, this.requestPath, tool) as any if (tool) { - options.body = {...tool, ...this.opts} + options.body = JSON.stringify({...tool, ...this.opts}) } const req = new Request(this.gptscriptURL + "/" + this.requestPath, options) this.promise = new Promise(async (resolve, reject) => { - fetch(req).then(resp => resp.json()).then(res => resolve(res.stdout)).catch(e => { + fetch(req).then(resp => { + return resp.json() + }).then(res => { + resolve(res.stdout) + }).catch(e => { reject(e) }) }) diff --git a/tests/gptscript.test.ts b/tests/gptscript.test.ts index cdac726..a267c10 100644 --- a/tests/gptscript.test.ts +++ b/tests/gptscript.test.ts @@ -3,6 +3,7 @@ import {ArgumentSchemaType, getEnv, PropertyType, RunEventType, ToolType} from " import path from "path" import {fileURLToPath} from "url" +let gFirst: gptscript.GPTScript let g: gptscript.GPTScript const __dirname = path.dirname(fileURLToPath(import.meta.url)) @@ -12,9 +13,13 @@ describe("gptscript module", () => { throw new Error("neither OPENAI_API_KEY nor GPTSCRIPT_URL is set") } + // Start an initial GPTScript instance. + // This one doesn't have any options, but it's there to ensure that using another instance works as expected in all cases. + gFirst = new gptscript.GPTScript() g = new gptscript.GPTScript({APIKey: process.env.OPENAI_API_KEY}) }) afterAll(() => { + gFirst.close() g.close() }) @@ -35,6 +40,39 @@ describe("gptscript module", () => { expect(models).toBeDefined() }) + test("listModels with providers returns a list of models from that provider", async () => { + if (!process.env.ANTHROPIC_API_KEY) { + return + } + + let models = await g.listModels(["github.com/gptscript-ai/claude3-anthropic-provider"], ["github.com/gptscript-ai/claude3-anthropic-provider/credential:ANTHROPIC_API_KEY"]) + expect(models).toBeDefined() + for (let model of models.split("\n")) { + expect(model).toBeDefined() + expect(model.startsWith("claude-3-")).toBe(true) + expect(model.endsWith("from github.com/gptscript-ai/claude3-anthropic-provider")).toBe(true) + } + }) + + test("listModels with default provider returns a list of models from that provider", async () => { + if (!process.env.ANTHROPIC_API_KEY) { + return + } + + const newg = new gptscript.GPTScript({DefaultModelProvider: "github.com/gptscript-ai/claude3-anthropic-provider"}) + try { + let models = await newg.listModels(undefined, ["github.com/gptscript-ai/claude3-anthropic-provider/credential:ANTHROPIC_API_KEY"]) + expect(models).toBeDefined() + for (let model of models.split("\n")) { + expect(model).toBeDefined() + expect(model.startsWith("claude-3-")).toBe(true) + expect(model.endsWith("from github.com/gptscript-ai/claude3-anthropic-provider")).toBe(true) + } + } finally { + newg.close() + } + }) + test("version returns a gptscript version", async () => { // Similar structure to listTools let version = await g.version()