Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
})
})