Download server.py from gaaaaaaaaaaa/R1-MATH: direct link, hf CLI and curl.
- Browser
- Download file 32.5 kB
-
https://huggingface.co/gaaaaaaaaaaa/R1-MATH/resolve/main/server.py
- Command line
-
hf download hf://gaaaaaaaaaaa/R1-MATH/server.py
-
curl -L -o server.py https://huggingface.co/gaaaaaaaaaaa/R1-MATH/resolve/main/server.py
32.5 kB
| import asyncio | |
| import json | |
| import logging | |
| import os | |
| import re | |
| import time | |
| from pathlib import Path | |
| from typing import Any, AsyncIterator, Iterator, Literal | |
| import path_explorer | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.responses import FileResponse, StreamingResponse | |
| from fastapi.staticfiles import StaticFiles | |
| from pydantic import BaseModel, Field | |
| from src.inference import ( | |
| BaseInferenceBackend, | |
| InferenceConfig, | |
| TransformersBackend, | |
| build_inference_backend, | |
| ) | |
| from src.pipeline.runtime_instructions import ( | |
| instruction_block, | |
| thinking_limit_reached_instruction, | |
| ) | |
| logging.basicConfig(level=os.getenv("LOG_LEVEL", "INFO")) | |
| logger = logging.getLogger(__name__) | |
| BASE_DIR = Path(__file__).resolve().parent | |
| OUTPUTS_DIR = BASE_DIR / "outputs" | |
| MODEL_JSON_DIR = BASE_DIR / "model_json" | |
| DISABLE_VLLM_MODE = True | |
| app = FastAPI(title="MATH Reasoning Lab") | |
| app.mount("/static", StaticFiles(directory=BASE_DIR / "static"), name="static") | |
| class ChatMessage(BaseModel): | |
| role: Literal["system", "user", "assistant"] | |
| content: str | |
| class ChatRequest(BaseModel): | |
| messages: list[ChatMessage] = Field(default_factory=list) | |
| model: str = Field(default="peft:1K") | |
| temperature: float = Field(default=0.6, ge=0.0, le=2.0) | |
| max_tokens: int = Field(default=2048, ge=1, le=32768) | |
| fast_mode: bool = Field(default=False) | |
| thinking_effort: Literal[ | |
| "none", | |
| "flash", | |
| "speed", | |
| "low", | |
| "medium", | |
| "high", | |
| "extreme", | |
| "max", | |
| ] = Field(default="medium") | |
| class ModelRequest(BaseModel): | |
| model: str = Field(default="peft:1K") | |
| class JsonSaveRequest(BaseModel): | |
| filename: str = Field(default="dpo_turns.jsonl") | |
| record: dict[str, Any] = Field(default_factory=dict) | |
| def discover_model_options() -> list[dict[str, Any]]: | |
| options: list[dict[str, Any]] = [ | |
| { | |
| "id": "base:qwen2.5-3b-math", | |
| "label": "Qwen2.5 3B base", | |
| "kind": "base", | |
| "path": None, | |
| "available": True, | |
| } | |
| ] | |
| if OUTPUTS_DIR.exists(): | |
| for adapter_config in sorted(OUTPUTS_DIR.rglob("adapter_config.json")): | |
| adapter_dir = adapter_config.parent | |
| relative_path = adapter_dir.relative_to(OUTPUTS_DIR).as_posix() | |
| checkpoint = checkpoint_name(adapter_dir) | |
| family = model_family_name(adapter_dir) | |
| label = adapter_label(adapter_dir) | |
| options.append( | |
| { | |
| "id": f"peft:{relative_path}", | |
| "label": label, | |
| "kind": "peft", | |
| "path": str(adapter_dir), | |
| "relative_path": relative_path, | |
| "family": family, | |
| "checkpoint": checkpoint, | |
| "available": True, | |
| } | |
| ) | |
| return options | |
| def checkpoint_name(adapter_dir: Path) -> str | None: | |
| for part in reversed(adapter_dir.parts): | |
| if part.startswith("checkpoint-"): | |
| return part | |
| return None | |
| def checkpoint_step(option: dict[str, Any]) -> int: | |
| checkpoint = option.get("checkpoint") or "" | |
| match = re.search(r"checkpoint-(\d+)", checkpoint) | |
| return int(match.group(1)) if match else -1 | |
| def model_family_name(adapter_dir: Path) -> str: | |
| relative_parts = adapter_dir.relative_to(OUTPUTS_DIR).parts | |
| checkpoint = checkpoint_name(adapter_dir) | |
| if checkpoint and len(relative_parts) > 1: | |
| return relative_parts[-2] | |
| return relative_parts[-1] | |
| def adapter_label(adapter_dir: Path) -> str: | |
| relative_path = adapter_dir.relative_to(OUTPUTS_DIR).as_posix() | |
| checkpoint = checkpoint_name(adapter_dir) | |
| family = model_family_name(adapter_dir) | |
| if checkpoint: | |
| checkpoint_label = checkpoint.replace("-", " ") | |
| return f"{checkpoint_label} - {family}" | |
| return f"{relative_path} PEFT" | |
| def model_options_by_id() -> dict[str, dict[str, Any]]: | |
| return {option["id"]: option for option in discover_model_options()} | |
| def default_model_id() -> str: | |
| options = discover_model_options() | |
| peft_options = [option for option in options if option["kind"] == "peft"] | |
| checkpoint_options = [option for option in peft_options if option.get("checkpoint")] | |
| if checkpoint_options: | |
| return max( | |
| checkpoint_options, | |
| key=lambda option: (checkpoint_step(option), option.get("relative_path", "")), | |
| )["id"] | |
| peft_by_id = {option["id"]: option for option in peft_options} | |
| if "peft:1K" in peft_by_id: | |
| return "peft:1K" | |
| if peft_options: | |
| return peft_options[0]["id"] | |
| return "base:qwen2.5-3b-math" | |
| MODEL_STATE: dict[str, Any] = { | |
| "connected": False, | |
| "loading": False, | |
| "mode": "real", | |
| "model": default_model_id(), | |
| "label": model_options_by_id().get(default_model_id(), {}).get("label", "Qwen2.5 3B base"), | |
| "error": None, | |
| } | |
| LOADED_MODELS: dict[str, Any] = {} | |
| MODEL_LOCK = asyncio.Lock() | |
| INFERENCE_BACKENDS: dict[tuple[str, str, str], BaseInferenceBackend] = {} | |
| INFERENCE_LOCK = asyncio.Lock() | |
| LAST_INFERENCE_HEALTH: dict[str, Any] = { | |
| "ok": None, | |
| "backend": None, | |
| "model": None, | |
| "error": None, | |
| } | |
| THINKING_TOKEN_BUDGETS = { | |
| "none": 64, | |
| "flash": 256, | |
| "speed": 512, | |
| "low": 1024, | |
| "medium": 2048, | |
| "high": 4096, | |
| "extreme": 5120, | |
| "max": 8192, | |
| } | |
| BEGIN_THOUGHT = "<|begin_of_thought|>" | |
| END_THOUGHT = "<|end_of_thought|>" | |
| BEGIN_SOLUTION = "<|begin_of_solution|>" | |
| END_SOLUTION = "<|end_of_solution|>" | |
| PROTOCOL_TAGS = (BEGIN_THOUGHT, END_THOUGHT, BEGIN_SOLUTION, END_SOLUTION) | |
| THOUGHT_STOP_SEQUENCES = (END_THOUGHT, BEGIN_SOLUTION, END_SOLUTION) | |
| STREAM_TEXT_BATCH_CHARS = 96 | |
| STREAM_TEXT_BATCH_SECONDS = 0.04 | |
| THOUGHT_SENTENCE_COMPLETION_TOKENS = 96 | |
| async def index() -> FileResponse: | |
| return FileResponse(BASE_DIR / "index.html") | |
| async def status() -> dict[str, Any]: | |
| return { | |
| **MODEL_STATE, | |
| **runtime_info(), | |
| "inference": inference_status(), | |
| "models": discover_model_options(), | |
| "default_model": default_model_id(), | |
| } | |
| async def models() -> dict[str, Any]: | |
| return {"models": discover_model_options(), "default_model": default_model_id()} | |
| async def preload(request: ModelRequest) -> dict[str, Any]: | |
| option = resolve_model_option(request.model) | |
| try: | |
| config = inference_config() | |
| if config.backend == "vllm": | |
| await check_inference_health() | |
| else: | |
| await get_local_model(option["id"]) | |
| except Exception: | |
| pass | |
| return {**MODEL_STATE, **runtime_info(), "inference": inference_status()} | |
| async def inference_health() -> dict[str, Any]: | |
| return await check_inference_health() | |
| async def model_json_files() -> dict[str, list[str]]: | |
| MODEL_JSON_DIR.mkdir(parents=True, exist_ok=True) | |
| files = sorted(path.name for path in MODEL_JSON_DIR.glob("*.jsonl") if path.is_file()) | |
| return {"files": files} | |
| async def save_model_json(request: JsonSaveRequest) -> dict[str, Any]: | |
| MODEL_JSON_DIR.mkdir(parents=True, exist_ok=True) | |
| path = model_json_path(request.filename) | |
| with path.open("a", encoding="utf-8") as file: | |
| file.write(json.dumps(request.record, ensure_ascii=False, separators=(",", ":")) + "\n") | |
| return {"ok": True, "file": path.name} | |
| def last_user_message(messages: list[ChatMessage]) -> str: | |
| for message in reversed(messages): | |
| if message.role == "user": | |
| return message.content | |
| return "Solve a sample olympiad-style algebra problem." | |
| def set_model_state(**updates: Any) -> None: | |
| MODEL_STATE.update(updates) | |
| def model_json_path(filename: str) -> Path: | |
| name = filename.strip() | |
| if not name: | |
| name = "dpo_turns.jsonl" | |
| if not name.endswith(".jsonl"): | |
| name = f"{name}.jsonl" | |
| if "/" in name or "\\" in name or name in {".jsonl", ".", ".."}: | |
| raise HTTPException(status_code=400, detail="Invalid JSONL filename.") | |
| path = (MODEL_JSON_DIR / name).resolve() | |
| if path.parent != MODEL_JSON_DIR.resolve(): | |
| raise HTTPException(status_code=400, detail="Invalid JSONL filename.") | |
| return path | |
| def runtime_info() -> dict[str, str]: | |
| try: | |
| from src.device import runtime_metadata | |
| return runtime_metadata() | |
| except Exception as exc: | |
| return { | |
| "device": "unknown", | |
| "device_label": "Runtime unavailable", | |
| "torch_dtype": "unknown", | |
| "device_error": str(exc), | |
| } | |
| def distillation_settings() -> dict[str, Any]: | |
| try: | |
| settings = path_explorer.Get("src/pipeline/distillation/deepseek/settings", raw=True) | |
| return settings if isinstance(settings, dict) else {} | |
| except Exception as exc: | |
| logger.warning("Could not load distillation settings for inference config: %r", exc) | |
| return {} | |
| def local_model_settings() -> dict[str, Any]: | |
| settings = distillation_settings().get("LOCAL_MODEL", {}) | |
| return settings if isinstance(settings, dict) else {} | |
| def qwen_system_prompt() -> str: | |
| try: | |
| value = path_explorer.Get("src/model/Qwen3BInstruct/settings/PROMPT") | |
| return str(value or "") | |
| except Exception as exc: | |
| logger.warning("Could not load Qwen system prompt for vLLM backend: %r", exc) | |
| return "" | |
| def inference_config() -> InferenceConfig: | |
| config = InferenceConfig.from_mapping(local_model_settings(), system_prompt=qwen_system_prompt()) | |
| if DISABLE_VLLM_MODE and config.backend == "vllm": | |
| config.backend = "transformers" | |
| return config | |
| def inference_status() -> dict[str, Any]: | |
| config = inference_config() | |
| return { | |
| "backend": config.backend, | |
| "vllm_model": config.vllm_model_name, | |
| "vllm_base_url": config.vllm_base_url, | |
| "fallback_to_transformers": config.vllm_fallback_to_transformers, | |
| "health": LAST_INFERENCE_HEALTH, | |
| } | |
| def resolve_model_option(model_id: str) -> dict[str, Any]: | |
| options = model_options_by_id() | |
| if model_id == "qwen2.5-3b-math": | |
| model_id = "base:qwen2.5-3b-math" | |
| elif model_id in {"1K", "outputs/1K"}: | |
| model_id = "peft:1K" | |
| elif model_id.startswith("outputs/"): | |
| model_id = f"peft:{model_id.removeprefix('outputs/')}" | |
| if model_id in options: | |
| return options[model_id] | |
| path_matches = [ | |
| option | |
| for option in options.values() | |
| if option.get("relative_path") == model_id | |
| or option.get("path") == model_id | |
| or Path(str(option.get("path"))).name == model_id | |
| ] | |
| if len(path_matches) == 1: | |
| return path_matches[0] | |
| return options[default_model_id()] | |
| async def get_local_model(model_id: str) -> Any: | |
| if model_id in LOADED_MODELS: | |
| option = resolve_model_option(model_id) | |
| set_model_state( | |
| loading=False, | |
| connected=True, | |
| mode=option["kind"], | |
| model=option["id"], | |
| label=option["label"], | |
| error=None, | |
| ) | |
| return LOADED_MODELS[model_id] | |
| async with MODEL_LOCK: | |
| if model_id in LOADED_MODELS: | |
| option = resolve_model_option(model_id) | |
| set_model_state( | |
| loading=False, | |
| connected=True, | |
| mode=option["kind"], | |
| model=option["id"], | |
| label=option["label"], | |
| error=None, | |
| ) | |
| return LOADED_MODELS[model_id] | |
| option = resolve_model_option(model_id) | |
| set_model_state( | |
| loading=True, | |
| connected=False, | |
| mode=option["kind"], | |
| model=option["id"], | |
| label=option["label"], | |
| error=None, | |
| ) | |
| def load() -> Any: | |
| from src.model.Qwen3BInstruct import BaseModel as QwenModel | |
| model = QwenModel(peft_adapter_path=option["path"]) | |
| model._ensure_init() | |
| return model | |
| try: | |
| loaded_model = await asyncio.to_thread(load) | |
| LOADED_MODELS[model_id] = loaded_model | |
| set_model_state(loading=False, connected=True, mode=option["kind"], error=None) | |
| return loaded_model | |
| except Exception as exc: | |
| set_model_state(loading=False, connected=False, mode=option["kind"], error=str(exc)) | |
| raise | |
| def request_messages(request: ChatRequest) -> list[dict[str, str]]: | |
| return [{"role": message.role, "content": message.content} for message in request.messages] | |
| async def transformers_backend(model_id: str) -> TransformersBackend: | |
| option = resolve_model_option(model_id) | |
| model = await get_local_model(option["id"]) | |
| return TransformersBackend(model=model, model_name=option["label"]) | |
| async def get_inference_backend(model_id: str) -> BaseInferenceBackend: | |
| config = inference_config() | |
| option = resolve_model_option(model_id) | |
| cache_key = ( | |
| config.backend, | |
| config.vllm_base_url if config.backend == "vllm" else option["id"], | |
| config.vllm_model_name if config.backend == "vllm" else option["label"], | |
| ) | |
| if cache_key in INFERENCE_BACKENDS: | |
| return INFERENCE_BACKENDS[cache_key] | |
| async with INFERENCE_LOCK: | |
| if cache_key in INFERENCE_BACKENDS: | |
| return INFERENCE_BACKENDS[cache_key] | |
| if config.backend == "vllm": | |
| backend = build_inference_backend(config) | |
| set_model_state( | |
| loading=False, | |
| connected=LAST_INFERENCE_HEALTH.get("ok") is True, | |
| mode="vllm", | |
| model=config.vllm_model_name, | |
| label=f"vLLM {config.vllm_model_name}", | |
| error=None, | |
| ) | |
| else: | |
| backend = await transformers_backend(option["id"]) | |
| INFERENCE_BACKENDS[cache_key] = backend | |
| return backend | |
| async def fallback_transformers_backend(model_id: str, reason: Exception) -> TransformersBackend: | |
| logger.warning("vllm_fallback_to_transformers reason=%r", reason) | |
| option = resolve_model_option(model_id) | |
| return await transformers_backend(option["id"]) | |
| async def check_inference_health() -> dict[str, Any]: | |
| config = inference_config() | |
| if config.backend == "transformers": | |
| result = { | |
| "ok": True, | |
| "backend": "transformers", | |
| "model": resolve_model_option(MODEL_STATE.get("model", default_model_id()))["label"], | |
| } | |
| LAST_INFERENCE_HEALTH.update(result, error=None) | |
| return dict(LAST_INFERENCE_HEALTH) | |
| backend = build_inference_backend(config) | |
| try: | |
| result = await asyncio.to_thread(backend.health_check) | |
| LAST_INFERENCE_HEALTH.update(result, error=None) | |
| set_model_state( | |
| loading=False, | |
| connected=True, | |
| mode="vllm", | |
| model=config.vllm_model_name, | |
| label=f"vLLM {config.vllm_model_name}", | |
| error=None, | |
| ) | |
| return dict(LAST_INFERENCE_HEALTH) | |
| except Exception as exc: | |
| result = { | |
| "ok": False, | |
| "backend": "vllm", | |
| "model": config.vllm_model_name, | |
| "base_url": config.vllm_base_url, | |
| "error": str(exc), | |
| } | |
| LAST_INFERENCE_HEALTH.update(result) | |
| set_model_state( | |
| loading=False, | |
| connected=False, | |
| mode="vllm", | |
| model=config.vllm_model_name, | |
| label=f"vLLM {config.vllm_model_name}", | |
| error=str(exc), | |
| ) | |
| if not config.vllm_fallback_to_transformers: | |
| raise HTTPException(status_code=503, detail=result) | |
| return dict(LAST_INFERENCE_HEALTH) | |
| def mock_response(request: ChatRequest) -> str: | |
| prompt = last_user_message(request.messages) | |
| return f"""<|begin_of_thought|> | |
| The local model is not connected, so this fallback response is being streamed to keep the UI testable. | |
| For the prompt "{prompt}", a simple sample computation is: | |
| \\[ | |
| 1 + 1 = 2. | |
| \\] | |
| <|end_of_thought|> | |
| <|begin_of_solution|> | |
| Mock fallback answer: \\(\\boxed{{2}}\\). | |
| <|end_of_solution|>""" | |
| async def mock_model_stream(request: ChatRequest) -> AsyncIterator[str]: | |
| text = mock_response(request) | |
| chunk_size = 18 | |
| for index in range(0, len(text), chunk_size): | |
| await asyncio.sleep(0.055) | |
| yield text[index : index + chunk_size] | |
| async def next_stream_chunk(streamer: Iterator[str]) -> str | None: | |
| sentinel = object() | |
| chunk = await asyncio.to_thread(next, streamer, sentinel) | |
| return None if chunk is sentinel else chunk | |
| def matching_protocol_tag(buffer: str) -> str | None: | |
| for tag in PROTOCOL_TAGS: | |
| if buffer.startswith(tag): | |
| return tag | |
| return None | |
| def could_be_protocol_tag(buffer: str) -> bool: | |
| return buffer.startswith("<") and any(tag.startswith(buffer) for tag in PROTOCOL_TAGS) | |
| async def stream_model_once( | |
| request: ChatRequest, | |
| max_tokens: int, | |
| assistant_prefix: str | None = None, | |
| state: dict[str, Any] | None = None, | |
| stop_sequences: tuple[str, ...] = (), | |
| ignore_eos: bool = False, | |
| temperature_override: float | None = None, | |
| ) -> AsyncIterator[str]: | |
| if state is not None: | |
| state["generated_tokens"] = 0 | |
| state["stop_reason"] = None | |
| state["backend"] = None | |
| state["fallback_reason"] = None | |
| config = inference_config() | |
| backend = await get_inference_backend(request.model) | |
| messages = request_messages(request) | |
| async def open_stream(active_backend: BaseInferenceBackend) -> Iterator[str]: | |
| effective_max_tokens = max_tokens | |
| effective_temperature = request.temperature if temperature_override is None else temperature_override | |
| effective_top_p = 0.95 | |
| if active_backend.name == "vllm": | |
| effective_max_tokens = min(max_tokens, config.vllm_max_tokens) if config.vllm_max_tokens > 0 else max_tokens | |
| effective_temperature = config.vllm_temperature | |
| effective_top_p = config.vllm_top_p | |
| if state is not None: | |
| state["active_max_tokens"] = effective_max_tokens | |
| return await asyncio.to_thread( | |
| active_backend.stream_generate, | |
| messages, | |
| effective_max_tokens, | |
| effective_temperature, | |
| effective_top_p, | |
| list(stop_sequences), | |
| assistant_prefix, | |
| ignore_eos, | |
| ) | |
| try: | |
| streamer = await open_stream(backend) | |
| except Exception as exc: | |
| if backend.name != "vllm" or not config.vllm_fallback_to_transformers: | |
| raise | |
| if state is not None: | |
| state["fallback_reason"] = str(exc) | |
| backend = await fallback_transformers_backend(request.model, exc) | |
| streamer = await open_stream(backend) | |
| if state is not None: | |
| state["backend"] = backend.name | |
| try: | |
| while True: | |
| try: | |
| chunk = await next_stream_chunk(streamer) | |
| except Exception as exc: | |
| if backend.name != "vllm" or not config.vllm_fallback_to_transformers: | |
| raise | |
| if getattr(streamer, "output_text", "") or int( | |
| getattr(streamer, "generated_token_count", 0) or 0, | |
| ) > 0: | |
| raise | |
| if state is not None: | |
| state["fallback_reason"] = str(exc) | |
| backend = await fallback_transformers_backend(request.model, exc) | |
| streamer = await open_stream(backend) | |
| if state is not None: | |
| state["backend"] = backend.name | |
| state["generated_tokens"] = 0 | |
| state["stop_reason"] = None | |
| continue | |
| if state is not None: | |
| state["generated_tokens"] = int( | |
| getattr(streamer, "generated_token_count", state.get("generated_tokens", 0)), | |
| ) | |
| state["stop_reason"] = getattr(streamer, "stop_reason", state.get("stop_reason")) | |
| if chunk is None: | |
| break | |
| if chunk: | |
| yield chunk | |
| finally: | |
| if state is not None: | |
| tokens = int(getattr(streamer, "generated_token_count", state.get("generated_tokens", 0))) | |
| state["generated_tokens"] = tokens | |
| stop_reason = getattr(streamer, "stop_reason", state.get("stop_reason")) | |
| if stop_reason == "stop": | |
| stop_reason = "stop_sequence" | |
| active_max_tokens = int(state.get("active_max_tokens") or max_tokens) | |
| state["stop_reason"] = stop_reason or ("length" if tokens >= active_max_tokens else "eos") | |
| def token_event(text: str) -> dict[str, Any]: | |
| return {"type": "token", "text": text} | |
| def usage_event( | |
| *, | |
| phase: str, | |
| generated_tokens: int, | |
| token_budget: int, | |
| stop_reason: str | None = None, | |
| done: bool = False, | |
| ) -> dict[str, Any]: | |
| return { | |
| "type": "usage", | |
| "phase": phase, | |
| "thinking_tokens": generated_tokens, | |
| "thinking_token_budget": token_budget, | |
| "thinking_stop_reason": stop_reason, | |
| "thinking_done": done, | |
| } | |
| def reported_thinking_tokens(generated_tokens: int, token_budget: int) -> int: | |
| return min(max(0, generated_tokens), token_budget) | |
| def thought_tail(thought_parts: list[str], max_chars: int = 256) -> str: | |
| if not thought_parts: | |
| return "" | |
| tail: list[str] = [] | |
| remaining = max_chars | |
| for part in reversed(thought_parts): | |
| if remaining <= 0: | |
| break | |
| segment = part[-remaining:] | |
| tail.append(segment) | |
| remaining -= len(segment) | |
| return "".join(reversed(tail)) | |
| def thought_ends_sentence(text: str) -> bool: | |
| stripped = text.rstrip() | |
| if not stripped: | |
| return False | |
| if re.search(r"""[.!?。!?]["')\]}»”’]*$""", stripped): | |
| return True | |
| if stripped.endswith(("\\]", "$$")): | |
| return True | |
| if re.search(r"""\\end\{[a-zA-Z*]+\}$""", stripped): | |
| return True | |
| return False | |
| async def stream_thought_phase( | |
| source: AsyncIterator[str], | |
| thought_parts: list[str], | |
| generation_state: dict[str, Any], | |
| token_budget: int, | |
| *, | |
| allow_sentence_completion_after_budget: bool = False, | |
| ) -> AsyncIterator[dict[str, Any]]: | |
| buffer = "" | |
| forwarding_thought = True | |
| yield token_event(BEGIN_THOUGHT) | |
| async for chunk in source: | |
| buffer += chunk | |
| output: list[str] = [] | |
| stop_after_output = False | |
| while buffer: | |
| tag = matching_protocol_tag(buffer) | |
| if tag is not None: | |
| buffer = buffer[len(tag):] | |
| if tag in {END_THOUGHT, BEGIN_SOLUTION, END_SOLUTION}: | |
| forwarding_thought = False | |
| generation_state["stop_reason"] = "stop_sequence" | |
| stop_after_output = True | |
| buffer = "" | |
| break | |
| continue | |
| if could_be_protocol_tag(buffer): | |
| break | |
| generated_tokens = int(generation_state.get("generated_tokens") or 0) | |
| budget_reached = generated_tokens >= token_budget | |
| if ( | |
| allow_sentence_completion_after_budget | |
| and budget_reached | |
| and thought_ends_sentence(thought_tail(thought_parts)) | |
| ): | |
| generation_state["stop_reason"] = "length" | |
| stop_after_output = True | |
| buffer = "" | |
| break | |
| char = buffer[0] | |
| buffer = buffer[1:] | |
| if forwarding_thought: | |
| thought_parts.append(char) | |
| output.append(char) | |
| if ( | |
| allow_sentence_completion_after_budget | |
| and budget_reached | |
| and thought_ends_sentence(thought_tail(thought_parts)) | |
| ): | |
| generation_state["stop_reason"] = "length" | |
| stop_after_output = True | |
| buffer = "" | |
| break | |
| if output: | |
| yield token_event("".join(output)) | |
| yield usage_event( | |
| phase="thought", | |
| generated_tokens=reported_thinking_tokens( | |
| int(generation_state.get("generated_tokens") or 0), | |
| token_budget, | |
| ), | |
| token_budget=token_budget, | |
| ) | |
| if stop_after_output: | |
| return | |
| async def stream_solution_phase(source: AsyncIterator[str]) -> AsyncIterator[str]: | |
| buffer = "" | |
| saw_end_solution = False | |
| async for chunk in source: | |
| buffer += chunk | |
| output: list[str] = [] | |
| while buffer: | |
| tag = matching_protocol_tag(buffer) | |
| if tag is not None: | |
| buffer = buffer[len(tag):] | |
| if tag == END_SOLUTION: | |
| output.append(tag) | |
| saw_end_solution = True | |
| buffer = "" | |
| break | |
| continue | |
| if could_be_protocol_tag(buffer): | |
| break | |
| output.append(buffer[0]) | |
| buffer = buffer[1:] | |
| if output: | |
| yield "".join(output) | |
| if saw_end_solution: | |
| return | |
| if buffer and not could_be_protocol_tag(buffer): | |
| yield buffer | |
| if not saw_end_solution: | |
| yield f"\n{END_SOLUTION}" | |
| async def real_model_events(request: ChatRequest) -> AsyncIterator[dict[str, Any]]: | |
| if request.fast_mode: | |
| async for event in fast_model_events(request): | |
| yield event | |
| return | |
| thinking_limit = THINKING_TOKEN_BUDGETS[request.thinking_effort] | |
| thought_parts: list[str] = [] | |
| thought_state: dict[str, Any] = {} | |
| thought_generation_limit = thinking_limit + THOUGHT_SENTENCE_COMPLETION_TOKENS | |
| first_pass = stream_model_once( | |
| request, | |
| max_tokens=thought_generation_limit, | |
| state=thought_state, | |
| stop_sequences=THOUGHT_STOP_SEQUENCES, | |
| ignore_eos=True, | |
| ) | |
| async for event in stream_thought_phase( | |
| first_pass, | |
| thought_parts, | |
| thought_state, | |
| thinking_limit, | |
| allow_sentence_completion_after_budget=True, | |
| ): | |
| yield event | |
| thought_stop_reason = thought_state.get("stop_reason") | |
| thought_tokens = reported_thinking_tokens( | |
| int(thought_state.get("generated_tokens") or 0), | |
| thinking_limit, | |
| ) | |
| if thought_stop_reason == "length": | |
| runtime_instruction = instruction_block(thinking_limit_reached_instruction()) | |
| thought_parts.append(runtime_instruction) | |
| yield token_event(runtime_instruction) | |
| thought = "".join(thought_parts).strip() | |
| assistant_prefix = f"{BEGIN_THOUGHT}\n{thought}\n{END_THOUGHT}\n\n{BEGIN_SOLUTION}\n" | |
| yield usage_event( | |
| phase="thought", | |
| generated_tokens=thought_tokens, | |
| token_budget=thinking_limit, | |
| stop_reason=thought_stop_reason, | |
| done=True, | |
| ) | |
| yield token_event(f"\n{END_THOUGHT}\n\n{BEGIN_SOLUTION}\n") | |
| second_pass = stream_model_once( | |
| request, | |
| max_tokens=request.max_tokens, | |
| assistant_prefix=assistant_prefix, | |
| stop_sequences=(END_SOLUTION,), | |
| ) | |
| async for chunk in stream_solution_phase(second_pass): | |
| yield token_event(chunk) | |
| async def fast_model_events(request: ChatRequest) -> AsyncIterator[dict[str, Any]]: | |
| thinking_limit = THINKING_TOKEN_BUDGETS[request.thinking_effort] | |
| generation_state: dict[str, Any] = {} | |
| max_tokens = thinking_limit + request.max_tokens | |
| source = stream_model_once( | |
| request, | |
| max_tokens=max_tokens, | |
| state=generation_state, | |
| stop_sequences=(END_SOLUTION,), | |
| temperature_override=0.0, | |
| ) | |
| async for chunk in source: | |
| yield token_event(chunk) | |
| yield usage_event( | |
| phase="thought", | |
| generated_tokens=reported_thinking_tokens( | |
| int(generation_state.get("generated_tokens") or 0), | |
| thinking_limit, | |
| ), | |
| token_budget=thinking_limit, | |
| stop_reason=generation_state.get("stop_reason"), | |
| done=True, | |
| ) | |
| async def mock_model_events(request: ChatRequest) -> AsyncIterator[dict[str, Any]]: | |
| async for chunk in mock_model_stream(request): | |
| yield token_event(chunk) | |
| async def model_events(request: ChatRequest) -> AsyncIterator[dict[str, Any]]: | |
| option = resolve_model_option(request.model) | |
| request.model = option["id"] | |
| config = inference_config() | |
| if config.backend == "vllm": | |
| set_model_state( | |
| model=config.vllm_model_name, | |
| label=f"vLLM {config.vllm_model_name}", | |
| mode="vllm", | |
| ) | |
| else: | |
| set_model_state(model=option["id"], label=option["label"], mode=option["kind"]) | |
| try: | |
| async for event in real_model_events(request): | |
| yield event | |
| except Exception as exc: | |
| set_model_state(loading=False, connected=False, error=str(exc)) | |
| logger.exception("local_model_events_failed backend=%s model=%s", config.backend, request.model) | |
| if config.backend == "vllm" and not config.vllm_fallback_to_transformers: | |
| raise | |
| async for event in mock_model_events(request): | |
| yield event | |
| async def model_stream(request: ChatRequest) -> AsyncIterator[str]: | |
| async for event in model_events(request): | |
| if event.get("type") == "token": | |
| yield event.get("text", "") | |
| async def model_complete(request: ChatRequest) -> str: | |
| chunks: list[str] = [] | |
| async for chunk in model_stream(request): | |
| chunks.append(chunk) | |
| return "".join(chunks) | |
| async def chat(request: ChatRequest) -> dict[str, str]: | |
| return {"text": await model_complete(request)} | |
| async def chat_stream(request: ChatRequest) -> StreamingResponse: | |
| async def events() -> AsyncIterator[str]: | |
| text_batch: list[str] = [] | |
| text_batch_len = 0 | |
| last_flush = time.monotonic() | |
| def flush_text_batch() -> str | None: | |
| nonlocal text_batch, text_batch_len, last_flush | |
| if not text_batch: | |
| return None | |
| payload = json.dumps({"text": "".join(text_batch)}) | |
| text_batch = [] | |
| text_batch_len = 0 | |
| last_flush = time.monotonic() | |
| return f"event: token\ndata: {payload}\n\n" | |
| try: | |
| async for event in model_events(request): | |
| event_type = event.get("type") | |
| if event_type == "token": | |
| text = event.get("text", "") | |
| if not text: | |
| continue | |
| text_batch.append(text) | |
| text_batch_len += len(text) | |
| if ( | |
| text_batch_len >= STREAM_TEXT_BATCH_CHARS | |
| or time.monotonic() - last_flush >= STREAM_TEXT_BATCH_SECONDS | |
| ): | |
| flushed = flush_text_batch() | |
| if flushed: | |
| yield flushed | |
| continue | |
| flushed = flush_text_batch() | |
| if flushed: | |
| yield flushed | |
| if event_type == "usage": | |
| payload = json.dumps({key: value for key, value in event.items() if key != "type"}) | |
| yield f"event: usage\ndata: {payload}\n\n" | |
| flushed = flush_text_batch() | |
| if flushed: | |
| yield flushed | |
| yield "event: done\ndata: {}\n\n" | |
| except asyncio.CancelledError: | |
| raise | |
| except Exception as exc: | |
| payload = json.dumps({"message": str(exc)}) | |
| yield f"event: error\ndata: {payload}\n\n" | |
| return StreamingResponse( | |
| events(), | |
| media_type="text/event-stream", | |
| headers={ | |
| "Cache-Control": "no-cache", | |
| "Connection": "keep-alive", | |
| "X-Accel-Buffering": "no", | |
| }, | |
| ) | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run( | |
| "server:app", | |
| host="127.0.0.1", | |
| port=8000, | |
| reload=False, | |
| ) | |