API reference

createAdkStream, server helpers, session adapter, threads, message editing.

Reference for the runtime's wire-up surface. Start with quickstart if you have not already.

createAdkStream

Creates an AdkStreamCallback that connects to an ADK endpoint via SSE. Supports two modes.

Proxy mode posts to your own API route:

import { createAdkStream } from "@assistant-ui/react-google-adk";

const stream = createAdkStream({ api: "/api/chat" });

Direct mode connects directly to an ADK server:

const stream = createAdkStream({
  api: "http://localhost:8000",
  appName: "my-app",
  userId: "user-1",
});
OptionTypeDescription
apistringURL to POST to (proxy route or ADK server base URL).
appNamestring?ADK app name (enables direct mode when set).
userIdstring?ADK user ID (required with appName).
headersRecord<string, string> or () => ...Static or dynamic request headers.

Direct ADK server connection

When connecting directly to an ADK server (without a proxy API route), use createAdkSessionAdapter to back your thread list with ADK sessions:

import {
  useAdkRuntime,
  createAdkStream,
  createAdkSessionAdapter,
} from "@assistant-ui/react-google-adk";

const ADK_URL = "http://localhost:8000";

const { adapter, load, artifacts } = createAdkSessionAdapter({
  apiUrl: ADK_URL,
  appName: "my-app",
  userId: "user-1",
});

const runtime = useAdkRuntime({
  stream: createAdkStream({
    api: ADK_URL,
    appName: "my-app",
    userId: "user-1",
  }),
  sessionAdapter: adapter,
  load,
});

The session adapter maps ADK sessions to assistant-ui threads:

  • adapter is a RemoteThreadListAdapter that uses ADK's session REST API for thread CRUD.
  • load reconstructs messages from session events via AdkEventAccumulator.
  • artifacts are functions to fetch, list, and delete session artifacts (see hooks → artifacts).
OptionTypeDescription
apiUrlstringADK server base URL.
appNamestringADK app name.
userIdstringADK user ID.
headersRecord<string, string> or () => ...Static or dynamic request headers.

Server helpers

Server helpers live under the /server subpath:

import {
  createAdkApiRoute,
  adkEventStream,
  parseAdkRequest,
  toAdkContent,
} from "@assistant-ui/react-google-adk/server";

createAdkApiRoute

One-liner API route handler that combines request parsing and SSE streaming:

import { requireUser } from "./auth"; // Your authentication helper

export const POST = createAdkApiRoute({
  runner,
  userId: async (req) => (await requireUser(req)).id,
  sessionId: (_req, clientSessionId) => {
    if (!clientSessionId) throw new Error("Missing ADK session ID");
    return clientSessionId;
  },
});

userId accepts a static string or a request resolver. sessionId accepts a static string or (req, clientSessionId) => string | Promise<string>; proxy mode supplies the initialized assistant-ui thread ID as clientSessionId. For ADK runners that expose appName and sessionService, the route creates a missing resolved session before the first run and coalesces concurrent creation within the server process. Authenticate the request when resolving userId, because a client-provided session ID is an identifier rather than authorization. Static IDs are intended only for local, single-user development.

adkEventStream

Converts an AsyncGenerator<Event> from ADK's Runner.runAsync() into an SSE Response. Sends an initial :ok comment to keep connections alive through proxies.

const events = runner.runAsync({ userId, sessionId, newMessage });
return adkEventStream(events);

parseAdkRequest and toAdkContent

Lower-level helpers for custom API routes. parseAdkRequest parses incoming requests; toAdkContent converts to ADK's Content format. Supports user messages, tool results, messageId, stateDelta, checkpointId, and multimodal content:

const parsed = await parseAdkRequest(req);
// parsed.type is "message" or "tool-result"
// parsed.config contains runConfig, checkpointId
// parsed.stateDelta contains session state changes

const newMessage = toAdkContent(parsed);
const stateDelta = validateSessionState(parsed.stateDelta);
const events = runner.runAsync({
  userId,
  sessionId,
  newMessage,
  stateDelta,
  customMetadata: parsed.customMetadata,
});
return adkEventStream(events);

