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
123 changes: 123 additions & 0 deletions apps/website/content/docs/ag-ui/api/fake-agent.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# FakeAgent

`FakeAgent` is an in-process AG-UI test double that emits a canned streaming response without a real backend. Use it for offline development, CI, and component tests.

## provideFakeAgent()

`provideFakeAgent()` is the DI-friendly entry point. It is a drop-in replacement for `provideAgent({ url })` when no backend is available.

```ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideFakeAgent } from '@threadplane/ag-ui';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, {
providers: [
provideFakeAgent(),
],
});
```

Pass a `FakeAgentConfig` to customize the canned response:

```ts
provideFakeAgent({
tokens: ['Hello', ' world', '!'],
delayMs: 40,
})
```

### FakeAgentConfig

| Option | Type | Description |
|--------|------|-------------|
| `tokens` | `string[]` | Assistant reply streamed token-by-token. Defaults to a fixed placeholder message. |
| `reasoningTokens` | `string[]` | Optional reasoning chunks emitted before the text reply. |
| `delayMs` | `number` | Milliseconds between successive token emissions. Defaults to `60`. |

## FakeAgent class

Construct a `FakeAgent` directly when you need lower-level control, for example to pass it to `toAgent()` in a test harness.

```ts
import { FakeAgent } from '@threadplane/ag-ui';
import { toAgent } from '@threadplane/ag-ui';

const agent = toAgent(new FakeAgent({
tokens: ['Thinking', '...', ' done.'],
reasoningTokens: ['Step 1', ': evaluate'],
delayMs: 30,
}));
```

`FakeAgent` extends `AbstractAgent` from `@ag-ui/client`. Its `run()` method returns an `Observable<BaseEvent>` that emits the full event sequence — `RUN_STARTED`, optional reasoning events, `TEXT_MESSAGE_START` / `TEXT_MESSAGE_CONTENT` tokens, `TEXT_MESSAGE_END`, `RUN_FINISHED` — then completes.

## TestBed example

```ts
import { TestBed } from '@angular/core/testing';
import { provideFakeAgent, injectAgent } from '@threadplane/ag-ui';
import { Component } from '@angular/core';

@Component({ template: '' })
class ChatComponent {
readonly chat = injectAgent();
}

describe('ChatComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ChatComponent],
providers: [
provideFakeAgent({ tokens: ['Hello', ' from', ' fake'] }),
],
});
});

it('streams a canned reply', async () => {
const fixture = TestBed.createComponent(ChatComponent);
fixture.detectChanges();

await fixture.componentInstance.chat.submit({ message: 'Hi' });
fixture.detectChanges();

expect(fixture.componentInstance.chat.messages().at(-1)?.content)
.toBe('Hello from fake');
});
});
```

<Callout type="info" title="Not for production">
`FakeAgent` and `provideFakeAgent()` are intended for development and tests
only. Do not use them in production builds.
</Callout>

See also: [Fake Agent guide](/docs/ag-ui/guides/fake-agent) for practical offline-development patterns, and [Testing guide](/docs/ag-ui/guides/testing) for full component-testing recipes.

## What's Next

<CardGroup cols={3}>
<Card
title="Fake Agent guide"
icon="flask"
href="/docs/ag-ui/guides/fake-agent"
>
Practical patterns for offline demos and rapid prototyping with FakeAgent.
</Card>
<Card
title="Testing guide"
icon="check"
href="/docs/ag-ui/guides/testing"
>
Full testing patterns for components that use `injectAgent()`.
</Card>
<Card
title="provideAgent() API"
icon="settings"
href="/docs/ag-ui/api/provide-agent"
>
The production provider `provideFakeAgent()` replaces in tests.
</Card>
</CardGroup>

{/* Auto-rendered from api-docs.json — see page component */}
121 changes: 121 additions & 0 deletions apps/website/content/docs/ag-ui/api/inject-agent.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# injectAgent()

`injectAgent()` retrieves the AG-UI agent from Angular's dependency injection container. Call it in an Angular injection context — typically as a component field initializer. The returned object exposes Angular Signals for reactive UI state and async methods for user actions.

Configuration is supplied globally via [provideAgent()](/docs/ag-ui/api/provide-agent) — `injectAgent()` itself takes no arguments.

```ts
import { injectAgent } from '@threadplane/ag-ui';

readonly chat = injectAgent();

await this.chat.submit({ message: 'Hello' });
```

Pair it with `provideAgent()` at bootstrap to configure the agent endpoint:

```ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideAgent } from '@threadplane/ag-ui';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, {
providers: [
provideAgent({ url: 'http://localhost:8000/my-agent' }),
],
});
```

## Runtime-neutral surface

These fields are stable across runtime adapters and are what chat components consume.

| Field | Type | Description |
|-------|------|-------------|
| `messages()` | `Message[]` | Chat messages with `role`, `content`, optional `toolCallIds`, citations, and reasoning. |
| `status()` | `'idle' \| 'running' \| 'error'` | UI lifecycle status. |
| `isLoading()` | `boolean` | Convenience signal for active streaming. |
| `error()` | `unknown` | Latest runtime error, when present. |
| `toolCalls()` | `ToolCall[]` | Tool calls projected into the chat contract. |
| `state()` | `Record<string, unknown>` | Latest agent state projected as a plain object. |
| `interrupt()` | `AgentInterrupt \| undefined` | Current interrupt, when the backend pauses for human input. |
| `submit(input, opts?)` | `Promise<void>` | Submit a user message or resume payload. |
| `stop()` | `Promise<void>` | Abort the active run. |
| `regenerate(index)` | `Promise<void>` | Remove the assistant message at `index` and rerun from the preceding user message. |

