"""Patch backend: register 4 new models using the Laguna S 2.1 model-path convention. Every model id is prefixed with `openai/`, which `_resolve_llm_params` strips before calling OpenRouter (and `patch_openrouter_fix.py` forces `custom_llm_provider="openrouter"` so LiteLLM uses the right client). New model ids: - openai/dots-studio/dots-3-note-preview:free - openai/qwen/qwen3.8-27b:free - openai/respan/span-01-lite - openai/inclusionai/ming-image-0.1-design Runs AFTER patch_models.py and patch_new_models.py. Fully idempotent: every edit is skipped when its target already exists, and the script asserts afterwards that no constant or _available_models entry was duplicated. Fails the Docker build loudly (exit 1) if an anchor cannot be matched. """ import ast import sys IDS_FILE = "/app/agent/core/model_ids.py" AGENT_FILE = "/app/backend/routes/agent.py" CONSTANTS = { "DOTS_3_NOTE_PREVIEW_FREE_MODEL_ID": "openai/dots-studio/dots-3-note-preview:free", "QWEN3_8_27B_FREE_MODEL_ID": "openai/qwen/qwen3.8-27b:free", "SPAN_01_LITE_MODEL_ID": "openai/respan/span-01-lite", "MING_IMAGE_0_1_DESIGN_MODEL_ID": "openai/inclusionai/ming-image-0.1-design", } LABELS = { "DOTS_3_NOTE_PREVIEW_FREE_MODEL_ID": "dots-studio/dots-3-note-preview:free", "QWEN3_8_27B_FREE_MODEL_ID": "qwen/qwen3.8-27b:free", "SPAN_01_LITE_MODEL_ID": "respan/span-01-lite", "MING_IMAGE_0_1_DESIGN_MODEL_ID": "inclusionai/ming-image-0.1-design", } failures = [] # ---------- 1. model_ids.py: add the 4 constants ---------- with open(IDS_FILE) as f: ids = f.read() missing_consts = [n for n in CONSTANTS if f"{n} = " not in ids] if not missing_consts: print("OK: model_ids.py constants already present (idempotent)") else: block = "".join( f'{name} = "{CONSTANTS[name]}"\n' for name in missing_consts ) ids_anchor = 'UPSTAGE_SOLAR_PRO4_MODEL_ID = "openai/upstage/solar-pro4"' if ids_anchor not in ids: ids_anchor = 'DEEPSEEK_V4_PRO_MODEL_ID = "nvidia/nemotron-3-super-120b-a12b:free"' if ids_anchor not in ids: print("FAIL: could not find insertion anchor in model_ids.py") failures.append("model_ids anchor") else: ids = ids.replace(ids_anchor, block + ids_anchor, 1) ast.parse(ids) with open(IDS_FILE, "w") as f: f.write(ids) print(f"OK: added {len(missing_consts)} model constants to model_ids.py") # ---------- 2. agent.py: imports + _available_models ---------- with open(AGENT_FILE) as f: agent = f.read() missing_imports = [n for n in CONSTANTS if f" {n},\n" not in agent] if not missing_imports: print("OK: agent.py imports already present (idempotent)") else: new_imports = "".join(f" {name},\n" for name in missing_imports) import_anchor = " LIQUID_LFM_2_5_2_6B_FREE_MODEL_ID,\n" if import_anchor not in agent: import_anchor = " LING_3_0_FLASH_FREE_MODEL_ID,\n" if import_anchor not in agent: print("FAIL: could not find import anchor in agent.py") failures.append("agent import anchor") else: agent = agent.replace(import_anchor, import_anchor + new_imports, 1) print(f"OK: added {len(missing_imports)} model imports to agent.py") # _available_models entries need their OWN guard: the constant can already exist # while the list entry is still missing (or vice versa) on a re-run. missing_entries = [n for n in CONSTANTS if f'{{"id": {n},' not in agent] if not missing_entries: print("OK: _available_models entries already present (idempotent)") else: new_entries = "".join( ' {{"id": {name}, "label": "{label}", "recommended": True}},\n'.format( name=name, label=LABELS[name] ) for name in missing_entries ) list_anchor = ( ' {"id": LIQUID_LFM_2_5_2_6B_FREE_MODEL_ID, ' '"label": "Liquid LFM 2.5 2.6B:free", "recommended": True},\n' ) if list_anchor not in agent: list_anchor = ( ' {"id": LING_3_0_FLASH_FREE_MODEL_ID, ' '"label": "inclusionai/ling-3.0-flash:free", "recommended": True},\n' ) if list_anchor not in agent: print("FAIL: could not find _available_models anchor in agent.py") failures.append("agent list anchor") else: agent = agent.replace(list_anchor, list_anchor + new_entries, 1) print(f"OK: added {len(missing_entries)} models to _available_models") # ---------- 3. verify: exactly one definition per constant / list entry ---------- for name in CONSTANTS: if ids.count(f"{name} = ") != 1: failures.append(f"duplicate/missing constant {name}") if agent.count(f'{{"id": {name},') != 1: failures.append(f"duplicate/missing list entry {name}") if agent.count(f" {name},\n") != 1: failures.append(f"duplicate/missing import {name}") if failures: for msg in failures: print(f"FAIL: {msg}") sys.exit(1) ast.parse(agent) with open(AGENT_FILE, "w") as f: f.write(agent) print("OK: agent.py written & syntax valid") print("OK: exactly one constant + import + list entry per new model") print("DONE: 4 new models registered (backend)")