Files
agents-marketplace/gateway/src/db/schema.ts
david 4ebe7c9683 feat(gateway,supplier,shared): shared demo key with session-backed OpenAI endpoint + tool-calling
MVP demo: one operator-funded API key anyone can plug into an IDE/harness,
running real escrow-per-session marketplace conversations (llm.chat.v1)
behind the standard OpenAI /chat/completions wire shape.

- shared: ChatMessage gains tool role + tool_calls; normalizeChatMessage/
  chatMessagesEquivalent keep supplier transcript and gateway mirror
  hash-identical for receipts (field presence is part of the identity)
- supplier: /v1/chat/message accepts {messages,tools,tool_choice} alongside
  legacy {content}; callOpenAiStream forwards tools, accumulates fragmented
  delta.tool_calls, allows content-less tool turns; done frame carries the
  assistant message verbatim + finish_reason + usage
- gateway: demo flag on api_keys (additive migration), create-demo-key CLI,
  withdraw 403 + hidden deposit address for demo keys, per-IP demo rate
  limit (demo keys skip the shared per-key limit), models filtered to chat
  capability
- gateway executor (demoChat.ts): transcript-prefix affinity maps stateless
  calls onto open sessions, forwards only unseen deltas, transparently
  reopens on supplier idle-close; never falls back to one-shot (its settle
  path assumes <=1 in-flight escrow per wallet)
- mutex scope: key mutex held only for chain work (open/close); off-chain
  turns serialize on per-session locks so replicas serve users concurrently
- sweeper: chat-aware reclaim guard (35min vs 11min) and abandoned-session
  cleanup; terminal state confirmed via on-chain queryUtxo (indexer keeps
  lineage rows at their old state forever)
- deploy: kimi-chat replicas 2+3 compose files; chat idle timeout 2.5min

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:30:17 +02:00

63 lines
2.2 KiB
TypeScript

/**
* gateway/src/db/schema.ts — SQLite schema for the gateway.
*
* Three tables:
* api_keys — one custodial wallet per key; the wallet priv is AES-GCM sealed
* (enc_priv_*). The raw API key is NEVER stored — only sha256(key).
* usage — one row per billable unit (a one-shot completion or a closed chat
* session), for /account spend reporting and audit.
* sessions — open chat sessions (one escrow per session), for streaming turns
* and sweeper recovery of abandoned sessions.
*/
export const CREATE_TABLES_SQL = `
CREATE TABLE IF NOT EXISTS api_keys (
id TEXT PRIMARY KEY,
key_hash TEXT NOT NULL UNIQUE,
key_prefix TEXT NOT NULL,
label TEXT,
wallet_pkh TEXT NOT NULL,
deposit_address TEXT NOT NULL,
enc_priv_nonce TEXT NOT NULL,
enc_priv_ct TEXT NOT NULL,
enc_priv_tag TEXT NOT NULL,
master_key_version INTEGER NOT NULL DEFAULT 1,
created_at INTEGER NOT NULL,
disabled INTEGER NOT NULL DEFAULT 0,
demo INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX IF NOT EXISTS idx_keys_hash ON api_keys(key_hash);
CREATE TABLE IF NOT EXISTS usage (
id TEXT PRIMARY KEY,
key_id TEXT NOT NULL,
created_at INTEGER NOT NULL,
kind TEXT NOT NULL,
model TEXT,
capability_id TEXT,
supplier_pkh TEXT,
escrow_ref TEXT,
cost_lovelace TEXT,
prompt_tokens INTEGER NOT NULL DEFAULT 0,
completion_tokens INTEGER NOT NULL DEFAULT 0,
status TEXT NOT NULL,
failure_reason TEXT
);
CREATE INDEX IF NOT EXISTS idx_usage_key ON usage(key_id, created_at DESC);
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY,
key_id TEXT NOT NULL,
escrow_ref TEXT NOT NULL,
session_nonce TEXT NOT NULL,
supplier_base_url TEXT NOT NULL,
supplier_pkh TEXT NOT NULL,
model TEXT NOT NULL,
price_lovelace TEXT NOT NULL,
state TEXT NOT NULL,
opened_at INTEGER NOT NULL,
closed_at INTEGER
);
CREATE INDEX IF NOT EXISTS idx_sessions_key ON sessions(key_id, opened_at DESC);
`;