from __future__ import annotations import argparse import sys import urllib.error from common import ROOT, call_chat_completion, read_json REGISTRY_INDEX = ROOT / "registry" / "index.json" DEFAULT_BASE_URL = "http://docker-gpu.cin.su:8000" PLUGIN_TASKS = { "text": {"text", "chat", "summarization", "code", "tool-use"}, "translation": {"translation"}, "audio": {"speech-to-text", "speech-translation"}, "video": {"video", "image-understanding", "document-understanding", "visual-question-answering"}, "1c": {"1c", "1c-rag", "bsl-code", "metadata-safety", "1c-query"}, } DEFAULT_PROMPTS = { "text": "Кратко объясни, зачем нужен реестр локальных моделей.", "translation": "Переведи на английский: Проверяем локальную модель перевода.", "audio": "Составь чек-лист проверки качества распознавания речи.", "video": "Какие тесты нужны для проверки модели анализа видео?", "1c": "Какие метаданные 1С нужно запросить перед изменением документа?", } STATUS_RANK = { "production": 0, "staging": 1, "candidate": 2, "draft": 3, "archived": 9, } def model_matches_plugin(model: dict, plugin: str) -> bool: tasks = set(model.get("task") or []) return bool(tasks & PLUGIN_TASKS.get(plugin, set())) def model_score(model: dict, plugin: str) -> tuple[int, int, str]: tasks = set(model.get("task") or []) status = str(model.get("status") or "") score = 0 if plugin == "text": if tasks & {"text", "chat"}: score -= 5 if tasks & {"1c", "1c-rag", "bsl-code"}: score += 6 elif plugin == "1c": if tasks & PLUGIN_TASKS["1c"]: score -= 6 else: if tasks & PLUGIN_TASKS.get(plugin, set()): score -= 5 return (score, STATUS_RANK.get(status, 8), str(model.get("id") or "")) def select_model(plugin: str, model_id: str | None) -> dict: registry = read_json(REGISTRY_INDEX) models = registry.get("models") or [] candidates = [model for model in models if model_matches_plugin(model, plugin)] if model_id: candidates = [model for model in candidates if model.get("id") == model_id] candidates.sort(key=lambda model: model_score(model, plugin)) if not candidates: raise ValueError(f"No model found for plugin={plugin!r} model={model_id!r}") return candidates[0] def main() -> int: parser = argparse.ArgumentParser(description="Chat with a registry model through an OpenAI-compatible endpoint.") parser.add_argument("--plugin", choices=sorted(PLUGIN_TASKS), default="text") parser.add_argument("--model-id", help="Registry model id. Defaults to the first model for the plugin.") parser.add_argument("--base-url", default=DEFAULT_BASE_URL) parser.add_argument("--prompt") parser.add_argument("--temperature", type=float, default=0.2) parser.add_argument("--max-tokens", type=int, default=1000) parser.add_argument("--system", default="Отвечай по-русски, кратко и по делу.") args = parser.parse_args() try: model = select_model(args.plugin, args.model_id) except ValueError as exc: print(exc, file=sys.stderr) return 1 served_model = model.get("served_model_name") or model.get("id") prompt = args.prompt or DEFAULT_PROMPTS[args.plugin] messages = [ {"role": "system", "content": args.system}, {"role": "user", "content": prompt}, ] print(f"Plugin: {args.plugin}") print(f"Model: {model.get('id')} -> {served_model}") print(f"Endpoint: {args.base_url}") print("") try: answer = call_chat_completion( base_url=args.base_url, model=served_model, messages=messages, temperature=args.temperature, max_tokens=args.max_tokens, ) except (urllib.error.URLError, ValueError) as exc: print(f"Chat request failed: {exc}", file=sys.stderr) return 1 print(answer) return 0 if __name__ == "__main__": raise SystemExit(main())