refactor: unify providers and models tables

- Rename `llm_providers` → `providers`, `llm_provider_oauth_tokens` → `provider_oauth_tokens`
- Remove `tts_providers` and `tts_models` tables; speech models now live in the unified `models` table with `type = 'speech'`
- Replace top-level `api_key`/`base_url` columns with a JSONB `config` field on `providers`
- Rename `llm_provider_id` → `provider_id` across all references
- Add `edge-speech` client type and `conf/providers/edge.yaml` default provider
- Create new read-only speech endpoints (`/speech-providers`, `/speech-models`) backed by filtered views of the unified tables
- Remove old TTS CRUD handlers; simplify speech page to read-only + test
- Update registry loader to skip malformed YAML files instead of failing entirely
- Fix YAML quoting for model names containing colons in openrouter.yaml
- Regenerate sqlc, swagger, and TypeScript SDK
This commit is contained in:
Acbox
2026-04-07 00:26:06 +08:00
parent 43c4153938
commit a04b8fd564
78 changed files with 3191 additions and 5737 deletions
+28 -33
View File
@@ -57,19 +57,21 @@ CREATE TABLE IF NOT EXISTS user_channel_bindings (
CREATE INDEX IF NOT EXISTS idx_user_channel_bindings_user_id ON user_channel_bindings(user_id);
CREATE TABLE IF NOT EXISTS llm_providers (
CREATE TABLE IF NOT EXISTS providers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
base_url TEXT NOT NULL,
api_key TEXT NOT NULL,
client_type TEXT NOT NULL DEFAULT 'openai-completions',
icon TEXT,
enable BOOLEAN NOT NULL DEFAULT true,
config JSONB NOT NULL DEFAULT '{}'::jsonb,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT llm_providers_name_unique UNIQUE (name),
CONSTRAINT llm_providers_client_type_check CHECK (client_type IN ('openai-responses', 'openai-completions', 'anthropic-messages', 'google-generative-ai', 'openai-codex'))
CONSTRAINT providers_name_unique UNIQUE (name),
CONSTRAINT providers_client_type_check CHECK (client_type IN (
'openai-responses', 'openai-completions', 'anthropic-messages',
'google-generative-ai', 'openai-codex', 'edge-speech'
))
);
CREATE TABLE IF NOT EXISTS search_providers (
@@ -87,13 +89,13 @@ CREATE TABLE IF NOT EXISTS models (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
model_id TEXT NOT NULL,
name TEXT,
llm_provider_id UUID NOT NULL REFERENCES llm_providers(id) ON DELETE CASCADE,
provider_id UUID NOT NULL REFERENCES providers(id) ON DELETE CASCADE,
type TEXT NOT NULL DEFAULT 'chat',
config JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT models_provider_model_id_unique UNIQUE (llm_provider_id, model_id),
CONSTRAINT models_type_check CHECK (type IN ('chat', 'embedding'))
CONSTRAINT models_provider_id_model_id_unique UNIQUE (provider_id, model_id),
CONSTRAINT models_type_check CHECK (type IN ('chat', 'embedding', 'speech'))
);
CREATE TABLE IF NOT EXISTS model_variants (
@@ -120,30 +122,6 @@ CREATE TABLE IF NOT EXISTS memory_providers (
CONSTRAINT memory_providers_name_unique UNIQUE (name)
);
-- tts_providers: pluggable TTS service backends
CREATE TABLE IF NOT EXISTS tts_providers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
provider TEXT NOT NULL,
config JSONB NOT NULL DEFAULT '{}'::jsonb,
enable BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT tts_providers_name_unique UNIQUE (name)
);
-- tts_models: available models per TTS provider with per-model configuration
CREATE TABLE IF NOT EXISTS tts_models (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
model_id TEXT NOT NULL,
name TEXT,
tts_provider_id UUID NOT NULL REFERENCES tts_providers(id) ON DELETE CASCADE,
config JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_tts_models_provider_id ON tts_models(tts_provider_id);
CREATE TABLE IF NOT EXISTS browser_contexts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
@@ -179,7 +157,7 @@ CREATE TABLE IF NOT EXISTS bots (
title_model_id UUID REFERENCES models(id) ON DELETE SET NULL,
image_model_id UUID REFERENCES models(id) ON DELETE SET NULL,
discuss_probe_model_id UUID REFERENCES models(id) ON DELETE SET NULL,
tts_model_id UUID REFERENCES tts_models(id) ON DELETE SET NULL,
tts_model_id UUID REFERENCES models(id) ON DELETE SET NULL,
browser_context_id UUID REFERENCES browser_contexts(id) ON DELETE SET NULL,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
@@ -651,3 +629,20 @@ CREATE TABLE IF NOT EXISTS email_outbox (
CREATE INDEX IF NOT EXISTS idx_email_outbox_provider_id ON email_outbox(provider_id);
CREATE INDEX IF NOT EXISTS idx_email_outbox_bot_id ON email_outbox(bot_id, created_at DESC);
-- provider_oauth_tokens: OAuth token storage for providers (e.g. OpenAI Codex OAuth)
CREATE TABLE IF NOT EXISTS provider_oauth_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
provider_id UUID NOT NULL UNIQUE REFERENCES providers(id) ON DELETE CASCADE,
access_token TEXT NOT NULL DEFAULT '',
refresh_token TEXT NOT NULL DEFAULT '',
expires_at TIMESTAMPTZ,
scope TEXT NOT NULL DEFAULT '',
token_type TEXT NOT NULL DEFAULT '',
state TEXT NOT NULL DEFAULT '',
pkce_code_verifier TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_provider_oauth_tokens_state ON provider_oauth_tokens(state) WHERE state != '';