Skip to content

Commit

Permalink
Merge pull request #45 from thedadams/make-prompt-explicit
Browse files Browse the repository at this point in the history
fix: make prompt explicit
  • Loading branch information
thedadams authored May 31, 2024
2 parents c3947ce + 993fafe commit 0c8c0f1
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 6 deletions.
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ None of the options is required, and the defaults will reduce the number of call
- `workspace`: Directory to use for the workspace, if specified it will not be deleted on exit
- `chatState`: The chat state to continue, or null to start a new chat and return the state
- `confirm`: Prompt before running potentially dangerous commands
- `prompt`: Allow scripts to prompt the user for input

## Functions

Expand Down Expand Up @@ -227,6 +228,44 @@ async function streamExecFileWithEvents() {
}
```

### Prompt

A gptscript may need to prompt the user for information like credentials. A user should listen for
the `RunEventType.Prompt`. Note that if `prompt: true` is not set in the options, then an error will occur if a
gptscript attempts to prompt the user.

```javascript
const gptscript = require('@gptscript-ai/gptscript');

const opts = {
disableCache: true,
input: "--testin how high is that there mouse?",
confirm: true
};

async function streamExecFileWithEvents() {
const client = new gptscript.Client();
try {
const run = await client.run('./test.gpt', opts);

run.on(gptscript.RunEventType.Prompt, async (data: gptscript.PromptFrame) => {
// data will have the information for what the gptscript is prompting.

await client.promptResponse({
id: data.id,
// response is a map of fields to values
responses: {[data.fields[0]]: "Some Value"}
})
});

await run.text();
} catch (e) {
console.error(e);
}
client.close();
}
```

### Chat support

For tools that support chat, you can use the `nextChat` method on the run object to continue the chat. This method takes
Expand Down
22 changes: 17 additions & 5 deletions src/gptscript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface RunOpts {
workspace?: string
chatState?: string
confirm?: boolean
prompt?: boolean
}

export enum RunEventType {
Expand Down Expand Up @@ -344,23 +345,27 @@ export class Run {
})

res.on("aborted", () => {
if (this.state !== RunState.Finished) {
if (this.state !== RunState.Finished && this.state !== RunState.Error) {
this.state = RunState.Error
this.err = "Run has been aborted"
reject(this.err)
}
})

res.on("error", (error: Error) => {
this.state = RunState.Error
this.err = error.message || ""
if (this.state !== RunState.Error) {
this.state = RunState.Error
this.err = error.message || ""
}
reject(this.err)
})
})

this.req.on("error", (error: Error) => {
this.state = RunState.Error
this.err = error.message || ""
if (this.state !== RunState.Error) {
this.state = RunState.Error
this.err = error.message || ""
}
reject(this.err)
})

Expand Down Expand Up @@ -434,6 +439,13 @@ export class Run {
this.state = RunState.Creating
}

if (f.type === RunEventType.Prompt && !this.opts.prompt) {
this.state = RunState.Error
this.err = `prompt occurred when prompt was not allowed: Message: ${f.message}\nFields: ${f.fields}\nSensitive: ${f.sensitive}`
this.close()
return ""
}

if (f.type === RunEventType.RunStart) {
this.state = RunState.Running
} else if (f.type === RunEventType.RunFinish) {
Expand Down
22 changes: 21 additions & 1 deletion tests/gptscript.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ describe("gptscript module", () => {
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.",
tools: ["sys.prompt"]
}
const run = await client.evaluate(t as any)
const run = await client.evaluate(t as any, {prompt: true})
run.on(gptscript.RunEventType.Prompt, async (data: gptscript.PromptFrame) => {
expect(data.message).toContain("first name")
expect(data.fields.length).toEqual(1)
Expand All @@ -450,4 +450,24 @@ describe("gptscript module", () => {
expect(run.err).toEqual("")
expect(promptFound).toBeTruthy()
})

test("prompt without prompt allowed should fail", async () => {
let promptFound = false
const t = {
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.",
tools: ["sys.prompt"]
}
const run = await client.evaluate(t as any)
run.on(gptscript.RunEventType.Prompt, async (data: gptscript.PromptFrame) => {
promptFound = true
})

try {
await run.text()
} catch (e) {
expect(e).toContain("prompt occurred")
}
expect(run.err).toContain("prompt occurred")
expect(promptFound).toBeFalsy()
})
})

0 comments on commit 0c8c0f1

Please sign in to comment.