Files
ferrum/internal/store/migrations/sqlite/00020_ai_provider_models.sql
Anand 22c1dd382c Add API keys, MCP server, admin AI providers, and a built-in local LLM option
- User-scoped API keys (Profile > API Keys) for 3rd-party REST API access
  and MCP clients, each locked to one scope at creation, with expiry,
  revocation, and last-used tracking.
- A hand-rolled MCP (Model Context Protocol) server exposing the fleet
  (connections, nodes, guests, storage, pools, alerts, cluster status) as
  read tools plus one admin-gated power-action tool, so Claude Code/Desktop
  or any other MCP client can query and operate the fleet directly.
- Both the REST API and MCP are off by default and toggleable instance-wide
  from Settings > API & MCP, enforced live on every request.
- Admin-managed AI providers (any OpenAI-chat-completions-compatible
  endpoint) backing the AI Assistant's tool-calling loop, replacing the
  single hardcoded provider.
- A built-in, zero-config, no-API-key local provider backed by Needle 2
  (internal/needle) for fully offline tool-calling, wired in as a one-click
  preset. Requires the operator to separately download the Needle 2 binary
  and point FERRUM_NEEDLE_BIN at it -- Ferrum never fetches executable
  content from the network itself; see README "Built-in LLM (Needle 2)".
- System settings (CORS allow-list, instance-wide toggles) moved to the
  admin Settings UI; environment variables are now scoped to true
  bootstrap-level config only (listen address, TLS, DB connection, secret,
  optional Needle binary path).
- Fixed: node Journal tab 502'ing with "unexpected end of JSON input" on an
  empty response, and separately with a decode error on PVE versions that
  return a bare-string journal line instead of the documented {n,t} object.
- Fixed: bottom content padding disappearing on every page except the AI
  Assistant (an unconditional h-full on the content wrapper let overflowing
  content bleed through where the padding should render).
- Fixed: Profile page felt cramped despite a wide viewport (stray max-w-2xl
  cap not present on the equivalent Settings page).
- Test coverage added for the previously-untested MCP package and the new
  Needle adapter (20 new Go tests), plus a regression test for the journal
  decode fix.
2026-09-06 13:26:30 +05:30

32 lines
1.6 KiB
SQL

-- +goose Up
-- A provider (one API endpoint + credential) can expose more than one
-- model — e.g. one OpenAI provider with gpt-4o-mini and gpt-4o, or one local
-- Ollama instance with several pulled models. label is the human-facing
-- name shown in the picker; model_id is the exact identifier sent to the
-- provider's API — kept as two fields since they're frequently different
-- (a friendly "GPT-4o mini" vs the wire value "gpt-4o-mini", or a local
-- runtime's tag like "qwen2.5-coder:7b-instruct-q4_K_M").
CREATE TABLE ai_provider_models (
id TEXT PRIMARY KEY,
provider_id TEXT NOT NULL REFERENCES ai_providers(id) ON DELETE CASCADE,
label TEXT NOT NULL,
model_id TEXT NOT NULL,
is_default INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL
);
CREATE INDEX idx_ai_provider_models_provider_id ON ai_provider_models(provider_id);
-- Existing rows carried a single model directly on ai_providers — migrate
-- each into a first model row so upgraded installs don't lose their
-- configuration. The ai_providers.model column itself is left in place
-- (unused going forward, always written as '' by new code) rather than
-- dropped, since SQLite's column drop requires a full table rebuild that
-- isn't worth the risk for a column that's simply ignored from here on.
INSERT INTO ai_provider_models (id, provider_id, label, model_id, is_default, created_at)
SELECT lower(hex(randomblob(16))), id, model, model, is_default, updated_at
FROM ai_providers
WHERE model IS NOT NULL AND model != '';
-- +goose Down
DROP TABLE ai_provider_models;