Cosmo Docs
Server

Chat Module

LLM streaming via SSE and model listing

The Chat module exposes two endpoints: a streaming chat endpoint and a model listing endpoint. Source: packages/server/src/modules/chat/.

REST Endpoints

ChatController (chat.controller.ts)

MethodPathGuardsDescription
POST/chatClerkAuthGuard + QuotaGuardStream LLM response via SSE

ModelsController (models.controller.ts)

MethodPathGuardsDescription
GET/modelsClerkAuthGuardList available LLM models

Chat Request

POST /api/v1/chat accepts:

FieldTypeRequiredDescription
messagestringYesUser's message
historyHistoryMessage[]NoPrior conversation turns
toolsToolDefinition[]NoClient-defined tool schemas
modelstringNoModel ID (default: claude-sonnet-4-20250514)
maxTokensnumberNoMax output tokens
systemPromptstringNoCustom system prompt
enableWebSearchbooleanNoEnable web_search built-in tool

HistoryMessage

Each history entry has:

{
  role: 'user' | 'assistant';
  content: string | ContentBlock[];  // text, tool_use, tool_result
}

SSE Response

The response is a text/event-stream stream. Each chunk is a JSON object with a type field:

TypeFieldsDescription
texttext: stringPartial text output
tool_calltoolCall: { id, name, args, serverHandled? }LLM invoked a tool
tool_resulttoolResult: { callId, result }Tool execution completed
errorerror: stringFatal error — stream ends
doneusage: { inputTokens, outputTokens }Stream complete

Chat Service (chat.service.ts)

streamChat(request, userId, orgId)

Async generator that yields ChatChunkDTO objects.

Processing steps:

  1. Validate model ID against the @cosmohq/core model registry.
  2. Serialise history to Anthropic MessageParam[] format:
    • Converts tool_use + tool_result blocks from the client's format to Anthropic format.
    • Skips server-handled tool calls (e.g. web_search) to avoid re-processing.
  3. Build tool schemas: client-defined tools + optional web_search (if enableWebSearch is true and the model has the web_search capability).
  4. Call anthropic.messages.stream(...) with the full message history.
  5. For each streaming event:
    • Yield text deltas as text chunks.
    • Yield tool invocations as tool_call chunks.
  6. After stream end, yield done with usage stats.
  7. Call MeteringService.recordLLMUsage(...) with token counts.
  8. Emit a PostHog $ai_generation event with timing and token metrics.

Error Handling

API errors are converted to human-readable messages:

  • overloaded_error → "Claude is currently overloaded. Please try again in a few moments."
  • rate_limit_error → "Rate limit exceeded."
  • Generic → "An error occurred while processing your request."

Models Listing

GET /api/v1/models returns all models registered in @cosmohq/core:

[
  {
    id: "claude-opus-4-7",
    displayName: "Claude Opus 4.7",
    provider: "anthropic",
    tier: "premium",
    contextWindow: 200000,
    maxOutputTokens: 32000,
    capabilities: ["web_search", "extended_thinking"]
  },
  ...
]