Download server/llm_client.py from Berlm/ppt_agent: direct link, hf CLI and curl.
- Browser
- Download file 7.3 kB
-
https://huggingface.co/spaces/Berlm/ppt_agent/resolve/main/server/llm_client.py
- Command line
-
hf download hf://spaces/Berlm/ppt_agent/server/llm_client.py
-
curl -L -o llm_client.py https://huggingface.co/spaces/Berlm/ppt_agent/resolve/main/server/llm_client.py
7.3 kB
| """Unified OpenAI-client wrapper for environment-side judge LLM calls.""" | |
| import json | |
| import logging | |
| import os | |
| import re | |
| from openai import OpenAI | |
| from .debug_logging import write_debug_event | |
| logger = logging.getLogger(__name__) | |
| def _exception_payload(error: Exception) -> dict[str, object]: | |
| payload: dict[str, object] = { | |
| "error": str(error), | |
| "error_type": error.__class__.__name__, | |
| "error_repr": repr(error), | |
| } | |
| if getattr(error, "args", None): | |
| payload["error_args"] = [str(arg) for arg in error.args] | |
| cause = error.__cause__ | |
| if cause is not None: | |
| payload["cause_type"] = cause.__class__.__name__ | |
| payload["cause"] = str(cause) | |
| payload["cause_repr"] = repr(cause) | |
| context = error.__context__ | |
| if context is not None and context is not cause: | |
| payload["context_type"] = context.__class__.__name__ | |
| payload["context"] = str(context) | |
| payload["context_repr"] = repr(context) | |
| return payload | |
| class LLMClient: | |
| """Thin wrapper around the OpenAI client using judge env vars.""" | |
| def __init__(self): | |
| self.model = os.environ.get( | |
| "JUDGE_MODEL_NAME", | |
| os.environ.get( | |
| "MODEL_NAME", | |
| ), | |
| ) | |
| self.base_url = os.environ.get( | |
| "JUDGE_API_BASE_URL", | |
| os.environ.get( | |
| "API_BASE_URL", | |
| "https://router.huggingface.co/v1", | |
| ), | |
| ) | |
| api_key = os.environ.get("JUDGE_API_KEY", os.environ.get("HF_TOKEN")) | |
| self.client = OpenAI(base_url=self.base_url, api_key=api_key) | |
| logger.info( | |
| "Judge LLM client configured model=%s base_url=%s", | |
| self.model, | |
| self.base_url, | |
| ) | |
| def chat( | |
| self, | |
| system: str, | |
| user: str, | |
| temperature: float = 0.3, | |
| max_tokens: int = 1024, | |
| *, | |
| debug_stage: str | None = None, | |
| ) -> str: | |
| """Send a chat completion request. Returns the raw response text.""" | |
| return self._chat_openai( | |
| system, | |
| user, | |
| temperature, | |
| max_tokens, | |
| debug_stage=debug_stage, | |
| ) | |
| def chat_json( | |
| self, | |
| system: str, | |
| user: str, | |
| temperature: float = 0.3, | |
| max_tokens: int = 1024, | |
| *, | |
| debug_stage: str | None = None, | |
| ) -> dict: | |
| """Send a chat request and parse the response as JSON.""" | |
| raw = self._chat_openai( | |
| system, | |
| user, | |
| temperature, | |
| max_tokens, | |
| json_mode=True, | |
| debug_stage=debug_stage, | |
| ) | |
| return self._parse_json(raw) | |
| def _parse_json(raw: str) -> dict: | |
| """Extract and parse JSON from LLM response, handling markdown fences.""" | |
| if raw is None: | |
| raise ValueError("LLM returned empty content") | |
| raw = str(raw).strip() | |
| raw = re.sub(r"<think>.*?</think>", "", raw, flags=re.DOTALL).strip() | |
| # Strip ```json ... ``` or ``` ... ``` wrappers | |
| fence_match = re.search(r"```(?:json)?\s*\n?(.*?)```", raw, re.DOTALL) | |
| if fence_match: | |
| raw = fence_match.group(1).strip() | |
| if not raw: | |
| raise ValueError("LLM returned empty content") | |
| try: | |
| return json.loads(raw) | |
| except json.JSONDecodeError: | |
| object_match = re.search(r"\{.*\}", raw, re.DOTALL) | |
| if object_match is None: | |
| raise | |
| return json.loads(object_match.group(0)) | |
| def _extract_chat_content(message_content) -> str: | |
| if isinstance(message_content, str): | |
| return message_content | |
| if message_content is None: | |
| return "" | |
| if isinstance(message_content, list): | |
| parts: list[str] = [] | |
| for item in message_content: | |
| if isinstance(item, str): | |
| parts.append(item) | |
| continue | |
| if isinstance(item, dict): | |
| text_value = item.get("text") | |
| if isinstance(text_value, str): | |
| parts.append(text_value) | |
| elif item.get("type") == "text" and isinstance( | |
| item.get("content"), str | |
| ): | |
| parts.append(item["content"]) | |
| continue | |
| text_attr = getattr(item, "text", None) | |
| if isinstance(text_attr, str): | |
| parts.append(text_attr) | |
| return "\n".join(part for part in parts if part) | |
| return str(message_content) | |
| def _chat_openai( | |
| self, | |
| system: str, | |
| user: str, | |
| temperature: float, | |
| max_tokens: int, | |
| *, | |
| json_mode: bool = False, | |
| debug_stage: str | None = None, | |
| ) -> str: | |
| request_kwargs = { | |
| "model": self.model, | |
| "messages": [ | |
| {"role": "system", "content": system}, | |
| {"role": "user", "content": user}, | |
| ], | |
| "temperature": temperature, | |
| "max_tokens": max_tokens, | |
| } | |
| if json_mode: | |
| request_kwargs["response_format"] = {"type": "json_object"} | |
| stage = debug_stage or "chat" | |
| write_debug_event( | |
| "llm.request", | |
| { | |
| "stage": stage, | |
| "model": self.model, | |
| "base_url": self.base_url, | |
| "json_mode": json_mode, | |
| "temperature": temperature, | |
| "max_tokens": max_tokens, | |
| "system": system, | |
| "user": user, | |
| }, | |
| ) | |
| try: | |
| response = self.client.chat.completions.create(**request_kwargs) | |
| except Exception as error: | |
| if not json_mode: | |
| write_debug_event( | |
| "llm.error", | |
| { | |
| "stage": stage, | |
| "model": self.model, | |
| "base_url": self.base_url, | |
| "json_mode": json_mode, | |
| **_exception_payload(error), | |
| }, | |
| ) | |
| raise | |
| request_kwargs.pop("response_format", None) | |
| try: | |
| response = self.client.chat.completions.create(**request_kwargs) | |
| except Exception as retry_error: | |
| write_debug_event( | |
| "llm.error", | |
| { | |
| "stage": stage, | |
| "model": self.model, | |
| "base_url": self.base_url, | |
| "json_mode": json_mode, | |
| **_exception_payload(retry_error), | |
| "response_format_retry": True, | |
| }, | |
| ) | |
| raise | |
| content = self._extract_chat_content(response.choices[0].message.content) | |
| write_debug_event( | |
| "llm.response", | |
| { | |
| "stage": stage, | |
| "model": self.model, | |
| "json_mode": json_mode, | |
| "content": content, | |
| }, | |
| ) | |
| return content | |