Misc SDK fixes (#4752)
Remove codex-level workingDirectory Throw on turn.failed in `run()` Cleanup readme
This commit is contained in:
@@ -10,7 +10,7 @@ npm install @openai/codex-sdk
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Call `startThread()` and `run()` to start a thead with Codex.
|
Call `startThread()` and `run()` to start a thread with Codex.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { Codex } from "@openai/codex-sdk";
|
import { Codex } from "@openai/codex-sdk";
|
||||||
@@ -32,17 +32,21 @@ console.log(result);
|
|||||||
|
|
||||||
### Streaming
|
### Streaming
|
||||||
|
|
||||||
The `await run()` method completes when a thread turn is complete and agent is prepared the final response.
|
The `run()` method completes when a thread turn is complete and the agent has produced the final response.
|
||||||
|
|
||||||
You can thread items while they are being produced by calling `await runStreamed()`.
|
You can stream events while they are being produced by calling `runStreamed()` and iterating the returned generator.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const result = thread.runStreamed("Diagnose the test failure and propose a fix");
|
const { events } = await thread.runStreamed("Diagnose the test failure and propose a fix");
|
||||||
|
|
||||||
|
for await (const event of events) {
|
||||||
|
console.log(event);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Resuming a thread
|
### Resuming a thread
|
||||||
|
|
||||||
If you don't have the original `Thread` instance to continue the thread, you can resume a thread by calling `resumeThread()` and providing the thread.
|
If you don't have the original `Thread` instance to continue the thread, you can resume by calling `resumeThread()` and providing the thread identifier.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const threadId = "...";
|
const threadId = "...";
|
||||||
@@ -54,7 +58,7 @@ console.log(result);
|
|||||||
|
|
||||||
### Working directory
|
### Working directory
|
||||||
|
|
||||||
By default, Codex will run in the current working directory. You can change the working directory by passing the `workingDirectory` option to the when creating a thread.
|
By default, Codex will run in the current working directory. You can change the working directory by passing the `workingDirectory` option when creating a thread.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const thread = codex.startThread({
|
const thread = codex.startThread({
|
||||||
@@ -62,7 +66,7 @@ const thread = codex.startThread({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
To avoid unrecoverable errors, Codex requires the working directory to be a git repository. You can skip the git repository check by passing the `skipGitRepoCheck` option to the when creating a thread.
|
To avoid unrecoverable errors, Codex requires the working directory to be a Git repository. You can skip the Git repository check by passing the `skipGitRepoCheck` option when creating a thread.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const thread = codex.startThread({
|
const thread = codex.startThread({
|
||||||
|
|||||||
@@ -2,5 +2,4 @@ export type CodexOptions = {
|
|||||||
codexPathOverride?: string;
|
codexPathOverride?: string;
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
apiKey?: string;
|
apiKey?: string;
|
||||||
workingDirectory?: string;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,4 +30,4 @@ export { Codex } from "./codex";
|
|||||||
|
|
||||||
export type { CodexOptions } from "./codexOptions";
|
export type { CodexOptions } from "./codexOptions";
|
||||||
|
|
||||||
export type { ThreadOptions as TheadOptions, ApprovalMode, SandboxMode } from "./threadOptions";
|
export type { ThreadOptions, ApprovalMode, SandboxMode } from "./threadOptions";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CodexOptions } from "./codexOptions";
|
import { CodexOptions } from "./codexOptions";
|
||||||
import { ThreadEvent, Usage } from "./events";
|
import { ThreadEvent, ThreadError, Usage } from "./events";
|
||||||
import { CodexExec } from "./exec";
|
import { CodexExec } from "./exec";
|
||||||
import { ThreadItem } from "./items";
|
import { ThreadItem } from "./items";
|
||||||
import { ThreadOptions } from "./threadOptions";
|
import { ThreadOptions } from "./threadOptions";
|
||||||
@@ -87,6 +87,7 @@ export class Thread {
|
|||||||
const items: ThreadItem[] = [];
|
const items: ThreadItem[] = [];
|
||||||
let finalResponse: string = "";
|
let finalResponse: string = "";
|
||||||
let usage: Usage | null = null;
|
let usage: Usage | null = null;
|
||||||
|
let turnFailure: ThreadError | null = null;
|
||||||
for await (const event of generator) {
|
for await (const event of generator) {
|
||||||
if (event.type === "item.completed") {
|
if (event.type === "item.completed") {
|
||||||
if (event.item.type === "agent_message") {
|
if (event.item.type === "agent_message") {
|
||||||
@@ -95,8 +96,14 @@ export class Thread {
|
|||||||
items.push(event.item);
|
items.push(event.item);
|
||||||
} else if (event.type === "turn.completed") {
|
} else if (event.type === "turn.completed") {
|
||||||
usage = event.usage;
|
usage = event.usage;
|
||||||
|
} else if (event.type === "turn.failed") {
|
||||||
|
turnFailure = event.error;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (turnFailure) {
|
||||||
|
throw new Error(turnFailure.message);
|
||||||
|
}
|
||||||
return { items, finalResponse, usage };
|
return { items, finalResponse, usage };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,16 +173,19 @@ export function assistantMessage(text: string, itemId: string = DEFAULT_MESSAGE_
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function responseFailed(errorMessage: string): SseEvent {
|
||||||
|
return {
|
||||||
|
type: "error",
|
||||||
|
error: { code: "rate_limit_exceeded", message: errorMessage },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function responseCompleted(
|
export function responseCompleted(
|
||||||
responseId: string = DEFAULT_RESPONSE_ID,
|
responseId: string = DEFAULT_RESPONSE_ID,
|
||||||
usage: ResponseCompletedUsage = DEFAULT_COMPLETED_USAGE,
|
usage: ResponseCompletedUsage = DEFAULT_COMPLETED_USAGE,
|
||||||
): SseEvent {
|
): SseEvent {
|
||||||
const inputDetails = usage.input_tokens_details
|
const inputDetails = usage.input_tokens_details ? { ...usage.input_tokens_details } : null;
|
||||||
? { ...usage.input_tokens_details }
|
const outputDetails = usage.output_tokens_details ? { ...usage.output_tokens_details } : null;
|
||||||
: null;
|
|
||||||
const outputDetails = usage.output_tokens_details
|
|
||||||
? { ...usage.output_tokens_details }
|
|
||||||
: null;
|
|
||||||
return {
|
return {
|
||||||
type: "response.completed",
|
type: "response.completed",
|
||||||
response: {
|
response: {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
responseCompleted,
|
responseCompleted,
|
||||||
responseStarted,
|
responseStarted,
|
||||||
sse,
|
sse,
|
||||||
|
responseFailed,
|
||||||
startResponsesTestProxy,
|
startResponsesTestProxy,
|
||||||
} from "./responsesProxy";
|
} from "./responsesProxy";
|
||||||
|
|
||||||
@@ -287,6 +288,23 @@ describe("Codex", () => {
|
|||||||
await close();
|
await close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
it("throws ThreadRunError on turn failures", async () => {
|
||||||
|
const { url, close } = await startResponsesTestProxy({
|
||||||
|
statusCode: 200,
|
||||||
|
responseBodies: [
|
||||||
|
sse(responseStarted("response_1")),
|
||||||
|
sse(responseFailed("rate limit exceeded")),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const client = new Codex({ codexPathOverride: codexExecPath, baseUrl: url, apiKey: "test" });
|
||||||
|
const thread = client.startThread();
|
||||||
|
await expect(thread.run("fail")).rejects.toThrow("stream disconnected before completion:");
|
||||||
|
} finally {
|
||||||
|
await close();
|
||||||
|
}
|
||||||
|
}, 10000); // TODO(pakrym): remove timeout
|
||||||
});
|
});
|
||||||
function expectPair(args: string[] | undefined, pair: [string, string]) {
|
function expectPair(args: string[] | undefined, pair: [string, string]) {
|
||||||
if (!args) {
|
if (!args) {
|
||||||
|
|||||||
Reference in New Issue
Block a user