Enhance Makefile and database schema for improved management
- Add new database management targets: db-up, db-schema, db-seed-interface-definitions, and db-init to streamline database setup and seeding processes. - Update db-seed and db-reset dependencies to include new initialization steps for better data integrity. - Introduce new SQL files for schema definition and interface definitions seeding, ensuring a consistent database structure. - Refactor existing seed scripts to align with new dependencies and improve clarity in database operations.
This commit is contained in:
21
Makefile
21
Makefile
@@ -7,7 +7,7 @@
|
||||
PSQL = docker compose exec -T postgres psql -U postgres -d postgres
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
.PHONY: help up down restart logs api-logs db db-list db-seed db-seed-model-resources db-seed-assistants db-clear db-reset db-drop-schema db-reset-hard
|
||||
.PHONY: help up down restart logs api-logs db db-list db-up db-schema db-seed-interface-definitions db-init db-seed db-seed-model-resources db-seed-assistants db-clear db-reset db-drop-schema db-reset-hard
|
||||
|
||||
help: ## 列出所有可用目标
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \
|
||||
@@ -37,22 +37,31 @@ db-list: ## 列出模型资源与助手
|
||||
@$(PSQL) -c "SELECT id, name, capability, interface_type, is_default FROM model_resources ORDER BY id;"
|
||||
@$(PSQL) -c "SELECT id, name, type FROM assistants ORDER BY id;"
|
||||
|
||||
db-up: ## 仅启动 postgres
|
||||
docker compose up -d postgres
|
||||
|
||||
db-schema: ## 用纯 SQL 建表/补齐开发库表结构
|
||||
$(PSQL) < backend/db/schema.sql
|
||||
|
||||
db-seed-interface-definitions: ## 灌入接口定义种子(幂等)
|
||||
$(PSQL) < backend/db/seed_interface_definitions.sql
|
||||
|
||||
db-init: db-schema db-seed-interface-definitions ## 用纯 SQL 建表并同步接口定义
|
||||
|
||||
db-seed-model-resources: ## 灌入 12 条模型资源种子(幂等)
|
||||
$(PSQL) < backend/db/seed_model_resources.sql
|
||||
|
||||
db-seed-assistants: ## 灌入 知识库 + 助手 种子(幂等;依赖模型资源已就绪)
|
||||
$(PSQL) < backend/db/seed_assistants.sql
|
||||
|
||||
db-seed: db-seed-model-resources db-seed-assistants ## 全量灌种子(模型资源→知识库→助手,幂等,可重复执行)
|
||||
db-seed: db-init db-seed-model-resources db-seed-assistants ## 全量灌种子(接口定义→模型资源→知识库→助手,幂等,可重复执行)
|
||||
|
||||
db-clear: ## 清空 助手/知识库/模型资源(按依赖顺序;表不存在时跳过)
|
||||
@$(PSQL) -c "DO \$$\$$ BEGIN IF EXISTS (SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'model_resources') THEN TRUNCATE assistant_model_bindings, assistants, knowledge_bases, model_resources CASCADE; END IF; END \$$\$$;"
|
||||
|
||||
db-reset: db-clear db-seed ## 清空后重新灌全部种子
|
||||
db-reset: db-up db-clear db-seed ## 启动库、建表/清空后重新灌全部种子
|
||||
|
||||
db-drop-schema: ## 删除并重建 public schema(开发用:表结构变更后会清空全部数据)
|
||||
@$(PSQL) -c "DROP SCHEMA IF EXISTS public CASCADE; CREATE SCHEMA public;"
|
||||
|
||||
db-reset-hard: db-drop-schema restart ## 重建 schema,重启 api 让 init_db 建表,再灌种子
|
||||
sleep 3
|
||||
$(MAKE) db-seed
|
||||
db-reset-hard: db-up db-drop-schema db-seed ## 启动库、重建 schema,建表,再灌种子
|
||||
|
||||
91
backend/db/schema.sql
Normal file
91
backend/db/schema.sql
Normal file
@@ -0,0 +1,91 @@
|
||||
-- Development schema bootstrap. Keep this in sync with backend/db/models.py.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS interface_definitions (
|
||||
interface_type VARCHAR(64) PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
capability VARCHAR(16) NOT NULL,
|
||||
field_schema JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
version INTEGER NOT NULL DEFAULT 1,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS model_resources (
|
||||
id VARCHAR(40) PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL DEFAULT '',
|
||||
capability VARCHAR(16) NOT NULL,
|
||||
interface_type VARCHAR(64) NOT NULL REFERENCES interface_definitions(interface_type) ON DELETE RESTRICT,
|
||||
values JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
secrets JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
support_image_input BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
is_default BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS knowledge_bases (
|
||||
id VARCHAR(40) PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(2048) NOT NULL DEFAULT '',
|
||||
embedding_model_resource_id VARCHAR(40) REFERENCES model_resources(id) ON DELETE SET NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS assistants (
|
||||
id VARCHAR(40) PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(16) NOT NULL DEFAULT 'prompt',
|
||||
runtime_mode VARCHAR(16) NOT NULL DEFAULT 'pipeline',
|
||||
greeting VARCHAR(2048) NOT NULL DEFAULT '',
|
||||
enable_interrupt BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
vision_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
vision_model_resource_id VARCHAR(40) REFERENCES model_resources(id) ON DELETE SET NULL,
|
||||
knowledge_base_id VARCHAR(40) REFERENCES knowledge_bases(id) ON DELETE RESTRICT,
|
||||
prompt VARCHAR(8192) NOT NULL DEFAULT '',
|
||||
api_url VARCHAR(512) NOT NULL DEFAULT '',
|
||||
api_key VARCHAR(512) NOT NULL DEFAULT '',
|
||||
app_id VARCHAR(128) NOT NULL DEFAULT '',
|
||||
graph JSON NOT NULL DEFAULT '{}'::json,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS assistant_model_bindings (
|
||||
assistant_id VARCHAR(40) NOT NULL REFERENCES assistants(id) ON DELETE CASCADE,
|
||||
capability VARCHAR(16) NOT NULL,
|
||||
model_resource_id VARCHAR(40) NOT NULL REFERENCES model_resources(id) ON DELETE RESTRICT,
|
||||
config JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (assistant_id, capability)
|
||||
);
|
||||
|
||||
-- Lightweight forward-compatible patches for existing dev databases.
|
||||
ALTER TABLE interface_definitions
|
||||
ALTER COLUMN field_schema TYPE JSONB USING field_schema::jsonb;
|
||||
|
||||
ALTER TABLE assistants
|
||||
ADD COLUMN IF NOT EXISTS vision_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
ADD COLUMN IF NOT EXISTS vision_model_resource_id VARCHAR(40);
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'assistants_vision_model_resource_id_fkey'
|
||||
) THEN
|
||||
ALTER TABLE assistants
|
||||
ADD CONSTRAINT assistants_vision_model_resource_id_fkey
|
||||
FOREIGN KEY (vision_model_resource_id) REFERENCES model_resources(id) ON DELETE SET NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_interface_definitions_capability ON interface_definitions(capability);
|
||||
CREATE INDEX IF NOT EXISTS ix_model_resources_capability ON model_resources(capability);
|
||||
CREATE INDEX IF NOT EXISTS ix_model_resources_interface_type ON model_resources(interface_type);
|
||||
CREATE INDEX IF NOT EXISTS ix_knowledge_bases_embedding_model_resource_id ON knowledge_bases(embedding_model_resource_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_assistants_type ON assistants(type);
|
||||
CREATE INDEX IF NOT EXISTS ix_assistant_model_bindings_model_resource_id ON assistant_model_bindings(model_resource_id);
|
||||
119
backend/db/seed_interface_definitions.sql
Normal file
119
backend/db/seed_interface_definitions.sql
Normal file
@@ -0,0 +1,119 @@
|
||||
-- Built-in interface definitions. Mirrors backend/services/interface_catalog.py.
|
||||
|
||||
INSERT INTO interface_definitions
|
||||
(interface_type, name, capability, field_schema, enabled, version)
|
||||
VALUES
|
||||
('openai-llm', 'OpenAI Compatible LLM', 'LLM',
|
||||
$$ {"fields":[
|
||||
{"key":"modelId","label":"Model ID","group":"values","type":"text","required":true},
|
||||
{"key":"apiUrl","label":"API URL","group":"values","type":"url","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true},
|
||||
{"key":"temperature","label":"Temperature","group":"values","type":"number","required":false,"default":0.7}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('openai-asr', 'OpenAI Compatible ASR', 'ASR',
|
||||
$$ {"fields":[
|
||||
{"key":"modelId","label":"Model ID","group":"values","type":"text","required":true},
|
||||
{"key":"apiUrl","label":"API URL","group":"values","type":"url","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true},
|
||||
{"key":"language","label":"Language","group":"values","type":"text","required":false,"default":"zh"}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('openai-tts', 'OpenAI Compatible TTS', 'TTS',
|
||||
$$ {"fields":[
|
||||
{"key":"modelId","label":"Model ID","group":"values","type":"text","required":true},
|
||||
{"key":"apiUrl","label":"API URL","group":"values","type":"url","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true},
|
||||
{"key":"voice","label":"Voice","group":"values","type":"text","required":false},
|
||||
{"key":"speed","label":"Speed","group":"values","type":"number","required":false,"default":1.0},
|
||||
{"key":"sourceSampleRate","label":"Source Sample Rate","group":"values","type":"number","required":false,"default":24000}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('openai-embedding', 'OpenAI Compatible Embedding', 'Embedding',
|
||||
$$ {"fields":[
|
||||
{"key":"modelId","label":"Model ID","group":"values","type":"text","required":true},
|
||||
{"key":"apiUrl","label":"API URL","group":"values","type":"url","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true},
|
||||
{"key":"dimensions","label":"Dimensions","group":"values","type":"number","required":false}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('openai-realtime', 'OpenAI Realtime', 'Realtime',
|
||||
$$ {"fields":[
|
||||
{"key":"modelId","label":"Model ID","group":"values","type":"text","required":true},
|
||||
{"key":"apiUrl","label":"API URL","group":"values","type":"url","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true},
|
||||
{"key":"voice","label":"Voice","group":"values","type":"text","required":false}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('stepfun-realtime', 'StepFun StepAudio Realtime', 'Realtime',
|
||||
$$ {"fields":[
|
||||
{"key":"modelId","label":"Model ID","group":"values","type":"text","required":true},
|
||||
{"key":"apiUrl","label":"API URL","group":"values","type":"url","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true},
|
||||
{"key":"voice","label":"Voice","group":"values","type":"text","required":true,"default":"linjiajiejie"},
|
||||
{"key":"inputSampleRate","label":"Input Sample Rate","group":"values","type":"number","required":false,"default":24000},
|
||||
{"key":"outputSampleRate","label":"Output Sample Rate","group":"values","type":"number","required":false,"default":24000},
|
||||
{"key":"prefixPaddingMs","label":"VAD Prefix Padding (ms)","group":"values","type":"number","required":false,"default":500},
|
||||
{"key":"silenceDurationMs","label":"VAD Silence Duration (ms)","group":"values","type":"number","required":false,"default":300},
|
||||
{"key":"energyAwakenessThreshold","label":"VAD Energy Threshold","group":"values","type":"number","required":false,"default":2500}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('xfyun-asr', 'Xfyun Streaming ASR', 'ASR',
|
||||
$$ {"fields":[
|
||||
{"key":"apiUrl","label":"WebSocket URL","group":"values","type":"url","required":true},
|
||||
{"key":"appId","label":"App ID","group":"secrets","type":"password","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true},
|
||||
{"key":"apiSecret","label":"API Secret","group":"secrets","type":"password","required":true},
|
||||
{"key":"language","label":"Language","group":"values","type":"text","required":false,"default":"zh_cn"},
|
||||
{"key":"domain","label":"Domain","group":"values","type":"text","required":false,"default":"iat"},
|
||||
{"key":"accent","label":"Accent","group":"values","type":"text","required":false,"default":"mandarin"},
|
||||
{"key":"dynamicCorrection","label":"Dynamic Correction","group":"values","type":"boolean","required":false,"default":false},
|
||||
{"key":"frameSize","label":"Frame Size","group":"values","type":"number","required":false,"default":1280}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('xfyun-tts', 'Xfyun TTS', 'TTS',
|
||||
$$ {"fields":[
|
||||
{"key":"apiUrl","label":"WebSocket URL","group":"values","type":"url","required":true},
|
||||
{"key":"appId","label":"App ID","group":"secrets","type":"password","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true},
|
||||
{"key":"apiSecret","label":"API Secret","group":"secrets","type":"password","required":true},
|
||||
{"key":"voice","label":"Voice","group":"values","type":"text","required":false},
|
||||
{"key":"speed","label":"Speed","group":"values","type":"number","required":false,"default":50},
|
||||
{"key":"volume","label":"Volume","group":"values","type":"number","required":false,"default":50},
|
||||
{"key":"pitch","label":"Pitch","group":"values","type":"number","required":false,"default":50},
|
||||
{"key":"sourceSampleRate","label":"Source Sample Rate","group":"values","type":"number","required":false,"default":16000}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('xfyun-super-tts', 'Xfyun Super TTS', 'TTS',
|
||||
$$ {"fields":[
|
||||
{"key":"apiUrl","label":"WebSocket URL","group":"values","type":"url","required":true},
|
||||
{"key":"appId","label":"App ID","group":"secrets","type":"password","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true},
|
||||
{"key":"apiSecret","label":"API Secret","group":"secrets","type":"password","required":true},
|
||||
{"key":"voice","label":"Voice","group":"values","type":"text","required":false},
|
||||
{"key":"speed","label":"Speed","group":"values","type":"number","required":false,"default":50},
|
||||
{"key":"volume","label":"Volume","group":"values","type":"number","required":false,"default":50},
|
||||
{"key":"pitch","label":"Pitch","group":"values","type":"number","required":false,"default":50},
|
||||
{"key":"oralLevel","label":"Oral Level","group":"values","type":"text","required":false,"default":"mid"},
|
||||
{"key":"sourceSampleRate","label":"Source Sample Rate","group":"values","type":"number","required":false,"default":24000},
|
||||
{"key":"textAggregationMode","label":"Text Aggregation Mode","group":"values","type":"text","required":false,"default":"token"}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('dashscope-asr', 'DashScope ASR', 'ASR',
|
||||
$$ {"fields":[
|
||||
{"key":"modelId","label":"Model ID","group":"values","type":"text","required":true},
|
||||
{"key":"apiUrl","label":"API URL","group":"values","type":"url","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true},
|
||||
{"key":"language","label":"Language","group":"values","type":"text","required":false,"default":"zh"}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('dashscope-tts', 'DashScope TTS', 'TTS',
|
||||
$$ {"fields":[
|
||||
{"key":"modelId","label":"Model ID","group":"values","type":"text","required":true},
|
||||
{"key":"apiUrl","label":"API URL","group":"values","type":"url","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true},
|
||||
{"key":"voice","label":"Voice","group":"values","type":"text","required":false}
|
||||
]} $$::jsonb, TRUE, 1),
|
||||
('gemini-realtime', 'Gemini Realtime', 'Realtime',
|
||||
$$ {"fields":[
|
||||
{"key":"modelId","label":"Model ID","group":"values","type":"text","required":true},
|
||||
{"key":"apiUrl","label":"API URL","group":"values","type":"url","required":true},
|
||||
{"key":"apiKey","label":"API Key","group":"secrets","type":"password","required":true}
|
||||
]} $$::jsonb, TRUE, 1)
|
||||
ON CONFLICT (interface_type) DO UPDATE SET
|
||||
name = EXCLUDED.name,
|
||||
capability = EXCLUDED.capability,
|
||||
field_schema = EXCLUDED.field_schema,
|
||||
enabled = TRUE,
|
||||
version = EXCLUDED.version,
|
||||
updated_at = now();
|
||||
@@ -1,4 +1,4 @@
|
||||
-- 模型资源种子数据。依赖应用启动时写入 interface_definitions。
|
||||
-- 模型资源种子数据。依赖 seed_interface_definitions.sql。
|
||||
|
||||
INSERT INTO model_resources
|
||||
(id, name, capability, interface_type, values, secrets, support_image_input, enabled, is_default)
|
||||
|
||||
Reference in New Issue
Block a user