CCAI-Demo / backend /app /services /model_picker.py
Jordan Miller
Harden Expert Persona builder and add model suggestion.
1961f74
Raw
History Blame Contribute Delete
2.04 kB
"""Pick a neutral, general-purpose model for meta-LLM tasks.
Used by role-prompt generation and model suggestion so those calls do
not run through a Neon character persona (which injects its own identity).
"""
from __future__ import annotations
from app.config import settings
def is_neon_character_model_id(model_id: str | None) -> bool:
return bool(model_id and model_id.startswith("neon:"))
def is_vanilla_neon_model_id(model_id: str | None) -> bool:
return is_neon_character_model_id(model_id) and model_id.rsplit(":", 1)[-1] == "vanilla"
def pick_general_purpose_model(
preferred: str | None = None,
*,
extra_model_ids: list[str] | None = None,
) -> str | None:
"""Return a model id suitable for neutral writing / analysis tasks.
Resolution order:
1. preferred if resolvable and NOT a Neon character
2. settings.orchestrator_model if resolvable and NOT Neon
3. first resolvable model from settings.providers
4. extra_model_ids Neon entries ending in ':vanilla'
5. any other resolvable Neon id from extra_model_ids (last resort)
"""
seen: set[str] = set()
non_neon: list[str] = []
vanilla_neon: list[str] = []
other_neon: list[str] = []
def _bucket(mid: str) -> None:
if not mid or mid in seen or not settings.resolve_model(mid):
return
seen.add(mid)
if is_neon_character_model_id(mid):
if is_vanilla_neon_model_id(mid):
vanilla_neon.append(mid)
else:
other_neon.append(mid)
else:
non_neon.append(mid)
for mid in [preferred, settings.orchestrator_model]:
if mid and not is_neon_character_model_id(mid):
_bucket(mid)
for prov in settings.providers:
for m in prov.get("models") or []:
_bucket((m.get("id") or "").strip())
for mid in extra_model_ids or []:
_bucket((mid or "").strip())
ordered = non_neon + vanilla_neon + other_neon
return ordered[0] if ordered else None