Add qwen/qwen3.7-flash and inclusionai/ling-2.6-flash models

#2
Files changed (5) hide show
  1. Dockerfile +1 -1
  2. README.md +7 -19
  3. fix_space_error_report.json +14 -21
  4. patch_frontend.py +4 -67
  5. patch_models.py +6 -141
Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- # Rebuild v11: Fix stale-cache blank page (immutable asset cache + no-cache index.html)
2
 
3
  # Stage 1: Clone upstream + patch frontend ChatInput + build
4
  FROM node:20-alpine AS patcher
 
1
+ # Rebuild v9: Fix model routing - Hy3 Preview (OpenRouter via openai/tencent/), cohere/north-mini-code, deepseek/deepseek-v4-flash OR route, gpt-oss-120b no double-prefix
2
 
3
  # Stage 1: Clone upstream + patch frontend ChatInput + build
4
  FROM node:20-alpine AS patcher
README.md CHANGED
@@ -25,9 +25,9 @@ secrets:
25
 
26
  # Space Multiverse — ML Intern V2 (FREE & SAVE MODEL)
27
 
28
- <!-- rebuild: 2026-09-12-add-nex-agi-2.5-mini-pro-ling-3.0-flash-vl -->
29
 
30
- **25 models — mix of HF Inference + OpenRouter.**
31
 
32
  ## Models
33
 
@@ -42,24 +42,12 @@ secrets:
42
  | 7 | **DeepSeek V4 Flash (OR)** | OpenRouter | `deepseek` | ✅ |
43
  | 8 | **Gemma 4 9B** | OpenRouter | `google` | ✅ |
44
  | 9 | **Gemma 4 31B** | OpenRouter | `google` | ✅ |
45
- | 10 | **Hy3 Preview** | OpenRouter | `tencent` | ✅ |
46
- | 11 | **north-mini-code:free** | OpenRouter | `cohere` | ✅ |
47
- | 12 | **Nemotron 3 Super 120B** | OpenRouter | `nvidia` | ✅ |
48
- | 13 | **Nemotron 3 Ultra 550B** | OpenRouter | `nvidia` | ✅ |
49
- | 14 | **Laguna M.1** | OpenRouter | `poolside` | ✅ |
50
- | 15 | **Laguna XS.2** | OpenRouter | `poolside` | ✅ |
51
- | 16 | **Qwen3.7 Flash** | OpenRouter | `qwen` | ✅ |
52
- | 17 | **Ling 2.6 Flash** | OpenRouter | `inclusionai` | ✅ |
53
- | 18 | **Ling 3.0 Flash** | OpenRouter | `inclusionai` | ✅ |
54
- | 19 | **Ling 3.0 Tiny** | OpenRouter | `inclusionai` | ✅ |
55
- | 20 | **Ling 3.0 Flash VL** | OpenRouter | `inclusionai` | ✅ |
56
- | 21 | **Nex-N2.5-Mini** | OpenRouter | `nex-agi` | ✅ |
57
- | 22 | **Nex-N2.5-Pro** | OpenRouter | `nex-agi` | ✅ |
58
- | 23 | **GLM 5.3 Flash** | OpenRouter | `z-ai` | ✅ |
59
- | 24 | **Owl Alpha** | OpenRouter | `openrouter` | ✅ |
60
- | 25 | **DeepSeek V4 Flash 0731** | HF Inference | `deepseek` | ✅ |
61
 
62
  > Owl Alpha REMOVED — its free provider "Stealth" on OpenRouter is broken (returns 400 error).
63
 
64
  ## Required Secrets
65
- - `OPENROUTER_API_KEY` — required for OpenRouter-routed models
 
25
 
26
  # Space Multiverse — ML Intern V2 (FREE & SAVE MODEL)
27
 
28
+ <!-- rebuild: 2026-06-06-owl-alpha-removed-stealth-broken -->
29
 
