Skip to content

Commit d8777f9

Browse files
authoredSep 27, 2024··
feat: add options for URL and token (#94)
If the URL option is passed, then the SDK will behave the same as if the GPTSCRIPT_URL env var is set. Signed-off-by: Donnie Adams <donnie@acorn.io>
1 parent 523aa08 commit d8777f9

File tree

1 file changed

+100
-74
lines changed

1 file changed

+100
-74
lines changed
 

‎src/gptscript.ts

+100-74
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {fileURLToPath} from "url"
55
import {gunzipSync} from "zlib"
66

77
export interface GlobalOpts {
8+
URL?: string
9+
Token?: string
810
CacheDir?: string
911
APIKey?: string
1012
BaseURL?: string
@@ -48,6 +50,8 @@ export interface RunOpts {
4850
env?: string[]
4951
forceSequential?: boolean
5052

53+
URL?: string
54+
Token?: string
5155
CacheDir?: string
5256
APIKey?: string
5357
BaseURL?: string
@@ -75,21 +79,24 @@ export class GPTScript {
7579
private static instanceCount: number = 0
7680

7781

78-
private ready: boolean
7982
private readonly opts: GlobalOpts
8083

8184
constructor(opts?: GlobalOpts) {
8285
this.opts = opts || {}
83-
this.ready = false
8486
GPTScript.instanceCount++
87+
88+
let startSDK = !GPTScript.serverProcess && !GPTScript.serverURL && !this.opts.URL
89+
8590
if (!GPTScript.serverURL) {
86-
GPTScript.serverURL = process.env.GPTSCRIPT_URL ?? "http://127.0.0.1:0"
87-
if (!GPTScript.serverURL.startsWith("http://") && !GPTScript.serverURL.startsWith("https://")) {
88-
GPTScript.serverURL = "http://" + GPTScript.serverURL
89-
}
91+
GPTScript.serverURL = process.env.GPTSCRIPT_URL ?? ""
92+
startSDK = startSDK && !GPTScript.serverURL
9093
}
9194

92-
if (GPTScript.instanceCount === 1 && !process.env.GPTSCRIPT_URL) {
95+
if (!this.opts.Token) {
96+
this.opts.Token = process.env.GPTSCRIPT_TOKEN
97+
}
98+
99+
if (startSDK) {
93100
let env = process.env
94101
if (this.opts.Env) {
95102
env = {
@@ -113,7 +120,7 @@ export class GPTScript {
113120
}
114121
})
115122

116-
GPTScript.serverProcess = child_process.spawn(getCmdPath(), ["sys.sdkserver", "--listen-address", GPTScript.serverURL.replace("http://", "")], {
123+
GPTScript.serverProcess = child_process.spawn(getCmdPath(), ["sys.sdkserver", "--listen-address", "127.0.0.1:0"], {
117124
env: env,
118125
stdio: ["pipe", "ignore", "pipe"]
119126
})
@@ -125,25 +132,31 @@ export class GPTScript {
125132
}
126133

127134
GPTScript.serverURL = `http://${url}`
128-
if (!this.opts.Env) {
129-
this.opts.Env = []
130-
}
131-
this.opts.Env.push(`GPTSCRIPT_URL=${GPTScript.serverURL}`)
132135

133136
GPTScript.serverProcess.stderr?.removeAllListeners()
134137
})
135138
} else {
139+
if (!this.opts.URL) {
140+
this.opts.URL = GPTScript.serverURL
141+
}
142+
136143
if (!this.opts.Env) {
137144
this.opts.Env = []
138145
}
139-
this.opts.Env.push("GPTSCRIPT_URL=" + GPTScript.serverURL)
146+
if (this.opts.URL) {
147+
this.opts.Env.push(`GPTSCRIPT_URL=${this.opts.URL}`)
148+
}
149+
150+
if (this.opts.Token) {
151+
this.opts.Env.push(`GPTSCRIPT_TOKEN=${this.opts.Token}`)
152+
}
140153
}
141154
}
142155

143156
close(): void {
144157
GPTScript.instanceCount--
145158
if (GPTScript.instanceCount === 0 && GPTScript.serverProcess) {
146-
GPTScript.serverURL = process.env.GPTSCRIPT_URL ?? "http://127.0.0.1:0"
159+
GPTScript.serverURL = process.env.GPTSCRIPT_URL ?? ""
147160
GPTScript.serverProcess.kill("SIGTERM")
148161
GPTScript.serverProcess.stdin?.end()
149162
}
@@ -168,10 +181,10 @@ export class GPTScript {
168181
}
169182

170183
async runBasicCommand(cmd: string, body?: any): Promise<string> {
171-
if (!this.ready) {
172-
this.ready = await this.testGPTScriptURL(20)
184+
if (!this.opts.URL) {
185+
await this.testGPTScriptURL(20)
173186
}
174-
const r = new RunSubcommand(cmd, "", {}, GPTScript.serverURL)
187+
const r = new RunSubcommand(cmd, "", {URL: this.opts.URL, Token: this.opts.Token})
175188
r.requestNoStream(body)
176189
return r.text()
177190
}
@@ -184,15 +197,14 @@ export class GPTScript {
184197
* @return {Run} The Run object representing the running tool.
185198
*/
186199
async run(toolName: string, opts: RunOpts = {}): Promise<Run> {
187-
if (!this.ready) {
188-
this.ready = await this.testGPTScriptURL(20)
200+
if (!this.opts.URL) {
201+
await this.testGPTScriptURL(20)
189202
}
190-
191203
if (this.opts.Env) {
192204
opts.env = this.opts.Env.concat(opts.env || [])
193205
}
194206

195-
return (new Run("run", toolName, {...this.opts, ...opts}, GPTScript.serverURL)).nextChat(opts.input)
207+
return (new Run("run", toolName, {...this.opts, ...opts})).nextChat(opts.input)
196208
}
197209

198210
/**
@@ -203,37 +215,40 @@ export class GPTScript {
203215
* @return {Run} The Run object representing the evaluation.
204216
*/
205217
async evaluate(tool: Tool | ToolDef | ToolDef[], opts: RunOpts = {}): Promise<Run> {
206-
if (!this.ready) {
207-
this.ready = await this.testGPTScriptURL(20)
218+
if (!this.opts.URL) {
219+
await this.testGPTScriptURL(20)
208220
}
209-
210221
if (this.opts.Env) {
211222
opts.env = this.opts.Env.concat(opts.env || [])
212223
}
213-
return (new Run("evaluate", tool, {...this.opts, ...opts}, GPTScript.serverURL)).nextChat(opts.input)
224+
return (new Run("evaluate", tool, {...this.opts, ...opts})).nextChat(opts.input)
214225
}
215226

216227
async parse(fileName: string, disableCache?: boolean): Promise<Block[]> {
217-
if (!this.ready) {
218-
this.ready = await this.testGPTScriptURL(20)
228+
if (!this.opts.URL) {
229+
await this.testGPTScriptURL(20)
219230
}
220-
const r: Run = new RunSubcommand("parse", fileName, {disableCache: disableCache}, GPTScript.serverURL)
231+
const r: Run = new RunSubcommand("parse", fileName, {
232+
disableCache: disableCache,
233+
URL: this.opts.URL,
234+
Token: this.opts.Token
235+
})
221236
r.request({file: fileName})
222237
return parseBlocksFromNodes((await r.json()).nodes)
223238
}
224239

225240
async parseContent(toolContent: string): Promise<Block[]> {
226-
if (!this.ready) {
227-
this.ready = await this.testGPTScriptURL(20)
241+
if (!this.opts.URL) {
242+
await this.testGPTScriptURL(20)
228243
}
229-
const r: Run = new RunSubcommand("parse", "", {}, GPTScript.serverURL)
244+
const r: Run = new RunSubcommand("parse", "", {URL: this.opts.URL, Token: this.opts.Token})
230245
r.request({content: toolContent})
231246
return parseBlocksFromNodes((await r.json()).nodes)
232247
}
233248

234249
async stringify(blocks: Block[]): Promise<string> {
235-
if (!this.ready) {
236-
this.ready = await this.testGPTScriptURL(20)
250+
if (!this.opts.URL) {
251+
await this.testGPTScriptURL(20)
237252
}
238253
const nodes: any[] = []
239254

@@ -253,16 +268,16 @@ export class GPTScript {
253268
}
254269
}
255270

256-
const r: Run = new RunSubcommand("fmt", "", {}, GPTScript.serverURL)
271+
const r: Run = new RunSubcommand("fmt", "", {URL: this.opts.URL, Token: this.opts.Token})
257272
r.request({nodes: nodes})
258273
return r.text()
259274
}
260275

261276
async confirm(response: AuthResponse): Promise<void> {
262-
if (!this.ready) {
263-
this.ready = await this.testGPTScriptURL(20)
277+
if (!this.opts.URL) {
278+
await this.testGPTScriptURL(20)
264279
}
265-
const resp = await fetch(`${GPTScript.serverURL}/confirm/${response.id}`, {
280+
const resp = await fetch(`${this.opts.URL}/confirm/${response.id}`, {
266281
method: "POST",
267282
body: JSON.stringify(response)
268283
})
@@ -273,10 +288,10 @@ export class GPTScript {
273288
}
274289

275290
async promptResponse(response: PromptResponse): Promise<void> {
276-
if (!this.ready) {
277-
this.ready = await this.testGPTScriptURL(20)
291+
if (!this.opts.URL) {
292+
await this.testGPTScriptURL(20)
278293
}
279-
const resp = await fetch(`${GPTScript.serverURL}/prompt-response/${response.id}`, {
294+
const resp = await fetch(`${this.opts.URL}/prompt-response/${response.id}`, {
280295
method: "POST",
281296
body: JSON.stringify(response.responses)
282297
})
@@ -335,42 +350,42 @@ export class GPTScript {
335350
}
336351

337352
async listCredentials(context: Array<string>, allContexts: boolean): Promise<Array<Credential>> {
338-
if (!this.ready) {
339-
this.ready = await this.testGPTScriptURL(20)
353+
if (!this.opts.URL) {
354+
await this.testGPTScriptURL(20)
340355
}
341356

342-
const r: Run = new RunSubcommand("credentials", "", {}, GPTScript.serverURL)
357+
const r: Run = new RunSubcommand("credentials", "", {URL: this.opts.URL, Token: this.opts.Token})
343358
r.request({context, allContexts})
344359
const out = await r.json()
345360
return out.map((c: any) => jsonToCredential(JSON.stringify(c)))
346361
}
347362

348363
async createCredential(credential: Credential): Promise<void> {
349-
if (!this.ready) {
350-
this.ready = await this.testGPTScriptURL(20)
364+
if (!this.opts.URL) {
365+
await this.testGPTScriptURL(20)
351366
}
352367

353-
const r: Run = new RunSubcommand("credentials/create", "", {}, GPTScript.serverURL)
368+
const r: Run = new RunSubcommand("credentials/create", "", {URL: this.opts.URL, Token: this.opts.Token})
354369
r.request({content: credentialToJSON(credential)})
355370
await r.text()
356371
}
357372

358373
async revealCredential(context: Array<string>, name: string): Promise<Credential> {
359-
if (!this.ready) {
360-
this.ready = await this.testGPTScriptURL(20)
374+
if (!this.opts.URL) {
375+
await this.testGPTScriptURL(20)
361376
}
362377

363-
const r: Run = new RunSubcommand("credentials/reveal", "", {}, GPTScript.serverURL)
378+
const r: Run = new RunSubcommand("credentials/reveal", "", {URL: this.opts.URL, Token: this.opts.Token})
364379
r.request({context, name})
365380
return jsonToCredential(await r.text())
366381
}
367382

368383
async deleteCredential(context: string, name: string): Promise<void> {
369-
if (!this.ready) {
370-
this.ready = await this.testGPTScriptURL(20)
384+
if (!this.opts.URL) {
385+
await this.testGPTScriptURL(20)
371386
}
372387

373-
const r: Run = new RunSubcommand("credentials/delete", "", {}, GPTScript.serverURL)
388+
const r: Run = new RunSubcommand("credentials/delete", "", {URL: this.opts.URL, Token: this.opts.Token})
374389
r.request({context: [context], name})
375390
await r.text()
376391
}
@@ -382,20 +397,29 @@ export class GPTScript {
382397
* @return {Promise<LoadResponse>} The loaded program.
383398
*/
384399
private async _load(payload: any): Promise<LoadResponse> {
385-
if (!this.ready) {
386-
this.ready = await this.testGPTScriptURL(20)
400+
if (!this.opts.URL) {
401+
await this.testGPTScriptURL(20)
387402
}
388-
const r: Run = new RunSubcommand("load", payload.toolDefs || [], {}, GPTScript.serverURL)
403+
const r: Run = new RunSubcommand("load", payload.toolDefs || [], {URL: this.opts.URL, Token: this.opts.Token})
389404

390405
r.request(payload)
391406
return (await r.json()) as LoadResponse
392407
}
393408

394-
private async testGPTScriptURL(count: number): Promise<boolean> {
409+
private async testGPTScriptURL(count: number): Promise<void> {
395410
while (count > 0) {
396411
try {
397412
await fetch(`${GPTScript.serverURL}/healthz`)
398-
return true
413+
this.opts.URL = GPTScript.serverURL
414+
if (!this.opts.Env) {
415+
this.opts.Env = []
416+
}
417+
this.opts.Env.push(`GPTSCRIPT_URL=${this.opts.URL}`)
418+
if (this.opts.Token) {
419+
this.opts.Env.push(`GPTSCRIPT_TOKEN=${this.opts.Token}`)
420+
}
421+
422+
return
399423
} catch {
400424
if (count === 0) {
401425
}
@@ -418,7 +442,6 @@ export class Run {
418442

419443
protected stdout?: string
420444

421-
private readonly gptscriptURL?: string
422445
private readonly requestPath: string = ""
423446
private promise?: Promise<string>
424447
private req?: http.ClientRequest
@@ -429,13 +452,11 @@ export class Run {
429452
private prg?: Program
430453
private respondingToolId?: string
431454

432-
constructor(subCommand: string, tools: ToolDef | ToolDef[] | string, opts: RunOpts, gptscriptURL?: string) {
455+
constructor(subCommand: string, tools: ToolDef | ToolDef[] | string, opts: RunOpts) {
433456
this.id = randomId("run-")
434457
this.requestPath = subCommand
435458
this.opts = opts
436459
this.tools = tools
437-
438-
this.gptscriptURL = gptscriptURL
439460
}
440461

441462
nextChat(input: string = ""): Run {
@@ -445,7 +466,7 @@ export class Run {
445466

446467
let run = this
447468
if (run.state !== RunState.Creating) {
448-
run = new (this.constructor as any)(this.requestPath, this.tools, this.opts, this.gptscriptURL)
469+
run = new (this.constructor as any)(this.requestPath, this.tools, this.opts)
449470
}
450471

451472
if (this.chatState && this.state === RunState.Continue) {
@@ -493,10 +514,10 @@ export class Run {
493514
}
494515

495516
request(tool: any) {
496-
if (!this.gptscriptURL) {
497-
throw new Error("request() requires gptscriptURL to be set")
517+
if (!this.opts.URL) {
518+
throw new Error("request() requires URL to be set")
498519
}
499-
const options = this.requestOptions(this.gptscriptURL, this.requestPath, tool)
520+
const options = this.requestOptions(this.opts.URL, this.opts.Token || "", this.requestPath, tool)
500521
options.headers = {"Transfer-Encoding": "chunked", ...options.headers} as any
501522

502523
this.promise = new Promise<string>(async (resolve, reject) => {
@@ -580,15 +601,15 @@ export class Run {
580601
}
581602

582603
requestNoStream(tool: any) {
583-
if (!this.gptscriptURL) {
604+
if (!this.opts.URL) {
584605
throw new Error("request() requires gptscriptURL to be set")
585606
}
586607

587-
const options = this.requestOptions(this.gptscriptURL, this.requestPath, tool) as any
608+
const options = this.requestOptions(this.opts.URL, this.opts.Token || "", this.requestPath, tool) as any
588609
if (tool) {
589610
options.body = JSON.stringify({...tool, ...this.opts})
590611
}
591-
const req = new Request(this.gptscriptURL + "/" + this.requestPath, options)
612+
const req = new Request(this.opts.URL + "/" + this.requestPath, options)
592613

593614
this.promise = new Promise<string>(async (resolve, reject) => {
594615
fetch(req).then(resp => {
@@ -601,23 +622,28 @@ export class Run {
601622
})
602623
}
603624

604-
requestOptions(gptscriptURL: string, path: string, tool: any) {
625+
requestOptions(gptscriptURL: string, token: string, path: string, tool: any) {
605626
let method = "GET"
606627
if (tool) {
607628
method = "POST"
608629
}
609630

610631
const url = new URL(gptscriptURL)
611632

633+
const headers = {
634+
"Content-Type": "application/json"
635+
} as any
636+
if (token) {
637+
headers["Authorization"] = `Bearer ${token}`
638+
}
639+
612640
return {
613641
hostname: url.hostname,
614642
port: url.port || 80,
615643
protocol: url.protocol || "http:",
616644
path: "/" + path,
617645
method: method,
618-
headers: {
619-
"Content-Type": "application/json"
620-
},
646+
headers: headers
621647
}
622648
}
623649

@@ -747,8 +773,8 @@ export class Run {
747773
}
748774

749775
class RunSubcommand extends Run {
750-
constructor(subCommand: string, tool: ToolDef | ToolDef[] | string, opts: RunOpts, gptscriptURL?: string) {
751-
super(subCommand, tool, opts, gptscriptURL)
776+
constructor(subCommand: string, tool: ToolDef | ToolDef[] | string, opts: RunOpts) {
777+
super(subCommand, tool, opts)
752778
}
753779

754780
processStdout(data: string | object): string {

0 commit comments

Comments
 (0)
Please sign in to comment.