Download app/utils/llm_client.py from Himankpro/Sub: direct link, hf CLI and curl.
- Browser
- Download file 3.4 kB
-
https://huggingface.co/spaces/Himankpro/Sub/resolve/main/app/utils/llm_client.py
- Command line
-
hf download hf://spaces/Himankpro/Sub/app/utils/llm_client.py
-
curl -L -o llm_client.py https://huggingface.co/spaces/Himankpro/Sub/resolve/main/app/utils/llm_client.py
3.4 kB
| """ | |
| LLM客户端封装 | |
| 统一使用OpenAI格式调用 | |
| """ | |
| import json | |
| import re | |
| from typing import Optional, Dict, Any, List | |
| from openai import OpenAI | |
| from ..config import Config | |
| from .api_key_runtime import call_with_limit_rotation, get_llm_key | |
| class LLMClient: | |
| """LLM客户端""" | |
| def __init__( | |
| self, | |
| api_key: Optional[str] = None, | |
| base_url: Optional[str] = None, | |
| model: Optional[str] = None | |
| ): | |
| self._fixed_api_key = (api_key or "").strip() or None | |
| self.base_url = base_url or Config.LLM_BASE_URL | |
| self.model = model or Config.LLM_MODEL_NAME | |
| if not self._fixed_api_key and not Config.get_llm_api_keys(): | |
| raise ValueError("LLM API key not configured (set LLM_1 or LLM_API_KEY)") | |
| def chat( | |
| self, | |
| messages: List[Dict[str, str]], | |
| temperature: float = 0.7, | |
| max_tokens: int = 4096, | |
| response_format: Optional[Dict] = None | |
| ) -> str: | |
| """ | |
| 发送聊天请求 | |
| Args: | |
| messages: 消息列表 | |
| temperature: 温度参数 | |
| max_tokens: 最大token数 | |
| response_format: 响应格式(如JSON模式) | |
| Returns: | |
| 模型响应文本 | |
| """ | |
| kwargs = { | |
| "model": self.model, | |
| "messages": messages, | |
| "temperature": temperature, | |
| "max_tokens": max_tokens, | |
| } | |
| if response_format: | |
| kwargs["response_format"] = response_format | |
| def _create(): | |
| key = (self._fixed_api_key or get_llm_key() or "").strip() | |
| if not key: | |
| raise ValueError("LLM API key not configured (set LLM_1 / LLM_2 or LLM_API_KEY)") | |
| client = OpenAI(api_key=key, base_url=self.base_url) | |
| return client.chat.completions.create(**kwargs) | |
| response = ( | |
| _create() | |
| if self._fixed_api_key | |
| else call_with_limit_rotation(_create) | |
| ) | |
| content = response.choices[0].message.content | |
| # 部分模型(如MiniMax M2.5)会在content中包含<think>思考内容,需要移除 | |
| content = re.sub(r'<think>[\s\S]*?</think>', '', content).strip() | |
| return content | |
| def chat_json( | |
| self, | |
| messages: List[Dict[str, str]], | |
| temperature: float = 0.3, | |
| max_tokens: int = 4096 | |
| ) -> Dict[str, Any]: | |
| """ | |
| 发送聊天请求并返回JSON | |
| Args: | |
| messages: 消息列表 | |
| temperature: 温度参数 | |
| max_tokens: 最大token数 | |
| Returns: | |
| 解析后的JSON对象 | |
| """ | |
| response = self.chat( | |
| messages=messages, | |
| temperature=temperature, | |
| max_tokens=max_tokens, | |
| response_format={"type": "json_object"} | |
| ) | |
| # 清理markdown代码块标记 | |
| cleaned_response = response.strip() | |
| cleaned_response = re.sub(r'^```(?:json)?\s*\n?', '', cleaned_response, flags=re.IGNORECASE) | |
| cleaned_response = re.sub(r'\n?```\s*$', '', cleaned_response) | |
| cleaned_response = cleaned_response.strip() | |
| try: | |
| return json.loads(cleaned_response) | |
| except json.JSONDecodeError: | |
| raise ValueError(f"LLM返回的JSON格式无效: {cleaned_response}") | |