30
+ **13 models — mix of HF Inference + OpenRouter.**
31
 
32
  ## Models
33
 
 
42
  | 7 | **DeepSeek V4 Flash (OR)** | OpenRouter | `deepseek` | ✅ |
43
  | 8 | **Gemma 4 9B** | OpenRouter | `google` | ✅ |
44
  | 9 | **Gemma 4 31B** | OpenRouter | `google` | ✅ |
45
+ | 10 | **Nemotron 3 Super 120B** | OpenRouter | `nvidia` | ✅ |
46
+ | 11 | **Nemotron 3 Ultra 550B** | OpenRouter | `nvidia` | ✅ |
47
+ | 12 | **Laguna M.1** | OpenRouter | `poolside` | ✅ |
48
+ | 13 | **Laguna XS.2** | OpenRouter | `poolside` | ✅ |
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
  > Owl Alpha REMOVED — its free provider "Stealth" on OpenRouter is broken (returns 400 error).
51
 
52
  ## Required Secrets
53
+ - `OPENROUTER_API_KEY` — required for OpenRouter-routed models
fix_space_error_report.json CHANGED
@@ -1,5 +1,5 @@
1
  {
2
- "status": "updated_with_20_models_v3",
3
  "space": "bep40/Space-Mutiverse-Control",
4
  "space_url": "https://huggingface.co/spaces/bep40/Space-Mutiverse-Control",
5
  "files_changed": ["patch_frontend.py", "patch_models.py"],
@@ -7,32 +7,25 @@
7
  {"row":1, "name":"Owl Alpha", "path":"openai/openrouter/owl-alpha"},
8
  {"row":2, "name":"DeepSeek V4 Pro", "path":"deepseek-ai/DeepSeek-V4-Pro"},
9
  {"row":3, "name":"DeepSeek V4 Flash", "path":"deepseek-ai/DeepSeek-V4-Flash"},
10
- {"row":4, "name":"gpt-oss-120b", "path":"openai/gpt-oss-120b"},
11
  {"row":5, "name":"Qwen3 Coder Next", "path":"Qwen/Qwen3-Coder-Next"},
12
  {"row":6, "name":"Gemma 3 1B", "path":"google/gemma-3-1b-it"},
13
- {"row":7, "name":"GLM 5.3 Flash", "path":"zai-org/GLM-5.3-Flash:novita"},
14
- {"row":8, "name":"Gemini 2.0 Flash", "path":"openai/google/gemini-2.0-flash-001"},
15
- {"row":9, "name":"DeepSeek V4 Flash (OR)", "path":"deepseek/deepseek-v4-flash"},
16
- {"row":10, "name":"Gemma 4 9B", "path":"openai/google/gemma-4-9b-it:free"},
17
- {"row":11, "name":"Gemma 4 31B", "path":"openai/google/gemma-4-31b-it:free"},
18
- {"row":12, "name":"Hy3 Preview", "path":"openai/tencent/hy3-preview"},
19
- {"row":13, "name":"north-mini-code:free", "path":"openai/cohere/north-mini-code:free"},
20
- {"row":14, "name":"Nemotron 3 Super 120B", "path":"openai/nvidia/nemotron-3-super-120b-a12b:free"},
21
- {"row":15, "name":"Nemotron 3 Ultra 550B", "path":"openai/nvidia/nemotron-3-ultra-550b-a55b:free"},
22
- {"row":16, "name":"Laguna M.1", "path":"openai/poolside/laguna-m.1:free"},
23
- {"row":17, "name":"Laguna XS.2", "path":"openai/poolside/laguna-xs.2:free"},
24
- {"row":18, "name":"Ling 3.0 Flash", "path":"openai/inclusionai/ling-3.0-flash"},
25
- {"row":19, "name":"Ling 3.0 Tiny", "path":"openai/inclusionai/ling-3.0-tiny:free"},
26
- {"row":20, "name":"DeepSeek V4 Flash 0731", "path":"deepseek/deepseek-v4-flash-0731"}
27
  ],
