"""Smoke-test the optional llama.cpp text runtime with an external GGUF model.""" from __future__ import annotations import argparse import json import os import sys from pathlib import Path from typing import Any PROJECT_ROOT = Path(__file__).resolve().parents[1] if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) from src.models.llama_cpp_runner import get_text_runtime_fallbacks, reply_as_object from src.pipeline import generate_object_diary DEFAULT_GGUF_REPO = "Qwen/Qwen2.5-1.5B-Instruct-GGUF" DEFAULT_GGUF_FILE = "qwen2.5-1.5b-instruct-q4_k_m.gguf" TEXT_FALLBACK_MARKER = "text-fallback-to-mock" def run_llama_cpp_smoke( *, model_path: Path, description: str, mode: str, save_trace: bool, ) -> dict[str, Any]: if not model_path.exists(): raise FileNotFoundError(f"GGUF model path does not exist: {model_path}") previous_text_backend = os.environ.get("OBJECTVERSE_TEXT_BACKEND") previous_text_model_path = os.environ.get("TEXT_MODEL_PATH") try: os.environ["OBJECTVERSE_TEXT_BACKEND"] = "llama-cpp" os.environ["TEXT_MODEL_PATH"] = str(model_path) result = generate_object_diary( None, description, mode, save=save_trace, ) chat_reply = reply_as_object( result.persona.model_dump(mode="json"), "What did you see today?", ) chat_fallbacks = get_text_runtime_fallbacks() finally: _restore_env("OBJECTVERSE_TEXT_BACKEND", previous_text_backend) _restore_env("TEXT_MODEL_PATH", previous_text_model_path) payload = { "status": "pass", "model_path": _display_model_path(model_path), "description": description, "mode": mode, "trace_id": result.trace.trace_id, "trace_path": result.trace_path, "model_runtime": result.trace.model_runtime, "fallbacks": result.trace.fallbacks, "object_name": result.object_understanding.object.name, "character_name": result.persona.persona.character_name, "diary_title": result.diary.title, "chat_reply_preview": chat_reply[:160], "chat_fallbacks": chat_fallbacks, } if result.trace.model_runtime.get("text") != "llama-cpp text generation": payload["status"] = "fail" payload["error"] = "trace did not record llama-cpp text generation" if TEXT_FALLBACK_MARKER in result.trace.fallbacks: payload["status"] = "fail" payload["error"] = "trace included text-fallback-to-mock" if TEXT_FALLBACK_MARKER in chat_fallbacks: payload["status"] = "fail" payload["error"] = "chat included text-fallback-to-mock" return payload def _restore_env(key: str, previous_value: str | None) -> None: if previous_value is None: os.environ.pop(key, None) else: os.environ[key] = previous_value def _print_json(payload: dict[str, Any]) -> None: print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True), flush=True) def _display_model_path(model_path: Path) -> str: try: return str(model_path.resolve().relative_to(PROJECT_ROOT)) except ValueError: return model_path.name def _parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "--model-path", type=Path, default=Path("models") / DEFAULT_GGUF_FILE, help=f"Path to {DEFAULT_GGUF_FILE} or another external GGUF file.", ) parser.add_argument( "--description", default="old white coffee mug on a developer desk", ) parser.add_argument("--mode", default="Cynical") parser.add_argument("--save-trace", action="store_true") return parser.parse_args() def main() -> None: args = _parse_args() try: payload = run_llama_cpp_smoke( model_path=args.model_path, description=args.description, mode=args.mode, save_trace=args.save_trace, ) except Exception as exc: _print_json( { "status": "fail", "model_path": _display_model_path(args.model_path), "recommended_repo": DEFAULT_GGUF_REPO, "recommended_file": DEFAULT_GGUF_FILE, "error_type": type(exc).__name__, "error": str(exc), } ) raise SystemExit(1) from exc _print_json(payload) if payload["status"] != "pass": raise SystemExit(1) if __name__ == "__main__": main()