Skip to content

Commit f50289f

Browse files
authored
Merge pull request #82 from thedadams/list-models-from-providers
feat: add ability to list models from other providers
2 parents 97480b2 + 923de60 commit f50289f

File tree

2 files changed

+73
-18
lines changed

2 files changed

+73
-18
lines changed

src/gptscript.ts

+35-18
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,20 @@ export class GPTScript {
7373

7474

7575
private ready: boolean
76+
private opts: GlobalOpts
7677

7778
constructor(opts?: GlobalOpts) {
79+
this.opts = opts || {}
7880
this.ready = false
7981
GPTScript.instanceCount++
8082
if (!GPTScript.serverURL) {
8183
GPTScript.serverURL = "http://" + (process.env.GPTSCRIPT_URL || "127.0.0.1:0")
8284
}
8385
if (GPTScript.instanceCount === 1 && process.env.GPTSCRIPT_DISABLE_SERVER !== "true") {
8486
let env = process.env
85-
if (opts && opts.Env) {
87+
if (this.opts.Env) {
8688
env = {}
87-
for (const v of opts.Env) {
89+
for (const v of this.opts.Env) {
8890
const equalIndex = v.indexOf("=")
8991
if (equalIndex === -1) {
9092
env[v] = ""
@@ -94,7 +96,7 @@ export class GPTScript {
9496
}
9597
}
9698

97-
globalOptsToEnv(env, opts)
99+
globalOptsToEnv(env, this.opts)
98100
process.on("exit", (code) => {
99101
if (GPTScript.serverProcess) {
100102
GPTScript.serverProcess.stdin?.end()
@@ -133,20 +135,30 @@ export class GPTScript {
133135
return this.runBasicCommand("list-tools")
134136
}
135137

136-
listModels(): Promise<string> {
137-
return this.runBasicCommand("list-models")
138+
listModels(providers?: string[], credentialOverrides?: string[]): Promise<string> {
139+
if (this.opts.DefaultModelProvider) {
140+
if (!providers) {
141+
providers = []
142+
}
143+
providers.push(this.opts.DefaultModelProvider)
144+
}
145+
return this.runBasicCommand("list-models", {
146+
"providers": providers,
147+
"env": this.opts.Env,
148+
"credentialOverrides": credentialOverrides
149+
})
138150
}
139151

140152
version(): Promise<string> {
141153
return this.runBasicCommand("version")
142154
}
143155

144-
async runBasicCommand(cmd: string): Promise<string> {
156+
async runBasicCommand(cmd: string, body?: any): Promise<string> {
145157
if (!this.ready) {
146158
this.ready = await this.testGPTScriptURL(20)
147159
}
148160
const r = new RunSubcommand(cmd, "", {}, GPTScript.serverURL)
149-
r.requestNoStream(null)
161+
r.requestNoStream(body)
150162
return r.text()
151163
}
152164

@@ -161,7 +173,8 @@ export class GPTScript {
161173
if (!this.ready) {
162174
this.ready = await this.testGPTScriptURL(20)
163175
}
164-
return (new Run("run", toolName, opts, GPTScript.serverURL)).nextChat(opts.input)
176+
177+
return (new Run("run", toolName, {...this.opts, ...opts}, GPTScript.serverURL)).nextChat(opts.input)
165178
}
166179

167180
/**
@@ -176,7 +189,7 @@ export class GPTScript {
176189
this.ready = await this.testGPTScriptURL(20)
177190
}
178191

179-
return (new Run("evaluate", tool, opts, GPTScript.serverURL)).nextChat(opts.input)
192+
return (new Run("evaluate", tool, {...this.opts, ...opts}, GPTScript.serverURL)).nextChat(opts.input)
180193
}
181194

182195
async parse(fileName: string, disableCache?: boolean): Promise<Block[]> {
@@ -265,7 +278,7 @@ export class GPTScript {
265278
disableCache?: boolean,
266279
subTool?: string
267280
): Promise<LoadResponse> {
268-
return this._load({ file: fileName, disableCache, subTool });
281+
return this._load({file: fileName, disableCache, subTool})
269282
}
270283

271284
/**
@@ -281,7 +294,7 @@ export class GPTScript {
281294
disableCache?: boolean,
282295
subTool?: string
283296
): Promise<LoadResponse> {
284-
return this._load({ content, disableCache, subTool });
297+
return this._load({content, disableCache, subTool})
285298
}
286299

287300
/**
@@ -297,7 +310,7 @@ export class GPTScript {
297310
disableCache?: boolean,
298311
subTool?: string
299312
): Promise<LoadResponse> {
300-
return this._load({ toolDefs, disableCache, subTool });
313+
return this._load({toolDefs, disableCache, subTool})
301314
}
302315

303316
/**
@@ -308,12 +321,12 @@ export class GPTScript {
308321
*/
309322
private async _load(payload: any): Promise<LoadResponse> {
310323
if (!this.ready) {
311-
this.ready = await this.testGPTScriptURL(20);
324+
this.ready = await this.testGPTScriptURL(20)
312325
}
313-
const r: Run = new RunSubcommand("load", payload.toolDefs || [], {}, GPTScript.serverURL);
326+
const r: Run = new RunSubcommand("load", payload.toolDefs || [], {}, GPTScript.serverURL)
314327

315-
r.request(payload);
316-
return (await r.json()) as LoadResponse;
328+
r.request(payload)
329+
return (await r.json()) as LoadResponse
317330
}
318331

319332
private async testGPTScriptURL(count: number): Promise<boolean> {
@@ -511,12 +524,16 @@ export class Run {
511524

512525
const options = this.requestOptions(this.gptscriptURL, this.requestPath, tool) as any
513526
if (tool) {
514-
options.body = {...tool, ...this.opts}
527+
options.body = JSON.stringify({...tool, ...this.opts})
515528
}
516529
const req = new Request(this.gptscriptURL + "/" + this.requestPath, options)
517530

518531
this.promise = new Promise<string>(async (resolve, reject) => {
519-
fetch(req).then(resp => resp.json()).then(res => resolve(res.stdout)).catch(e => {
532+
fetch(req).then(resp => {
533+
return resp.json()
534+
}).then(res => {
535+
resolve(res.stdout)
536+
}).catch(e => {
520537
reject(e)
521538
})
522539
})

tests/gptscript.test.ts

+38
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {ArgumentSchemaType, getEnv, PropertyType, RunEventType, ToolType} from "
33
import path from "path"
44
import {fileURLToPath} from "url"
55

6+
let gFirst: gptscript.GPTScript
67
let g: gptscript.GPTScript
78
const __dirname = path.dirname(fileURLToPath(import.meta.url))
89

@@ -12,9 +13,13 @@ describe("gptscript module", () => {
1213
throw new Error("neither OPENAI_API_KEY nor GPTSCRIPT_URL is set")
1314
}
1415

16+
// Start an initial GPTScript instance.
17+
// This one doesn't have any options, but it's there to ensure that using another instance works as expected in all cases.
18+
gFirst = new gptscript.GPTScript()
1519
g = new gptscript.GPTScript({APIKey: process.env.OPENAI_API_KEY})
1620
})
1721
afterAll(() => {
22+
gFirst.close()
1823
g.close()
1924
})
2025

@@ -35,6 +40,39 @@ describe("gptscript module", () => {
3540
expect(models).toBeDefined()
3641
})
3742

43+
test("listModels with providers returns a list of models from that provider", async () => {
44+
if (!process.env.ANTHROPIC_API_KEY) {
45+
return
46+
}
47+
48+
let models = await g.listModels(["github.com/gptscript-ai/claude3-anthropic-provider"], ["github.com/gptscript-ai/claude3-anthropic-provider/credential:ANTHROPIC_API_KEY"])
49+
expect(models).toBeDefined()
50+
for (let model of models.split("\n")) {
51+
expect(model).toBeDefined()
52+
expect(model.startsWith("claude-3-")).toBe(true)
53+
expect(model.endsWith("from github.com/gptscript-ai/claude3-anthropic-provider")).toBe(true)
54+
}
55+
})
56+
57+
test("listModels with default provider returns a list of models from that provider", async () => {
58+
if (!process.env.ANTHROPIC_API_KEY) {
59+
return
60+
}
61+
62+
const newg = new gptscript.GPTScript({DefaultModelProvider: "github.com/gptscript-ai/claude3-anthropic-provider"})
63+
try {
64+
let models = await newg.listModels(undefined, ["github.com/gptscript-ai/claude3-anthropic-provider/credential:ANTHROPIC_API_KEY"])
65+
expect(models).toBeDefined()
66+
for (let model of models.split("\n")) {
67+
expect(model).toBeDefined()
68+
expect(model.startsWith("claude-3-")).toBe(true)
69+
expect(model.endsWith("from github.com/gptscript-ai/claude3-anthropic-provider")).toBe(true)
70+
}
71+
} finally {
72+
newg.close()
73+
}
74+
})
75+
3876
test("version returns a gptscript version", async () => {
3977
// Similar structure to listTools
4078
let version = await g.version()

0 commit comments

Comments
 (0)