28
  "changes": {
29
- "stealth/ox-alpha": "-> z-ai/glm-5.3-flash (OpenRouter routing, path preserved as openai/z-ai/glm-5.3-flash)",
30
- "GLM 5.2": "-> GLM 5.3 Flash (model.ts: GLM_52_MODEL_PATH -> zai-org/GLM-5.3-Flash:novita)",
31
  "Riverflow V2.5 Fast": "→ Hy3 Preview (tencent/hy3-preview)",
32
  "Riverflow V2.5 Pro": "prefix changed to cohere/north-mini-code:free",
33
  "DeepSeek V4 Flash (OR)": "→ deepseek/deepseek-v4-flash (now HF direct)",
34
  "GPT-5.5": "→ openai/gpt-oss-120b"
35
  },
36
- "default_free_model": "Owl Alpha",
37
- "default_model_path": "deepseek-ai/DeepSeek-V4-Pro:novita"
38
- }
 
1
  {
2
+ "status": "updated_with_16_models_v2",
3
  "space": "bep40/Space-Mutiverse-Control",
4
  "space_url": "https://huggingface.co/spaces/bep40/Space-Mutiverse-Control",
5
  "files_changed": ["patch_frontend.py", "patch_models.py"],
 
7
  {"row":1, "name":"Owl Alpha", "path":"openai/openrouter/owl-alpha"},
8
  {"row":2, "name":"DeepSeek V4 Pro", "path":"deepseek-ai/DeepSeek-V4-Pro"},
9
  {"row":3, "name":"DeepSeek V4 Flash", "path":"deepseek-ai/DeepSeek-V4-Flash"},
10
+ {"row":4, "name":"GPT-5.5", "path":"openai/gpt-oss-120b"},
11
  {"row":5, "name":"Qwen3 Coder Next", "path":"Qwen/Qwen3-Coder-Next"},
12
  {"row":6, "name":"Gemma 3 1B", "path":"google/gemma-3-1b-it"},
13
+ {"row":7, "name":"Gemini 2.0 Flash", "path":"openai/google/gemini-2.0-flash-001"},
14
+ {"row":8, "name":"DeepSeek V4 Flash (OR)", "path":"deepseek/deepseek-v4-flash"},
15
+ {"row":9, "name":"Gemma 4 9B", "path":"openai/google/gemma-4-9b-it:free"},
16
+ {"row":10, "name":"Gemma 4 31B", "path":"openai/google/gemma-4-31b-it:free"},
17
+ {"row":11, "name":"Hy3 Preview", "path":"tencent/hy3-preview"},
18
+ {"row":12, "name":"Riverflow V2.5 Pro", "path":"cohere/north-mini-code:free"},
19
+ {"row":13, "name":"Nemotron 3 Super 120B", "path":"openai/nvidia/nemotron-3-super-120b-a12b:free"},
20
+ {"row":14, "name":"Nemotron 3 Ultra 550B", "path":"openai/nvidia/nemotron-3-ultra-550b-a55b:free"},
21
+ {"row":15, "name":"Laguna M.1", "path":"openai/poolside/laguna-m.1:free"},
22
+ {"row":16, "name":"Laguna XS.2", "path":"openai/poolside/laguna-xs.2:free"}
 
 
 
 
23
  ],
24
  "changes": {
 
 
25
  "Riverflow V2.5 Fast": "→ Hy3 Preview (tencent/hy3-preview)",
26
  "Riverflow V2.5 Pro": "prefix changed to cohere/north-mini-code:free",
27
  "DeepSeek V4 Flash (OR)": "→ deepseek/deepseek-v4-flash (now HF direct)",
28
  "GPT-5.5": "→ openai/gpt-oss-120b"
29
  },
30
+ "default_free_model": "Owl Alpha"
31
+ }
 
