> ## Documentation Index
> Fetch the complete documentation index at: https://docs.brainstormer.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Bot Service

> Agent/Bot service: agent CRUD, AI chat pipeline, RAG context retrieval, prompt configuration, conversation management, and billing.

## Overview

**Port:** 4002

The Bot Service handles agent CRUD operations, AI chat via OpenRouter/LangChain, RAG context retrieval from linked knowledge bases, prompt configuration, conversation management, and credit billing.

## Endpoints

### Agent CRUD

| Route                       | Method | Purpose                                                |
| --------------------------- | ------ | ------------------------------------------------------ |
| `/bots`                     | POST   | Create bot                                             |
| `/bots`                     | GET    | List org bots                                          |
| `/bots/:id`                 | GET    | Get bot (includes linked KBs)                          |
| `/bots/:id`                 | PUT    | Update bot (syncs KB links via `knowledgeBaseIds`)     |
| `/bots/:id`                 | DELETE | Delete bot                                             |
| `/bots/:id/usage`           | GET    | Bot usage stats                                        |
| `/bots/:id/indexing-status` | GET    | Proxies knowledge-service indexing status for this bot |

### Chat

| Route                         | Method | Purpose                                              |
| ----------------------------- | ------ | ---------------------------------------------------- |
| `/bots/:id/chat`              | POST   | Chat with bot (core endpoint)                        |
| `/bots/:id/conversations`     | POST   | Create conversation (returns welcome message)        |
| `/conversations`              | GET    | List conversations (filter: botId, userId, platform) |
| `/conversations/:id`          | GET    | Get conversation                                     |
| `/conversations/:id/messages` | GET    | Get messages                                         |

### Prompt Configuration

| Route                                     | Method | Purpose                                            |
| ----------------------------------------- | ------ | -------------------------------------------------- |
| `/bots/:id/prompt-configs`                | GET    | List all prompt configs for a bot                  |
| `/bots/:id/prompt-configs/:type`          | GET    | Get specific prompt config                         |
| `/bots/:id/prompt-configs/:type`          | PUT    | Create or update prompt config                     |
| `/bots/:id/prompt-configs/:type`          | DELETE | Remove bot override (falls back to system default) |
| `/bots/:id/prompt-configs/:type/versions` | GET    | Prompt config version history                      |
| `/bots/:id/prompt-configs/:type/rollback` | POST   | Rollback to a specific version                     |

### Variables and Context Tokens

| Route                                       | Method | Purpose                                               |
| ------------------------------------------- | ------ | ----------------------------------------------------- |
| `/bots/:id/conversations/:convId/variables` | POST   | Set server-side / sensitive variables                 |
| `/bots/:id/conversations/:convId/variables` | GET    | Get current variables (excludes sensitive)            |
| `/bots/:id/context-token`                   | POST   | Generate signed context token for sensitive variables |

### Models and Files

| Route          | Method | Purpose                               |
| -------------- | ------ | ------------------------------------- |
| `/models`      | GET    | Available AI models (from OpenRouter) |
| `/models/:id`  | GET    | Model details                         |
| `/attachments` | POST   | Upload file attachment                |
| `/analytics/*` | GET    | Usage analytics                       |

## Knowledge Base Readiness Gate

All paths that make an agent publicly or organizationally "live" verify KB readiness before proceeding:

* `POST /bots` with `isDraft: false`
* `PUT /bots/:id` when setting `isDraft: false`
* `POST /bots/:id/publish`
* `POST /bots/:id/approve`
* `POST /bots/:botId/distribution/publish`

The `GET /bots/:id/indexing-status` endpoint proxies `GET /knowledge/agents/:id/indexing-status` and returns:

| Field              | Meaning                                                    |
| ------------------ | ---------------------------------------------------------- |
| `linkedKbCount`    | Number of linked KBs                                       |
| `totalDocuments`   | Total documents across linked KBs                          |
| `indexedDocuments` | Documents with `processing_status = completed`             |
| `failedDocuments`  | Failed documents                                           |
| `pendingDocuments` | Pending/processing documents                               |
| `ready`            | `true` when `linkedKbCount > 0` and `indexedDocuments > 0` |

If the knowledge service is unreachable, the check is **fail-closed** and publishing is blocked.

## Chat Flow Pipeline

The core chat endpoint (`POST /bots/:id/chat`) follows this pipeline:

