DeepCitation + AG-UI

Integrate citation verification into AG-UI protocol agents. AG-UI’s event-driven SSE streaming pairs naturally with DeepCitation’s deferred citation pattern — verification results are sent as STATE_DELTA events after the LLM finishes streaming.

A complete, runnable example is available at agui-chat (live demo).


Install

npm install deepcitation @ag-ui/core

Architecture

Client ──SSE──> /api/agent ──> LLM (streaming tokens via TEXT_MESSAGE_CONTENT)
                     │
                     ├── TEXT_MESSAGE_END (LLM done)
                     ├── DeepCitation.verify() (runs after LLM completes)
                     └── STATE_DELTA (verification results pushed to client)

The key insight: AG-UI’s STATE_DELTA events let you push verification results to the client after the LLM finishes streaming, without a separate API call.


Key Integration Pattern

import { EventType } from "@ag-ui/core";
import { DeepCitation, extractVisibleText } from "deepcitation/client";
import { wrapCitationPrompt } from "deepcitation/prompts";

const dc = new DeepCitation({ apiKey: process.env.DEEPCITATION_API_KEY });

// In your AG-UI agent route handler:
export async function POST(req: Request) {
  const encoder = new TextEncoder();
  const stream = new ReadableStream({
    async start(controller) {
      function send(event: Record<string, unknown>) {
        controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`));
      }

      // 1. Prepare attachment (cached across requests)
      const { fileDataParts, deepTextPages } = await dc.prepareAttachments([
        { file: pdfBuffer, filename: "report.pdf" },
      ]);

      // 2. Wrap prompts
      const { enhancedSystemPrompt, enhancedUserPrompt } = wrapCitationPrompt({
        systemPrompt, userPrompt, deepTextPages,
      });

      // 3. Stream LLM response via AG-UI events
      send({ type: EventType.RUN_STARTED, threadId, runId });
      send({ type: EventType.TEXT_MESSAGE_START, messageId, role: "assistant" });

      let fullResponse = "";
      // ... stream LLM tokens, sending TEXT_MESSAGE_CONTENT events ...
      // ... collect fullResponse ...

      send({ type: EventType.TEXT_MESSAGE_END, messageId });

      // 4. Verify citations and push results as STATE_DELTA
      const { verifications } = await dc.verify({ llmOutput: fullResponse });
      const visibleText = extractVisibleText(fullResponse);

      send({
        type: EventType.STATE_DELTA,
        delta: [
          { op: "replace", path: "/verifications", value: verifications },
          { op: "replace", path: "/visibleText", value: visibleText },
        ],
      });

      send({ type: EventType.RUN_FINISHED, threadId, runId });
      controller.close();
    },
  });

  return new Response(stream, {
    headers: { "Content-Type": "text/event-stream" },
  });
}

Client-Side: Consuming Verification Results

Import the DeepCitation stylesheet in your client entry point: import "deepcitation/styles.css" (or deepcitation/tailwind.css for Tailwind projects).

On the client, listen for STATE_DELTA events to receive verification results and render CitationComponent:

import { CitationComponent } from "deepcitation/react";
import type { StateDeltaEvent } from "@ag-ui/core";

// When STATE_DELTA arrives with verifications:
function onStateDelta(delta: StateDeltaEvent["delta"]) {
  for (const op of delta) {
    if (op.path === "/verifications") {
      setVerifications(op.value);
    }
  }
}

Next Steps


Back to top

© 2026 DeepCitation — a product of FileLasso, Inc.