patch_frontend.py CHANGED
@@ -1,23 +1,21 @@
1
- """Patch frontend ChatInput.tsx: Replace DEFAULT_MODEL_OPTIONS array + fix imports + GLM 5.3."""
2
 
3
  import re
4
  import sys
5
 
6
  FILE = "/source/frontend/src/components/Chat/ChatInput.tsx"
7
- MODEL_TS_FILE = "/source/frontend/src/utils/model.ts"
8
 
9
  with open(FILE, "r", encoding="utf-8") as f:
10
  content = f.read()
11
 
12
  # === Step 1: Fix imports — replace any model-constants import block ===
13
- # Restore GLM_52_MODEL_PATH import so we can use it in the model options
14
  import_pattern = re.compile(
15
  r'import\s*\{[^}]+\}\s*from\s*[\'"]@/utils/model[\'"];'
16
  )
17
  match = import_pattern.search(content)
18
  if match:
19
- content = content.replace(match.group(0), "import { GLM_52_MODEL_PATH, isClaudePath } from '@/utils/model';")
20
- print("OK: Replaced imports with GLM_52_MODEL_PATH + isClaudePath")
21
  else:
22
  print("OK: Imports already clean or not found")
23
 
@@ -72,13 +70,6 @@ new_array = """const DEFAULT_MODEL_OPTIONS: ModelOption[] = [
72
  avatarUrl: 'https://huggingface.co/api/avatars/google',
73
  recommended: true,
74
  },
75
- {
76
- id: 'glm-5.3-flash',
77
- name: 'GLM 5.3 Flash',
78
- modelPath: GLM_52_MODEL_PATH,
79
- avatarUrl: 'https://huggingface.co/api/avatars/zai-org',
80
- recommended: true,
81
- },
82
  {
83
  id: 'gemini-2.0-flash',
84
  name: 'Gemini 2.0 Flash',
@@ -145,45 +136,6 @@ new_array = """const DEFAULT_MODEL_OPTIONS: ModelOption[] = [
145
  modelPath: 'openai/poolside/laguna-xs.2:free',
146
  avatarUrl: 'https://huggingface.co/api/avatars/poolside',
147
  },