```mermaid theme={null}
graph TD
    A["POST /bots/:id/chat<br/>{message, conversationId, attachments, variables, stream}"] --> B[Validate + Extract Attachments]
    B --> C[Merge + Store Variables]
    C --> D[retrieveKBContext]

    D --> D1["Enhanced query<br/>(last 5 messages + attachment text)"]
    D1 --> D2["POST /knowledge/kb/:id/search<br/>(topK: 3 per KB)"]
    D2 --> D3["Filter: score > 0.35"]
    D3 --> D4["Rerank: 60% vector + 25% text relevance + 15% importance weight"]
    D4 --> D5["Deduplicate: max 2 chunks/doc"]
    D5 --> D6["Post-filter: score > 0.25"]
    D6 --> D7["Format citations [1], [2]"]

    C --> E["GET /knowledge/kb/:id/map<br/>(document registry)"]
    C --> F["POST /knowledge/kb/:id/graph/search<br/>(entity context)"]

    D7 & E & F --> G["Resolve System Prompt<br/>(bot config -> system default -> code fallback)"]
    G --> H["Render with conversation variables"]
    H --> I["LangChain: prompt + KB context + history + message"]
    I --> J[OpenRouter API Call]
    J --> K{Streaming?}
    K -->|Yes| L[Stream response]
    K -->|No| M[Return full response]
    L & M --> N[Save messages, record billing, return sources]
```

## Provider Error Handling

Provider-level failures are mapped to user-friendly messages before reaching the UI. The mapping is centralized in `services/bot/src/utils/error-utils.ts` via `mapProviderError()`.

| Error condition                                     | `code`                   | User-facing message                                                                           |
| --------------------------------------------------- | ------------------------ | --------------------------------------------------------------------------------------------- |
| OpenRouter 402 / credit-limit / "fewer max\_tokens" | `provider_limit_reached` | "This assistant is temporarily unavailable. Please try again later."                          |
| Rate limit / 429                                    | `rate_limit`             | "Rate limit exceeded. Please wait a moment and try again."                                    |
| Auth failure / 401 / 403                            | `auth_failed`            | "AI provider authentication failed. Please check the API key configuration."                  |
| Model not available                                 | `model_unavailable`      | "The selected AI model is not available. Please try a different model in the agent settings." |

The raw provider error message — including any API key URLs — is sanitized by `sanitizeProviderMessage()` before logging. Streaming chat emits error events as `data: { "type": "error", "message": "...", "code": "..." }`. Non-streaming chat returns `{ "error": "...", "code": "..." }` when a provider-level failure occurs. Public widget chat and `chat-core` headless consumers receive the same codes.

## Welcome Message Flow

When a conversation is created (`POST /bots/:id/conversations`):

<Steps>
  <Step title="Create Conversation">
    Create conversation record in database.
  </Step>

  <Step title="Resolve Variables">
    Merge variables from context\_token, request body, and agent defaults.
  </Step>

  <Step title="Resolve Welcome Config">
    Welcome is read from `bots.widget_config.welcome` (web-widget-scoped), **not** via prompt-resolution. If absent or disabled, the conversation returns with no welcome message.
  </Step>

  <Step title="Render and Generate">
    * If no config or `enabled=false`: return conversation with no welcome message.
    * If `mode='fixed'`: render content with variables, return directly.
    * If `mode='generated'`: render prompt template, call LLM, optionally stream response.
  </Step>

  <Step title="Save and Return">
    Save welcome message as first assistant message. Return `{ conversationId, welcomeMessage }`.
  </Step>
</Steps>

<Note>
  Fixed mode has no cost. Generated mode calls `record_external_cost_event()` with operation `welcome_message_generation`.
</Note>

## Prompt Resolution Flow

All prompts resolve via the same three-tier chain:

```
1. Look up bot_prompt_configs for (bot_id, prompt_type) WHERE enabled=true
2. If found -> use bot-level config
3. If not found -> look up system_prompt_defaults for prompt_type
4. If not found -> use hardcoded fallback constant (safety net)
5. Render template with conversation variables via renderPrompt()
6. mode='fixed': return rendered content
7. mode='generated': send rendered prompt to LLM, return output
```

## RAG Quality Controls

