Skip to main content

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:

ColumnDescription
actor_idWho triggered the interaction (Slack user or agent)
model_idWhich LLM model was used
interaction_typeOne of the interaction types listed below
statussuccess or error
input_tokensPrompt token count (nullable)
output_tokensCompletion token count (nullable)
total_tokensGenerated column: input + output
latency_msWall-clock time from request start to completion
metadataJSONB bag for extra context (error message, etc.)
occurred_atTimestamp of the event

Interaction types recorded in V1:

TypeTriggered 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 using slack_user_id as 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. References actors and models.

Migration state is tracked in sequelize_migrations / sequelize_seeds tables in the same database.

Configuration

VariableRequiredDescription
ANALYTICS_DATABASE_URLNoPostgreSQL 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

  1. Provision a local PostgreSQL database:

    createdb alakai_analytics
  2. Set the connection string in core/.env — use the format ANALYTICS_DATABASE_URL=<connection-string> with your local PostgreSQL credentials and the alakai_analytics database.

  3. 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_ms
    FROM alakai_events e
    JOIN actors a ON a.id = e.actor_id
    JOIN models m ON m.id = e.model_id
    ORDER BY e.occurred_at DESC
    LIMIT 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.