""" Cloud inference connector for Pocket Dungeon Master. Streams RPG narrative from the Modal-hosted vLLM backend (OpenAI-compatible). Configured via environment variables (set as HF Space secrets): MODAL_BACKEND_URL e.g. https://--pocket-dm-backend-serve.modal.run MODAL_API_KEY the VLLM_API_KEY chosen when deploying the Modal secret """ import os from openai import OpenAI from engine import load_system_prompt, log_trace MODAL_BACKEND_URL = os.environ.get( "MODAL_BACKEND_URL", "https://your-unique-modal-subdomain.modal.run" ) MODAL_API_KEY = os.environ.get("MODAL_API_KEY", "EMPTY") MODEL_NAME = os.environ.get("MODEL_NAME", "Qwen/Qwen2.5-3B-Instruct") # tiny ≤4B (Tiny Titan) _client = OpenAI( base_url=MODAL_BACKEND_URL.rstrip("/") + "/v1", api_key=MODAL_API_KEY or "EMPTY", timeout=120.0, ) def call_modal_generator(item_id, history_messages): """Yields the cumulative narrative text as it streams from the backend.""" messages = [{"role": "system", "content": load_system_prompt(item_id)}] for msg in history_messages: role = getattr(msg, "role", None) or (msg.get("role") if isinstance(msg, dict) else None) content = getattr(msg, "content", None) or (msg.get("content") if isinstance(msg, dict) else None) if content: messages.append({"role": role, "content": content}) full_reply_text = "" try: stream = _client.chat.completions.create( model=MODEL_NAME, messages=messages, max_tokens=220, temperature=0.85, stream=True, ) for chunk in stream: delta = chunk.choices[0].delta.content or "" if delta: full_reply_text += delta yield full_reply_text except Exception as e: yield f"⚠️ The artifact's voice falters — the cloud core is unreachable ({e})." return # Log the interaction (Sharing is Caring badge / public trace). try: last_user = next( (m["content"] for m in reversed(messages) if m["role"] == "user"), "" ) log_trace({"item": item_id, "user": last_user, "model": full_reply_text}) except Exception: pass def get_combat_keywords(): return ['rage', 'boil', 'heat', 'kill', 'attack', 'shatter', 'fight', 'force', 'mutate', 'scream', 'wield', 'blade', 'potion', 'key']