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
|
||||
|
||||
Call `startThread()` and `run()` to start a thead with Codex.
|
||||
Call `startThread()` and `run()` to start a thread with Codex.
|
||||
|
||||
```typescript
|
||||
import { Codex } from "@openai/codex-sdk";
|
||||
@@ -32,17 +32,21 @@ console.log(result);
|
||||
|
||||
### 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
|
||||
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
|
||||
|
||||
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
|
||||
const threadId = "...";
|
||||
@@ -54,7 +58,7 @@ console.log(result);
|
||||
|
||||
### 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
|
||||
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
|
||||
const thread = codex.startThread({
|
||||
|
||||
@@ -2,5 +2,4 @@ export type CodexOptions = {
|
||||
codexPathOverride?: string;
|
||||
baseUrl?: string;
|
||||
apiKey?: string;
|
||||
workingDirectory?: string;
|
||||
};
|
||||
|
||||
@@ -30,4 +30,4 @@ export { Codex } from "./codex";
|
||||
|
||||
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 { ThreadEvent, Usage } from "./events";
|
||||
import { ThreadEvent, ThreadError, Usage } from "./events";
|
||||
import { CodexExec } from "./exec";
|
||||
import { ThreadItem } from "./items";
|
||||
import { ThreadOptions } from "./threadOptions";
|
||||
@@ -87,6 +87,7 @@ export class Thread {
|
||||
const items: ThreadItem[] = [];
|
||||
let finalResponse: string = "";
|
||||
let usage: Usage | null = null;
|
||||
let turnFailure: ThreadError | null = null;
|
||||
for await (const event of generator) {
|
||||
if (event.type === "item.completed") {
|
||||
if (event.item.type === "agent_message") {
|
||||
@@ -95,8 +96,14 @@ export class Thread {
|
||||
items.push(event.item);
|
||||
} else if (event.type === "turn.completed") {
|
||||
usage = event.usage;
|
||||
} else if (event.type === "turn.failed") {
|
||||
turnFailure = event.error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (turnFailure) {
|
||||
throw new Error(turnFailure.message);
|
||||
}
|
||||
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(
|
||||
responseId: string = DEFAULT_RESPONSE_ID,
|
||||
usage: ResponseCompletedUsage = DEFAULT_COMPLETED_USAGE,
|
||||
): SseEvent {
|
||||
const inputDetails = usage.input_tokens_details
|
||||
? { ...usage.input_tokens_details }
|
||||
: null;
|
||||
const outputDetails = usage.output_tokens_details
|
||||
? { ...usage.output_tokens_details }
|
||||
: null;
|
||||
const inputDetails = usage.input_tokens_details ? { ...usage.input_tokens_details } : null;
|
||||
const outputDetails = usage.output_tokens_details ? { ...usage.output_tokens_details } : null;
|
||||
return {
|
||||
type: "response.completed",
|
||||
response: {
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
responseCompleted,
|
||||
responseStarted,
|
||||
sse,
|
||||
responseFailed,
|
||||
startResponsesTestProxy,
|
||||
} from "./responsesProxy";
|
||||
|
||||
@@ -287,6 +288,23 @@ describe("Codex", () => {
|
||||
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]) {
|
||||
if (!args) {
|
||||
|
||||
Reference in New Issue
Block a user