Skip to content

Commit 0c8c0f1

Browse files
authored
Merge pull request #45 from thedadams/make-prompt-explicit
fix: make prompt explicit
2 parents c3947ce + 993fafe commit 0c8c0f1

File tree

3 files changed

+77
-6
lines changed

3 files changed

+77
-6
lines changed

README.md

+39
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ None of the options is required, and the defaults will reduce the number of call
4949
- `workspace`: Directory to use for the workspace, if specified it will not be deleted on exit
5050
- `chatState`: The chat state to continue, or null to start a new chat and return the state
5151
- `confirm`: Prompt before running potentially dangerous commands
52+
- `prompt`: Allow scripts to prompt the user for input
5253

5354
## Functions
5455

@@ -227,6 +228,44 @@ async function streamExecFileWithEvents() {
227228
}
228229
```
229230

231+
### Prompt
232+
233+
A gptscript may need to prompt the user for information like credentials. A user should listen for
234+
the `RunEventType.Prompt`. Note that if `prompt: true` is not set in the options, then an error will occur if a
235+
gptscript attempts to prompt the user.
236+
237+
```javascript
238+
const gptscript = require('@gptscript-ai/gptscript');
239+
240+
const opts = {
241+
disableCache: true,
242+
input: "--testin how high is that there mouse?",
243+
confirm: true
244+
};
245+
246+
async function streamExecFileWithEvents() {
247+
const client = new gptscript.Client();
248+
try {
249+
const run = await client.run('./test.gpt', opts);
250+
251+
run.on(gptscript.RunEventType.Prompt, async (data: gptscript.PromptFrame) => {
252+
// data will have the information for what the gptscript is prompting.
253+
254+
await client.promptResponse({
255+
id: data.id,
256+
// response is a map of fields to values
257+
responses: {[data.fields[0]]: "Some Value"}
258+
})
259+
});
260+
261+
await run.text();
262+
} catch (e) {
263+
console.error(e);
264+
}
265+
client.close();
266+
}
267+
```
268+
230269
### Chat support
231270

232271
For tools that support chat, you can use the `nextChat` method on the run object to continue the chat. This method takes

src/gptscript.ts

+17-5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export interface RunOpts {
1212
workspace?: string
1313
chatState?: string
1414
confirm?: boolean
15+
prompt?: boolean
1516
}
1617

1718
export enum RunEventType {
@@ -344,23 +345,27 @@ export class Run {
344345
})
345346

346347
res.on("aborted", () => {
347-
if (this.state !== RunState.Finished) {
348+
if (this.state !== RunState.Finished && this.state !== RunState.Error) {
348349
this.state = RunState.Error
349350
this.err = "Run has been aborted"
350351
reject(this.err)
351352
}
352353
})
353354

354355
res.on("error", (error: Error) => {
355-
this.state = RunState.Error
356-
this.err = error.message || ""
356+
if (this.state !== RunState.Error) {
357+
this.state = RunState.Error
358+
this.err = error.message || ""
359+
}
357360
reject(this.err)
358361
})
359362
})
360363

361364
this.req.on("error", (error: Error) => {
362-
this.state = RunState.Error
363-
this.err = error.message || ""
365+
if (this.state !== RunState.Error) {
366+
this.state = RunState.Error
367+
this.err = error.message || ""
368+
}
364369
reject(this.err)
365370
})
366371

@@ -434,6 +439,13 @@ export class Run {
434439
this.state = RunState.Creating
435440
}
436441

442+
if (f.type === RunEventType.Prompt && !this.opts.prompt) {
443+
this.state = RunState.Error
444+
this.err = `prompt occurred when prompt was not allowed: Message: ${f.message}\nFields: ${f.fields}\nSensitive: ${f.sensitive}`
445+
this.close()
446+
return ""
447+
}
448+
437449
if (f.type === RunEventType.RunStart) {
438450
this.state = RunState.Running
439451
} else if (f.type === RunEventType.RunFinish) {

tests/gptscript.test.ts

+21-1
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ describe("gptscript module", () => {
435435
instructions: "Use the sys.prompt user to ask the user for 'first name' which is not sensitive. After you get their first name, say hello.",
436436
tools: ["sys.prompt"]
437437
}
438-
const run = await client.evaluate(t as any)
438+
const run = await client.evaluate(t as any, {prompt: true})
439439
run.on(gptscript.RunEventType.Prompt, async (data: gptscript.PromptFrame) => {
440440
expect(data.message).toContain("first name")
441441
expect(data.fields.length).toEqual(1)
@@ -450,4 +450,24 @@ describe("gptscript module", () => {
450450
expect(run.err).toEqual("")
451451
expect(promptFound).toBeTruthy()
452452
})
453+
454+
test("prompt without prompt allowed should fail", async () => {
455+
let promptFound = false
456+
const t = {
457+
instructions: "Use the sys.prompt user to ask the user for 'first name' which is not sensitive. After you get their first name, say hello.",
458+
tools: ["sys.prompt"]
459+
}
460+
const run = await client.evaluate(t as any)
461+
run.on(gptscript.RunEventType.Prompt, async (data: gptscript.PromptFrame) => {
462+
promptFound = true
463+
})
464+
465+
try {
466+
await run.text()
467+
} catch (e) {
468+
expect(e).toContain("prompt occurred")
469+
}
470+
expect(run.err).toContain("prompt occurred")
471+
expect(promptFound).toBeFalsy()
472+
})
453473
})

0 commit comments

Comments
 (0)