| Control            | Threshold/Rule                                                                    |
| ------------------ | --------------------------------------------------------------------------------- |
| Initial filter     | Chunks with vector score \< 0.35 are discarded                                    |
| Reranking          | Hybrid score = 60% vector similarity + 25% text relevance + 15% importance weight |
| Post-rerank filter | Chunks with combined score \< 0.25 are discarded                                  |
| Document diversity | Max 2 chunks per document                                                         |
| Empty context      | If no chunks pass filters, KB context section is omitted                          |

## Core Services

| Service                             | Purpose                                                   |
| ----------------------------------- | --------------------------------------------------------- |
| `bot.service.ts`                    | Chat orchestration, KB retrieval, reranking, billing      |
| `langchain-chat.service.ts`         | LangChain wrapper, conversation memory, LangSmith tracing |
| `openrouter.service.ts`             | OpenRouter API client, model listing                      |
| `model-sync.service.ts`             | Periodic sync of models from OpenRouter to DB             |
| `file-upload.service.ts`            | S3/local file upload with signed URLs                     |
| `cloud-storage.service.ts`          | S3 + local storage abstraction                            |
| `prompt-resolution.service.ts`      | Three-tier prompt resolution                              |
| `conversation-variables.service.ts` | Variable merge, storage, sensitive variable encryption    |

## Database Tables

| Table                        | Purpose                                                                                                                |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `bots`                       | Bot config (name, model, system\_prompt, `model_config` JSONB, organization\_id, `streaming_enabled`, `widget_config`) |
| `conversations`              | Chat sessions (bot\_id, user\_id, platform, metadata)                                                                  |
| `messages`                   | Chat messages (conversation\_id, role, content)                                                                        |
| `message_attachments`        | File uploads (access\_token, cloud\_storage\_key)                                                                      |
| `bot_versions`               | Version history on updates                                                                                             |
| `models`                     | AI models with pricing (synced from OpenRouter)                                                                        |
| `usage`                      | Token + cost tracking per conversation                                                                                 |
| `external_cost_events`       | Billing ledger                                                                                                         |
| `agent_builds`               | Provisioning build records (agent-build / from-url flow)                                                               |
| `kb_agent_links`             | KB-to-bot junction (managed on bot create/update)                                                                      |
| `bot_prompt_configs`         | Per-agent prompt overrides                                                                                             |
| `bot_prompt_config_versions` | Version history for bot prompt configs                                                                                 |
| `system_prompt_defaults`     | Platform-wide fallback prompts                                                                                         |
| `conversation_variables`     | Per-conversation variable store (encrypted sensitive vars)                                                             |

## External Integrations

| Integration       | Config                                                                            |
| ----------------- | --------------------------------------------------------------------------------- |
| OpenRouter        | `OPENROUTER_API_KEY`, `OPENROUTER_BASE_URL`                                       |
| Knowledge Service | `KNOWLEDGE_SERVICE_URL` (default: [http://localhost:4005](http://localhost:4005)) |
| LangSmith         | `LANGSMITH_TRACING`, `LANGSMITH_API_KEY`, `LANGSMITH_PROJECT`                     |
| S3 (optional)     | `CLOUD_STORAGE_PROVIDER`, `CLOUD_STORAGE_BUCKET`, S3 creds                        |

## Critical Patterns

<AccordionGroup>
  <Accordion title="Authentication">
    Use `request.user.organizationId` / `request.user.id` from JWT middleware. NEVER read raw headers.
  </Accordion>

  <Accordion title="Billing">
    Every chat MUST call `record_external_cost_event()` with provider, operation, cost, orgId, userId. Generated-mode welcome messages also bill.
  </Accordion>

  <Accordion title="KB Linking">
    On bot update, `knowledgeBaseIds` array replaces all links in `kb_agent_links`. If frontend sends `[]`, all links are deleted.
  </Accordion>

  <Accordion title="RBAC">
    `checkResourceAccess()` before all bot operations.
  </Accordion>

  <Accordion title="Graceful Degradation">
    KB retrieval failures do not block chat -- response continues without context.
  </Accordion>

  <Accordion title="Prompt Resolution">
    NEVER read `bots.system_prompt` directly in new code. Always go through `prompt-resolution.service.ts`.
  </Accordion>

  <Accordion title="Sensitive Variables">
    NEVER return `conversation_variables.sensitive_variables` in any API response.
  </Accordion>

  <Accordion title="Streaming">
    Check `bot.streaming_enabled AND request.stream === true` before streaming any response.
  </Accordion>
</AccordionGroup>
