feat: unified prompt system migration schema (Issue #28 Phase 1)
- Migration 020: Add type, stages, output_format columns to ai_prompts
- Migrate existing prompts to 1-stage pipeline format
- Migrate pipeline_configs into ai_prompts as multi-stage pipelines
- Add UnifiedPrompt Pydantic models for new API
- Backup pipeline_configs table (keep during transition)
Schema structure:
- type: 'base' (reusable) or 'pipeline' (multi-stage)
- stages: JSONB array [{stage:1, prompts:[{source, slug, template, output_key, output_format}]}]
- output_format: 'text' or 'json'
- output_schema: JSON validation schema (optional)
Next: Backend executor + Frontend UI consolidation
This commit is contained in:
parent
1b7fdb1739
commit
2e0838ca08
124
backend/migrations/020_unified_prompt_system.sql
Normal file
124
backend/migrations/020_unified_prompt_system.sql
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
-- Migration 020: Unified Prompt System (Issue #28)
|
||||
-- Consolidate ai_prompts and pipeline_configs into single system
|
||||
-- Type: 'base' (reusable building blocks) or 'pipeline' (workflows)
|
||||
|
||||
-- Step 1: Add new columns to ai_prompts
|
||||
ALTER TABLE ai_prompts
|
||||
ADD COLUMN IF NOT EXISTS type VARCHAR(20) DEFAULT 'pipeline',
|
||||
ADD COLUMN IF NOT EXISTS stages JSONB,
|
||||
ADD COLUMN IF NOT EXISTS output_format VARCHAR(10) DEFAULT 'text',
|
||||
ADD COLUMN IF NOT EXISTS output_schema JSONB;
|
||||
|
||||
-- Step 2: Migrate existing single-prompts to 1-stage pipeline format
|
||||
-- All existing prompts become single-stage pipelines with inline source
|
||||
UPDATE ai_prompts
|
||||
SET
|
||||
type = 'pipeline',
|
||||
stages = jsonb_build_array(
|
||||
jsonb_build_object(
|
||||
'stage', 1,
|
||||
'prompts', jsonb_build_array(
|
||||
jsonb_build_object(
|
||||
'source', 'inline',
|
||||
'template', template,
|
||||
'output_key', REPLACE(slug, 'pipeline_', ''),
|
||||
'output_format', 'text'
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
output_format = 'text'
|
||||
WHERE stages IS NULL;
|
||||
|
||||
-- Step 3: Migrate pipeline_configs into ai_prompts as multi-stage pipelines
|
||||
-- Each pipeline_config becomes a pipeline-type prompt with multiple stages
|
||||
INSERT INTO ai_prompts (
|
||||
slug,
|
||||
name,
|
||||
description,
|
||||
type,
|
||||
stages,
|
||||
output_format,
|
||||
active,
|
||||
is_system_default,
|
||||
category
|
||||
)
|
||||
SELECT
|
||||
'pipeline_config_' || LOWER(REPLACE(name, ' ', '_')) || '_' || SUBSTRING(id::TEXT FROM 1 FOR 8),
|
||||
name,
|
||||
description,
|
||||
'pipeline',
|
||||
-- Build stages JSONB structure
|
||||
(
|
||||
SELECT jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'stage', stage_num,
|
||||
'prompts', stage_prompts
|
||||
)
|
||||
ORDER BY stage_num
|
||||
)
|
||||
FROM (
|
||||
-- Stage 1: All parallel prompts
|
||||
SELECT
|
||||
1 as stage_num,
|
||||
jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'source', 'reference',
|
||||
'slug', stage1_slug,
|
||||
'output_key', REPLACE(stage1_slug, 'pipeline_', 'stage1_'),
|
||||
'output_format', 'json'
|
||||
)
|
||||
) as stage_prompts
|
||||
FROM UNNEST(pc.stage1_prompts) as stage1_slug
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Stage 2: Synthesis prompt
|
||||
SELECT
|
||||
2 as stage_num,
|
||||
jsonb_build_array(
|
||||
jsonb_build_object(
|
||||
'source', 'reference',
|
||||
'slug', pc.stage2_prompt,
|
||||
'output_key', 'synthesis',
|
||||
'output_format', 'text'
|
||||
)
|
||||
) as stage_prompts
|
||||
WHERE pc.stage2_prompt IS NOT NULL
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Stage 3: Optional goals/analysis prompt
|
||||
SELECT
|
||||
3 as stage_num,
|
||||
jsonb_build_array(
|
||||
jsonb_build_object(
|
||||
'source', 'reference',
|
||||
'slug', pc.stage3_prompt,
|
||||
'output_key', 'goals',
|
||||
'output_format', 'text'
|
||||
)
|
||||
) as stage_prompts
|
||||
WHERE pc.stage3_prompt IS NOT NULL
|
||||
) stages_union
|
||||
) as stages,
|
||||
'text',
|
||||
pc.active,
|
||||
pc.is_default,
|
||||
'pipeline'
|
||||
FROM pipeline_configs pc
|
||||
WHERE pc.id IS NOT NULL;
|
||||
|
||||
-- Step 4: Add indices for performance
|
||||
CREATE INDEX IF NOT EXISTS idx_ai_prompts_type ON ai_prompts(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_ai_prompts_stages ON ai_prompts USING GIN (stages);
|
||||
|
||||
-- Step 5: Add comment explaining stages structure
|
||||
COMMENT ON COLUMN ai_prompts.stages IS 'JSONB array of stages, each with prompts array. Structure: [{"stage":1,"prompts":[{"source":"reference|inline","slug":"...","template":"...","output_key":"key","output_format":"text|json"}]}]';
|
||||
|
||||
-- Step 6: Backup pipeline_configs before eventual deletion
|
||||
CREATE TABLE IF NOT EXISTS pipeline_configs_backup_pre_020 AS
|
||||
SELECT * FROM pipeline_configs;
|
||||
|
||||
-- Note: We keep pipeline_configs table for now during transition period
|
||||
-- It can be dropped in a later migration once all code is migrated
|
||||
|
|
@ -158,7 +158,61 @@ class PromptGenerateRequest(BaseModel):
|
|||
example_output: Optional[str] = None
|
||||
|
||||
|
||||
# ── Unified Prompt System Models (Issue #28 Phase 2) ───────────────────────
|
||||
|
||||
class StagePromptCreate(BaseModel):
|
||||
"""Single prompt within a stage"""
|
||||
source: str # 'inline' or 'reference'
|
||||
slug: Optional[str] = None # Required if source='reference'
|
||||
template: Optional[str] = None # Required if source='inline'
|
||||
output_key: str # Key for storing result (e.g., 'nutrition', 'stage1_body')
|
||||
output_format: str = 'text' # 'text' or 'json'
|
||||
output_schema: Optional[dict] = None # JSON schema if output_format='json'
|
||||
|
||||
|
||||
class StageCreate(BaseModel):
|
||||
"""Single stage with multiple prompts"""
|
||||
stage: int # Stage number (1, 2, 3, ...)
|
||||
prompts: list[StagePromptCreate]
|
||||
|
||||
|
||||
class UnifiedPromptCreate(BaseModel):
|
||||
"""Create a new unified prompt (base or pipeline type)"""
|
||||
name: str
|
||||
slug: str
|
||||
display_name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
type: str # 'base' or 'pipeline'
|
||||
category: str = 'ganzheitlich'
|
||||
active: bool = True
|
||||
sort_order: int = 0
|
||||
|
||||
# For base prompts (single reusable template)
|
||||
template: Optional[str] = None # Required if type='base'
|
||||
output_format: str = 'text'
|
||||
output_schema: Optional[dict] = None
|
||||
|
||||
# For pipeline prompts (multi-stage workflow)
|
||||
stages: Optional[list[StageCreate]] = None # Required if type='pipeline'
|
||||
|
||||
|
||||
class UnifiedPromptUpdate(BaseModel):
|
||||
"""Update an existing unified prompt"""
|
||||
name: Optional[str] = None
|
||||
display_name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
type: Optional[str] = None
|
||||
category: Optional[str] = None
|
||||
active: Optional[bool] = None
|
||||
sort_order: Optional[int] = None
|
||||
template: Optional[str] = None
|
||||
output_format: Optional[str] = None
|
||||
output_schema: Optional[dict] = None
|
||||
stages: Optional[list[StageCreate]] = None
|
||||
|
||||
|
||||
# ── Pipeline Config Models (Issue #28) ─────────────────────────────────────
|
||||
# NOTE: These will be deprecated in favor of UnifiedPrompt models above
|
||||
|
||||
class PipelineConfigCreate(BaseModel):
|
||||
name: str
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user