messageId is the ID of the first human message a run sends, which is the ID the merged user message shows. parseAdkRequest turns it into parsed.customMetadata; forward that to runner.runAsync as shown above so the stored user event keeps the message's ID across reloads. createAdkApiRoute forwards it for you.

runConfig and stateDelta arrive from the browser and must be treated as untrusted input. Custom routes using parseAdkRequest must validate parsed.stateDelta before passing it to runAsync. createAdkApiRoute ignores both values by default; opt in only after validating them on the server:

export const POST = createAdkApiRoute({
  runner,
  userId,
  sessionId,
  resolveRunConfig: () => ({ maxLlmCalls: 10 }),
  resolveStateDelta: (_req, delta) => validateSessionState(delta),
});

Do not accept client-provided app: or user: keys unless the caller is authorized to update application-wide or user-wide ADK state.

Thread management

Three options, picked by what you want to own. See threads for the general model.

ADK session adapter

Use createAdkSessionAdapter to persist threads via ADK's session API; see Direct ADK server connection above.

Custom thread management

const runtime = useAdkRuntime({
  stream: createAdkStream({ api: "/api/chat" }),
  create: async () => {
    const sessionId = await createSession();
    return { externalId: sessionId };
  },
  load: async (externalId) => {
    const history = await loadSession(externalId);
    return { messages: history };
  },
  delete: async (externalId) => {
    await deleteSession(externalId);
  },
});

Cloud persistence

Pass a cloud instance to use Assistant Cloud for the thread list and a dashboard copy of settled messages. Thread history loads through the load function from the ADK session, not from Assistant Cloud:

const runtime = useAdkRuntime({
  cloud, // see "AssistantCloud" in /docs/runtimes/concepts/threads
  stream: createAdkStream({ api: "/api/chat" }),
});

Message editing and regeneration

Provide a getCheckpointId callback to enable edit and regenerate buttons:

const runtime = useAdkRuntime({
  stream: createAdkStream({ api: "/api/chat" }),
  getCheckpointId: async (threadId, parentMessages) => {
    // Resolve checkpoint ID for server-side forking
    return checkpointId;
  },
});

When getCheckpointId is provided:

  • Edit buttons appear on user messages.
  • Regenerate buttons appear on assistant messages.

The resolved checkpointId is passed to your stream callback via config.checkpointId.

Info

Without getCheckpointId, edit and regenerate buttons will not appear. This is intentional; truncating client-side messages without forking from the correct server-side checkpoint would produce incorrect state.

Warning

createAdkApiRoute and the direct /run_sse mode do not rewind the ADK session to the checkpoint yet. After an edit or regenerate, the session still holds the turns the client removed, and the agent sees them on the next run. adk-js has no session rewind API, and adk api_server does not expose the rewind that adk-python's Runner.rewind_async provides. A custom Python route can call rewind_async before it runs the new message, if your getCheckpointId returns the ID of the invocation to rewind before.

Event handlers

Listen to streaming events:

const runtime = useAdkRuntime({
  stream: createAdkStream({ api: "/api/chat" }),
  eventHandlers: {
    onError: (error) => {
      console.error("Stream error:", error);
    },
    onAgentTransfer: (toAgent) => {
      console.log("Agent transferred to:", toAgent);
    },
    onCustomEvent: (key, value) => {
      // Fired for each entry in event.customMetadata
      console.log("Custom metadata:", key, value);
    },
  },
});

RunConfig and stateDelta

Pass AdkRunConfig to control agent behavior, and stateDelta to mutate session state along with messages:

import { useAdkSend } from "@assistant-ui/react-google-adk";

const send = useAdkSend();

send(messages, {
  runConfig: {
    streamingMode: "sse",
    maxLlmCalls: 10,
    pauseOnToolCalls: true,
  },
  stateDelta: { taskId: "abc", mode: "verbose" },
});

In direct mode, stateDelta maps to ADK's stateDelta parameter on /run_sse. With createAdkApiRoute, client runConfig and stateDelta values are ignored unless the corresponding server resolver validates or replaces them.

Next