## AG-UI-specific surface

The AG-UI adapter extends the neutral `Agent` contract with one additional signal:

| Field | Type | Description |
|-------|------|-------------|
| `customEvents()` | `CustomStreamEvent[]` | Custom events emitted by the backend during a run. Accumulates per run; resets on each new `submit()`. |

`injectAgent()` is typed as the neutral `Agent` interface; to access `customEvents` you must cast to `AgUiAgent`:

```ts
import { injectAgent, type AgUiAgent } from '@threadplane/ag-ui';

const chat = injectAgent() as AgUiAgent;
chat.customEvents(); // Signal<CustomStreamEvent[]>
```

The chat a2ui bridge reads `customEvents` to light up live generative-UI streaming when your backend emits `a2ui-partial` events. See the [Custom Events guide](/docs/ag-ui/guides/custom-events) for backend wiring details.

## Submit and resume

Use the runtime-neutral submit shape for normal chat input:

```ts
await chat.submit({ message: 'Summarize this document' });
```

Resume an interrupt by passing a `resume` payload:

```ts
await chat.submit({ resume: { approved: true } });
```

## Regenerate semantics

`regenerate(assistantMessageIndex)` has replace semantics: it keeps the user message before the selected assistant message, removes the selected assistant message and all later messages, syncs the rollback to the AG-UI source, then reruns with no new user message appended.

```ts
await chat.regenerate(3);
```

The method throws when the selected index is not an assistant message, when no preceding user message exists, or while another response is already loading.

<Callout type="warning" title="Injection context required">
`injectAgent()` must be called during construction, inside an injection context
(e.g. a component constructor, field initializer, or a function passed to
`runInInjectionContext`). Calling it outside an injection context will throw.
</Callout>

## What's Next

<CardGroup cols={3}>
<Card
title="provideAgent() API"
icon="settings"
href="/docs/ag-ui/api/provide-agent"
>
Configure the endpoint URL, headers, and telemetry for the agent provider.
</Card>
<Card
title="Custom Events guide"
icon="wave-sine"
href="/docs/ag-ui/guides/custom-events"
>
Wire `customEvents` to live generative-UI streaming from any AG-UI backend.
</Card>
<Card
title="Testing guide"
icon="flask"
href="/docs/ag-ui/guides/testing"
>
Test components that call `injectAgent()` with the in-process FakeAgent.
</Card>
</CardGroup>

{/* Auto-rendered from api-docs.json — see page component */}
83 changes: 83 additions & 0 deletions apps/website/content/docs/ag-ui/api/provide-agent.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# provideAgent()

`provideAgent()` registers the singleton AG-UI agent configuration for every [injectAgent()](/docs/ag-ui/api/inject-agent) call in an Angular application. Call it once in `bootstrapApplication` or an `ApplicationConfig` to wire up the endpoint URL, optional identifiers, custom headers, and telemetry.

`injectAgent()` itself takes no arguments — all configuration flows through `provideAgent()`.

```ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideAgent } from '@threadplane/ag-ui';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, {
providers: [
provideAgent({
url: 'http://localhost:8000/my-agent',
}),
],
});
```

## Configuration options

| Option | Type | Description |
|--------|------|-------------|
| `url` | `string` | HTTP endpoint for the AG-UI backend agent. Required. |
| `agentId` | `string` | Optional agent identifier forwarded to the backend. |
| `threadId` | `string` | Optional thread identifier for session continuity. |
| `headers` | `Record<string, string>` | Optional custom HTTP headers included on every request. |
| `telemetry` | `AgentRuntimeTelemetrySink \| false` | Optional app-owned telemetry sink. No telemetry is emitted unless this is provided. |

## Static vs factory config

Pass a plain `AgentConfig` object when the URL is known up front. Pass a `() => AgentConfig` factory when the config depends on runtime DI state — the factory runs inside an Angular injection context, so it may call `inject()` to read services or environment tokens.

```ts
// Factory form — reads an environment token at runtime
provideAgent(() => {
const env = inject(APP_ENV);
return {
url: env.agentUrl,
headers: { Authorization: `Bearer ${env.apiKey}` },
};
});
```

## Singleton model

A single `provideAgent({...})` call configures the entire application. Every `injectAgent()` call resolves to the same configured agent.

```ts
provideAgent({ url: 'https://api.example.com/agent' });

// Elsewhere, inside an injection context:
const chat = injectAgent();
```

## What's Next

<CardGroup cols={3}>
<Card
title="injectAgent() API"
icon="code"
href="/docs/ag-ui/api/inject-agent"
>
The primitive you call inside components after registering a provider.
</Card>
<Card
title="Quick Start"
icon="rocket"
href="/docs/ag-ui/getting-started/quickstart"
>
End-to-end setup connecting a real AG-UI backend in minutes.
</Card>
<Card
title="Fake Agent guide"
icon="flask"
href="/docs/ag-ui/guides/fake-agent"
>
Swap the live backend for an in-process test double during development.
</Card>
</CardGroup>

{/* Auto-rendered from api-docs.json — see page component */}
Loading
Loading