148
- {
149
- id: 'ling-3.0-flash',
150
- name: 'Ling 3.0 Flash',
151
- modelPath: 'openai/inclusionai/ling-3.0-flash',
152
- avatarUrl: 'https://huggingface.co/api/avatars/inclusionai',
153
- },
154
- {
155
- id: 'ling-3.0-tiny',
156
- name: 'Ling 3.0 Tiny',
157
- modelPath: 'openai/inclusionai/ling-3.0-tiny:free',
158
- avatarUrl: 'https://huggingface.co/api/avatars/inclusionai',
159
- },
160
- {
161
- id: 'ling-3.0-flash-vl',
162
- name: 'Ling 3.0 Flash VL',
163
- modelPath: 'openai/inclusionai/ling-3.0-flash-vl:free',
164
- avatarUrl: 'https://huggingface.co/api/avatars/inclusionai',
165
- recommended: true,
166
- },
167
- {
168
- id: 'nex-n2.5-mini',
169
- name: 'Nex-N2.5-Mini',
170
- modelPath: 'openai/nex-agi/nex-n2.5-mini:free',
171
- avatarUrl: 'https://huggingface.co/api/avatars/nex-agi',
172
- recommended: true,
173
- },
174
- {
175
- id: 'nex-n2.5-pro',
176
- name: 'Nex-N2.5-Pro',
177
- modelPath: 'openai/nex-agi/nex-n2.5-pro:free',
178
- avatarUrl: 'https://huggingface.co/api/avatars/nex-agi',
179
- recommended: true,
180
- },
181
- {
182
- id: 'deepseek-v4-flash-0731',
183
- name: 'DeepSeek V4 Flash 0731',
184
- modelPath: 'deepseek/deepseek-v4-flash-0731',
185
- avatarUrl: 'https://huggingface.co/api/avatars/deepseek-ai',
186
- },
187
  ];"""
188
 
189
  content = content[:match.start()] + new_array + content[match.end():]
@@ -199,19 +151,4 @@ else:
199
  with open(FILE, "w", encoding="utf-8") as f:
200
  f.write(content)
201
 
202
- print("OK: Frontend patched - 23 models (incl GLM 5.3 Flash + 3 new), deepseek-ai/DeepSeek-V4-Pro:novita default")
203
-
204
- # === Step 4: Patch model.ts — GLM_52_MODEL_PATH -> zai-org/GLM-5.3-Flash:novita ===
205
- with open(MODEL_TS_FILE, "r", encoding="utf-8") as f:
206
- model_content = f.read()
207
-
208
- model_content = model_content.replace(
209
- "export const GLM_52_MODEL_PATH = 'zai-org/GLM-5.2:novita';",
210
- "export const GLM_52_MODEL_PATH = 'zai-org/GLM-5.3-Flash:novita';"
211
- )
212
- print("OK: Patched GLM_52_MODEL_PATH -> zai-org/GLM-5.3-Flash:novita in model.ts")
213
-
214
- with open(MODEL_TS_FILE, "w", encoding="utf-8") as f:
215
- f.write(model_content)
216
-
217
- print("OK: Frontend fully patched")
 
1
+ """Patch frontend ChatInput.tsx: Replace DEFAULT_MODEL_OPTIONS array + fix imports."""
2
 
3
  import re
4
  import sys
5
 
6
  FILE = "/source/frontend/src/components/Chat/ChatInput.tsx"
 
7
 
8
  with open(FILE, "r", encoding="utf-8") as f:
9
  content = f.read()
10
 
11
  # === Step 1: Fix imports — replace any model-constants import block ===
 
12
  import_pattern = re.compile(
13
  r'import\s*\{[^}]+\}\s*from\s*[\'"]@/utils/model[\'"];'
14
  )
15
  match = import_pattern.search(content)
16
  if match:
17
+ content = content.replace(match.group(0), "import { isClaudePath } from '@/utils/model';")
18
+ print("OK: Replaced imports with just isClaudePath")
19
  else:
20
  print("OK: Imports already clean or not found")
21
 
 
70
  avatarUrl: 'https://huggingface.co/api/avatars/google',
71
  recommended: true,
72
  },
 
 
 
 
 
 
 
73
  {
74
  id: 'gemini-2.0-flash',
75
  name: 'Gemini 2.0 Flash',
 
136
  modelPath: 'openai/poolside/laguna-xs.2:free',
137
  avatarUrl: 'https://huggingface.co/api/avatars/poolside',
138
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  ];"""
140
 
141
  content = content[:match.start()] + new_array + content[match.end():]
 
151
  with open(FILE, "w", encoding="utf-8") as f:
152
  f.write(content)
153
 
154
+ print("OK: Frontend patched - 16 models, deepseek-ai/DeepSeek-V4-Pro:novita default")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
patch_models.py CHANGED
@@ -1,4 +1,4 @@
1
- """Patch backend: replace AVAILABLE_MODELS (remove old 6, add 25 new) + OpenRouter routing."""
2
 
3
  import ast
4
  import os
