Skip to content

Architecture

Dual‑store pattern

  • Convex (tables: runs_live, kpis_1m, projects, apiKeys, entitlements) for real‑time reads and live subscriptions.
  • Postgres (Prisma models) for durable analytics and joins.
  • A lightweight internal sync bridges Convex→Postgres.

Ingestion path

flowchart LR
  App -->|LLM call (BYOK)| Provider
  App ==> |RunForge.track metrics| RunForge[/api/ingest/]
  RunForge --> Convex[(Realtime)]
  RunForge --> Postgres[(History)]
  Convex --> Dashboard
  • /api/ingest validates payloads with Zod and authenticates via INGEST_API_KEY.
  • api.runs.ingestRun/ingestRuns insert into runs_live and schedule runsActions.internalSyncToPg.
  • Internal HMAC‑signed POST to /api/internal/pg-sync marks syncedToPg on success.

Cost resolution

flowchart TB
  Response -->|usage.total_cost?| Decision{OpenRouter cost?}
  Decision -->|Yes| UseProviderCost[Use provider cost\ncostSource=provider]
  Decision -->|No| Registry[Compute from pricing registry\ncostSource=catalog]
  Registry --> Flags[costEstimated?]

Streaming timing

sequenceDiagram
  participant App
  participant Provider
  App->>Provider: stream=true (+include_usage)
  Provider-->>App: chunks...
  Provider-->>App: final chunk (usage)
  App->>RunForge: POST /ingest (tokens, latency, model)
  Note over App: No prompts/outputs sent

Dashboard read path

flowchart LR
  Convex[(runs_live)] -->|query listRuns/listRunsByOrganization| UI
  Convex[(kpis_1m)] -->|rollup-1m cron| UI
  • UI subscribes to api.runs.listRuns and api.runs.listRunsByOrganization.
  • kpis.rollup1m computes per‑minute aggregates for projects.

Experiment run matrix (concept)

sequenceDiagram
  participant User
  participant UI
  participant Convex
  participant API
  participant Postgres
  User->>UI: Start experiment matrix
  UI->>Convex: create Experiment doc (future)
  UI->>API: enqueue runs per model/temp
  API->>Convex: write runs_live
  Convex-->>UI: stream live results
  Convex->>API: internalSyncToPg
  API->>Postgres: upsert runs
  UI->>API: fetch metrics/export

Idempotency, IDs, retention

  • Idempotency by id check on runs_live (by_run_id index), using provided runId when set.
  • generateRunId() uses UUIDv4 fallback; ULIDs optional.
  • Retention policy for runs_live TBD. TODO: Add TTL cleanup for runs_live (see convex/runs.ts).

See also: 05-apis.md, 08-convex-realtime.md