Chat API

Core conversation API handling all Agent interactions

Overview

The Chat API is the core endpoint of the Lens OS SDK, handling all Agent conversations. It receives user messages and streams responses via SSE (Server-Sent Events), including Agent thinking process, tool calls, and final answers.

Data Flow

SSE Stream Flow
Client                    Server                    LLM
  │                         │                        │
  │  POST /chat             │                        │
  │ ───────────────────────►│                        │
  │                         │  Request completion    │
  │                         │ ──────────────────────►│
  │                         │                        │
  │  SSE: tool_call         │◄─ Tool call needed ────│
  │◄────────────────────────│                        │
  │                         │  Execute tool          │
  │                         │ ────────┐              │
  │                         │◄────────┘              │
  │  SSE: tool_result       │                        │
  │◄────────────────────────│  Return result         │
  │                         │ ──────────────────────►│
  │  SSE: text              │◄─ Final response ──────│
  │◄────────────────────────│                        │
  │  SSE: done              │                        │
  │◄────────────────────────│                        │

Flow explanation:

  • Client → Server: Frontend sends POST request to Chat API
  • Server → LLM: SDK sends message to LLM model
  • tool_call: LLM decides to execute a tool, sends tool call event
  • Execute tool: Backend executes the corresponding Tool Executor
  • tool_result: Tool execution complete, returns result to LLM
  • text: LLM generates final response text
  • done: Conversation round complete

Basic Setup

Create the Chat API route, the core file for handling all Agent conversations:

src/app/api/lens/agent/chat/route.ts
// src/app/api/lens/agent/chat/route.ts

import { createAgentHandler } from "@lens-os/sdk/server";
import { auth } from "@/auth";
import type { ToolExecutorConfig } from "@lens-os/sdk";

// Define your tools here — see Tool Executors docs
const tools: Record<string, ToolExecutorConfig> = {};

// Create Handler (initialized once on app start)
const handler = createAgentHandler({
  // Lens OS API Key
  apiKey: process.env.LENS_OS_API_KEY!,

  // LLM settings
  openaiKey: process.env.LENS_MODEL_API_KEY!,
  openaiBaseUrl: process.env.LENS_MODEL_BASE_URL || "",
  model: process.env.LENS_MODEL_NAME || "gpt-4o",

  // Agent behavior
  maxTurns: 10,  // Max 10 tool call rounds
  debug: process.env.NODE_ENV === "development",

  // Tool executors
  toolExecutors: tools,
});

export async function POST(req: Request) {
  // 1. Authenticate
  const session = await auth();
  if (!session?.user) {
    return new Response(JSON.stringify({ error: "Unauthorized" }), {
      status: 401,
      headers: { "Content-Type": "application/json" },
    });
  }

  // 2. Get userId
  const userId = session.user.id || "default-user";

  // 3. Inject userId into request
  const body = await req.json();
  const newReq = new Request(req.url, {
    method: "POST",
    headers: req.headers,
    body: JSON.stringify({ ...body, userId }),
  });

  // 4. Let SDK handle the request
  return handler.POST(newReq);
}

Tip

See the "Tool Executors" docs to learn how to create tools.

Handler Options

OptionTypeDefaultDescription
apiKeystringrequiredLens OS API Key
openaiKeystringrequiredLLM API Key
openaiBaseUrlstring""LLM API Base URL
modelstring"gpt-4o"LLM model name
maxTurnsnumber10Max tool call rounds
debugbooleanfalseEnable verbose logging
toolExecutorsobject{}Tool executors

Request Body

The frontend sendMessage sends requests in this format:

Request
interface ChatRequest {
  message: string;           // User message
  sessionId?: string;        // Session ID (auto-generated)
  userId?: string;           // User ID (injected by backend)
  context?: {
    currentUrl?: string;     // Current page URL
    pageState?: PageState;   // Page state (for visual understanding)
  };
}

Response Format

Response is an SSE stream, each event formatted as:

SSE Events
// Text response
{ "type": "text", "content": "Let me search for you..." }

// Tool call started
{ "type": "tool_call", "name": "product_search", "parameters": { "query": "TypeScript" } }

// Tool execution completed
{ "type": "tool_result", "name": "product_search", "result": { ... } }

// Error
{ "type": "error", "error": "Something went wrong" }

// Done
{ "type": "done" }

Event type descriptions:

  • text: Agent text response, may be sent multiple times
  • tool_call: Agent starts executing a tool
  • tool_result: Tool execution complete, includes result
  • error: Error occurred
  • done: Entire conversation round complete

Without Authentication

If your app doesn't need user authentication, use anonymous mode:

route.ts
// src/app/api/lens/agent/chat/route.ts

export async function POST(req: Request) {
  const body = await req.json();

  // Use anonymous ID or get from cookie
  const userId = body.userId || "anonymous";

  return handler.POST(
    new Request(req.url, {
      method: "POST",
      headers: req.headers,
      body: JSON.stringify({ ...body, userId }),
    })
  );
}

Error Handling

Recommended to add complete error handling:

route.ts
export async function POST(req: Request) {
  try {
    const session = await auth();
    if (!session?.user) {
      return new Response(
        JSON.stringify({ error: "Unauthorized" }),
        { status: 401 }
      );
    }

    const body = await req.json();
    // ... handler logic

  } catch (error) {
    console.error("[Chat API] Error:", error);

    return new Response(
      JSON.stringify({
        error: error instanceof Error ? error.message : "Internal error",
      }),
      { status: 500 }
    );
  }
}

Next Steps