File size: 5,207 Bytes
82d81e8
75f4728
 
 
 
 
82d81e8
75f4728
 
82d81e8
 
75f4728
1c4ea53
 
 
 
75f4728
 
 
 
 
 
 
 
 
 
 
82d81e8
 
75f4728
 
 
 
 
82d81e8
 
75f4728
 
1c4ea53
 
82d81e8
75f4728
 
 
1c4ea53
 
75f4728
 
82d81e8
 
 
75f4728
 
 
1c4ea53
 
 
 
75f4728
 
 
 
1c4ea53
75f4728
 
 
 
 
1c4ea53
 
75f4728
 
1c4ea53
75f4728
 
 
1c4ea53
75f4728
1c4ea53
 
 
 
75f4728
1c4ea53
 
 
 
 
 
 
 
 
 
 
75f4728
 
1c4ea53
 
75f4728
1c4ea53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75f4728
 
 
 
 
 
1c4ea53
82d81e8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
"""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)")