sunee3 / PHASE4_DIFF.patch
sagarmythos
DevAI Studio HCBH v3.1 - clean deploy, no binaries
df6cd5e
Raw
History Blame Contribute Delete
98.7 kB
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/.pytest_cache/.gitignore devai_phase4/.pytest_cache/.gitignore
--- devai_phase4_orig/.pytest_cache/.gitignore 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/.pytest_cache/.gitignore 2026-07-06 10:28:54.061919928 +0000
@@ -0,0 +1,2 @@
+# Created by pytest automatically.
+*
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/.pytest_cache/CACHEDIR.TAG devai_phase4/.pytest_cache/CACHEDIR.TAG
--- devai_phase4_orig/.pytest_cache/CACHEDIR.TAG 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/.pytest_cache/CACHEDIR.TAG 2026-07-06 10:28:54.061919928 +0000
@@ -0,0 +1,4 @@
+Signature: 8a477f597d28d172789f06886806bc55
+# This file is a cache directory tag created by pytest.
+# For information about cache directory tags, see:
+# https://bford.info/cachedir/spec.html
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/.pytest_cache/README.md devai_phase4/.pytest_cache/README.md
--- devai_phase4_orig/.pytest_cache/README.md 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/.pytest_cache/README.md 2026-07-06 10:28:54.061919928 +0000
@@ -0,0 +1,8 @@
+# pytest cache directory #
+
+This directory contains data from the pytest's cache plugin,
+which provides the `--lf` and `--ff` options, as well as the `cache` fixture.
+
+**Do not** commit this to version control.
+
+See [the docs](https://docs.pytest.org/en/stable/how-to/cache.html) for more information.
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/.pytest_cache/v/cache/nodeids devai_phase4/.pytest_cache/v/cache/nodeids
--- devai_phase4_orig/.pytest_cache/v/cache/nodeids 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/.pytest_cache/v/cache/nodeids 2026-07-06 10:28:54.061919928 +0000
@@ -0,0 +1,5 @@
+[
+ "tests/test_phase4_docs_presets.py::test_guides_exist_and_share_same_core_sections",
+ "tests/test_phase4_docs_presets.py::test_onboarding_and_ui_hooks_present",
+ "tests/test_phase4_docs_presets.py::test_provider_presets_catalog_exists"
+]
\ No newline at end of file
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/CHANGELOG_v4.7.md devai_phase4/CHANGELOG_v4.7.md
--- devai_phase4_orig/CHANGELOG_v4.7.md 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/CHANGELOG_v4.7.md 2026-07-06 10:28:32.013919928 +0000
@@ -0,0 +1,24 @@
+# DevAI Studio v4.7 Combined Changelog
+
+## Phase 1 — BYOM foundation
+- Added secure encrypted storage for custom provider API keys.
+- Added custom router with provider-aware request building.
+- Added quota/error handling for custom models.
+
+## Phase 2 — Management UI
+- Added three-dot custom model management panel.
+- Added add/edit/delete flows for custom models.
+- Added routing rules editor and quota card visuals.
+
+## Phase 3 — Quota + fallback behavior
+- Added quota exhaustion handling.
+- Added local fallback / ask-for-new-key recovery paths.
+- Added extended tests and screenshots for save/edit/rules flows.
+
+## Phase 4 — Documentation & polish
+- Added `data/provider_presets.json` with curated providers and rule sets.
+- Added `ui/onboarding_tour.py` with driver.js first-run tour.
+- Added Hindi and English BYOM user guides.
+- Added migration guide and searchable troubleshooting KB.
+- Added About-tab docs hub and restart-tour control.
+- Added release assets, extra screenshots, diff patch, and a demo video storyboard.
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/PHASE4_DIFF.patch devai_phase4/PHASE4_DIFF.patch
--- devai_phase4_orig/PHASE4_DIFF.patch 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/PHASE4_DIFF.patch 2026-07-06 10:29:01.289919928 +0000
@@ -0,0 +1,64 @@
+diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/.pytest_cache/.gitignore devai_phase4/.pytest_cache/.gitignore
+--- devai_phase4_orig/.pytest_cache/.gitignore 1970-01-01 00:00:00.000000000 +0000
++++ devai_phase4/.pytest_cache/.gitignore 2026-07-06 10:28:54.061919928 +0000
+@@ -0,0 +1,2 @@
++# Created by pytest automatically.
++*
+diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/.pytest_cache/CACHEDIR.TAG devai_phase4/.pytest_cache/CACHEDIR.TAG
+--- devai_phase4_orig/.pytest_cache/CACHEDIR.TAG 1970-01-01 00:00:00.000000000 +0000
++++ devai_phase4/.pytest_cache/CACHEDIR.TAG 2026-07-06 10:28:54.061919928 +0000
+@@ -0,0 +1,4 @@
++Signature: 8a477f597d28d172789f06886806bc55
++# This file is a cache directory tag created by pytest.
++# For information about cache directory tags, see:
++# https://bford.info/cachedir/spec.html
+diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/.pytest_cache/README.md devai_phase4/.pytest_cache/README.md
+--- devai_phase4_orig/.pytest_cache/README.md 1970-01-01 00:00:00.000000000 +0000
++++ devai_phase4/.pytest_cache/README.md 2026-07-06 10:28:54.061919928 +0000
+@@ -0,0 +1,8 @@
++# pytest cache directory #
++
++This directory contains data from the pytest's cache plugin,
++which provides the `--lf` and `--ff` options, as well as the `cache` fixture.
++
++**Do not** commit this to version control.
++
++See [the docs](https://docs.pytest.org/en/stable/how-to/cache.html) for more information.
+diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/.pytest_cache/v/cache/nodeids devai_phase4/.pytest_cache/v/cache/nodeids
+--- devai_phase4_orig/.pytest_cache/v/cache/nodeids 1970-01-01 00:00:00.000000000 +0000
++++ devai_phase4/.pytest_cache/v/cache/nodeids 2026-07-06 10:28:54.061919928 +0000
+@@ -0,0 +1,5 @@
++[
++ "tests/test_phase4_docs_presets.py::test_guides_exist_and_share_same_core_sections",
++ "tests/test_phase4_docs_presets.py::test_onboarding_and_ui_hooks_present",
++ "tests/test_phase4_docs_presets.py::test_provider_presets_catalog_exists"
++]
+\ No newline at end of file
+diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/CHANGELOG_v4.7.md devai_phase4/CHANGELOG_v4.7.md
+--- devai_phase4_orig/CHANGELOG_v4.7.md 1970-01-01 00:00:00.000000000 +0000
++++ devai_phase4/CHANGELOG_v4.7.md 2026-07-06 10:28:32.013919928 +0000
+@@ -0,0 +1,24 @@
++# DevAI Studio v4.7 Combined Changelog
++
++## Phase 1 — BYOM foundation
++- Added secure encrypted storage for custom provider API keys.
++- Added custom router with provider-aware request building.
++- Added quota/error handling for custom models.
++
++## Phase 2 — Management UI
++- Added three-dot custom model management panel.
++- Added add/edit/delete flows for custom models.
++- Added routing rules editor and quota card visuals.
++
++## Phase 3 — Quota + fallback behavior
++- Added quota exhaustion handling.
++- Added local fallback / ask-for-new-key recovery paths.
++- Added extended tests and screenshots for save/edit/rules flows.
++
++## Phase 4 — Documentation & polish
++- Added `data/provider_presets.json` with curated providers and rule sets.
++- Added `ui/onboarding_tour.py` with driver.js first-run tour.
++- Added Hindi and English BYOM user guides.
++- Added migration guide and searchable troubleshooting KB.
++- Added About-tab docs hub and restart-tour control.
++- Added release assets, extra screenshots, diff patch, and a demo video storyboard.
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/data/provider_presets.json devai_phase4/data/provider_presets.json
--- devai_phase4_orig/data/provider_presets.json 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/data/provider_presets.json 2026-07-06 10:26:42.069919928 +0000
@@ -0,0 +1,286 @@
+{
+ "presets": [
+ {
+ "provider": "openrouter",
+ "display_name": "OpenRouter (100+ models)",
+ "description": "एक API key से 100+ models — Claude, GPT, Gemini, Llama सब",
+ "signup_url": "https://openrouter.ai/keys",
+ "docs_url": "https://openrouter.ai/docs",
+ "popular_models": [
+ {
+ "model_id": "anthropic/claude-3.5-sonnet",
+ "display_name": "Claude 3.5 Sonnet",
+ "best_for": [
+ "security",
+ "refactor",
+ "complex reasoning"
+ ],
+ "approx_cost_per_1k_tokens": {
+ "input": 0.003,
+ "output": 0.015
+ },
+ "context_window": 200000
+ },
+ {
+ "model_id": "anthropic/claude-3.5-haiku",
+ "display_name": "Claude 3.5 Haiku (fast+cheap)",
+ "best_for": [
+ "bug_fix",
+ "test_gen"
+ ],
+ "approx_cost_per_1k_tokens": {
+ "input": 0.001,
+ "output": 0.005
+ },
+ "context_window": 200000
+ },
+ {
+ "model_id": "openai/gpt-4o",
+ "display_name": "GPT-4o",
+ "best_for": [
+ "feature",
+ "new_app"
+ ],
+ "approx_cost_per_1k_tokens": {
+ "input": 0.0025,
+ "output": 0.01
+ },
+ "context_window": 128000
+ },
+ {
+ "model_id": "google/gemini-2.0-flash-exp:free",
+ "display_name": "Gemini 2.0 Flash (Free tier)",
+ "best_for": [
+ "any"
+ ],
+ "approx_cost_per_1k_tokens": {
+ "input": 0,
+ "output": 0
+ },
+ "context_window": 1000000,
+ "notes": "Free but rate-limited to 15 req/min"
+ },
+ {
+ "model_id": "deepseek/deepseek-r1",
+ "display_name": "DeepSeek R1 (reasoning)",
+ "best_for": [
+ "refactor",
+ "security"
+ ],
+ "approx_cost_per_1k_tokens": {
+ "input": 0.00055,
+ "output": 0.00219
+ },
+ "context_window": 64000
+ }
+ ]
+ },
+ {
+ "provider": "anthropic",
+ "display_name": "Anthropic (direct)",
+ "description": "Claude models directly — lowest latency, no OpenRouter markup",
+ "signup_url": "https://console.anthropic.com/",
+ "docs_url": "https://docs.anthropic.com/",
+ "popular_models": [
+ {
+ "model_id": "claude-3-5-sonnet-20241022",
+ "display_name": "Claude 3.5 Sonnet (latest)",
+ "best_for": [
+ "security",
+ "refactor",
+ "complex reasoning"
+ ],
+ "approx_cost_per_1k_tokens": {
+ "input": 0.003,
+ "output": 0.015
+ },
+ "context_window": 200000
+ },
+ {
+ "model_id": "claude-3-5-haiku-20241022",
+ "display_name": "Claude 3.5 Haiku",
+ "best_for": [
+ "bug_fix",
+ "test_gen"
+ ],
+ "approx_cost_per_1k_tokens": {
+ "input": 0.0008,
+ "output": 0.004
+ },
+ "context_window": 200000
+ }
+ ]
+ },
+ {
+ "provider": "openai",
+ "display_name": "OpenAI (direct)",
+ "signup_url": "https://platform.openai.com/api-keys",
+ "docs_url": "https://platform.openai.com/docs",
+ "popular_models": [
+ {
+ "model_id": "gpt-4o",
+ "display_name": "GPT-4o",
+ "best_for": [
+ "feature",
+ "new_app"
+ ],
+ "approx_cost_per_1k_tokens": {
+ "input": 0.0025,
+ "output": 0.01
+ }
+ },
+ {
+ "model_id": "gpt-4o-mini",
+ "display_name": "GPT-4o Mini",
+ "best_for": [
+ "bug_fix"
+ ],
+ "approx_cost_per_1k_tokens": {
+ "input": 0.00015,
+ "output": 0.0006
+ }
+ }
+ ]
+ },
+ {
+ "provider": "groq",
+ "display_name": "Groq (fastest inference)",
+ "description": "Ultra-fast LPU inference — best for iterative dev loops",
+ "signup_url": "https://console.groq.com/keys",
+ "popular_models": [
+ {
+ "model_id": "llama-3.3-70b-versatile",
+ "display_name": "Llama 3.3 70B",
+ "best_for": [
+ "any"
+ ],
+ "approx_cost_per_1k_tokens": {
+ "input": 0.00059,
+ "output": 0.00079
+ },
+ "notes": "~500 tokens/sec — feels instant"
+ },
+ {
+ "model_id": "deepseek-r1-distill-llama-70b",
+ "display_name": "DeepSeek R1 Distill",
+ "best_for": [
+ "refactor"
+ ],
+ "approx_cost_per_1k_tokens": {
+ "input": 0.00075,
+ "output": 0.00099
+ }
+ }
+ ]
+ },
+ {
+ "provider": "together",
+ "display_name": "Together AI",
+ "signup_url": "https://api.together.xyz/settings/api-keys",
+ "popular_models": [
+ {
+ "model_id": "Qwen/Qwen2.5-Coder-32B-Instruct",
+ "display_name": "Qwen 2.5 Coder 32B",
+ "best_for": [
+ "bug_fix",
+ "feature"
+ ],
+ "approx_cost_per_1k_tokens": {
+ "input": 0.0008,
+ "output": 0.0008
+ }
+ }
+ ]
+ },
+ {
+ "provider": "deepinfra",
+ "display_name": "DeepInfra",
+ "signup_url": "https://deepinfra.com/dash/api_keys",
+ "popular_models": [
+ {
+ "model_id": "meta-llama/Meta-Llama-3.1-405B-Instruct",
+ "display_name": "Llama 3.1 405B",
+ "best_for": [
+ "complex reasoning"
+ ],
+ "approx_cost_per_1k_tokens": {
+ "input": 0.0027,
+ "output": 0.0027
+ }
+ }
+ ]
+ }
+ ],
+ "example_rule_sets": [
+ {
+ "name": "Cost-conscious (Free tier maximizer)",
+ "description": "जितना हो सके free models — सिर्फ़ जटिल tasks पर paid",
+ "rules": [
+ {
+ "model": "google/gemini-2.0-flash-exp:free (OpenRouter)",
+ "task_types": [
+ "any"
+ ],
+ "min_complexity": 0
+ },
+ {
+ "model": "anthropic/claude-3.5-sonnet (OpenRouter)",
+ "task_types": [
+ "security",
+ "refactor"
+ ],
+ "min_complexity": 8
+ }
+ ]
+ },
+ {
+ "name": "Speed-focused (dev iteration)",
+ "description": "Groq की LPU speed का फायदा — instant feedback loop",
+ "rules": [
+ {
+ "model": "llama-3.3-70b-versatile (Groq)",
+ "task_types": [
+ "bug_fix",
+ "test_gen"
+ ],
+ "min_complexity": 0
+ }
+ ]
+ },
+ {
+ "name": "Quality-first (enterprise)",
+ "description": "हर task पर best-in-class model — cost secondary",
+ "rules": [
+ {
+ "model": "claude-3-5-sonnet (Anthropic direct)",
+ "task_types": [
+ "security",
+ "refactor",
+ "new_app"
+ ],
+ "always_use": true
+ },
+ {
+ "model": "gpt-4o (OpenAI direct)",
+ "task_types": [
+ "feature"
+ ],
+ "min_complexity": 5
+ }
+ ]
+ },
+ {
+ "name": "Privacy-hybrid",
+ "description": "Local first, cloud सिर्फ़ security audits पर",
+ "rules": [
+ {
+ "model": "claude-3-5-sonnet",
+ "task_types": [
+ "security"
+ ],
+ "min_complexity": 7
+ }
+ ]
+ }
+ ]
+}
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/demo/README_demo_script.md devai_phase4/demo/README_demo_script.md
--- devai_phase4_orig/demo/README_demo_script.md 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/demo/README_demo_script.md 2026-07-06 10:28:33.917919928 +0000
@@ -0,0 +1,10 @@
+# Demo storyboard script
+
+Use the bundled MP4 as a silent walkthrough. Suggested live narration order:
+1. Open Advanced options.
+2. Start BYOM via the three-dot menu.
+3. Choose provider preset and popular model.
+4. Load an example rule set.
+5. Test connection and save the model.
+6. Show quota cards and fallback behavior.
+7. Open About tab docs hub.
Binary files devai_phase4_orig/demo/devai_studio_v4.7_phase4_demo.mp4 and devai_phase4/demo/devai_studio_v4.7_phase4_demo.mp4 differ
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/demo/slides.txt devai_phase4/demo/slides.txt
--- devai_phase4_orig/demo/slides.txt 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/demo/slides.txt 2026-07-06 10:28:32.237919928 +0000
@@ -0,0 +1,29 @@
+file '/home/user/work/devai_phase4/demo/story_frames/01.png'
+duration 22
+file '/home/user/work/devai_phase4/demo/story_frames/02.png'
+duration 22
+file '/home/user/work/devai_phase4/demo/story_frames/03.png'
+duration 22
+file '/home/user/work/devai_phase4/demo/story_frames/04.png'
+duration 22
+file '/home/user/work/devai_phase4/demo/story_frames/05.png'
+duration 22
+file '/home/user/work/devai_phase4/demo/story_frames/06.png'
+duration 22
+file '/home/user/work/devai_phase4/demo/story_frames/07.png'
+duration 22
+file '/home/user/work/devai_phase4/demo/story_frames/08.png'
+duration 22
+file '/home/user/work/devai_phase4/demo/story_frames/09.png'
+duration 22
+file '/home/user/work/devai_phase4/demo/story_frames/10.png'
+duration 22
+file '/home/user/work/devai_phase4/demo/story_frames/11.png'
+duration 22
+file '/home/user/work/devai_phase4/demo/story_frames/12.png'
+duration 22
+file '/home/user/work/devai_phase4/demo/story_frames/13.png'
+duration 22
+file '/home/user/work/devai_phase4/demo/story_frames/14.png'
+duration 22
+file '/home/user/work/devai_phase4/demo/story_frames/14.png'
Binary files devai_phase4_orig/demo/story_frames/01.png and devai_phase4/demo/story_frames/01.png differ
Binary files devai_phase4_orig/demo/story_frames/02.png and devai_phase4/demo/story_frames/02.png differ
Binary files devai_phase4_orig/demo/story_frames/03.png and devai_phase4/demo/story_frames/03.png differ
Binary files devai_phase4_orig/demo/story_frames/04.png and devai_phase4/demo/story_frames/04.png differ
Binary files devai_phase4_orig/demo/story_frames/05.png and devai_phase4/demo/story_frames/05.png differ
Binary files devai_phase4_orig/demo/story_frames/06.png and devai_phase4/demo/story_frames/06.png differ
Binary files devai_phase4_orig/demo/story_frames/07.png and devai_phase4/demo/story_frames/07.png differ
Binary files devai_phase4_orig/demo/story_frames/08.png and devai_phase4/demo/story_frames/08.png differ
Binary files devai_phase4_orig/demo/story_frames/09.png and devai_phase4/demo/story_frames/09.png differ
Binary files devai_phase4_orig/demo/story_frames/10.png and devai_phase4/demo/story_frames/10.png differ
Binary files devai_phase4_orig/demo/story_frames/11.png and devai_phase4/demo/story_frames/11.png differ
Binary files devai_phase4_orig/demo/story_frames/12.png and devai_phase4/demo/story_frames/12.png differ
Binary files devai_phase4_orig/demo/story_frames/13.png and devai_phase4/demo/story_frames/13.png differ
Binary files devai_phase4_orig/demo/story_frames/14.png and devai_phase4/demo/story_frames/14.png differ
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/docs/BYOM_USER_GUIDE_EN.md devai_phase4/docs/BYOM_USER_GUIDE_EN.md
--- devai_phase4_orig/docs/BYOM_USER_GUIDE_EN.md 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/docs/BYOM_USER_GUIDE_EN.md 2026-07-06 10:28:32.013919928 +0000
@@ -0,0 +1,398 @@
+# 🎨 DevAI Studio v4.7 — Bring Your Own Model
+
+## Table of contents
+1. [What is this feature](#1-what-is-this-feature)
+2. [Setup — first model in 5 minutes](#2-setup--first-model-in-5-minutes)
+3. [Choosing a provider](#3-choosing-a-provider)
+4. [Task Rules — which model runs when](#4-task-rules--which-model-runs-when)
+5. [Cost Management](#5-cost-management)
+6. [Quota Exhaustion — what happens](#6-quota-exhaustion--what-happens)
+7. [Security & Privacy](#7-security--privacy)
+8. [Troubleshooting](#8-troubleshooting)
+9. [Advanced: Custom endpoints](#9-advanced-custom-endpoints)
+10. [FAQ](#10-faq)
+
+## 1. What is this feature?
+
+DevAI Studio v4.7 now has three model tiers: local, built-in cloud, and custom BYOM. This feature lets you attach your own API keys to production-grade models.
+
+### Tier summary
+- **Local models** — free, offline, private, but speed depends on your hardware.
+- **Built-in cloud models** — zero setup, quick start, but shared limits.
+- **Custom models** — latest Claude, GPT, Gemini, Groq, Together, DeepInfra, or a custom endpoint; quota and billing belong to you.
+
+### Local models (always available)
+- Qwen2.5-Coder-7B → coding tasks
+- DeepSeek-R1-Distill-8B → complex reasoning
+- InternLM3-8B → security audits
+- Llama-3.2-3B → general chat
+- MiniCPM-V-2.6 → vision (screenshot → code)
+- Phi-3.5-mini → fast small tasks
+
+### What BYOM adds
+- Latest flagship models without waiting for an app release.
+- Personal quota instead of shared free-tier bottlenecks.
+- Task-aware routing: bug fix, feature, refactor, security, tests, deployment, new app.
+- Fallback control: pause and ask for a new key, or continue on a local model.
+
+## 2. Setup — first model in 5 minutes
+
+### Step 1 — Set a master key
+Enter a strong passphrase in the Settings tab. It encrypts every stored API key. Save it in a password manager. If you lose it, re-add the models.
+
+### Step 2 — Pick a provider
+- Fastest start: **OpenRouter**
+- Best direct Claude latency: **Anthropic**
+- Best multimodal generalist: **OpenAI GPT-4o**
+- Fast iterative dev loops: **Groq**
+
+### Step 3 — Create an API key
+Generate a key from the provider dashboard and verify billing or quota before pasting it into the app.
+
+### Step 4 — Studio > Advanced options > ⋮ > Add model
+In the v4.7 UX you can choose a provider preset, then choose a popular model. The UI auto-fills model_id, base URL and a rough cost estimate.
+
+### Step 5 — Choose rules and fallback
+- Task types: where the model is eligible.
+- Min complexity: 0 for broad use, 8-10 for hard jobs only.
+- Always use: overrides all routing rules.
+- Fallback: ask for a new key or switch to a local model.
+
+### Step 6 — Test Connection
+Once you see a green ✅, click Save Model and run a small validation task.
+
+### Recommended first prompt
+```text
+Audit this repository for SQL injection, XSS, hardcoded secrets, insecure dependencies and auth flaws.
+```
+
+## 3. Choosing a provider
+
+| Provider | Why choose it | Popular models |
+|---|---|---|
+| OpenRouter (100+ models) | एक API key से 100+ models — Claude, GPT, Gemini, Llama सब | Claude 3.5 Sonnet, Claude 3.5 Haiku (fast+cheap), GPT-4o |
+| Anthropic (direct) | Claude models directly — lowest latency, no OpenRouter markup | Claude 3.5 Sonnet (latest), Claude 3.5 Haiku |
+| OpenAI (direct) | OpenAI-compatible endpoint | GPT-4o, GPT-4o Mini |
+| Groq (fastest inference) | Ultra-fast LPU inference — best for iterative dev loops | Llama 3.3 70B, DeepSeek R1 Distill |
+| Together AI | OpenAI-compatible endpoint | Qwen 2.5 Coder 32B |
+| DeepInfra | OpenAI-compatible endpoint | Llama 3.1 405B |
+
+### Quick recommendations
+- Budget-sensitive solo developer: OpenRouter + free/cheap presets
+- Security-review heavy team: Anthropic direct Sonnet
+- UI + multimodal heavy team: GPT-4o
+- Tight iteration loops: Groq Llama 3.3 70B
+- Self-hosted compliance path: Custom endpoint / LiteLLM / vLLM
+
+## 4. Task Rules — which model runs when
+
+The rules engine evaluates task type, complexity score, and explicit overrides. If `always_use=true`, that model wins immediately. If multiple rules match, specificity and saved priority decide the winner.
+
+### Complexity scoring guide
+- 0-2 → tiny edits, log cleanup, one-file tweaks
+- 3-5 → medium bug fix, small feature, unit tests
+- 6-7 → multi-file refactor, architectural reasoning
+- 8-10 → security audits, migrations, large design work
+
+### Example rule sets shipped in v4.7
+- Cost-conscious (Free tier maximizer)
+- Speed-focused (dev iteration)
+- Quality-first (enterprise)
+- Privacy-hybrid
+
+## 5. Cost Management
+
+### What the UI estimate means
+The popular-model selector shows a rough estimate using **2K input / 500 output tokens**. It is not exact billing, but it is useful for sprint planning and model comparison.
+
+### Budgeting best practices
+- Set hard spend caps in the provider dashboard.
+- Keep a cheap model as the default and reserve premium models for complex tasks.
+- Use task-specific routing so every small request does not hit Claude or GPT-4o.
+- Use local models as fallback to reduce overage exposure.
+
+### Suggested budgets
+| Profile | Monthly strategy |
+|---|---|
+| Solo hobby | Free-tier default + one premium audit model |
+| Freelancer | Speed preset for iteration + Sonnet for client-critical reviews |
+| Small team | Enterprise preset + provider spend caps + weekly dashboard review |
+
+## 6. Quota Exhaustion — what happens
+
+When the provider returns 402 or 429, v4.7 gives two options:
+1. **ask_new_key** → pause and request a replacement key.
+2. **local_model** → continue on the mapped local model.
+
+## 7. Security & Privacy
+
+- API keys are stored with **Fernet (AES-128 + HMAC-SHA256)** encryption at rest.
+- The master key is not stored on disk.
+- Secrets are masked in logs.
+- Writes are atomic and file permissions stay restrictive.
+- DevAI Studio does not add its own telemetry on top of your chosen provider path.
+
+## 8. Troubleshooting
+
+Below is a short version of the top issues. The full searchable knowledge base lives in `docs/TROUBLESHOOTING_KB.md`.
+
+### Issue #1: Invalid API key on Test Connection
+
+**Symptoms:** Red ❌ appears after clicking Test Connection.
+
+**Common causes**
+- Extra spaces in the key
+- Wrong provider selected
+- Key revoked or expired
+
+**Recommended fix**
+1. Regenerate the key from the provider dashboard
+2. Paste it again without leading/trailing spaces
+3. Confirm the provider dropdown matches the key type
+
+### Issue #2: Model replies in the wrong language
+
+**Symptoms:** Responses ignore the language of the prompt or system instruction.
+
+**Common causes**
+- Model preset optimized for another language
+- Task prompt lacks a language instruction
+- Provider-side system prompt contamination
+
+**Recommended fix**
+1. Add an explicit language instruction to the task
+2. Prefer GPT-4o or Claude Sonnet for multilingual work
+3. Test the model directly in the provider console
+
+### Issue #3: Fallback not triggering on 429
+
+**Symptoms:** Pipeline pauses instead of moving to the chosen fallback behavior.
+
+**Common causes**
+- Saved fallback policy is ask_new_key
+- 429 body is returned as a generic error by a proxy
+- Outdated custom model config
+
+**Recommended fix**
+1. Re-open the model card and verify fallback policy
+2. Retest using direct provider endpoint
+3. Save the model again to refresh metadata
+
+### Issue #4: Pipeline hangs during Coder stage
+
+**Symptoms:** Status remains on coder or reviewer for too long.
+
+**Common causes**
+- Provider latency spike
+- Very large prompt or repository
+- Provider queue saturation
+
+**Recommended fix**
+1. Retry with a smaller ZIP
+2. Switch to a faster preset like Groq or Claude Haiku
+3. Use Private Mode for small/local tasks
+
+### Issue #5: Custom model is used even when disabled
+
+**Symptoms:** Router keeps picking a model after you thought it was inactive.
+
+**Common causes**
+- always_use is enabled
+- Task rules still match any task
+- Priority order favors that model
+
+**Recommended fix**
+1. Edit the model and disable always_use
+2. Change task_types from any to specific categories
+3. Adjust the rule priority matrix
+
+### Issue #6: Base URL looks correct but requests fail
+
+**Symptoms:** Connection test fails with network or 404 errors.
+
+**Common causes**
+- Provider endpoint missing /v1
+- Anthropic direct uses /messages instead of /chat/completions
+- Proxy strips auth headers
+
+**Recommended fix**
+1. Verify the endpoint in provider docs
+2. Use the built-in preset instead of manual typing
+3. Try a direct provider endpoint to isolate the issue
+
+### Issue #7: Saved model disappears after restart
+
+**Symptoms:** A model is visible in-session but missing later.
+
+**Common causes**
+- Master key changed
+- Encrypted file corrupted
+- Data directory permissions issue
+
+**Recommended fix**
+1. Reuse the same master key
+2. Restore data/custom_models.enc.json from backup
+3. Ensure the process can write to the data folder
+
+### Issue #8: Quota bar never updates
+
+**Symptoms:** Model card always shows unknown quota.
+
+**Common causes**
+- Provider does not expose headers
+- First request never completed successfully
+- Proxy removed rate-limit headers
+
+**Recommended fix**
+1. Run one successful generation
+2. Use a direct provider endpoint if quota visibility matters
+3. Treat the bar as optional and rely on provider dashboard billing
+
+### Issue #9: Anthropic direct fails while OpenRouter works
+
+**Symptoms:** Claude via Anthropic shows HTTP errors.
+
+**Common causes**
+- Anthropic key pasted into OpenRouter provider
+- Account lacks billing
+- Model name typo
+
+**Recommended fix**
+1. Select Anthropic direct provider
+2. Check billing and usage limits
+3. Use a preset rather than a hand-typed model ID
+
+### Issue #10: OpenRouter free tier is rate-limited
+
+**Symptoms:** Requests work, then suddenly slow down or fail.
+
+**Common causes**
+- Free tier cap reached
+- Burst traffic from multiple runs
+- No paid balance
+
+**Recommended fix**
+1. Wait for the reset window
+2. Use a paid key or alternate provider
+3. Route simple tasks to a cheaper preset
+
+### Issue #11: Task Rules matrix saves unexpected priorities
+
+**Symptoms:** The top model in cards differs from the table order.
+
+**Common causes**
+- Priority value duplicates
+- Higher min complexity wins ties
+- Model list sorted after save
+
+**Recommended fix**
+1. Give each model a unique priority
+2. Check min_complexity values
+3. Refresh the panel after saving
+
+### Issue #12: Local fallback quality is poor
+
+**Symptoms:** Fallback works but output quality drops noticeably.
+
+**Common causes**
+- Fallback switched to small local model
+- Task too complex for local model
+- Prompt too large for context
+
+**Recommended fix**
+1. Use ask_new_key for critical work
+2. Trim prompt size
+3. Reserve local fallback for continuation, not best quality
+
+### Issue #13: Edit modal requires a new key unexpectedly
+
+**Symptoms:** Changing provider or model ID forces key replacement.
+
+**Common causes**
+- Security design prevents silent target change
+- Provider changed endpoints
+
+**Recommended fix**
+1. Paste a new key when changing provider/model target
+2. Keep provider/model unchanged to reuse the stored encrypted key
+
+### Issue #14: Model ID preset dropdown is empty
+
+**Symptoms:** Provider selected but no popular models appear.
+
+**Common causes**
+- provider_presets.json missing
+- Provider not curated
+- JSON failed to load
+
+**Recommended fix**
+1. Validate data/provider_presets.json
+2. Choose a curated provider preset
+3. Restart the app after restoring the file
+
+### Issue #15: Tour never starts
+
+**Symptoms:** First-run experience does not appear.
+
+**Common causes**
+- localStorage already marks it complete
+- Advanced options never opened
+- CDN blocked
+
+**Recommended fix**
+1. Use the Restart onboarding tour button in About
+2. Open Advanced options once
+3. Allow driver.js CDN or self-host the asset
+
+## 9. Advanced: Custom endpoints
+
+### Supported patterns
+- **vLLM** — self-hosted OpenAI-compatible gateway.
+- **LiteLLM proxy** — unified multi-provider gateway.
+- **LM Studio HTTP server** — local desktop endpoint for experimentation.
+- **Ollama via proxy bridge** — useful when a team wants one OpenAI-compatible URL.
+
+### Endpoint checklist
+- Use the correct `/v1` base path.
+- Verify auth header behavior.
+- Manually test large context claims.
+- Preserve upstream 401/403/429 status codes through proxies.
+
+## 10. FAQ
+
+### Do I need a separate API key for every provider?
+Yes. Every direct provider has its own key. OpenRouter can expose many models through one key.
+
+### Can I recover stored keys if I forget the master key?
+No. For safety, encrypted keys are not recoverable; re-add the models instead.
+
+### Do custom models replace local models?
+No. They are a third tier. Local models remain available at all times.
+
+### Can I stay on free models only?
+Yes. The cost-conscious preset is designed for that workflow.
+
+### Will local fallback produce the exact same answer?
+Not always, but it preserves continuity when cloud quota is exhausted.
+
+### Are provider presets editable?
+Yes. The JSON file is only a curated default; manual values still work.
+
+### Can the docs and KB be read offline?
+Yes. The markdown files ship inside the release ZIP.
+
+### Are custom endpoints supported?
+Yes, as long as the endpoint is OpenAI-compatible or you can supply a custom base URL.
+
+### Does billing happen inside the app?
+No. Billing is handled by the provider; the app only shows an estimate.
+
+### Is this feature merge-safe for team repositories?
+Yes. Storage is JSON-based and all existing v4.6 APIs stay unchanged.
+
+### Do I need a database migration?
+No. The feature is additive and uses JSON-based storage.
+
+### Will screenshot-to-code flows use custom models?
+Yes, when routing rules allow it and the provider is capable.
+
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/docs/BYOM_USER_GUIDE_HI.md devai_phase4/docs/BYOM_USER_GUIDE_HI.md
--- devai_phase4_orig/docs/BYOM_USER_GUIDE_HI.md 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/docs/BYOM_USER_GUIDE_HI.md 2026-07-06 10:28:32.013919928 +0000
@@ -0,0 +1,429 @@
+# 🎨 DevAI Studio v4.7 — अपना AI Model जोड़ें (Bring Your Own Model)
+
+## विषय-सूची
+1. [यह feature क्या है](#1-यह-feature-क्या-है)
+2. [Setup — पहला model 5 मिनट में](#2-setup--पहला-model-5-मिनट-में)
+3. [Provider चुनना — कौन-सा best है](#3-provider-चुनना--कौन-सा-best-है)
+4. [Task Rules — कब कौन-सा model](#4-task-rules--कब-कौन-सा-model)
+5. [Cost Management](#5-cost-management)
+6. [Quota Exhaustion — क्या होगा](#6-quota-exhaustion--क्या-होगा)
+7. [Security & Privacy](#7-security--privacy)
+8. [Troubleshooting](#8-troubleshooting)
+9. [Advanced: Custom endpoints](#9-advanced-custom-endpoints)
+10. [FAQ](#10-faq)
+
+## 1. यह Feature क्या है?
+
+DevAI Studio v4.7 में तीन model tiers हैं: local, built-in cloud, और custom BYOM. यह नया feature आपको अपने personal API keys के साथ production-grade models जोड़ने देता है।
+
+### Tier summary
+- **Local models** — free, offline, private, लेकिन speed hardware पर depend करती है।
+- **Built-in cloud models** — quick start, no setup, लेकिन shared limits हैं।
+- **Custom models** — latest Claude, GPT, Gemini, Groq, Together, DeepInfra या custom endpoint; quota और billing आपकी।
+
+### Local models (हमेशा available)
+- Qwen2.5-Coder-7B → coding tasks
+- DeepSeek-R1-Distill-8B → complex reasoning
+- InternLM3-8B → security audits
+- Llama-3.2-3B → general chat
+- MiniCPM-V-2.6 → vision (screenshot → code)
+- Phi-3.5-mini → fast small tasks
+
+### Custom models feature से क्या मिलता है
+- Latest flagship models बिना app update का इंतज़ार किए।
+- आपकी personal quota — shared free-tier bottlenecks से बचाव।
+- Task-aware routing: bug fix, feature, refactor, security, test generation, deployment, new app.
+- Fallback control: pause and ask for a new key, या local model पर continue.
+
+## 2. Setup — पहला Model 5 मिनट में
+
+### Step 1 — Master Key set करें
+Settings tab में strong passphrase डालें। यही आपकी API keys को encrypt करेगी। इसे password manager में save करें। भूल गए तो models फिर से जोड़ने पड़ेंगे।
+
+### Step 2 — Provider चुनें
+- Fastest start: **OpenRouter**
+- Best direct Claude latency: **Anthropic**
+- Best general multimodal experience: **OpenAI GPT-4o**
+- Fast iterative dev loops: **Groq**
+
+### Step 3 — API key बनाएँ
+Provider dashboard में जाकर API key बनाइए और billing/quota verify कीजिए। OpenRouter free-tier भी दे सकता है, पर rate limits जल्दी hit हो सकती हैं।
+
+### Step 4 — Studio > Advanced options > ⋮ > Add model
+नई v4.7 UX में provider preset चुनिए, फिर Popular models dropdown से common model pick कीजिए। इससे model_id, base URL और rough cost estimate अपने-आप भर जाते हैं।
+
+### Step 5 — Rules and fallback चुनें
+- Task types: model किस category के कामों पर eligible होगा।
+- Min complexity: 0 मतलब broad usage, 8-10 मतलब सिर्फ़ tough jobs।
+- Always use: बाकी rules override करेगा।
+- Fallback: नई key माँगे या local model पर switch करे।
+
+### Step 6 — Test Connection
+Green ✅ आने के बाद Save Model दबाएँ। पहला run ideally छोटा security audit या bug-fix prompt होना चाहिए ताकि routing clearly verify हो जाए।
+
+### Recommended first prompt
+```text
+इस repo का security audit करो — SQL injection, XSS, hardcoded secrets, insecure dependencies और auth flaws check करो।
+```
+
+## 3. Provider चुनना — कौन-सा best है?
+
+| Provider | Why choose it | Popular models |
+|---|---|---|
+| OpenRouter (100+ models) | एक API key से 100+ models — Claude, GPT, Gemini, Llama सब | Claude 3.5 Sonnet, Claude 3.5 Haiku (fast+cheap), GPT-4o |
+| Anthropic (direct) | Claude models directly — lowest latency, no OpenRouter markup | Claude 3.5 Sonnet (latest), Claude 3.5 Haiku |
+| OpenAI (direct) | OpenAI-compatible endpoint | GPT-4o, GPT-4o Mini |
+| Groq (fastest inference) | Ultra-fast LPU inference — best for iterative dev loops | Llama 3.3 70B, DeepSeek R1 Distill |
+| Together AI | OpenAI-compatible endpoint | Qwen 2.5 Coder 32B |
+| DeepInfra | OpenAI-compatible endpoint | Llama 3.1 405B |
+
+### Quick recommendations
+- Budget-sensitive solo developer: OpenRouter + free/cheap presets
+- Security review heavy team: Anthropic direct Sonnet
+- UI + multimodal heavy team: GPT-4o
+- Tight iteration loops: Groq Llama 3.3 70B
+- Self-hosted compliance path: Custom endpoint / LiteLLM / vLLM
+
+## 4. Task Rules — कब कौन-सा model?
+
+Rules engine तीन चीज़ें देखता है: task type, complexity score, और explicit override. अगर `always_use=true` है तो वही model top priority ले लेता है। अगर multiple rules match करें तो higher specificity और saved priority matter करती है।
+
+### Complexity scoring practical guide
+- 0-2 → tiny edits, log cleanup, one-file tweaks
+- 3-5 → medium bug fix, small feature, unit tests
+- 6-7 → multi-file refactor, architectural reasoning
+- 8-10 → security audits, migrations, large design work
+
+### Example rule sets shipped in v4.7
+- Cost-conscious (Free tier maximizer)
+- Speed-focused (dev iteration)
+- Quality-first (enterprise)
+- Privacy-hybrid
+
+### Recommended patterns
+1. Cheap-first rule for `any` tasks with complexity 0.
+2. Stronger model for `security` and `refactor` from complexity 7 or 8.
+3. Local fallback for continuity, ask_new_key for quality-critical outputs.
+
+## 5. Cost Management
+
+### What the UI estimate means
+Popular-model selection shows a rough estimate using **2K input / 500 output tokens**. यह exact billing नहीं है, पर sprint planning और model comparison के लिए काफी useful है।
+
+### Budgeting best practices
+- हर month के लिए provider-side spend cap set करें।
+- Free या cheap model को default रखें; premium rules सिर्फ़ complex work के लिए रखें।
+- Task-type specific routing use करें ताकि GPT/Claude हर छोटी request पर न जाए।
+- Local models को fallback में रखकर cloud overage कम करें।
+
+### Suggested budgets
+| Profile | Monthly strategy |
+|---|---|
+| Solo hobby | Free-tier default + one premium audit model |
+| Freelancer | Speed preset for iteration + Sonnet for client-critical reviews |
+| Small team | Enterprise preset + provider spend caps + weekly dashboard review |
+
+## 6. Quota Exhaustion — क्या होगा?
+
+जब provider 402/429 दे, v4.7 का flow दो रास्ते देता है:
+1. **ask_new_key** → pipeline pause, user से नई key माँगना।
+2. **local_model** → task mapping के हिसाब से local model पर continue करना।
+
+### Recommended walkthrough
+- A model card quota bar low दिखाती है।
+- Run के दौरान provider limit hit होती है।
+- Status में clear message आता है।
+- User या तो key replace करता है या local fallback पर continue करता है।
+
+### What to document for teams
+- कौन-से tasks पर cloud-only quality चाहिए।
+- कौन-से tasks local fallback tolerate कर सकते हैं।
+- किस provider का spend alert कौन monitor करेगा।
+
+## 7. Security & Privacy
+
+- API keys **Fernet (AES-128 + HMAC-SHA256)** से encrypted-at-rest रहती हैं।
+- Master key disk पर store नहीं होती; session context में use होकर disappear हो जाती है।
+- Secret values logs में mask रहते हैं।
+- Atomic writes और restrictive permissions accidental leakage risk कम करते हैं।
+- No vendor telemetry by DevAI Studio; billing और request flow आपके चुने provider तक सीमित रहता है।
+
+### Operational security checklist
+- Personal and team keys अलग रखें।
+- Revoked keys तुरंत replace करें।
+- Shared demo environments में real production keys use न करें।
+- Master key को password manager में रखें, plain text note में नहीं।
+
+## 8. Troubleshooting
+
+नीचे top issues का short version है। Full searchable knowledge base `docs/TROUBLESHOOTING_KB.md` में है।
+
+### समस्या #1: Invalid API key on Test Connection
+
+**Symptoms:** Red ❌ appears after clicking Test Connection.
+
+**Common causes**
+- Extra spaces in the key
+- Wrong provider selected
+- Key revoked or expired
+
+**Recommended fix**
+1. Regenerate the key from the provider dashboard
+2. Paste it again without leading/trailing spaces
+3. Confirm the provider dropdown matches the key type
+
+### समस्या #2: Model replies in the wrong language
+
+**Symptoms:** Responses ignore the language of the prompt or system instruction.
+
+**Common causes**
+- Model preset optimized for another language
+- Task prompt lacks a language instruction
+- Provider-side system prompt contamination
+
+**Recommended fix**
+1. Add an explicit language instruction to the task
+2. Prefer GPT-4o or Claude Sonnet for multilingual work
+3. Test the model directly in the provider console
+
+### समस्या #3: Fallback not triggering on 429
+
+**Symptoms:** Pipeline pauses instead of moving to the chosen fallback behavior.
+
+**Common causes**
+- Saved fallback policy is ask_new_key
+- 429 body is returned as a generic error by a proxy
+- Outdated custom model config
+
+**Recommended fix**
+1. Re-open the model card and verify fallback policy
+2. Retest using direct provider endpoint
+3. Save the model again to refresh metadata
+
+### समस्या #4: Pipeline hangs during Coder stage
+
+**Symptoms:** Status remains on coder or reviewer for too long.
+
+**Common causes**
+- Provider latency spike
+- Very large prompt or repository
+- Provider queue saturation
+
+**Recommended fix**
+1. Retry with a smaller ZIP
+2. Switch to a faster preset like Groq or Claude Haiku
+3. Use Private Mode for small/local tasks
+
+### समस्या #5: Custom model is used even when disabled
+
+**Symptoms:** Router keeps picking a model after you thought it was inactive.
+
+**Common causes**
+- always_use is enabled
+- Task rules still match any task
+- Priority order favors that model
+
+**Recommended fix**
+1. Edit the model and disable always_use
+2. Change task_types from any to specific categories
+3. Adjust the rule priority matrix
+
+### समस्या #6: Base URL looks correct but requests fail
+
+**Symptoms:** Connection test fails with network or 404 errors.
+
+**Common causes**
+- Provider endpoint missing /v1
+- Anthropic direct uses /messages instead of /chat/completions
+- Proxy strips auth headers
+
+**Recommended fix**
+1. Verify the endpoint in provider docs
+2. Use the built-in preset instead of manual typing
+3. Try a direct provider endpoint to isolate the issue
+
+### समस्या #7: Saved model disappears after restart
+
+**Symptoms:** A model is visible in-session but missing later.
+
+**Common causes**
+- Master key changed
+- Encrypted file corrupted
+- Data directory permissions issue
+
+**Recommended fix**
+1. Reuse the same master key
+2. Restore data/custom_models.enc.json from backup
+3. Ensure the process can write to the data folder
+
+### समस्या #8: Quota bar never updates
+
+**Symptoms:** Model card always shows unknown quota.
+
+**Common causes**
+- Provider does not expose headers
+- First request never completed successfully
+- Proxy removed rate-limit headers
+
+**Recommended fix**
+1. Run one successful generation
+2. Use a direct provider endpoint if quota visibility matters
+3. Treat the bar as optional and rely on provider dashboard billing
+
+### समस्या #9: Anthropic direct fails while OpenRouter works
+
+**Symptoms:** Claude via Anthropic shows HTTP errors.
+
+**Common causes**
+- Anthropic key pasted into OpenRouter provider
+- Account lacks billing
+- Model name typo
+
+**Recommended fix**
+1. Select Anthropic direct provider
+2. Check billing and usage limits
+3. Use a preset rather than a hand-typed model ID
+
+### समस्या #10: OpenRouter free tier is rate-limited
+
+**Symptoms:** Requests work, then suddenly slow down or fail.
+
+**Common causes**
+- Free tier cap reached
+- Burst traffic from multiple runs
+- No paid balance
+
+**Recommended fix**
+1. Wait for the reset window
+2. Use a paid key or alternate provider
+3. Route simple tasks to a cheaper preset
+
+### समस्या #11: Task Rules matrix saves unexpected priorities
+
+**Symptoms:** The top model in cards differs from the table order.
+
+**Common causes**
+- Priority value duplicates
+- Higher min complexity wins ties
+- Model list sorted after save
+
+**Recommended fix**
+1. Give each model a unique priority
+2. Check min_complexity values
+3. Refresh the panel after saving
+
+### समस्या #12: Local fallback quality is poor
+
+**Symptoms:** Fallback works but output quality drops noticeably.
+
+**Common causes**
+- Fallback switched to small local model
+- Task too complex for local model
+- Prompt too large for context
+
+**Recommended fix**
+1. Use ask_new_key for critical work
+2. Trim prompt size
+3. Reserve local fallback for continuation, not best quality
+
+### समस्या #13: Edit modal requires a new key unexpectedly
+
+**Symptoms:** Changing provider or model ID forces key replacement.
+
+**Common causes**
+- Security design prevents silent target change
+- Provider changed endpoints
+
+**Recommended fix**
+1. Paste a new key when changing provider/model target
+2. Keep provider/model unchanged to reuse the stored encrypted key
+
+### समस्या #14: Model ID preset dropdown is empty
+
+**Symptoms:** Provider selected but no popular models appear.
+
+**Common causes**
+- provider_presets.json missing
+- Provider not curated
+- JSON failed to load
+
+**Recommended fix**
+1. Validate data/provider_presets.json
+2. Choose a curated provider preset
+3. Restart the app after restoring the file
+
+### समस्या #15: Tour never starts
+
+**Symptoms:** First-run experience does not appear.
+
+**Common causes**
+- localStorage already marks it complete
+- Advanced options never opened
+- CDN blocked
+
+**Recommended fix**
+1. Use the Restart onboarding tour button in About
+2. Open Advanced options once
+3. Allow driver.js CDN or self-host the asset
+
+## 9. Advanced: Custom endpoints
+
+### Supported patterns
+- **vLLM** — self-hosted OpenAI-compatible gateway for large models.
+- **LiteLLM proxy** — multi-provider normalization layer with your own routing/billing policies.
+- **LM Studio HTTP server** — local desktop endpoint for experimentation.
+- **Ollama via proxy bridge** — useful when a team wants a unified OpenAI-compatible URL.
+
+### Endpoint checklist
+- Base URL सही `/v1` prefix के साथ दें।
+- Auth header behavior verify करें।
+- Large context claims को manually test करें।
+- 401/403 और 429 status codes proxy preserve कर रहा हो।
+
+### Example endpoint patterns
+```text
+https://api.openai.com/v1
+https://api.together.xyz/v1
+https://api.groq.com/openai/v1
+https://your-vllm-gateway.example.com/v1
+http://localhost:1234/v1
+```
+
+## 10. FAQ
+
+### क्या मुझे हर provider के लिए अलग API key चाहिए?
+हाँ। हर direct provider की अपनी key होती है। OpenRouter एक key से कई models दे सकता है।
+
+### क्या master key भूलने पर keys recover होंगी?
+नहीं। सुरक्षा कारणों से encrypted keys दोबारा नहीं निकाली जा सकतीं; models फिर से जोड़ने पड़ेंगे।
+
+### क्या custom models local models को replace कर देंगे?
+नहीं। यह तीसरी tier है; local models हमेशा available रहते हैं।
+
+### क्या मैं सिर्फ़ free models use कर सकता हूँ?
+हाँ, cost-conscious preset इसी use case के लिए है।
+
+### क्या fallback local model हमेशा exact same answer देगा?
+नहीं, पर pipeline continue रहेगी और context बचा रहेगा।
+
+### क्या provider presets editable हैं?
+हाँ, JSON curated defaults है; आप manual values भी डाल सकते हैं।
+
+### क्या docs और KB offline पढ़े जा सकते हैं?
+हाँ, release ZIP के docs folder में markdown files रहेंगी।
+
+### क्या custom endpoints supported हैं?
+हाँ, अगर endpoint OpenAI-compatible है या आप custom base URL दे सकते हैं।
+
+### क्या billing app के अंदर होती है?
+नहीं। billing provider dashboard पर होती है; app सिर्फ़ estimation दिखाता है।
+
+### क्या team repo में ये feature merge-safe है?
+हाँ, क्योंकि storage JSON-based है और existing APIs unchanged हैं।
+
+### क्या कोई database migration चाहिए?
+नहीं। Feature additive है और JSON-based storage use करता है।
+
+### क्या screenshots to code flow पर भी custom models apply होंगे?
+हाँ, जहाँ routing allows और provider capable हो।
+
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/docs/MIGRATION_v4.6_to_v4.7.md devai_phase4/docs/MIGRATION_v4.6_to_v4.7.md
--- devai_phase4_orig/docs/MIGRATION_v4.6_to_v4.7.md 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/docs/MIGRATION_v4.6_to_v4.7.md 2026-07-06 10:28:32.013919928 +0000
@@ -0,0 +1,62 @@
+# DevAI Studio v4.6 → v4.7 Migration Guide
+
+## Table of contents
+1. Scope
+2. Compatibility guarantees
+3. Pre-upgrade checklist
+4. Deployment steps
+5. Validation checklist
+6. Rollback procedure
+7. FAQ
+
+## 1. Scope
+- This guide upgrades a working v4.6 deployment to v4.7 with BYOM support.
+- The feature is additive and JSON-based.
+
+## 2. Compatibility guarantees
+- All v4.6 APIs remain unchanged.
+- Existing local-model and built-in cloud flows keep working.
+- No database migration is required.
+- No mandatory environment variables are introduced.
+- Breaking changes: **zero**.
+
+## 3. Pre-upgrade checklist
+- Keep a backup copy of the current v4.6 ZIP.
+- Record the current commit/tag and release artifact name.
+- Confirm `data/custom_models.enc.json` does not exist in v4.6 (expected, feature is new).
+- If you have local edits, export a patch before replacing files.
+
+## 4. Deployment steps
+1. Stop the running v4.6 app.
+2. Extract the combined v4.7 package over a clean directory.
+3. Preserve your existing environment file if you use one.
+4. Start the app normally; no migration command is needed.
+5. Open About tab and confirm the docs buttons render.
+6. Open Advanced options and verify the three-dot BYOM entry point exists.
+
+## 5. Validation checklist
+- App launches successfully.
+- Existing v4.6 tasks run unchanged.
+- BYOM provider presets load.
+- Tour can be restarted from About.
+- Troubleshooting KB opens from About.
+- No data loss is observed in existing workspace exports or logs.
+
+## 6. Rollback procedure
+1. Stop v4.7.
+2. Restore the saved v4.6 ZIP into a clean directory.
+3. Restore your previous environment file.
+4. Delete any newly created BYOM files only if your deployment policy requires a pristine rollback:
+ - `data/provider_presets.json`
+ - `ui/onboarding_tour.py`
+ - `docs/BYOM_USER_GUIDE_HI.md`
+ - `docs/BYOM_USER_GUIDE_EN.md`
+ - `docs/MIGRATION_v4.6_to_v4.7.md`
+ - `docs/TROUBLESHOOTING_KB.md`
+5. Start v4.6 and run the same smoke tests you used before the upgrade.
+
+## 7. FAQ
+- **Do I need to convert old data?** No.
+- **Do I need new env vars?** No required ones.
+- **Is there any schema migration?** No.
+- **Can I keep the docs folder on rollback?** Yes, if your packaging policy allows it.
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/docs/TROUBLESHOOTING_KB.md devai_phase4/docs/TROUBLESHOOTING_KB.md
--- devai_phase4_orig/docs/TROUBLESHOOTING_KB.md 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/docs/TROUBLESHOOTING_KB.md 2026-07-06 10:28:32.013919928 +0000
@@ -0,0 +1,492 @@
+# DevAI Studio v4.7 Troubleshooting Knowledge Base
+
+## Table of contents
+1. [Invalid API key on Test Connection](#issue-1-invalid-api-key-on-test-connection)
+2. [Model replies in the wrong language](#issue-2-model-replies-in-the-wrong-language)
+3. [Fallback not triggering on 429](#issue-3-fallback-not-triggering-on-429)
+4. [Pipeline hangs during Coder stage](#issue-4-pipeline-hangs-during-coder-stage)
+5. [Custom model is used even when disabled](#issue-5-custom-model-is-used-even-when-disabled)
+6. [Base URL looks correct but requests fail](#issue-6-base-url-looks-correct-but-requests-fail)
+7. [Saved model disappears after restart](#issue-7-saved-model-disappears-after-restart)
+8. [Quota bar never updates](#issue-8-quota-bar-never-updates)
+9. [Anthropic direct fails while OpenRouter works](#issue-9-anthropic-direct-fails-while-openrouter-works)
+10. [OpenRouter free tier is rate-limited](#issue-10-openrouter-free-tier-is-rate-limited)
+11. [Task Rules matrix saves unexpected priorities](#issue-11-task-rules-matrix-saves-unexpected-priorities)
+12. [Local fallback quality is poor](#issue-12-local-fallback-quality-is-poor)
+13. [Edit modal requires a new key unexpectedly](#issue-13-edit-modal-requires-a-new-key-unexpectedly)
+14. [Model ID preset dropdown is empty](#issue-14-model-id-preset-dropdown-is-empty)
+15. [Tour never starts](#issue-15-tour-never-starts)
+16. [Tour restarts every session](#issue-16-tour-restarts-every-session)
+17. [Security note mentions AES-128 but audit expects Fernet](#issue-17-security-note-mentions-aes-128-but-audit-expects-fernet)
+18. [Provider preset cost seems too low](#issue-18-provider-preset-cost-seems-too-low)
+19. [Migration from v4.6 loses nothing but docs are missing](#issue-19-migration-from-v4.6-loses-nothing-but-docs-are-missing)
+20. [Troubleshooting search via Ctrl+F is hard](#issue-20-troubleshooting-search-via-ctrl+f-is-hard)
+21. [Custom endpoint with LM Studio returns 404](#issue-21-custom-endpoint-with-lm-studio-returns-404)
+22. [vLLM proxy rejects large context requests](#issue-22-vllm-proxy-rejects-large-context-requests)
+23. [Self-hosted proxy masks 401 as 500](#issue-23-self-hosted-proxy-masks-401-as-500)
+24. [API key copy/paste includes newline](#issue-24-api-key-copypaste-includes-newline)
+25. [Budget control expectations are too high](#issue-25-budget-control-expectations-are-too-high)
+26. [JSON file merge conflict on team repo](#issue-26-json-file-merge-conflict-on-team-repo)
+27. [Rules mismatch because task is misclassified](#issue-27-rules-mismatch-because-task-is-misclassified)
+28. [About tab links open a blank page](#issue-28-about-tab-links-open-a-blank-page)
+29. [Demo video is present but silent](#issue-29-demo-video-is-present-but-silent)
+30. [Gradio update changes DOM selectors](#issue-30-gradio-update-changes-dom-selectors)
+31. [Quota exhaustion pauses without resume context](#issue-31-quota-exhaustion-pauses-without-resume-context)
+32. [Enterprise reviewer asks for rollback plan](#issue-32-enterprise-reviewer-asks-for-rollback-plan)
+
+## How to use this document
+- Use the table of contents for anchor links.
+- Use Ctrl+F with the exact symptom phrase shown in bold.
+- Cross-check the About tab docs links if a deployment strips local file routing.
+
+<a id="issue-1-invalid-api-key-on-test-connection"></a>
+### Issue #1: Invalid API key on Test Connection
+
+**Symptoms:** Red ❌ appears after clicking Test Connection.
+
+**Common causes**
+- Extra spaces in the key
+- Wrong provider selected
+- Key revoked or expired
+
+**Recommended fix**
+1. Regenerate the key from the provider dashboard
+2. Paste it again without leading/trailing spaces
+3. Confirm the provider dropdown matches the key type
+
+<a id="issue-2-model-replies-in-the-wrong-language"></a>
+### Issue #2: Model replies in the wrong language
+
+**Symptoms:** Responses ignore the language of the prompt or system instruction.
+
+**Common causes**
+- Model preset optimized for another language
+- Task prompt lacks a language instruction
+- Provider-side system prompt contamination
+
+**Recommended fix**
+1. Add an explicit language instruction to the task
+2. Prefer GPT-4o or Claude Sonnet for multilingual work
+3. Test the model directly in the provider console
+
+<a id="issue-3-fallback-not-triggering-on-429"></a>
+### Issue #3: Fallback not triggering on 429
+
+**Symptoms:** Pipeline pauses instead of moving to the chosen fallback behavior.
+
+**Common causes**
+- Saved fallback policy is ask_new_key
+- 429 body is returned as a generic error by a proxy
+- Outdated custom model config
+
+**Recommended fix**
+1. Re-open the model card and verify fallback policy
+2. Retest using direct provider endpoint
+3. Save the model again to refresh metadata
+
+<a id="issue-4-pipeline-hangs-during-coder-stage"></a>
+### Issue #4: Pipeline hangs during Coder stage
+
+**Symptoms:** Status remains on coder or reviewer for too long.
+
+**Common causes**
+- Provider latency spike
+- Very large prompt or repository
+- Provider queue saturation
+
+**Recommended fix**
+1. Retry with a smaller ZIP
+2. Switch to a faster preset like Groq or Claude Haiku
+3. Use Private Mode for small/local tasks
+
+<a id="issue-5-custom-model-is-used-even-when-disabled"></a>
+### Issue #5: Custom model is used even when disabled
+
+**Symptoms:** Router keeps picking a model after you thought it was inactive.
+
+**Common causes**
+- always_use is enabled
+- Task rules still match any task
+- Priority order favors that model
+
+**Recommended fix**
+1. Edit the model and disable always_use
+2. Change task_types from any to specific categories
+3. Adjust the rule priority matrix
+
+<a id="issue-6-base-url-looks-correct-but-requests-fail"></a>
+### Issue #6: Base URL looks correct but requests fail
+
+**Symptoms:** Connection test fails with network or 404 errors.
+
+**Common causes**
+- Provider endpoint missing /v1
+- Anthropic direct uses /messages instead of /chat/completions
+- Proxy strips auth headers
+
+**Recommended fix**
+1. Verify the endpoint in provider docs
+2. Use the built-in preset instead of manual typing
+3. Try a direct provider endpoint to isolate the issue
+
+<a id="issue-7-saved-model-disappears-after-restart"></a>
+### Issue #7: Saved model disappears after restart
+
+**Symptoms:** A model is visible in-session but missing later.
+
+**Common causes**
+- Master key changed
+- Encrypted file corrupted
+- Data directory permissions issue
+
+**Recommended fix**
+1. Reuse the same master key
+2. Restore data/custom_models.enc.json from backup
+3. Ensure the process can write to the data folder
+
+<a id="issue-8-quota-bar-never-updates"></a>
+### Issue #8: Quota bar never updates
+
+**Symptoms:** Model card always shows unknown quota.
+
+**Common causes**
+- Provider does not expose headers
+- First request never completed successfully
+- Proxy removed rate-limit headers
+
+**Recommended fix**
+1. Run one successful generation
+2. Use a direct provider endpoint if quota visibility matters
+3. Treat the bar as optional and rely on provider dashboard billing
+
+<a id="issue-9-anthropic-direct-fails-while-openrouter-works"></a>
+### Issue #9: Anthropic direct fails while OpenRouter works
+
+**Symptoms:** Claude via Anthropic shows HTTP errors.
+
+**Common causes**
+- Anthropic key pasted into OpenRouter provider
+- Account lacks billing
+- Model name typo
+
+**Recommended fix**
+1. Select Anthropic direct provider
+2. Check billing and usage limits
+3. Use a preset rather than a hand-typed model ID
+
+<a id="issue-10-openrouter-free-tier-is-rate-limited"></a>
+### Issue #10: OpenRouter free tier is rate-limited
+
+**Symptoms:** Requests work, then suddenly slow down or fail.
+
+**Common causes**
+- Free tier cap reached
+- Burst traffic from multiple runs
+- No paid balance
+
+**Recommended fix**
+1. Wait for the reset window
+2. Use a paid key or alternate provider
+3. Route simple tasks to a cheaper preset
+
+<a id="issue-11-task-rules-matrix-saves-unexpected-priorities"></a>
+### Issue #11: Task Rules matrix saves unexpected priorities
+
+**Symptoms:** The top model in cards differs from the table order.
+
+**Common causes**
+- Priority value duplicates
+- Higher min complexity wins ties
+- Model list sorted after save
+
+**Recommended fix**
+1. Give each model a unique priority
+2. Check min_complexity values
+3. Refresh the panel after saving
+
+<a id="issue-12-local-fallback-quality-is-poor"></a>
+### Issue #12: Local fallback quality is poor
+
+**Symptoms:** Fallback works but output quality drops noticeably.
+
+**Common causes**
+- Fallback switched to small local model
+- Task too complex for local model
+- Prompt too large for context
+
+**Recommended fix**
+1. Use ask_new_key for critical work
+2. Trim prompt size
+3. Reserve local fallback for continuation, not best quality
+
+<a id="issue-13-edit-modal-requires-a-new-key-unexpectedly"></a>
+### Issue #13: Edit modal requires a new key unexpectedly
+
+**Symptoms:** Changing provider or model ID forces key replacement.
+
+**Common causes**
+- Security design prevents silent target change
+- Provider changed endpoints
+
+**Recommended fix**
+1. Paste a new key when changing provider/model target
+2. Keep provider/model unchanged to reuse the stored encrypted key
+
+<a id="issue-14-model-id-preset-dropdown-is-empty"></a>
+### Issue #14: Model ID preset dropdown is empty
+
+**Symptoms:** Provider selected but no popular models appear.
+
+**Common causes**
+- provider_presets.json missing
+- Provider not curated
+- JSON failed to load
+
+**Recommended fix**
+1. Validate data/provider_presets.json
+2. Choose a curated provider preset
+3. Restart the app after restoring the file
+
+<a id="issue-15-tour-never-starts"></a>
+### Issue #15: Tour never starts
+
+**Symptoms:** First-run experience does not appear.
+
+**Common causes**
+- localStorage already marks it complete
+- Advanced options never opened
+- CDN blocked
+
+**Recommended fix**
+1. Use the Restart onboarding tour button in About
+2. Open Advanced options once
+3. Allow driver.js CDN or self-host the asset
+
+<a id="issue-16-tour-restarts-every-session"></a>
+### Issue #16: Tour restarts every session
+
+**Symptoms:** Users see the tour repeatedly.
+
+**Common causes**
+- localStorage blocked in browser
+- Incognito session
+- Browser privacy extension
+
+**Recommended fix**
+1. Use a standard browser profile
+2. Disable aggressive storage cleaners for the app origin
+3. Treat it as a browser setting rather than an app bug
+
+<a id="issue-17-security-note-mentions-aes-128-but-audit-expects-fernet"></a>
+### Issue #17: Security note mentions AES-128 but audit expects Fernet
+
+**Symptoms:** Documentation wording causes confusion.
+
+**Common causes**
+- Fernet uses AES-128-CBC plus HMAC-SHA256
+- Reader expects a lower-level cipher description
+
+**Recommended fix**
+1. Document the exact primitive and wrapper name together
+2. Keep the wording consistent across guides and release notes
+
+<a id="issue-18-provider-preset-cost-seems-too-low"></a>
+### Issue #18: Provider preset cost seems too low
+
+**Symptoms:** Estimated bug-fix cost differs from dashboard billing.
+
+**Common causes**
+- Estimate assumes 2K input and 500 output tokens
+- Provider bills cached tokens or additional overhead
+- Round-off in UI
+
+**Recommended fix**
+1. Treat it as a rough planning estimate
+2. Check exact bill in provider console
+3. Adjust your internal budgeting spreadsheet
+
+<a id="issue-19-migration-from-v4.6-loses-nothing-but-docs-are-missing"></a>
+### Issue #19: Migration from v4.6 loses nothing but docs are missing
+
+**Symptoms:** App works, but new guides are absent.
+
+**Common causes**
+- Phase 4 files not deployed
+- Incomplete ZIP extraction
+
+**Recommended fix**
+1. Confirm docs/ folder exists in deployment package
+2. Redeploy using the combined Phase 1-4 archive
+
+<a id="issue-20-troubleshooting-search-via-ctrl+f-is-hard"></a>
+### Issue #20: Troubleshooting search via Ctrl+F is hard
+
+**Symptoms:** Finding issues in KB takes too long.
+
+**Common causes**
+- Heading text inconsistent
+- No anchor-friendly titles
+
+**Recommended fix**
+1. Use the TOC links first
+2. Keep issue titles short and unique
+3. Search the exact symptom phrase in quotes
+
+<a id="issue-21-custom-endpoint-with-lm-studio-returns-404"></a>
+### Issue #21: Custom endpoint with LM Studio returns 404
+
+**Symptoms:** Local LM Studio server responds but chat route fails.
+
+**Common causes**
+- Wrong port
+- Different API path
+- Server not started in OpenAI-compatible mode
+
+**Recommended fix**
+1. Verify server URL in browser
+2. Use the documented /v1 endpoint
+3. Enable OpenAI compatibility in LM Studio settings
+
+<a id="issue-22-vllm-proxy-rejects-large-context-requests"></a>
+### Issue #22: vLLM proxy rejects large context requests
+
+**Symptoms:** Long prompts fail despite large advertised context.
+
+**Common causes**
+- Server max model len lower than preset value
+- Proxy truncation
+- Model served with smaller rope scaling
+
+**Recommended fix**
+1. Set a realistic context window in your infra
+2. Use a smaller chunking strategy
+3. Test maximum prompt size manually
+
+<a id="issue-23-self-hosted-proxy-masks-401-as-500"></a>
+### Issue #23: Self-hosted proxy masks 401 as 500
+
+**Symptoms:** App shows generic error while creds are wrong.
+
+**Common causes**
+- Reverse proxy rewrites status code
+- Custom middleware
+
+**Recommended fix**
+1. Inspect proxy logs
+2. Prefer direct provider URL when debugging auth
+3. Update middleware to preserve upstream status
+
+<a id="issue-24-api-key-copypaste-includes-newline"></a>
+### Issue #24: API key copy/paste includes newline
+
+**Symptoms:** Invisible characters break auth.
+
+**Common causes**
+- Clipboard copied from terminal with newline
+- Password manager inserted formatting
+
+**Recommended fix**
+1. Paste into a plain-text editor first
+2. Re-copy from provider dashboard
+
+<a id="issue-25-budget-control-expectations-are-too-high"></a>
+### Issue #25: Budget control expectations are too high
+
+**Symptoms:** User expects hard spend caps inside the app.
+
+**Common causes**
+- Feature only estimates cost; billing lives with provider
+- No provider budget API configured
+
+**Recommended fix**
+1. Use provider-side spend limits
+2. Treat app estimate as advisory
+
+<a id="issue-26-json-file-merge-conflict-on-team-repo"></a>
+### Issue #26: JSON file merge conflict on team repo
+
+**Symptoms:** Two branches modify provider_presets.json differently.
+
+**Common causes**
+- Manual edits to the same preset list
+- Different ordering
+
+**Recommended fix**
+1. Sort presets by provider name
+2. Review both additions before merge
+
+<a id="issue-27-rules-mismatch-because-task-is-misclassified"></a>
+### Issue #27: Rules mismatch because task is misclassified
+
+**Symptoms:** Router picks a model for feature instead of refactor.
+
+**Common causes**
+- Classifier confidence low
+- Prompt mixes multiple intents
+
+**Recommended fix**
+1. Rewrite the task to emphasize the primary intent
+2. Use always_use temporarily for critical flows
+
+<a id="issue-28-about-tab-links-open-a-blank-page"></a>
+### Issue #28: About tab links open a blank page
+
+**Symptoms:** Clicking docs buttons does not show files.
+
+**Common causes**
+- Deployment blocks local file serving
+- Reverse proxy strips /file route
+
+**Recommended fix**
+1. Expose the docs directory in deployment config
+2. Fall back to packaging docs in release ZIP
+
+<a id="issue-29-demo-video-is-present-but-silent"></a>
+### Issue #29: Demo video is present but silent
+
+**Symptoms:** Stakeholder expects narration.
+
+**Common causes**
+- Video is a storyboard walkthrough rather than a recorded voiceover
+
+**Recommended fix**
+1. Use the screenshots plus release notes as the spoken script
+2. Record narration later if needed
+
+<a id="issue-30-gradio-update-changes-dom-selectors"></a>
+### Issue #30: Gradio update changes DOM selectors
+
+**Symptoms:** Tour highlights break after dependency upgrades.
+
+**Common causes**
+- Button or accordion DOM changed
+- driver.js selector stale
+
+**Recommended fix**
+1. Revalidate selectors after any Gradio upgrade
+2. Keep tour hooks tied to explicit elem_id values
+
+<a id="issue-31-quota-exhaustion-pauses-without-resume-context"></a>
+### Issue #31: Quota exhaustion pauses without resume context
+
+**Symptoms:** User adds a new key and loses track of partial progress.
+
+**Common causes**
+- Completed work context not surfaced in current UX
+
+**Recommended fix**
+1. Persist task summary in status panel
+2. Re-run with the same prompt after key update and compare output
+
+<a id="issue-32-enterprise-reviewer-asks-for-rollback-plan"></a>
+### Issue #32: Enterprise reviewer asks for rollback plan
+
+**Symptoms:** Team needs a low-risk revert path.
+
+**Common causes**
+- Change review process requires a documented rollback
+
+**Recommended fix**
+1. Use the migration guide rollback steps
+2. Keep the v4.6 ZIP backup alongside the v4.7 release package
+
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/release_notes_v4.7.md devai_phase4/release_notes_v4.7.md
--- devai_phase4_orig/release_notes_v4.7.md 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/release_notes_v4.7.md 2026-07-06 10:28:32.013919928 +0000
@@ -0,0 +1,19 @@
+# DevAI Studio v4.7 Release Notes
+
+## Highlights
+- Bring Your Own Model (BYOM) is now production-ready.
+- Curated provider presets remove the need to memorize base URLs and model IDs.
+- First-run onboarding tour explains the three-dot workflow.
+- Full Hindi and English user guides are bundled in the release.
+- Migration remains zero-breaking and zero-schema-change.
+
+## Included artifacts
+- Combined Phase 1 + 2 + 3 + 4 source tree
+- `CHANGELOG_v4.7.md`
+- `PHASE4_DIFF.patch`
+- `screenshots/` with 12+ walkthrough images
+- `demo/devai_studio_v4.7_phase4_demo.mp4`
+- `docs/` with user guides, migration guide and troubleshooting KB
+
+## Upgrade risk
+Low. All changes are additive. Existing v4.6 APIs remain unchanged.
Binary files devai_phase4_orig/screenshots/09_provider_presets_overview.png and devai_phase4/screenshots/09_provider_presets_overview.png differ
Binary files devai_phase4_orig/screenshots/10_cost_estimator.png and devai_phase4/screenshots/10_cost_estimator.png differ
Binary files devai_phase4_orig/screenshots/11_example_rule_sets.png and devai_phase4/screenshots/11_example_rule_sets.png differ
Binary files devai_phase4_orig/screenshots/12_onboarding_tour.png and devai_phase4/screenshots/12_onboarding_tour.png differ
Binary files devai_phase4_orig/screenshots/13_docs_hub_about_tab.png and devai_phase4/screenshots/13_docs_hub_about_tab.png differ
Binary files devai_phase4_orig/screenshots/14_quota_exhaustion_storyboard.png and devai_phase4/screenshots/14_quota_exhaustion_storyboard.png differ
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/tests/test_phase4_docs_presets.py devai_phase4/tests/test_phase4_docs_presets.py
--- devai_phase4_orig/tests/test_phase4_docs_presets.py 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/tests/test_phase4_docs_presets.py 2026-07-06 10:26:42.073919928 +0000
@@ -0,0 +1,26 @@
+from pathlib import Path
+import json
+
+ROOT = Path(__file__).resolve().parent.parent
+
+
+def test_provider_presets_catalog_exists():
+ data = json.loads((ROOT / "data/provider_presets.json").read_text(encoding="utf-8"))
+ assert len(data["presets"]) >= 6
+ assert len(data["example_rule_sets"]) >= 4
+
+
+def test_guides_exist_and_share_same_core_sections():
+ hi = (ROOT / "docs/BYOM_USER_GUIDE_HI.md").read_text(encoding="utf-8")
+ en = (ROOT / "docs/BYOM_USER_GUIDE_EN.md").read_text(encoding="utf-8")
+ for marker in ["1.", "2.", "3.", "4.", "5.", "6.", "7.", "8.", "9.", "10."]:
+ assert marker in hi
+ assert marker in en
+
+
+def test_onboarding_and_ui_hooks_present():
+ tour = (ROOT / "ui/onboarding_tour.py").read_text(encoding="utf-8")
+ panel = (ROOT / "ui/custom_models_panel.py").read_text(encoding="utf-8")
+ assert "devai_tour_completed_v4.7" in tour
+ assert "add-model-btn" in panel
+ assert "fallback-radio" in panel
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/ui/custom_models_panel.py devai_phase4/ui/custom_models_panel.py
--- devai_phase4_orig/ui/custom_models_panel.py 2026-07-06 10:20:00.089919928 +0000
+++ devai_phase4/ui/custom_models_panel.py 2026-07-06 10:28:42.293919928 +0000
@@ -8,9 +8,10 @@
from __future__ import annotations
import html
+import json
import tempfile
from pathlib import Path
-from typing import Any, List, Optional
+from typing import Any, Dict, List, Optional
import gradio as gr
@@ -68,6 +69,93 @@
"number", "bool", "str",
]
+PRESETS_PATH = Path(__file__).resolve().parent.parent / "data" / "provider_presets.json"
+
+
+def _load_presets() -> Dict[str, Any]:
+ try:
+ return json.loads(PRESETS_PATH.read_text(encoding="utf-8"))
+ except Exception:
+ return {"presets": [], "example_rule_sets": []}
+
+
+PRESET_DATA = _load_presets()
+PROVIDER_PRESETS = {item.get("provider"): item for item in PRESET_DATA.get("presets", []) if item.get("provider")}
+RULE_SET_PRESETS = {item.get("name"): item for item in PRESET_DATA.get("example_rule_sets", []) if item.get("name")}
+RULE_SET_CHOICES = list(RULE_SET_PRESETS.keys())
+
+
+def _provider_meta(provider: str) -> Dict[str, Any]:
+ return PROVIDER_PRESETS.get(provider, {})
+
+
+def _provider_summary_html(provider: str) -> str:
+ meta = _provider_meta(provider)
+ desc = html.escape(str(meta.get("description") or "OpenAI-compatible endpoint"))
+ signup = str(meta.get("signup_url") or "")
+ docs = str(meta.get("docs_url") or "")
+ base_url = str((SUPPORTED_PROVIDERS.get(provider) or {}).get("base_url") or "Custom / user supplied")
+ links = []
+ if signup:
+ links.append(f'<a href="{html.escape(signup)}" target="_blank">Signup</a>')
+ if docs:
+ links.append(f'<a href="{html.escape(docs)}" target="_blank">Docs</a>')
+ return (
+ '<div class="devai-inline-status ok" style="margin-bottom:12px;">'
+ f'<b>{html.escape(str(meta.get("display_name") or provider.title()))}</b><br>{desc}<br>'
+ f'<span style="font-size:12px;">Base URL: <code>{html.escape(base_url)}</code></span>'
+ + (f'<br><span style="font-size:12px;">{" · ".join(links)}</span>' if links else '')
+ + '</div>'
+ )
+
+
+def _popular_choices(provider: str) -> List[str]:
+ return [str(item.get("display_name")) for item in _provider_meta(provider).get("popular_models", []) if item.get("display_name")]
+
+
+def _find_popular_model(provider: str, label: str) -> Optional[dict]:
+ for item in _provider_meta(provider).get("popular_models", []):
+ if str(item.get("display_name")) == str(label):
+ return item
+ return None
+
+
+def _cost_hint_html(model: Optional[dict], provider: Optional[str] = None) -> str:
+ if not model:
+ return '<div class="devai-model-sub">Select a popular model to auto-fill model_id, endpoint and a rough cost hint.</div>'
+ cost = dict(model.get("approx_cost_per_1k_tokens") or {})
+ input_cost = float(cost.get("input", 0) or 0)
+ output_cost = float(cost.get("output", 0) or 0)
+ typical = (input_cost * 2.0) + (output_cost * 0.5)
+ ctx = model.get("context_window")
+ best_for = ", ".join(model.get("best_for") or ["any"])
+ notes = model.get("notes")
+ parts = [f'≈ ${typical:.2f} per typical bug fix (2K in / 500 out tokens)', f'Best for: {html.escape(best_for)}']
+ if ctx:
+ parts.append(f'Context: {int(ctx):,} tokens')
+ if notes:
+ parts.append(html.escape(str(notes)))
+ return '<div class="devai-inline-status ok">' + '<br>'.join(parts) + '</div>'
+
+
+def _rule_set_defaults(rule_name: str, current_model_id: str = "", current_label: str = ""):
+ preset = RULE_SET_PRESETS.get(rule_name) or {}
+ rules = list(preset.get("rules") or [])
+ selected = rules[0] if rules else {}
+ needle_id = str(current_model_id or "").lower()
+ needle_label = str(current_label or "").lower()
+ for rule in rules:
+ target = str(rule.get("model") or "").lower()
+ if needle_id and needle_id in target:
+ selected = rule
+ break
+ if needle_label and needle_label.split(' (')[0] in target:
+ selected = rule
+ break
+ fallback = "local_model" if rule_name == "Privacy-hybrid" else "ask_new_key"
+ return list(selected.get("task_types") or ["any"]), int(selected.get("min_complexity", 0) or 0), bool(selected.get("always_use")), fallback
+
+
CUSTOM_MODELS_CSS = """
<style>
.devai-custom-models-root { position: relative; }
@@ -238,7 +326,7 @@
def _empty_state() -> str:
return (
- '<div class="devai-empty-state">'
+ '<div id="devai-model-list-anchor" class="devai-empty-state devai-model-card">'
'<div class="devai-empty-illu">🧠✨</div>'
'<div style="font-weight:700;font-size:16px;">No custom models yet</div>'
'<div style="font-size:13px;color:#6b7280;margin-top:6px;">Get started by adding your first model.</div>'
@@ -318,7 +406,7 @@
f' <div class="devai-quota-bar"><div class="devai-quota-fill {cls}" style="width:{pct:.1f}%;"></div></div>'
'</div>'
)
- return "".join(parts)
+ return '<div id="devai-model-list-anchor">' + ''.join(parts) + '</div>'
def _rules_table(models: List[dict]) -> List[List[Any]]:
@@ -384,7 +472,7 @@
with gr.Group(visible=False, elem_classes=["devai-modal-overlay"], elem_id="devai-cm-menu") as menu_group:
gr.HTML('<div class="devai-modal-title">🎨 Custom AI Models</div>')
gr.HTML('<div class="devai-modal-subtitle">अपने personal API keys से कोई भी OpenAI-compatible मॉडल जोड़ें</div>')
- add_new_btn = gr.Button("➕ अपना AI मॉडल जोड़ें", variant="primary")
+ add_new_btn = gr.Button("➕ अपना AI मॉडल जोड़ें", variant="primary", elem_id="add-model-btn")
models_display = gr.HTML(value=_empty_state())
with gr.Row():
edit_rules_btn = gr.Button("⚙️ Task Rules", size="sm")
@@ -392,20 +480,25 @@
with gr.Group(visible=False, elem_classes=["devai-modal-overlay"], elem_id="devai-cm-add-modal") as add_modal:
gr.HTML('<div class="devai-modal-title">➕ नया AI Model जोड़ें</div>')
- gr.HTML('<div class="devai-modal-subtitle">यह model तभी use होगा जब आप rules में specify करेंगे</div>')
+ gr.HTML('<div class="devai-modal-subtitle">Provider preset, popular model और rule-set starter के साथ 60-second setup</div>')
+ add_provider_help = gr.HTML(_provider_summary_html("openrouter"))
with gr.Row():
with gr.Column(scale=1):
add_name = gr.Textbox(label="Model Name (आपका पहचानने के लिए)", placeholder="e.g., My Claude 3.5 for security audits", max_lines=1)
add_provider = gr.Dropdown(label="Provider", choices=list(SUPPORTED_PROVIDERS.keys()), value="openrouter", info="OpenRouter सबसे flexible है (100+ models)")
- add_base_url = gr.Textbox(label="Base URL (सिर्फ़ 'custom' provider के लिए)", placeholder="https://your-endpoint.com/v1", visible=False, max_lines=1)
+ add_popular_model = gr.Dropdown(label="Popular models", choices=_popular_choices("openrouter"), value=None, info="Curated presets — model_id याद रखने की ज़रूरत नहीं")
+ add_base_url = gr.Textbox(label="Base URL (सिर्फ़ 'custom' provider के लिए)", placeholder="https://your-endpoint.com/v1", visible=False, max_lines=1, value="https://openrouter.ai/api/v1")
add_model_id = gr.Textbox(label="Model ID (provider के docs से copy करें)", placeholder="anthropic/claude-3.5-sonnet", max_lines=1)
add_api_key = gr.Textbox(label="🔑 API Key", placeholder="sk-or-v1-...", type="password", max_lines=1, info="Encrypted at rest with your master key. कभी logs में नहीं दिखेगी।")
+ add_cost_hint = gr.HTML(_cost_hint_html(None, "openrouter"))
with gr.Column(scale=1):
- gr.HTML("<div style='font-weight:700;color:#6d28d9;margin-bottom:8px;'>📌 यह मॉडल कब use हो?</div>")
- add_task_types = gr.CheckboxGroup(label="Task types", choices=TASK_CHOICES, value=["any"])
- add_complexity = gr.Slider(label="Minimum complexity (0=कोई भी, 10=बहुत जटिल)", minimum=0, maximum=10, value=0, step=1, info="0 रखें अगर हर task पर use करना है")
- add_always_use = gr.Checkbox(label="⚡ हमेशा use करें (override सारे rules)", value=False)
- add_fallback = gr.Radio(choices=[("नई API key माँगो (pause pipeline)", "ask_new_key"), ("Local model पर switch करो (task-mapping के अनुसार)", "local_model")], value="ask_new_key", label="📉 जब API limit ख़त्म हो")
+ with gr.Group(elem_id="task-rules-section"):
+ gr.HTML("<div style='font-weight:700;color:#6d28d9;margin-bottom:8px;'>📌 यह मॉडल कब use हो?</div>")
+ add_rule_set = gr.Dropdown(label="Load Example Rule Set", choices=RULE_SET_CHOICES, value=None, info="One click से starter routing rules भरें")
+ add_task_types = gr.CheckboxGroup(label="Task types", choices=TASK_CHOICES, value=["any"])
+ add_complexity = gr.Slider(label="Minimum complexity (0=कोई भी, 10=बहुत जटिल)", minimum=0, maximum=10, value=0, step=1, info="0 रखें अगर हर task पर use करना है")
+ add_always_use = gr.Checkbox(label="⚡ हमेशा use करें (override सारे rules)", value=False)
+ add_fallback = gr.Radio(choices=[("नई API key माँगो (pause pipeline)", "ask_new_key"), ("Local model पर switch करो (task-mapping के अनुसार)", "local_model")], value="ask_new_key", label="📉 जब API limit ख़त्म हो", elem_id="fallback-radio")
add_status = gr.HTML("")
with gr.Row():
add_test_btn = gr.Button("🔌 Test Connection", variant="secondary")
@@ -466,12 +559,39 @@
False,
gr.update(visible=False),
gr.update(visible=True),
- "", "openrouter", gr.update(value="", visible=False), "", "",
+ "", "openrouter", _provider_summary_html("openrouter"), gr.update(choices=_popular_choices("openrouter"), value=None),
+ gr.update(value=(SUPPORTED_PROVIDERS.get("openrouter") or {}).get("base_url") or "", visible=False),
+ "", "", _cost_hint_html(None, "openrouter"), None,
["any"], 0, False, "ask_new_key", "",
)
+ def handle_provider_change(provider: str):
+ base = (SUPPORTED_PROVIDERS.get(provider) or {}).get("base_url") or ""
+ return (
+ _provider_summary_html(provider),
+ gr.update(choices=_popular_choices(provider), value=None),
+ gr.update(value=base, visible=(provider == "custom")),
+ "",
+ _cost_hint_html(None, provider),
+ )
+
def toggle_base_url_visibility(provider: str):
- return gr.update(visible=(provider == "custom"))
+ base = (SUPPORTED_PROVIDERS.get(provider) or {}).get("base_url") or ""
+ return gr.update(value=base, visible=(provider == "custom"))
+
+ def apply_popular_model(provider: str, popular_label: str, current_name: str):
+ meta = _find_popular_model(provider, popular_label or "")
+ if not meta:
+ return current_name, "", gr.update(value=(SUPPORTED_PROVIDERS.get(provider) or {}).get("base_url") or "", visible=(provider == "custom")), _cost_hint_html(None, provider)
+ suggested_name = (current_name or "").strip() or str(meta.get("display_name") or "")
+ base = (SUPPORTED_PROVIDERS.get(provider) or {}).get("base_url") or ""
+ return suggested_name, str(meta.get("model_id") or ""), gr.update(value=base, visible=(provider == "custom")), _cost_hint_html(meta, provider)
+
+ def apply_rule_set(rule_name: str, popular_label: str, model_id: str):
+ if not rule_name:
+ return ["any"], 0, False, "ask_new_key", ""
+ tasks, complexity, always_use, fallback = _rule_set_defaults(rule_name, model_id, popular_label)
+ return tasks, complexity, always_use, fallback, _status_html(f"Loaded rule set: {rule_name}", ok=True)
def _build_rules(task_types, complexity, always_use, fallback, priority=None):
tasks = list(task_types or []) or ["any"]
@@ -705,9 +825,11 @@
)
add_new_btn.click(
fn=show_add_modal,
- outputs=[menu_open_state, menu_group, add_modal, add_name, add_provider, add_base_url, add_model_id, add_api_key, add_task_types, add_complexity, add_always_use, add_fallback, add_status],
+ outputs=[menu_open_state, menu_group, add_modal, add_name, add_provider, add_provider_help, add_popular_model, add_base_url, add_model_id, add_api_key, add_cost_hint, add_rule_set, add_task_types, add_complexity, add_always_use, add_fallback, add_status],
)
- add_provider.change(fn=toggle_base_url_visibility, inputs=[add_provider], outputs=[add_base_url])
+ add_provider.change(fn=handle_provider_change, inputs=[add_provider], outputs=[add_provider_help, add_popular_model, add_base_url, add_model_id, add_cost_hint])
+ add_popular_model.change(fn=apply_popular_model, inputs=[add_provider, add_popular_model, add_name], outputs=[add_name, add_model_id, add_base_url, add_cost_hint])
+ add_rule_set.change(fn=apply_rule_set, inputs=[add_rule_set, add_popular_model, add_model_id], outputs=[add_task_types, add_complexity, add_always_use, add_fallback, add_status])
add_test_btn.click(fn=test_connection_handler, inputs=[add_name, add_provider, add_model_id, add_api_key, add_base_url, master_key_state], outputs=[add_status])
add_save_btn.click(
fn=save_model_handler,
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/ui/gradio_ui.py devai_phase4/ui/gradio_ui.py
--- devai_phase4_orig/ui/gradio_ui.py 2026-07-06 10:20:00.089919928 +0000
+++ devai_phase4/ui/gradio_ui.py 2026-07-06 10:26:42.069919928 +0000
@@ -26,6 +26,7 @@
preview_project_contents,
)
from ui.custom_models_panel import render_custom_models_panel
+from ui.onboarding_tour import render_tour
try:
from core.graph_mapper import build_graph, export_cytoscape_json
@@ -1003,6 +1004,7 @@
system_info_fn() -> dict
"""
info = system_info_fn() or {}
+ docs_root = Path(__file__).resolve().parent.parent / "docs"
model_choices = list(info.get("router_models") or [])
key_status = info.get("key_status", {})
router_default = info.get("router_default") or (model_choices[0] if model_choices else AUTO_MODEL_OPTION)
@@ -1195,7 +1197,7 @@
file_types=[".zip"],
file_count="single",
)
- with gr.Accordion("Advanced options", open=False):
+ with gr.Accordion("Advanced options", open=False, elem_id="devai-advanced-options"):
enable_jury = gr.Checkbox(
label="Enable multi-model Jury vote (slower, uses all 4 models)",
value=False,
@@ -1242,6 +1244,7 @@
custom_panel = render_custom_models_panel(
master_key_state=master_key_state
)
+ render_tour(show_restart_button=False)
master_api_hidden = gr.Textbox(
value="",
elem_id="devai-master-api-hidden",
@@ -1815,6 +1818,18 @@
+ pretty
+ "\n\n**Stack:** Python · Gradio · OpenRouter free-tier · Piston sandbox"
)
+ gr.Markdown("### 📚 BYOM docs, migration and help")
+ gr.HTML(
+ f"""
+ <div style='display:flex;flex-wrap:wrap;gap:12px;margin:8px 0 16px;'>
+ <a class='devai-doc-btn' href='/file={docs_root / "BYOM_USER_GUIDE_HI.md"}' target='_blank' style='padding:10px 16px;border-radius:999px;background:#f3e8ff;color:#6d28d9;text-decoration:none;font-weight:600;border:1px solid #ddd6fe;'>📖 User Guide (HI)</a>
+ <a class='devai-doc-btn' href='/file={docs_root / "BYOM_USER_GUIDE_EN.md"}' target='_blank' style='padding:10px 16px;border-radius:999px;background:#f3e8ff;color:#6d28d9;text-decoration:none;font-weight:600;border:1px solid #ddd6fe;'>📘 User Guide (EN)</a>
+ <a class='devai-doc-btn' href='/file={docs_root / "MIGRATION_v4.6_to_v4.7.md"}' target='_blank' style='padding:10px 16px;border-radius:999px;background:#f3e8ff;color:#6d28d9;text-decoration:none;font-weight:600;border:1px solid #ddd6fe;'>🚚 Migration Guide</a>
+ <a class='devai-doc-btn' href='/file={docs_root / "TROUBLESHOOTING_KB.md"}' target='_blank' style='padding:10px 16px;border-radius:999px;background:#fef2f2;color:#be123c;text-decoration:none;font-weight:600;border:1px solid #fecaca;'>🐛 Troubleshooting</a>
+ </div>
+ """
+ )
+ render_tour(show_restart_button=True)
return demo
diff -ruN '--exclude=__pycache__' '--exclude=*.pyc' devai_phase4_orig/ui/onboarding_tour.py devai_phase4/ui/onboarding_tour.py
--- devai_phase4_orig/ui/onboarding_tour.py 1970-01-01 00:00:00.000000000 +0000
+++ devai_phase4/ui/onboarding_tour.py 2026-07-06 10:26:42.069919928 +0000
@@ -0,0 +1,86 @@
+"""First-run onboarding tour for BYOM feature using driver.js."""
+from __future__ import annotations
+
+import gradio as gr
+
+TOUR_JS = """
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/driver.js@1.3.6/dist/driver.css" />
+<script src="https://cdn.jsdelivr.net/npm/driver.js@1.3.6/dist/driver.js.iife.js"></script>
+<script>
+(() => {
+ if (window.__devaiTourBootstrapped) return;
+ window.__devaiTourBootstrapped = true;
+ const TOUR_KEY = 'devai_tour_completed_v4.7';
+ const waitFor = (selector, timeout=10000) => new Promise((resolve) => {
+ const start = Date.now();
+ const tick = () => {
+ const el = document.querySelector(selector);
+ if (el) return resolve(el);
+ if (Date.now() - start > timeout) return resolve(null);
+ setTimeout(tick, 200);
+ };
+ tick();
+ });
+ const visible = (el) => !!el && el.offsetParent !== null;
+ async function prepTour() {
+ const three = await waitFor('.devai-three-dot-btn');
+ if (!three) return false;
+ if (!visible(three)) return false;
+ three.click();
+ await new Promise(r => setTimeout(r, 500));
+ const addBtn = await waitFor('#add-model-btn');
+ if (addBtn && visible(addBtn)) addBtn.click();
+ await new Promise(r => setTimeout(r, 500));
+ return true;
+ }
+ window.startDevAITour = async function(force=false) {
+ if (!force && localStorage.getItem(TOUR_KEY)) return;
+ if (!window.driver || !window.driver.js) return;
+ const ok = await prepTour();
+ if (!ok) return;
+ const driverObj = window.driver.js.driver({
+ showProgress: true,
+ allowClose: true,
+ nextBtnText: 'आगे →',
+ prevBtnText: '← पीछे',
+ doneBtnText: 'Done ✓',
+ onDestroyed: () => localStorage.setItem(TOUR_KEY, 'true'),
+ steps: [
+ { element: '.devai-three-dot-btn', popover: { title: '✨ नया: Custom AI Models', description: 'यहाँ से अपने personal API keys के साथ Claude, GPT, Gemini या कोई भी OpenAI-compatible model जोड़ें।' } },
+ { element: '#add-model-btn', popover: { title: 'Model जोड़ना', description: 'Provider preset चुनें, popular model चुनें, API key डालें और DevAI Studio बाकी fields auto-fill कर देगा।' } },
+ { element: '#task-rules-section', popover: { title: 'Task Rules ⚙️', description: 'यह model कब use हो? हर task पर, सिर्फ़ security audits पर, या complexity threshold के बाद — सब यहाँ तय होता है।' } },
+ { element: '#fallback-radio', popover: { title: 'Fallback Policy 📉', description: 'Quota खत्म हो जाए तो नई key माँगनी है या local model पर continue करना है — यह भी आप control करते हैं।' } },
+ { element: '#devai-model-list-anchor', popover: { title: 'Model Cards', description: 'Saved models, quota hints और routing priority यहाँ दिखेगी। यहीं से edit/update भी कर सकते हैं।' } },
+ ],
+ });
+ driverObj.drive();
+ };
+ const boot = () => {
+ if (localStorage.getItem(TOUR_KEY)) return;
+ let started = false;
+ const tick = () => {
+ if (started) return;
+ const three = document.querySelector('.devai-three-dot-btn');
+ if (visible(three)) {
+ started = true;
+ setTimeout(() => window.startDevAITour(false), 1200);
+ } else {
+ setTimeout(tick, 700);
+ }
+ };
+ tick();
+ };
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot);
+ else boot();
+})();
+</script>
+"""
+
+
+def render_tour(show_restart_button: bool = True):
+ gr.HTML(TOUR_JS)
+ if show_restart_button:
+ restart_btn = gr.Button('🔄 Restart onboarding tour', size='sm')
+ restart_btn.click(fn=None, js="() => { localStorage.removeItem('devai_tour_completed_v4.7'); window.startDevAITour(true); }")
+ return restart_btn
+ return None