Skip to content
Open
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
2 changes: 1 addition & 1 deletion src/data/languages/languageData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default {
android: '1.2',
},
aiTransport: {
javascript: '0.6',
javascript: '0.7',
},
spaces: {
javascript: '0.5',
Expand Down
21 changes: 21 additions & 0 deletions src/data/nav/aitransport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export default {
name: 'Core SDK',
link: '/docs/ai-transport/getting-started/core-sdk',
},
{
name: 'OpenAI',
link: '/docs/ai-transport/getting-started/openai',
},
{
name: 'Vercel AI SDK',
link: '/docs/ai-transport/getting-started/vercel-ai-sdk',
Expand Down Expand Up @@ -86,6 +90,10 @@ export default {
{
name: 'Frameworks',
pages: [
{
name: 'OpenAI',
link: '/docs/ai-transport/frameworks/openai',
},
{
name: 'Vercel AI SDK UI',
link: '/docs/ai-transport/frameworks/vercel-ai-sdk-ui',
Expand Down Expand Up @@ -226,6 +234,19 @@ export default {
},
],
},
{
name: 'OpenAI',
pages: [
{
name: 'Codec',
link: '/docs/ai-transport/api/javascript/openai/codec',
},
{
name: 'Conversation helpers',
link: '/docs/ai-transport/api/javascript/openai/conversation-helpers',
},
],
},
{
name: 'Vercel',
pages: [
Expand Down
2 changes: 1 addition & 1 deletion src/pages/docs/ai-transport/api/errors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ agentSession.on('error', (error) => {
console.error('Session error:', error);
});

const run = agentSession.createRun(invocation, {
const run = agentSession.createRun(invocation, {}, {
signal: req.signal,
onError(error) {
console.error('Run error:', error);
Expand Down
73 changes: 37 additions & 36 deletions src/pages/docs/ai-transport/api/javascript/core/agent-session.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const session = createAgentSession({
});

await session.connect();
const run = session.createRun(invocation, { signal: req.signal });
const run = session.createRun(invocation, {}, { signal: req.signal });
await run.start();
```
</Code>
Expand Down Expand Up @@ -105,14 +105,16 @@ await session.connect();

## Create a run <a id="create-run"/>

<MethodSignature>{`createRun(invocation: Invocation, runtime?: RunRuntime<TOutput>): AgentRun<TOutput, TProjection, TMessage>`}</MethodSignature>
<MethodSignature>{`createRun(invocation: Invocation, identity?: Partial<RunIdentity>, hooks?: RunHooks<TOutput>): OpenableRun<TOutput, TProjection, TMessage>`}</MethodSignature>

Create a new `AgentRun` for the input event named in the `Invocation`. Returns synchronously and publishes nothing to the session until [`AgentRun.start`](#run-start) is called. The run is registered for cancel routing immediately so early cancels fire the `abortSignal`.
Create a new run for the input event named in the `Invocation`. Returns synchronously and publishes nothing to the session until [`start`](#run-start) is called. The run is registered for cancel routing immediately so early cancels fire the `abortSignal`.

Identity and behaviour are separate arguments. The second overrides the run's ids, and the third carries the abort signal and the hooks. On the normal one-request path there is no id to override, so the second argument is `{}`:

<Code>
```javascript
const invocation = Invocation.fromJSON(await req.json());
const run = session.createRun(invocation, { signal: req.signal });
const run = session.createRun(invocation, {}, { signal: req.signal });
```
</Code>

Expand All @@ -123,7 +125,8 @@ const run = session.createRun(invocation, { signal: req.signal });
| Parameter | Required | Description | Type |
| --- | --- | --- | --- |
| invocation | required | The `Invocation` carrying run identity and conversation context. | <Table id='Invocation'/> |
| runtime | optional | Per-run hooks and an external abort signal. | <Table id='RunRuntime'/> |
| identity | optional | Run ids to override. Each absent field is generated. Omit it on the one-request path. | <Table id='RunIdentity'/> |
| hooks | optional | Per-run hooks and an external abort signal. | <Table id='RunHooks'/> |

</Table>

Expand All @@ -136,14 +139,21 @@ const run = session.createRun(invocation, { signal: req.signal });

</Table>

<Table id='RunRuntime' hidden>
<Table id='RunIdentity' hidden>

| Property | Description | Type |
| --- | --- | --- |
| invocationId | Override the invocation id for this run. Defaults to a fresh `crypto.randomUUID()` (the normal path; one per HTTP request). Supply a non-empty value for deterministic ids in tests. The empty string is rejected. | String |
| runId | Override the run id for a fresh run. Defaults to a fresh `crypto.randomUUID()`. Continuations ignore this and read the existing `runId` off the triggering input event. Supply a non-empty value for deterministic ids in tests. The empty string is rejected. Under [durable execution](/docs/ai-transport/features/durable-execution), supply a stable value so a fresh-process retry re-enters the same run instead of opening a parallel one. | String |

</Table>

<Table id='RunHooks' hidden>

| Property | Description | Type |
| --- | --- | --- |
| invocationId | Override the invocation id for this run. Defaults to a fresh `crypto.randomUUID()` (the normal path; one per HTTP request). Supply a non-empty value for deterministic ids in tests. | String |
| runId | Override the run id for a fresh run. Defaults to a fresh `crypto.randomUUID()`. Continuations ignore this and read the existing `runId` off the triggering input event. Supply a non-empty value for deterministic ids in tests. | String |
| signal | External `AbortSignal` (typically the HTTP request's `req.signal`) that cancels the run when fired. | `AbortSignal` |
| onMessage | Called before each Ably message is published. Mutate the message in place to add custom headers under `extras.ai`. | `(message: Ably.Message) => void` |
| onAblyMessage | Called before each Ably message is published. Mutate the message in place to add custom headers under `extras.ai`. | `(message: Ably.Message) => void` |
| onCancelled | Called when the run is cancelled. Receives a `write` function to publish final outputs before cancellation finalises. | `(write: (output: TOutput) => Promise<void>) => void \| Promise<void>` |
| onCancel | Called when a cancel arrives. Return `true` to accept, `false` to reject. Defaults to accepting all. | `(req: CancelRequest) => Promise<boolean>` |
| onError | Called with non-fatal run-scoped errors. | `(error: Ably.ErrorInfo) => void` |
Expand All @@ -153,18 +163,19 @@ const run = session.createRun(invocation, { signal: req.signal });

### Returns <a id="create-run-returns"/>

An [`AgentRun<TOutput, TProjection, TMessage>`](#run) handle for publishing lifecycle events, user messages, and streamed output.
An `OpenableRun<TOutput, TProjection, TMessage>` handle for publishing lifecycle events, user messages, and streamed output. It extends [`AgentRun`](#run) with the [`start`](#run-start) method the caller must call before any other.

## Adopt an existing run <a id="adopt-run"/>

<MethodSignature>{`adoptRun(identity: AdoptIdentity, runtime?: RunRuntime<TOutput>): AdoptedRun<TOutput, TProjection, TMessage>`}</MethodSignature>
<MethodSignature>{`adoptRun(invocation: Invocation, identity: RunIdentity, hooks?: RunHooks<TOutput>): AdoptedRun<TOutput, TProjection, TMessage>`}</MethodSignature>

Adopt an already-open run by its identity so a fresh process can publish further steps and lifecycle events for it. Returns synchronously and does no I/O; publishes nothing to the session until [`AdoptedRun.load`](#adopted-load) resolves. Use it from a step, tool, or cleanup activity that [runs in a separate process](/docs/ai-transport/features/durable-execution) from the one that opened the run.

<Code>
```javascript
const run = session.adoptRun(
{ runId, invocationId, triggerEventId },
Invocation.fromJSON(invocationData),
{ runId, invocationId },
{ signal: Context.current().cancellationSignal },
);
await run.load();
Expand All @@ -177,18 +188,18 @@ await run.load();

| Parameter | Required | Description | Type |
| --- | --- | --- | --- |
| identity | required | The run's identity, threaded across the process boundary by the workflow that opened it. | <Table id='AdoptIdentity'/> |
| runtime | optional | Per-run hooks and an external abort signal. `runId` and `invocationId` overrides do not apply here; identity comes from the `identity` argument. | <Table id='RunRuntime'/> |
| invocation | required | The `Invocation` pointing at the event whose headers resolve the run's write-time anchors, typically an `ai-input` for a normal turn. Every activity of an invocation resolves against the same trigger. | <Table id='Invocation'/> |
| identity | required | The run's identity, threaded across the process boundary by the workflow that opened it. | <Table id='AdoptRunIdentity'/> |
| hooks | optional | Per-run hooks and an external abort signal. | <Table id='RunHooks'/> |

</Table>

<Table id='AdoptIdentity' hidden>
<Table id='AdoptRunIdentity' hidden>

| Property | Description | Type |
| --- | --- | --- |
| runId | The existing run's id. Authoritative: unlike `createRun`'s continuation path, `AdoptedRun.load` does not re-key the run from the trigger event's `run-id` header. | String |
| invocationId | This activity's invocation id (a step activity's id, or a cancel-cleanup id). Stamped on every event this process publishes for the run. Independent of the run's owner identity. | String |
| triggerEventId | The id of the event whose headers resolve the run's write-time anchors, typically an `ai-input` for a normal turn. Every activity of an invocation resolves against the same trigger. | String |

</Table>

Expand Down Expand Up @@ -267,28 +278,19 @@ await run.end({ reason: 'complete' });

### Pipe the response stream <a id="pipe"/>

<MethodSignature>{`pipe(stream: ReadableStream<TOutput>, options?: PipeOptions<TOutput>): Promise<StreamResult>`}</MethodSignature>
<MethodSignature>{`pipe(source: PipeSource<TOutput>): Promise<StreamResult>`}</MethodSignature>

Pipe a `ReadableStream` of outputs through the encoder to the session. Returns when the stream completes, is cancelled, or errors. Does NOT call `end()`; the caller must call `end()` after `pipe()` returns.
Pipe a source of outputs through the encoder to the session. Returns when the source completes, is cancelled, or errors. Does NOT call `end()`; the caller must call `end()` after `pipe()` returns.

The source is a `ReadableStream` or any `AsyncIterable` of codec outputs, so a provider SDK stream that is async-iterable pipes in directly with no `ReadableStream` wrapper.

#### Parameters <a id="pipe-params"/>

<Table id='PipeParameters'>

| Parameter | Required | Description | Type |
| --- | --- | --- | --- |
| stream | required | The output stream from your LLM call. | `ReadableStream<TOutput>` |
| options | optional | Branching and per-output hooks. | <Table id='PipeOptions'/> |

</Table>

<Table id='PipeOptions' hidden>

| Property | Description | Type |
| --- | --- | --- |
| parent | The codec-message-id of the immediately preceding message in this branch. | String |
| forkOf | The codec-message-id of the message this response replaces (for regeneration). | String |
| resolveWriteOptions | Per-output hook returning `WriteOptions` overrides for a single encode call. | Function |
| source | required | The output source from your LLM call. | `ReadableStream<TOutput> \| AsyncIterable<TOutput>` |

</Table>

Expand Down Expand Up @@ -400,18 +402,17 @@ Publish `ai-step-start`, opening the step for output. Call once, after the run i

### Pipe outputs <a id="step-pipe"/>

<MethodSignature>{`pipe(stream: ReadableStream<TOutput>, options?: PipeOptions<TOutput>): Promise<StreamResult>`}</MethodSignature>
<MethodSignature>{`pipe(source: PipeSource<TOutput>): Promise<StreamResult>`}</MethodSignature>

Pipe an output stream through the encoder to the session, stamping every output with this step's `step-id` and its attempt's `start-serial`. Otherwise identical to [`AgentRun.pipe`](#pipe): resolves when the stream completes, is cancelled, or errors. A stream error returns `{ reason: 'error' }` rather than throwing, and marks the step `'failed'` when [`end`](#step-end) closes it.
Pipe an output source through the encoder to the session, stamping every output with this step's `step-id` and its attempt's `step-start-serial`. Otherwise identical to [`AgentRun.pipe`](#pipe): resolves when the source completes, is cancelled, or errors. A stream error returns `{ reason: 'error' }` rather than throwing, and marks the step `'failed'` when [`end`](#step-end) closes it.

#### Parameters <a id="step-pipe-params"/>

<Table id='StepPipeParameters'>

| Parameter | Required | Description | Type |
| --- | --- | --- | --- |
| stream | required | The output stream from your LLM call. | `ReadableStream<TOutput>` |
| options | optional | Per-stream overrides. A per-output `resolveWriteOptions` merges over the step's default headers, so a normal override leaves `step-id` intact. | <Table id='PipeOptions'/> |
| source | required | The output source from your LLM call. | `ReadableStream<TOutput> \| AsyncIterable<TOutput>` |

</Table>

Expand All @@ -423,7 +424,7 @@ Pipe an output stream through the encoder to the session, stamping every output

<MethodSignature>{`send(output: TOutput): Promise<void>`}</MethodSignature>

Publish a single discrete output as one assistant message on the session, stamped with this step's `step-id` and its attempt's `start-serial`. Use it when the output is already resolved (a tool result, a data payload, a metadata event) rather than a streamed source. Each `send` mints its own `codec-message-id`, so N calls produce N assistant messages rather than one. For streamed output from a long-running source, use [`pipe`](#step-pipe) instead.
Publish a single discrete output as one assistant message on the session, stamped with this step's `step-id` and its attempt's `step-start-serial`. Use it when the output is already resolved (a tool result, a data payload, a metadata event) rather than a streamed source. Each `send` generates its own `codec-message-id`, so N calls produce N assistant messages rather than one. For streamed output from a long-running source, use [`pipe`](#step-pipe) instead.

The step must be active (started and not yet ended). Rejects otherwise. A publish failure throws.

Expand Down Expand Up @@ -624,7 +625,7 @@ export async function POST(req: Request) {

await session.connect();

const run = session.createRun(invocation, { signal: req.signal });
const run = session.createRun(invocation, {}, { signal: req.signal });

try {
// Rebuild the conversation from run.view before run.start(): draining pages
Expand Down
6 changes: 3 additions & 3 deletions src/pages/docs/ai-transport/api/javascript/core/codec.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ redirect_from:

The `Codec` interface is the bridge between an AI framework and Ably channel messages. It describes the wire as a flat stream of input and output events and folds those events into a per-run projection that the SDK extracts messages from for the conversation tree.

Implement `Codec<TInput, TOutput, TProjection, TMessage>` to integrate any AI framework with AI Transport. The SDK ships the Vercel codec via [`createUIMessageCodec()`](/docs/ai-transport/api/javascript/vercel/codec) for the Vercel AI SDK. For other frameworks, implement the methods below.
Implement `Codec<TInput, TOutput, TProjection, TMessage>` to integrate any AI framework with AI Transport. The SDK ships two codecs: [`createUIMessageCodec()`](/docs/ai-transport/api/javascript/vercel/codec) for the Vercel AI SDK, and [`ResponsesCodec`](/docs/ai-transport/api/javascript/openai/codec) from `@ably/ai-transport/openai` for the OpenAI Responses API. For other frameworks, implement the methods below.

<Code>
```javascript
Expand Down Expand Up @@ -92,7 +92,7 @@ Build a stateful encoder bound to a channel. The encoder owns the message-append
| Parameter | Required | Description | Type |
| --- | --- | --- | --- |
| channel | required | The channel writer to publish through. An `Ably.RealtimeChannel` satisfies this directly. | `ChannelWriter` |
| options | optional | Per-encoder defaults (extras, messageId, onMessage hook). | <Table id='EncoderOptions'/> |
| options | optional | Per-encoder defaults (extras, messageId, onAblyMessage hook). | <Table id='EncoderOptions'/> |

</Table>

Expand All @@ -101,7 +101,7 @@ Build a stateful encoder bound to a channel. The encoder owns the message-append
| Property | Description | Type |
| --- | --- | --- |
| extras | Default extras merged into every Ably message. | <Table id='Extras'/> |
| onMessage | Hook called before each Ably message is published. Mutate the message in place to add transport-level headers under `extras.ai`. | `(message: Ably.Message) => void` |
| onAblyMessage | Hook called before each Ably message is published. Mutate the message in place to add transport-level headers under `extras.ai`. | `(message: Ably.Message) => void` |
| messageId | Default `codec-message-id` for messages where the event payload doesn't supply one. | String |

</Table>
Expand Down
Loading