feat: add provider-neutral LLM layer with secrets, budgets, recording and prompts
Phase 7 (ADR-0004). Cognitive code asks the router for a role; the router maps roles to configured models with ordered fallbacks, applies global and per-mind daily token budgets and a concurrency limit, records every call (request, response, usage, latency, cost, seed, request hash) and can serve recordings back for exact replay. Providers are raw HTTP clients for OpenAI-compatible, Anthropic and Ollama endpoints, with embedding support for the embed role wired into memory retrieval. Secrets are references only: env:, file:, or enc: values encrypted with AES-256-GCM under NOEMA_MASTER_KEY; keys are resolved at call time and never rendered, logged or exported. Outbound HTTP refuses private and loopback addresses at dial time unless a provider explicitly allows it, bounds redirects and caps bodies. Provider errors are scrubbed. Prompts are named, versioned rows with system and user templates and an optional JSON schema; untrusted text enters only through delimited data blocks. Six defaults are seeded and never overwritten. The UI adds the LLM page (providers, models, role matrix, usage, errors, budgets), a provider page with connectivity test, and a prompt manager with editor, test runner, history, restore, clone and diff. CLI: provider, prompt.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
DROP TABLE IF EXISTS prompt_versions;
|
||||
DROP TABLE IF EXISTS prompts;
|
||||
DROP TABLE IF EXISTS llm_calls;
|
||||
DROP TABLE IF EXISTS llm_roles;
|
||||
DROP TABLE IF EXISTS llm_models;
|
||||
DROP TABLE IF EXISTS llm_providers;
|
||||
@@ -0,0 +1,95 @@
|
||||
-- LLM providers, models, role assignments, recorded calls and versioned prompts.
|
||||
|
||||
CREATE TABLE llm_providers (
|
||||
id uuid PRIMARY KEY,
|
||||
name text NOT NULL,
|
||||
kind text NOT NULL CHECK (kind IN ('openai','anthropic','ollama')),
|
||||
endpoint text NOT NULL,
|
||||
api_key_ref text NOT NULL DEFAULT '', -- env:, file: or enc: reference; never plaintext
|
||||
allow_private boolean NOT NULL DEFAULT false,
|
||||
headers jsonb NOT NULL DEFAULT '{}'::jsonb,
|
||||
timeout_ms integer NOT NULL DEFAULT 60000,
|
||||
enabled boolean NOT NULL DEFAULT true,
|
||||
last_test_at timestamptz,
|
||||
last_test_ok boolean,
|
||||
last_test_msg text NOT NULL DEFAULT '',
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE UNIQUE INDEX llm_providers_name_idx ON llm_providers (lower(name));
|
||||
|
||||
CREATE TABLE llm_models (
|
||||
id uuid PRIMARY KEY,
|
||||
provider_id uuid NOT NULL REFERENCES llm_providers(id) ON DELETE CASCADE,
|
||||
name text NOT NULL,
|
||||
kind text NOT NULL DEFAULT 'chat' CHECK (kind IN ('chat','embedding')),
|
||||
context_window integer NOT NULL DEFAULT 0,
|
||||
dims integer NOT NULL DEFAULT 0,
|
||||
input_cost_per_m double precision NOT NULL DEFAULT 0, -- USD per million tokens
|
||||
output_cost_per_m double precision NOT NULL DEFAULT 0,
|
||||
params jsonb NOT NULL DEFAULT '{}'::jsonb, -- default temperature, top_p, max_tokens
|
||||
enabled boolean NOT NULL DEFAULT true,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE (provider_id, name)
|
||||
);
|
||||
|
||||
CREATE TABLE llm_roles (
|
||||
role text PRIMARY KEY,
|
||||
model_id uuid REFERENCES llm_models(id) ON DELETE SET NULL,
|
||||
fallback_ids uuid[] NOT NULL DEFAULT '{}',
|
||||
temperature double precision,
|
||||
max_tokens integer,
|
||||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_by text NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
-- Every model call, for inspection, cost accounting and replay.
|
||||
CREATE TABLE llm_calls (
|
||||
id uuid PRIMARY KEY,
|
||||
mind_id uuid REFERENCES minds(id),
|
||||
correlation_id uuid,
|
||||
role text NOT NULL,
|
||||
purpose text NOT NULL DEFAULT '',
|
||||
provider text NOT NULL,
|
||||
model text NOT NULL,
|
||||
prompt_name text NOT NULL DEFAULT '',
|
||||
prompt_version integer NOT NULL DEFAULT 0,
|
||||
request_hash text NOT NULL,
|
||||
request jsonb NOT NULL,
|
||||
response jsonb,
|
||||
error text NOT NULL DEFAULT '',
|
||||
prompt_tokens integer NOT NULL DEFAULT 0,
|
||||
output_tokens integer NOT NULL DEFAULT 0,
|
||||
cost double precision NOT NULL DEFAULT 0,
|
||||
latency_ms integer NOT NULL DEFAULT 0,
|
||||
seed bigint,
|
||||
replayed boolean NOT NULL DEFAULT false,
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX llm_calls_time_idx ON llm_calls (created_at DESC);
|
||||
CREATE INDEX llm_calls_mind_idx ON llm_calls (mind_id, created_at DESC);
|
||||
CREATE INDEX llm_calls_hash_idx ON llm_calls (request_hash);
|
||||
CREATE INDEX llm_calls_corr_idx ON llm_calls (correlation_id) WHERE correlation_id IS NOT NULL;
|
||||
|
||||
CREATE TABLE prompts (
|
||||
name text PRIMARY KEY,
|
||||
description text NOT NULL DEFAULT '',
|
||||
role text NOT NULL,
|
||||
current_version integer NOT NULL DEFAULT 1,
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE prompt_versions (
|
||||
id uuid PRIMARY KEY,
|
||||
name text NOT NULL REFERENCES prompts(name),
|
||||
version integer NOT NULL,
|
||||
system text NOT NULL DEFAULT '',
|
||||
template text NOT NULL,
|
||||
schema jsonb,
|
||||
note text NOT NULL DEFAULT '',
|
||||
created_by text NOT NULL DEFAULT '',
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE (name, version)
|
||||
);
|
||||
CREATE TRIGGER prompt_versions_append_only BEFORE UPDATE OR DELETE ON prompt_versions
|
||||
FOR EACH ROW EXECUTE FUNCTION noema_forbid_change();
|
||||
Reference in New Issue
Block a user