diff --git a/INTEGRATION-SETTINGS-WORKFLOW.md b/INTEGRATION-SETTINGS-WORKFLOW.md new file mode 100644 index 00000000..167b6187 --- /dev/null +++ b/INTEGRATION-SETTINGS-WORKFLOW.md @@ -0,0 +1,223 @@ +# Integration Settings Workflow & Data Flow + +## Part 1: How Global Settings Load on Frontend + +### Admin Configures Global Settings +**URL**: `https://api.igny8.com/admin/system/globalintegrationsettings/1/change/` + +**What's Stored**: +- Platform-wide API keys (OpenAI, DALL-E, Runware) +- Default model selections (gpt-4o-mini, dall-e-3, runware:97@1) +- Default parameters (temperature: 0.7, max_tokens: 8192) +- Default image settings (size, quality, style) + +**Who Can Access**: Only platform administrators + +### Normal User Opens Integration Page +**URL**: `https://app.igny8.com/settings/integration` + +**What Happens**: + +1. **Frontend Request**: + - User browser requests: `GET /api/v1/system/settings/integrations/openai/` + - User browser requests: `GET /api/v1/system/settings/integrations/image_generation/` + +2. **Backend Processing** (`integration_views.py` - `get_settings()` method): + - Checks if user's account has custom overrides in `IntegrationSettings` table + - Gets global defaults from `GlobalIntegrationSettings` singleton + - Merges data with this priority: + - If account has overrides → use account settings + - If no overrides → use global defaults + - **NEVER returns API keys** (security) + +3. **Response to Frontend**: + ``` + { + "id": "openai", + "enabled": true, + "model": "gpt-4o-mini", // From global OR account override + "temperature": 0.7, // From global OR account override + "max_tokens": 8192, // From global OR account override + "using_global": true // Flag: true if using defaults + } + ``` + +4. **Frontend Display**: + - Shows current model selection + - Shows "Using platform defaults" badge if `using_global: true` + - Shows "Custom settings" badge if `using_global: false` + - User can change model, temperature, etc. + - **API key status is NOT shown** (user cannot see/change platform keys) + +--- + +## Part 2: How User Changes Are Saved + +### User Changes Settings on Frontend + +1. **User Actions**: + - Opens settings modal + - Changes model from `gpt-4o-mini` to `gpt-4o` + - Changes temperature from `0.7` to `0.8` + - Clicks "Save" + +2. **Frontend Request**: + - Sends: `PUT /api/v1/system/settings/integrations/openai/` + - Body: `{"model": "gpt-4o", "temperature": 0.8, "max_tokens": 8192}` + +3. **Backend Processing** (`integration_views.py` - `save_settings()` method): + - **CRITICAL SECURITY**: Strips ANY API keys from request (apiKey, api_key, openai_api_key, etc.) + - Validates account exists + - Builds clean config with ONLY allowed overrides: + - For OpenAI: model, temperature, max_tokens + - For Image: service, model, image_quality, image_style, sizes + - Saves to `IntegrationSettings` table: + ``` + account_id: 123 + integration_type: "openai" + config: {"model": "gpt-4o", "temperature": 0.8, "max_tokens": 8192} + is_active: true + ``` + +4. **Database Structure**: + - **GlobalIntegrationSettings** (1 row, pk=1): + - Contains: API keys + default settings + - Used by: ALL accounts for API keys + + - **IntegrationSettings** (multiple rows): + - Row per account per integration type + - Contains: ONLY overrides (no API keys) + - Example: + ``` + id | account_id | integration_type | config + 100 | 123 | openai | {"model": "gpt-4o", "temperature": 0.8} + 101 | 456 | openai | {"model": "gpt-4.1", "max_tokens": 4000} + 102 | 123 | image_generation| {"service": "runware", "model": "runware:100@1"} + ``` + +5. **Next Request from User**: + - Frontend requests: `GET /api/v1/system/settings/integrations/openai/` + - Backend finds IntegrationSettings row for account 123 + - Returns: `{"model": "gpt-4o", "temperature": 0.8, "using_global": false}` + - User sees their custom settings + +--- + +## Data Flow Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ ADMIN SIDE │ +│ https://api.igny8.com/admin/ │ +│ │ +│ GlobalIntegrationSettings (pk=1) │ +│ ├── openai_api_key: "sk-xxx" ← Platform-wide │ +│ ├── openai_model: "gpt-4o-mini" ← Default │ +│ ├── openai_temperature: 0.7 ← Default │ +│ ├── dalle_api_key: "sk-xxx" ← Platform-wide │ +│ ├── runware_api_key: "xxx" ← Platform-wide │ +│ └── image_quality: "standard" ← Default │ +└─────────────────────────────────────────────────────────────┘ + │ + │ Backend reads from + ↓ +┌─────────────────────────────────────────────────────────────┐ +│ BACKEND API LAYER │ +│ integration_views.py │ +│ │ +│ get_settings(): │ +│ 1. Load GlobalIntegrationSettings (for defaults) │ +│ 2. Check IntegrationSettings (for account overrides) │ +│ 3. Merge: account overrides > global defaults │ +│ 4. Return to frontend (NO API keys) │ +│ │ +│ save_settings(): │ +│ 1. Receive request from frontend │ +│ 2. Strip ALL API keys (security) │ +│ 3. Save ONLY overrides to IntegrationSettings │ +└─────────────────────────────────────────────────────────────┘ + │ + │ API sends data + ↓ +┌─────────────────────────────────────────────────────────────┐ +│ FRONTEND - USER SIDE │ +│ https://app.igny8.com/settings/integration │ +│ │ +│ User sees: │ +│ ├── Model: gpt-4o-mini (dropdown) │ +│ ├── Temperature: 0.7 (slider) │ +│ ├── Status: ✓ Connected (test connection works) │ +│ └── Badge: "Using platform defaults" │ +│ │ +│ User CANNOT see: │ +│ ✗ API keys (security) │ +│ ✗ Platform configuration │ +└─────────────────────────────────────────────────────────────┘ + │ + │ User changes settings + ↓ +┌─────────────────────────────────────────────────────────────┐ +│ IntegrationSettings Table │ +│ (Per-account overrides - NO API KEYS) │ +│ │ +│ Account 123: │ +│ ├── openai: {"model": "gpt-4o", "temperature": 0.8} │ +│ └── image_generation: {"service": "runware"} │ +│ │ +│ Account 456: │ +│ ├── openai: {"model": "gpt-4.1"} │ +│ └── image_generation: (no row = uses global defaults) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Important Security Rules + +1. **API Keys Flow**: + - Admin sets → GlobalIntegrationSettings + - Backend uses → For ALL accounts + - Frontend NEVER sees → Security + - Users NEVER save → Stripped by backend + +2. **Settings Flow**: + - Admin sets defaults → GlobalIntegrationSettings + - Users customize → IntegrationSettings (overrides only) + - Backend merges → Global defaults + account overrides + - Frontend displays → Merged result (no keys) + +3. **Free Plan Restriction**: + - Cannot create IntegrationSettings rows + - Must use global defaults only + - Enforced at frontend (UI disabled) + - TODO: Add backend validation + +--- + +## Example Scenarios + +### Scenario 1: New User First Visit +- User has NO IntegrationSettings row +- Backend returns global defaults +- `using_global: true` +- User sees platform defaults +- API operations use platform API key + +### Scenario 2: User Customizes Model +- User changes model to "gpt-4o" +- Frontend sends: `{"model": "gpt-4o"}` +- Backend creates IntegrationSettings row +- Next visit: `using_global: false` +- API operations use platform API key + user's model choice + +### Scenario 3: User Resets to Default +- Frontend sends: `{"model": "gpt-4o-mini"}` (same as global) +- Backend still saves override row +- Alternative: Delete row to truly use global +- TODO: Add "Reset to defaults" button + +### Scenario 4: Admin Changes Global Default +- Admin changes global model to "gpt-4.1" +- Users WITH overrides: See their custom model +- Users WITHOUT overrides: See new "gpt-4.1" default +- All users: Use platform API key diff --git a/backend/igny8_core/admin/site.py b/backend/igny8_core/admin/site.py index f995d12d..fe3a383c 100644 --- a/backend/igny8_core/admin/site.py +++ b/backend/igny8_core/admin/site.py @@ -157,6 +157,14 @@ class Igny8AdminSite(UnfoldAdminSite): ('igny8_core_auth', 'SeedKeyword'), ], }, + 'Global Settings': { + 'models': [ + ('system', 'GlobalIntegrationSettings'), + ('system', 'GlobalAIPrompt'), + ('system', 'GlobalAuthorProfile'), + ('system', 'GlobalStrategy'), + ], + }, 'Plans and Billing': { 'models': [ ('igny8_core_auth', 'Plan'), diff --git a/backend/igny8_core/ai/prompts.py b/backend/igny8_core/ai/prompts.py index d95e65bf..8d021f7c 100644 --- a/backend/igny8_core/ai/prompts.py +++ b/backend/igny8_core/ai/prompts.py @@ -1,6 +1,6 @@ """ Prompt Registry - Centralized prompt management with override hierarchy -Supports: task-level overrides → DB prompts → default fallbacks +Supports: task-level overrides → DB prompts → GlobalAIPrompt (REQUIRED) """ import logging from typing import Dict, Any, Optional @@ -14,583 +14,11 @@ class PromptRegistry: Centralized prompt registry with hierarchical resolution: 1. Task-level prompt_override (if exists) 2. DB prompt for (account, function) - 3. Default fallback from registry + 3. GlobalAIPrompt (REQUIRED - no hardcoded fallbacks) """ - # Default prompts stored in registry - DEFAULT_PROMPTS = { - 'clustering': """You are a semantic strategist and SEO architecture engine. Your task is to analyze the provided keyword list and group them into meaningful, intent-driven topic clusters that reflect how real users search, think, and act online. - -Return a single JSON object with a "clusters" array. Each cluster must follow this structure: - -{ - "name": "[Descriptive cluster name — natural, SEO-relevant, clearly expressing the topic]", - "description": "[1–2 concise sentences explaining what this cluster covers and why these keywords belong together]", - "keywords": ["keyword 1", "keyword 2", "keyword 3", "..."] -} - -CLUSTERING STRATEGY: - -1. Keyword-first, structure-follows: - - Do NOT rely on assumed categories or existing content structures. - - Begin purely from the meaning, intent, and behavioral connection between keywords. - -2. Use multi-dimensional grouping logic: - - Group keywords by these behavioral dimensions: - • Search Intent → informational, commercial, transactional, navigational - • Use-Case or Problem → what the user is trying to achieve or solve - • Function or Feature → how something works or what it does - • Persona or Audience → who the content or product serves - • Context → location, time, season, platform, or device - - Combine 2–3 dimensions naturally where they make sense. - -3. Model real search behavior: - - Favor clusters that form natural user journeys such as: - • Problem ➝ Solution - • General ➝ Specific - • Product ➝ Use-case - • Buyer ➝ Benefit - • Tool ➝ Function - • Task ➝ Method - - Each cluster should feel like a real topic hub users would explore in depth. - -4. Avoid superficial groupings: - - Do not cluster keywords just because they share words. - - Do not force-fit outliers or unrelated keywords. - - Exclude keywords that don't logically connect to any cluster. - -5. Quality rules: - - Each cluster should include between 3–10 strongly related keywords. - - Never duplicate a keyword across multiple clusters. - - Prioritize semantic strength, search intent, and usefulness for SEO-driven content structure. - - It's better to output fewer, high-quality clusters than many weak or shallow ones. - -INPUT FORMAT: -{ - "keywords": [IGNY8_KEYWORDS] -} - -OUTPUT FORMAT: -Return ONLY the final JSON object in this format: -{ - "clusters": [ - { - "name": "...", - "description": "...", - "keywords": ["...", "...", "..."] - } - ] -} - -Do not include any explanations, text, or commentary outside the JSON output. -""", - - 'ideas': """Generate SEO-optimized, high-quality content ideas and outlines for each keyword cluster. -Input: -Clusters: [IGNY8_CLUSTERS] -Keywords: [IGNY8_CLUSTER_KEYWORDS] - -Output: JSON with "ideas" array. -Each cluster → 1 cluster_hub + 2–4 supporting ideas. -Each idea must include: -title, description, content_type, content_structure, cluster_id, estimated_word_count (1500–2200), and covered_keywords. - -Outline Rules: - -Intro: 1 hook (30–40 words) + 2 intro paragraphs (50–60 words each). - -5–8 H2 sections, each with 2–3 H3s. - -Each H2 ≈ 250–300 words, mixed content (paragraphs, lists, tables, blockquotes). - -Vary section format and tone; no bullets or lists at start. - -Tables have columns; blockquotes = expert POV or data insight. - -Use depth, examples, and real context. - -Avoid repetitive structure. - -Tone: Professional editorial flow. No generic phrasing. Use varied sentence openings and realistic examples. - -Output JSON Example: - -{ - "ideas": [ - { - "title": "Best Organic Cotton Duvet Covers for All Seasons", - "description": { - "introduction": { - "hook": "Transform your sleep with organic cotton that blends comfort and sustainability.", - "paragraphs": [ - {"format": "paragraph", "details": "Overview of organic cotton's rise in bedding industry."}, - {"format": "paragraph", "details": "Why consumers prefer organic bedding over synthetic alternatives."} - ] - }, - "H2": [ - { - "heading": "Why Choose Organic Cotton for Bedding?", - "subsections": [ - {"subheading": "Health and Skin Benefits", "format": "paragraph", "details": "Discuss hypoallergenic and chemical-free aspects."}, - {"subheading": "Environmental Sustainability", "format": "list", "details": "Eco benefits like low water use, no pesticides."}, - {"subheading": "Long-Term Cost Savings", "format": "table", "details": "Compare durability and pricing over time."} - ] - } - ] - }, - "content_type": "post", - "content_structure": "review", - "cluster_id": 12, - "estimated_word_count": 1800, - "covered_keywords": "organic duvet covers, eco-friendly bedding, sustainable sheets" - } - ] -} - -Valid content_type values: post, page, product, taxonomy - -Valid content_structure by type: -- post: article, guide, comparison, review, listicle -- page: landing_page, business_page, service_page, general, cluster_hub -- product: product_page -- taxonomy: category_archive, tag_archive, attribute_archive""", - - 'content_generation': """You are an editorial content strategist. Your task is to generate a complete JSON response object based on the provided content idea, keyword cluster, keyword list, and metadata context. - -================== -Generate a complete JSON response object matching this structure: -================== - -{ - "title": "[Article title using target keywords — full sentence case]", - "content": "[HTML content — full editorial structure with

,

,

,