Analytics (LLM usage tracking)
Alakai ships an optional analytics pipeline that records every LLM interaction to a PostgreSQL database — with zero impact on Slack response time (all writes are fire-and-forget).
What is tracked
Each row in alakai_events captures:
| Column | Description |
|---|---|
actor_id | Who triggered the interaction (Slack user or agent) |
model_id | Which LLM model was used |
interaction_type | One of the interaction types listed below |
status | success or error |
input_tokens | Prompt token count (nullable) |
output_tokens | Completion token count (nullable) |
total_tokens | Generated column: input + output |
latency_ms | Wall-clock time from request start to completion |
metadata | JSONB bag for extra context (error message, etc.) |
occurred_at | Timestamp of the event |
Interaction types recorded in V1:
| Type | Triggered by |
|---|---|
SLACK_CODE | /prompt — LLM-generated code prompt |
SLACK_PROMPT | /prompt-pr — prompt-only PR |
CODING | /implement — Codex agent run (tracked in the worker after completion) |
SLACK_CLICKUP_ASK | /clickup ask — ClickUp question answered by LLM |
SLACK_CLICKUP_TASK | /clickup-task or thread app_mention — ClickUp task creation |
Schema overview
Three tables are created automatically via umzug migrations on every startup (already-applied migrations are skipped):
actors— Slack users and agents (GitHub Actions Bot, etc.). Upserted on each event usingslack_user_idas the stable key for humans, and(display_name, type)for agents.models— LLM model registry with per-token cost fields. Pre-populated by seeds; unknown models get a placeholder row with a console warning so events are never dropped.alakai_events— The event log. Referencesactorsandmodels.
Migration state is tracked in sequelize_migrations / sequelize_seeds tables in the same database.
Configuration
| Variable | Required | Description |
|---|---|---|
ANALYTICS_DATABASE_URL | No | PostgreSQL connection string. When absent, analytics are silently disabled. |
In production, store ANALYTICS_DATABASE_URL in the AWS Secrets Manager secret alongside the other application secrets — it is already listed in PROD_SECRET_KEYS in core/src/config/env.ts.
Local setup
-
Provision a local PostgreSQL database:
createdb alakai_analytics -
Set the connection string in
core/.env— use the formatANALYTICS_DATABASE_URL=<connection-string>with your local PostgreSQL credentials and thealakai_analyticsdatabase. -
Start the service — migrations and seeds run automatically. Verify after sending a Slack command:
SELECT e.occurred_at, a.display_name, m.name AS model,e.interaction_type, e.status, e.total_tokens, e.latency_msFROM alakai_events eJOIN actors a ON a.id = e.actor_idJOIN models m ON m.id = e.model_idORDER BY e.occurred_at DESCLIMIT 10;
Production setup
Provision a dedicated RDS PostgreSQL instance. Restrict the application user to SELECT, INSERT, UPDATE, DELETE on the analytics schema only — no DDL privileges are needed after the initial migration.
Implementation package
Analytics logic lives in packages/analytics (@alakai/analytics). It is a pure library — it does not read from process.env. Each consumer (core, workers/implementation) is responsible for reading ANALYTICS_DATABASE_URL, constructing a Sequelize instance via createSequelize(), and injecting it into AnalyticsService.
Degraded mode
If ANALYTICS_DATABASE_URL is not set, or if migrations/seeds fail at startup (for example, the database is unreachable), the service logs a warning and continues without analytics. Individual write failures after startup are also swallowed and logged as warnings — they never surface to users.