@@ -148,58 +148,13 @@ new_func = '''def _available_models() -> list[dict[str, Any]]:
148
  "provider": "openrouter",
149
  "tier": "free",
150
  },
151
- {
152
- "id": "openai/inclusionai/ling-3.0-flash",
153
- "label": "Ling 3.0 Flash",
154
- "provider": "openrouter",
155
- "tier": "free",
156
- },
157
- {
158
- "id": "openai/inclusionai/ling-3.0-tiny:free",
159
- "label": "Ling 3.0 Tiny",
160
- "provider": "openrouter",
161
- "tier": "free",
162
- },
163
- {
164
- "id": "openai/inclusionai/ling-3.0-flash-vl:free",
165
- "label": "Ling 3.0 Flash VL",
166
- "provider": "openrouter",
167
- "tier": "free",
168
- "recommended": True,
169
- },
170
- {
171
- "id": "openai/nex-agi/nex-n2.5-mini:free",
172
- "label": "Nex-N2.5-Mini",
173
- "provider": "openrouter",
174
- "tier": "free",
175
- "recommended": True,
176
- },
177
- {
178
- "id": "openai/nex-agi/nex-n2.5-pro:free",
179
- "label": "Nex-N2.5-Pro",
180
- "provider": "openrouter",
181
- "tier": "free",
182
- "recommended": True,
183
- },
184
- {
185
- "id": "z-ai/glm-5.3-flash",
186
- "label": "GLM 5.3 Flash",
187
- "provider": "openrouter",
188
- "tier": "paid",
189
- },
190
- {
191
- "id": "deepseek/deepseek-v4-flash-0731",
192
- "label": "DeepSeek V4 Flash 0731",
193
- "provider": "openrouter",
194
- "tier": "free",
195
- },
196
  ]
197
  return models'''
198
 
199
  func_match = func_pattern.search(content)
200
  if func_match:
201
  content = content[:func_match.start()] + new_func + content[func_match.end():]
202
- print("OK: Replaced _available_models() with 25 new models via regex")
203
  else:
204
  print("WARN: Could not find _available_models() via regex, trying extend approach...")
205
  target = "AVAILABLE_MODELS = _available_models()\n"
@@ -226,13 +181,6 @@ else:
226
  ' {"id": "openai/poolside/laguna-xs.2:free", "label": "Laguna XS.2", "provider": "openrouter", "tier": "free"},\n'
227
  ' {"id": "openai/qwen/qwen3.7-flash", "label": "Qwen3.7 Flash", "provider": "openrouter", "tier": "free"},\n'
228
  ' {"id": "openai/inclusionai/ling-2.6-flash", "label": "Ling 2.6 Flash", "provider": "openrouter", "tier": "free"},\n'
229
- ' {"id": "openai/inclusionai/ling-3.0-flash", "label": "Ling 3.0 Flash", "provider": "openrouter", "tier": "free"},\n'
230
- ' {"id": "openai/inclusionai/ling-3.0-tiny:free", "label": "Ling 3.0 Tiny", "provider": "openrouter", "tier": "free"},\n'
231
- ' {"id": "openai/inclusionai/ling-3.0-flash-vl:free", "label": "Ling 3.0 Flash VL", "provider": "openrouter", "tier": "free", "recommended": True},\n'
232
- ' {"id": "openai/nex-agi/nex-n2.5-mini:free", "label": "Nex-N2.5-Mini", "provider": "openrouter", "tier": "free", "recommended": True},\n'
233
- ' {"id": "openai/nex-agi/nex-n2.5-pro:free", "label": "Nex-N2.5-Pro", "provider": "openrouter", "tier": "free", "recommended": True},\n'
234
- ' {"id": "z-ai/glm-5.3-flash", "label": "GLM 5.3 Flash", "provider": "openrouter", "tier": "paid"},\n'
235
- ' {"id": "deepseek/deepseek-v4-flash-0731", "label": "DeepSeek V4 Flash 0731", "provider": "openrouter", "tier": "free"},\n'
236
  "])\n"
237
  'DEFAULT_FREE_MODEL_ID = "openai/openrouter/owl-alpha"\n'
238
  )
