Page actions

Fake Agent

FakeAgent is an in-process AG-UI AbstractAgent for frontend work when the backend is not ready yet.

It emits a canned stream:

  1. RUN_STARTED
  2. optional REASONING_MESSAGE_*
  3. TEXT_MESSAGE_START
  4. one TEXT_MESSAGE_CONTENT event per token
  5. TEXT_MESSAGE_END
  6. RUN_FINISHED

It is for demos, story-like development, and tests. It is not a production transport.

Use the provider

For Angular apps, use provideFakeAgent().

import { ApplicationConfig } from '@angular/core';
import { provideFakeAgent } from '@threadplane/ag-ui';
 
export const appConfig: ApplicationConfig = {
  providers: [
    provideFakeAgent({
      tokens: ['Hello', ' from', ' a', ' fake', ' agent.'],
      delayMs: 50,
    }),
  ],
};

Your component stays the same as the real backend version:

import { Component } from '@angular/core';
import { ChatComponent } from '@threadplane/chat';
import { injectAgent } from '@threadplane/ag-ui';
 
@Component({
  standalone: true,
  imports: [ChatComponent],
  template: `<chat [agent]="agent" />`,
})
export class DemoChat {
  protected readonly agent = injectAgent();
}

Switching to a real backend is a provider change:

- provideFakeAgent({ tokens: ['Offline', ' response.'] })
+ provideAgent({ url: '/api/agent' })

Add reasoning

Pass reasoningTokens when you need to exercise reasoning UI.

provideFakeAgent({
  reasoningTokens: ['Reading policy. ', 'Checking account status.'],
  tokens: ['The account is eligible.'],
  delayMs: 40,
});

Reasoning events are emitted before text events and use the same message id, so the reducer stores reasoning and final content on one assistant message.

Use the class directly

Use FakeAgent directly when a test needs an AG-UI source rather than Angular DI.

import { FakeAgent, toAgent } from '@threadplane/ag-ui';
 
const source = new FakeAgent({
  tokens: ['One', ' two', ' three.'],
  delayMs: 1,
});
 
const agent = toAgent(source);

That gives you the same Agent contract as provideFakeAgent().

Configuration

OptionTypeDefaultNotes
tokensstring[]A short canned greetingEmitted as text deltas in order.
reasoningTokensstring[][]Emitted before text deltas.
delayMsnumber60Delay between events after the initial start delay.
scriptFakeAgentScript[]Raw AG-UI event branches that replace the canned reply, each wrapped in RUN_STARTED / RUN_FINISHED. Accepted by the constructor and by provideFakeAgent().

Scripting exact events

Set script when the canned text reply is not enough — tool calls, shared state, custom events, and interrupts all reach the UI through it, with no backend and no direct construction:

import { EventType, type BaseEvent } from '@ag-ui/client';
import { provideFakeAgent } from '@threadplane/ag-ui';
 
providers: [
  provideFakeAgent({
    delayMs: 0,
    script: [
      {
        when: 'initial',
        events: [
          {
            type: EventType.CUSTOM,
            name: 'on_interrupt',
            value: { kind: 'approval', amount: 42 },
          } as BaseEvent,
        ],
      },
    ],
  }),
]

when: 'initial' matches a turn whose history carries no tool result. { toolMessageFor: 'tool-1' } matches the follow-up turn whose history carries a tool result for tool-1, which is what resolving a client tool produces — so a two-branch script plays a tool call and then the reply that follows it. The first matching branch wins; when none matches, the canned token reply streams instead.

What it does not do

provideFakeAgent() does not call a model, execute tools, or persist history. It streams what you give it: a canned token reply by default, or the exact events in script.

It is deliberately small. Use it to keep UI work moving, not to validate backend behavior.

For backend integration, test against your real AG-UI endpoint and the event map in Event Mapping.

Looking for something specific?