@@ -261,34 +209,10 @@ api_key_match = re.search(r'api_key\s*=\s*_resolve_hf_router_token\(session_hf_t
261
  if api_key_match:
262
  line_end = llm_content.find('\n', api_key_match.end()) + 1
263
  if line_end > 0:
264
- routing_insert = ''' # === PATCH: Route deepseek/ + z-ai/ + openai/-prefixed models to OpenRouter ===
265
- # FIX: Add explicit deepseek/ route so "deepseek/deepseek-v4-flash-0731" goes
266
- # to OpenRouter (HF Router does NOT have this id -> "model does not exist").
267
- if normalized_model.startswith("deepseek/"):
268
- return {
269
- "model": normalized_model,
270
- "api_base": "https://openrouter.ai/api/v1",
271
- "api_key": os.environ.get("OPENROUTER_API_KEY") or api_key or "",
272
- }
273
- # FIX: Route z-ai/glm-5.3-flash to OpenRouter with the provider prefix so litellm
274
- # recognizes the provider. Strip any trailing ":free" suffix.
275
- if normalized_model.startswith("z-ai/"):
276
- or_model = normalized_model
277
- if or_model.endswith(":free"):
278
- or_model = or_model[: -len(":free")]
279
- return {
280
- "model": "openrouter/" + or_model,
281
- "api_base": "https://openrouter.ai/api/v1",
282
- "api_key": os.environ.get("OPENROUTER_API_KEY") or api_key or "",
283
- }
284
- # FIX: Strip the "openai/" prefix before sending to OpenRouter.
285
- # OpenRouter model ids do NOT include the "openai/" prefix, so sending the
286
- # prefixed name causes a 404 (model does not exist) on OpenRouter. Prefix
287
- # with "openrouter/" so litellm recognizes the provider.
288
  if normalized_model.startswith("openai/"):
289
- or_model = "openrouter/" + normalized_model[len("openai/"):]
290
  return {
291
- "model": or_model,
292
  "api_base": "https://openrouter.ai/api/v1",
293
  "api_key": os.environ.get("OPENROUTER_API_KEY") or api_key or "",
294
  }
@@ -296,7 +220,7 @@ if api_key_match:
296
 
297
  '''
298
  llm_content = llm_content[:line_end] + routing_insert + llm_content[line_end:]
299
- print("OK: Patched _resolve_llm_params (deepseek/ + z-ai/[:free stripped] + stripped openai/ -> OR with provider prefix)")
300
  else:
301
  print("OK: Could not find end of api_key line")
302
  else:
@@ -312,63 +236,4 @@ except SyntaxError as e:
312
  with open(LLM_PARAMS_FILE, "w") as f:
313
  f.write(llm_content)
314
 
315
- print("OK: Backend patched - 25 models, deepseek/ + z-ai/[:free stripped] + stripped openai/ -> OR")
316
-
317
- # === Step 3: Patch backend/main.py — safe static cache headers ===
318
- # FIX: Stale-cache blank page. HF Spaces serves the static build with NO
319
- # Cache-Control headers. After every rebuild the hashed asset filename
320
- # changes but the browser keeps caching index.html which then references a
321
- # bundle hash that no longer exists -> 404 on /assets/index-*.js -> blank
322
- # page. Fix: 1yr immutable cache for hashed assets, no-cache for index.html.
323
- MAIN_FILE = "/app/backend/main.py"
324
- with open(MAIN_FILE) as f:
325
- main_content = f.read()
326
-
327
- static_import_old = "from fastapi.staticfiles import StaticFiles"
328
- static_import_new = """from fastapi.staticfiles import StaticFiles
329
-
330
-
331
- class _CachedStaticFiles(StaticFiles):
332
- \"\"\"StaticFiles with safe cache headers.
333
-
334
- - Hashed build assets (vite emits index-<hash>.js) get a 1-year
335
- immutable Cache-Control so repeat visits skip the download.
336
- - index.html and everything else get no-cache so the browser always
337
- revalidates and never pins a stale HTML that references a bundle
338
- hash that no longer exists after a rebuild (blank-page 404 bug).
339
- \"\"\"
340
-
341
- async def get_response(self, path: str, scope):
342
- resp = await super().get_response(path, scope)
343
- if scope["method"] == "GET" and resp.status_code == 200:
344
- if path.startswith("assets/") or path.endswith(
345
- (".js", ".css", ".woff2", ".png", ".webp", ".svg", ".ico", ".jpg", ".jpeg", ".ttf")
346
- ):
347
- resp.headers["Cache-Control"] = "public, max-age=31536000, immutable"
348
- else:
349
- resp.headers["Cache-Control"] = "no-cache"
350
- return resp"""
351
-
352
- if static_import_old in main_content:
353
- main_content = main_content.replace(static_import_old, static_import_new)
354
- else:
355
- print("WARN: StaticFiles import not found for cache patch, may already be applied")
356
-
357
- static_mount_old = 'app.mount("/", StaticFiles(directory=str(static_path), html=True), name="static")'
358
- static_mount_new = 'app.mount("/", _CachedStaticFiles(directory=str(static_path), html=True), name="static")'
359
- if static_mount_old in main_content:
360
- main_content = main_content.replace(static_mount_old, static_mount_new)
361
- else:
362
- print("WARN: StaticFiles mount not found for cache patch, may already be applied")
363
-
364
- try:
365
- ast.parse(main_content)
366
- print("OK: main.py syntax OK")
367
- except SyntaxError as e:
368
- print(f"FAIL: Syntax error in main.py: {e}")
369
- raise
370
-
371
- with open(MAIN_FILE, "w") as f:
372
- f.write(main_content)
373
-
374
- print("OK: main.py patched - immutable asset cache + no-cache index.html (fixes stale-hash blank page)")
 
1
+ """Patch backend: replace AVAILABLE_MODELS (remove old 6, add 18 new) + OpenRouter routing."""
2
 
3
  import ast
4
  import os
 
148
  "provider": "openrouter",
149
  "tier": "free",
150
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  ]
152
  return models'''
153
 
154
  func_match = func_pattern.search(content)
155
  if func_match:
156
  content = content[:func_match.start()] + new_func + content[func_match.end():]
157
+ print("OK: Replaced _available_models() with 18 new models via regex")
158
  else:
159
  print("WARN: Could not find _available_models() via regex, trying extend approach...")
160
  target = "AVAILABLE_MODELS = _available_models()\n"
 
181
  ' {"id": "openai/poolside/laguna-xs.2:free", "label": "Laguna XS.2", "provider": "openrouter", "tier": "free"},\n'
182
  ' {"id": "openai/qwen/qwen3.7-flash", "label": "Qwen3.7 Flash", "provider": "openrouter", "tier": "free"},\n'
183
  ' {"id": "openai/inclusionai/ling-2.6-flash", "label": "Ling 2.6 Flash", "provider": "openrouter", "tier": "free"},\n'
 
 
 
 
 
 
 
184
  "])\n"
185
  'DEFAULT_FREE_MODEL_ID = "openai/openrouter/owl-alpha"\n'
186
  )
 
209
  if api_key_match:
210
  line_end = llm_content.find('\n', api_key_match.end()) + 1
211
  if line_end > 0:
212
+ routing_insert = ''' # === PATCH: Route ALL openai/-prefixed models to OpenRouter ===
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  if normalized_model.startswith("openai/"):
 
214
  return {
215
+ "model": normalized_model,
216
  "api_base": "https://openrouter.ai/api/v1",
217
  "api_key": os.environ.get("OPENROUTER_API_KEY") or api_key or "",
218
  }
 
220
 
221
  '''
222
  llm_content = llm_content[:line_end] + routing_insert + llm_content[line_end:]
223
+ print("OK: Patched _resolve_llm_params (catch-all openai/ -> OR)")
224
  else:
225
  print("OK: Could not find end of api_key line")
226
  else:
 
236
  with open(LLM_PARAMS_FILE, "w") as f:
237
  f.write(llm_content)
238
 
239
+ print("OK: Backend patched - 18 models, ALL openai/ -> OR")