isa-model 0.3.5__py3-none-any.whl → 0.3.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. isa_model/__init__.py +30 -1
  2. isa_model/client.py +937 -0
  3. isa_model/core/config/__init__.py +16 -0
  4. isa_model/core/config/config_manager.py +514 -0
  5. isa_model/core/config.py +426 -0
  6. isa_model/core/models/model_billing_tracker.py +476 -0
  7. isa_model/core/models/model_manager.py +399 -0
  8. isa_model/core/{storage/supabase_storage.py → models/model_repo.py} +72 -73
  9. isa_model/core/pricing_manager.py +426 -0
  10. isa_model/core/services/__init__.py +19 -0
  11. isa_model/core/services/intelligent_model_selector.py +547 -0
  12. isa_model/core/types.py +291 -0
  13. isa_model/deployment/__init__.py +2 -0
  14. isa_model/deployment/cloud/modal/isa_vision_doc_service.py +157 -3
  15. isa_model/deployment/cloud/modal/isa_vision_table_service.py +532 -0
  16. isa_model/deployment/cloud/modal/isa_vision_ui_service.py +104 -3
  17. isa_model/deployment/cloud/modal/register_models.py +321 -0
  18. isa_model/deployment/runtime/deployed_service.py +338 -0
  19. isa_model/deployment/services/__init__.py +9 -0
  20. isa_model/deployment/services/auto_deploy_vision_service.py +538 -0
  21. isa_model/deployment/services/model_service.py +332 -0
  22. isa_model/deployment/services/service_monitor.py +356 -0
  23. isa_model/deployment/services/service_registry.py +527 -0
  24. isa_model/deployment/services/simple_auto_deploy_vision_service.py +275 -0
  25. isa_model/eval/__init__.py +80 -44
  26. isa_model/eval/config/__init__.py +10 -0
  27. isa_model/eval/config/evaluation_config.py +108 -0
  28. isa_model/eval/evaluators/__init__.py +18 -0
  29. isa_model/eval/evaluators/base_evaluator.py +503 -0
  30. isa_model/eval/evaluators/llm_evaluator.py +472 -0
  31. isa_model/eval/factory.py +417 -709
  32. isa_model/eval/infrastructure/__init__.py +24 -0
  33. isa_model/eval/infrastructure/experiment_tracker.py +466 -0
  34. isa_model/eval/metrics.py +191 -21
  35. isa_model/inference/ai_factory.py +257 -601
  36. isa_model/inference/services/audio/base_stt_service.py +65 -1
  37. isa_model/inference/services/audio/base_tts_service.py +75 -1
  38. isa_model/inference/services/audio/openai_stt_service.py +189 -151
  39. isa_model/inference/services/audio/openai_tts_service.py +12 -10
  40. isa_model/inference/services/audio/replicate_tts_service.py +61 -56
  41. isa_model/inference/services/base_service.py +55 -17
  42. isa_model/inference/services/embedding/base_embed_service.py +65 -1
  43. isa_model/inference/services/embedding/ollama_embed_service.py +103 -43
  44. isa_model/inference/services/embedding/openai_embed_service.py +8 -10
  45. isa_model/inference/services/helpers/stacked_config.py +148 -0
  46. isa_model/inference/services/img/__init__.py +18 -0
  47. isa_model/inference/services/{vision → img}/base_image_gen_service.py +80 -1
  48. isa_model/inference/services/{stacked → img}/flux_professional_service.py +25 -1
  49. isa_model/inference/services/{stacked → img/helpers}/base_stacked_service.py +40 -35
  50. isa_model/inference/services/{vision → img}/replicate_image_gen_service.py +44 -31
  51. isa_model/inference/services/llm/__init__.py +3 -3
  52. isa_model/inference/services/llm/base_llm_service.py +492 -40
  53. isa_model/inference/services/llm/helpers/llm_prompts.py +258 -0
  54. isa_model/inference/services/llm/helpers/llm_utils.py +280 -0
  55. isa_model/inference/services/llm/ollama_llm_service.py +51 -17
  56. isa_model/inference/services/llm/openai_llm_service.py +70 -19
  57. isa_model/inference/services/llm/yyds_llm_service.py +24 -23
  58. isa_model/inference/services/vision/__init__.py +38 -4
  59. isa_model/inference/services/vision/base_vision_service.py +218 -117
  60. isa_model/inference/services/vision/{isA_vision_service.py → disabled/isA_vision_service.py} +98 -0
  61. isa_model/inference/services/{stacked → vision}/doc_analysis_service.py +1 -1
  62. isa_model/inference/services/vision/helpers/base_stacked_service.py +274 -0
  63. isa_model/inference/services/vision/helpers/image_utils.py +272 -3
  64. isa_model/inference/services/vision/helpers/vision_prompts.py +297 -0
  65. isa_model/inference/services/vision/openai_vision_service.py +104 -307
  66. isa_model/inference/services/vision/replicate_vision_service.py +140 -325
  67. isa_model/inference/services/{stacked → vision}/ui_analysis_service.py +2 -498
  68. isa_model/scripts/register_models.py +370 -0
  69. isa_model/scripts/register_models_with_embeddings.py +510 -0
  70. isa_model/serving/api/fastapi_server.py +6 -1
  71. isa_model/serving/api/routes/unified.py +274 -0
  72. {isa_model-0.3.5.dist-info → isa_model-0.3.7.dist-info}/METADATA +4 -1
  73. {isa_model-0.3.5.dist-info → isa_model-0.3.7.dist-info}/RECORD +78 -53
  74. isa_model/config/__init__.py +0 -9
  75. isa_model/config/config_manager.py +0 -213
  76. isa_model/core/model_manager.py +0 -213
  77. isa_model/core/model_registry.py +0 -375
  78. isa_model/core/vision_models_init.py +0 -116
  79. isa_model/inference/billing_tracker.py +0 -406
  80. isa_model/inference/services/llm/triton_llm_service.py +0 -481
  81. isa_model/inference/services/stacked/__init__.py +0 -26
  82. isa_model/inference/services/stacked/config.py +0 -426
  83. isa_model/inference/services/vision/ollama_vision_service.py +0 -194
  84. /isa_model/core/{model_storage.py → models/model_storage.py} +0 -0
  85. /isa_model/inference/services/{vision → embedding}/helpers/text_splitter.py +0 -0
  86. /isa_model/inference/services/llm/{llm_adapter.py → helpers/llm_adapter.py} +0 -0
  87. {isa_model-0.3.5.dist-info → isa_model-0.3.7.dist-info}/WHEEL +0 -0
  88. {isa_model-0.3.5.dist-info → isa_model-0.3.7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,258 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ LLM通用提示词模板和工具函数
6
+ 提供标准化的提示词模板,用于不同类型的LLM任务
7
+ """
8
+
9
+ from typing import Dict, List, Optional, Any
10
+ import json
11
+
12
+ class LLMPrompts:
13
+ """LLM提示词模板管理器"""
14
+
15
+ # ==================== 对话类提示词 ====================
16
+
17
+ @staticmethod
18
+ def chat_prompt(user_message: str, system_message: Optional[str] = None) -> List[Dict[str, str]]:
19
+ """生成标准对话提示词格式"""
20
+ messages = []
21
+ if system_message:
22
+ messages.append({"role": "system", "content": system_message})
23
+ messages.append({"role": "user", "content": user_message})
24
+ return messages
25
+
26
+ @staticmethod
27
+ def completion_prompt(text: str, instruction: Optional[str] = None) -> str:
28
+ """生成文本补全提示词"""
29
+ if instruction:
30
+ return f"{instruction}\n\n{text}"
31
+ return text
32
+
33
+ @staticmethod
34
+ def instruct_prompt(task: str, content: str, format_instruction: Optional[str] = None) -> str:
35
+ """生成指令跟随提示词"""
36
+ prompt = f"Task: {task}\n\nContent:\n{content}\n\n"
37
+ if format_instruction:
38
+ prompt += f"Output Format: {format_instruction}\n\n"
39
+ prompt += "Response:"
40
+ return prompt
41
+
42
+ # ==================== 文本生成类提示词 ====================
43
+
44
+ @staticmethod
45
+ def rewrite_prompt(text: str, style: Optional[str] = None, tone: Optional[str] = None) -> str:
46
+ """生成文本重写提示词"""
47
+ prompt = f"Please rewrite the following text"
48
+ if style:
49
+ prompt += f" in {style} style"
50
+ if tone:
51
+ prompt += f" with a {tone} tone"
52
+ prompt += f":\n\n{text}\n\nRewritten text:"
53
+ return prompt
54
+
55
+ @staticmethod
56
+ def summarize_prompt(text: str, max_length: Optional[int] = None, style: Optional[str] = None) -> str:
57
+ """生成文本摘要提示词"""
58
+ prompt = f"Please summarize the following text"
59
+ if max_length:
60
+ prompt += f" in no more than {max_length} words"
61
+ if style:
62
+ prompt += f" in {style} style"
63
+ prompt += f":\n\n{text}\n\nSummary:"
64
+ return prompt
65
+
66
+ @staticmethod
67
+ def translate_prompt(text: str, target_language: str, source_language: Optional[str] = None) -> str:
68
+ """生成翻译提示词"""
69
+ prompt = f"Please translate the following text"
70
+ if source_language:
71
+ prompt += f" from {source_language}"
72
+ prompt += f" to {target_language}:\n\n{text}\n\nTranslation:"
73
+ return prompt
74
+
75
+ # ==================== 分析类提示词 ====================
76
+
77
+ @staticmethod
78
+ def analyze_prompt(text: str, analysis_type: Optional[str] = None) -> str:
79
+ """生成文本分析提示词"""
80
+ if analysis_type:
81
+ prompt = f"Please perform {analysis_type} analysis on the following text:\n\n{text}\n\nAnalysis:"
82
+ else:
83
+ prompt = f"Please analyze the following text:\n\n{text}\n\nAnalysis:"
84
+ return prompt
85
+
86
+ @staticmethod
87
+ def classify_prompt(text: str, categories: Optional[List[str]] = None) -> str:
88
+ """生成文本分类提示词"""
89
+ prompt = f"Please classify the following text"
90
+ if categories:
91
+ prompt += f" into one of these categories: {', '.join(categories)}"
92
+ prompt += f":\n\n{text}\n\nClassification:"
93
+ return prompt
94
+
95
+ @staticmethod
96
+ def extract_prompt(text: str, extract_type: Optional[str] = None) -> str:
97
+ """生成信息提取提示词"""
98
+ if extract_type:
99
+ prompt = f"Please extract {extract_type} from the following text:\n\n{text}\n\nExtracted {extract_type}:"
100
+ else:
101
+ prompt = f"Please extract key information from the following text:\n\n{text}\n\nExtracted information:"
102
+ return prompt
103
+
104
+ @staticmethod
105
+ def sentiment_prompt(text: str) -> str:
106
+ """生成情感分析提示词"""
107
+ return f"Please analyze the sentiment of the following text and classify it as positive, negative, or neutral:\n\n{text}\n\nSentiment:"
108
+
109
+ # ==================== 编程类提示词 ====================
110
+
111
+ @staticmethod
112
+ def code_generation_prompt(description: str, language: Optional[str] = None, style: Optional[str] = None) -> str:
113
+ """生成代码生成提示词"""
114
+ prompt = f"Please write code"
115
+ if language:
116
+ prompt += f" in {language}"
117
+ prompt += f" for the following requirement:\n\n{description}\n\n"
118
+ if style:
119
+ prompt += f"Code style: {style}\n\n"
120
+ prompt += "Code:"
121
+ return prompt
122
+
123
+ @staticmethod
124
+ def code_explanation_prompt(code: str, language: Optional[str] = None) -> str:
125
+ """生成代码解释提示词"""
126
+ prompt = f"Please explain the following"
127
+ if language:
128
+ prompt += f" {language}"
129
+ prompt += f" code:\n\n```{language or ''}\n{code}\n```\n\nExplanation:"
130
+ return prompt
131
+
132
+ @staticmethod
133
+ def code_debug_prompt(code: str, language: Optional[str] = None, error: Optional[str] = None) -> str:
134
+ """生成代码调试提示词"""
135
+ prompt = f"Please debug the following"
136
+ if language:
137
+ prompt += f" {language}"
138
+ prompt += f" code:\n\n```{language or ''}\n{code}\n```\n\n"
139
+ if error:
140
+ prompt += f"Error message: {error}\n\n"
141
+ prompt += "Fixed code:"
142
+ return prompt
143
+
144
+ @staticmethod
145
+ def code_refactor_prompt(code: str, language: Optional[str] = None, improvements: Optional[List[str]] = None) -> str:
146
+ """生成代码重构提示词"""
147
+ prompt = f"Please refactor the following"
148
+ if language:
149
+ prompt += f" {language}"
150
+ prompt += f" code"
151
+ if improvements:
152
+ prompt += f" with these improvements: {', '.join(improvements)}"
153
+ prompt += f":\n\n```{language or ''}\n{code}\n```\n\nRefactored code:"
154
+ return prompt
155
+
156
+ # ==================== 推理类提示词 ====================
157
+
158
+ @staticmethod
159
+ def reasoning_prompt(question: str, reasoning_type: Optional[str] = None) -> str:
160
+ """生成推理分析提示词"""
161
+ if reasoning_type:
162
+ prompt = f"Please use {reasoning_type} reasoning to answer the following question:\n\n{question}\n\nReasoning and answer:"
163
+ else:
164
+ prompt = f"Please think step by step and answer the following question:\n\n{question}\n\nReasoning and answer:"
165
+ return prompt
166
+
167
+ @staticmethod
168
+ def problem_solving_prompt(problem: str, problem_type: Optional[str] = None) -> str:
169
+ """生成问题求解提示词"""
170
+ prompt = f"Please solve the following"
171
+ if problem_type:
172
+ prompt += f" {problem_type}"
173
+ prompt += f" problem:\n\n{problem}\n\nSolution:"
174
+ return prompt
175
+
176
+ @staticmethod
177
+ def planning_prompt(goal: str, plan_type: Optional[str] = None) -> str:
178
+ """生成计划制定提示词"""
179
+ prompt = f"Please create a"
180
+ if plan_type:
181
+ prompt += f" {plan_type}"
182
+ prompt += f" plan for the following goal:\n\n{goal}\n\nPlan:"
183
+ return prompt
184
+
185
+ # ==================== 工具调用类提示词 ====================
186
+
187
+ @staticmethod
188
+ def tool_call_prompt(query: str, available_tools: List[Dict[str, Any]]) -> str:
189
+ """生成工具调用提示词"""
190
+ tools_desc = json.dumps(available_tools, indent=2)
191
+ prompt = f"You have access to the following tools:\n\n{tools_desc}\n\n"
192
+ prompt += f"User query: {query}\n\n"
193
+ prompt += "Please use the appropriate tools to answer the query. Respond with tool calls in JSON format."
194
+ return prompt
195
+
196
+ @staticmethod
197
+ def function_call_prompt(description: str, function_name: str, parameters: Dict[str, Any]) -> str:
198
+ """生成函数调用提示词"""
199
+ params_desc = json.dumps(parameters, indent=2)
200
+ prompt = f"Please call the function '{function_name}' with the following parameters to {description}:\n\n"
201
+ prompt += f"Parameters:\n{params_desc}\n\n"
202
+ prompt += "Function call result:"
203
+ return prompt
204
+
205
+ class LLMPromptTemplates:
206
+ """预定义的提示词模板"""
207
+
208
+ # 系统消息模板
209
+ SYSTEM_MESSAGES = {
210
+ "assistant": "You are a helpful AI assistant. Please provide accurate and helpful responses.",
211
+ "code_expert": "You are an expert programmer. Please provide clean, efficient, and well-documented code.",
212
+ "analyst": "You are a data analyst. Please provide detailed and insightful analysis.",
213
+ "writer": "You are a professional writer. Please provide clear, engaging, and well-structured content.",
214
+ "teacher": "You are a patient and knowledgeable teacher. Please explain concepts clearly and provide examples.",
215
+ "translator": "You are a professional translator. Please provide accurate and natural translations."
216
+ }
217
+
218
+ # 输出格式模板
219
+ OUTPUT_FORMATS = {
220
+ "json": "Please format your response as valid JSON.",
221
+ "markdown": "Please format your response using Markdown syntax.",
222
+ "bullet_points": "Please format your response as bullet points.",
223
+ "numbered_list": "Please format your response as a numbered list.",
224
+ "table": "Please format your response as a table.",
225
+ "code": "Please format your response as code with appropriate syntax highlighting."
226
+ }
227
+
228
+ # 任务特定模板
229
+ TASK_TEMPLATES = {
230
+ "email": "Please write a professional email with the following content:",
231
+ "report": "Please write a detailed report on the following topic:",
232
+ "proposal": "Please write a project proposal for the following idea:",
233
+ "documentation": "Please write clear documentation for the following:",
234
+ "test_cases": "Please write comprehensive test cases for the following:",
235
+ "user_story": "Please write user stories for the following feature:"
236
+ }
237
+
238
+ def get_prompt_template(task_type: str, **kwargs) -> str:
239
+ """获取指定任务类型的提示词模板"""
240
+ prompts = LLMPrompts()
241
+
242
+ if hasattr(prompts, f"{task_type}_prompt"):
243
+ method = getattr(prompts, f"{task_type}_prompt")
244
+ return method(**kwargs)
245
+ else:
246
+ raise ValueError(f"Unsupported task type: {task_type}")
247
+
248
+ def format_messages(messages: List[Dict[str, str]], system_message: Optional[str] = None) -> List[Dict[str, str]]:
249
+ """格式化消息列表,添加系统消息"""
250
+ formatted = []
251
+ if system_message:
252
+ formatted.append({"role": "system", "content": system_message})
253
+ formatted.extend(messages)
254
+ return formatted
255
+
256
+ def combine_prompts(prompts: List[str], separator: str = "\n\n") -> str:
257
+ """组合多个提示词"""
258
+ return separator.join(prompts)
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ LLM工具函数和实用程序
6
+ 提供LLM服务的通用工具函数,包括文本处理、token计算、响应解析等
7
+ """
8
+
9
+ import re
10
+ import json
11
+ import tiktoken
12
+ from typing import Dict, List, Optional, Any, Union, Tuple
13
+ import logging
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ class TokenCounter:
18
+ """Token计数器"""
19
+
20
+ def __init__(self, model_name: str = "gpt-4"):
21
+ """初始化token计数器"""
22
+ try:
23
+ self.encoding = tiktoken.encoding_for_model(model_name)
24
+ except KeyError:
25
+ # 如果模型不支持,使用默认编码
26
+ self.encoding = tiktoken.get_encoding("cl100k_base")
27
+
28
+ def count_tokens(self, text: str) -> int:
29
+ """计算文本的token数量"""
30
+ return len(self.encoding.encode(text))
31
+
32
+ def count_messages_tokens(self, messages: List[Dict[str, str]]) -> int:
33
+ """计算消息列表的总token数量"""
34
+ total_tokens = 0
35
+ for message in messages:
36
+ # 每个消息有固定的开销(role等)
37
+ total_tokens += 4 # 每个消息的基本开销
38
+ for key, value in message.items():
39
+ total_tokens += self.count_tokens(str(value))
40
+ total_tokens += 2 # 对话的基本开销
41
+ return total_tokens
42
+
43
+ def truncate_text(self, text: str, max_tokens: int) -> str:
44
+ """截断文本以适应token限制"""
45
+ tokens = self.encoding.encode(text)
46
+ if len(tokens) <= max_tokens:
47
+ return text
48
+
49
+ truncated_tokens = tokens[:max_tokens]
50
+ return self.encoding.decode(truncated_tokens)
51
+
52
+ def split_text_by_tokens(self, text: str, chunk_size: int, overlap: int = 0) -> List[str]:
53
+ """按token数量分割文本"""
54
+ tokens = self.encoding.encode(text)
55
+ chunks = []
56
+
57
+ start = 0
58
+ while start < len(tokens):
59
+ end = min(start + chunk_size, len(tokens))
60
+ chunk_tokens = tokens[start:end]
61
+ chunks.append(self.encoding.decode(chunk_tokens))
62
+
63
+ if end >= len(tokens):
64
+ break
65
+
66
+ start = end - overlap
67
+
68
+ return chunks
69
+
70
+ class TextProcessor:
71
+ """文本处理工具"""
72
+
73
+ @staticmethod
74
+ def clean_text(text: str) -> str:
75
+ """清理文本,移除多余的空白字符"""
76
+ # 移除多余的空白字符
77
+ text = re.sub(r'\s+', ' ', text)
78
+ # 移除首尾空白
79
+ text = text.strip()
80
+ return text
81
+
82
+ @staticmethod
83
+ def extract_code_blocks(text: str) -> List[Dict[str, str]]:
84
+ """从文本中提取代码块"""
85
+ code_pattern = r'```(\w+)?\n(.*?)\n```'
86
+ matches = re.findall(code_pattern, text, re.DOTALL)
87
+
88
+ code_blocks = []
89
+ for language, code in matches:
90
+ code_blocks.append({
91
+ "language": language or "text",
92
+ "code": code.strip()
93
+ })
94
+
95
+ return code_blocks
96
+
97
+ @staticmethod
98
+ def extract_json_from_text(text: str) -> Optional[Dict[str, Any]]:
99
+ """从文本中提取JSON"""
100
+ # 尝试直接解析
101
+ try:
102
+ return json.loads(text)
103
+ except json.JSONDecodeError:
104
+ pass
105
+
106
+ # 尝试提取JSON代码块
107
+ json_pattern = r'```json\n(.*?)\n```'
108
+ match = re.search(json_pattern, text, re.DOTALL)
109
+ if match:
110
+ try:
111
+ return json.loads(match.group(1))
112
+ except json.JSONDecodeError:
113
+ pass
114
+
115
+ # 尝试提取花括号内容
116
+ brace_pattern = r'\{.*\}'
117
+ match = re.search(brace_pattern, text, re.DOTALL)
118
+ if match:
119
+ try:
120
+ return json.loads(match.group(0))
121
+ except json.JSONDecodeError:
122
+ pass
123
+
124
+ return None
125
+
126
+ @staticmethod
127
+ def split_into_sentences(text: str) -> List[str]:
128
+ """将文本分割为句子"""
129
+ # 简单的句子分割,基于句号、问号、感叹号
130
+ sentences = re.split(r'[.!?]+', text)
131
+ return [s.strip() for s in sentences if s.strip()]
132
+
133
+ @staticmethod
134
+ def extract_entities(text: str, entity_types: List[str] = None) -> Dict[str, List[str]]:
135
+ """提取文本中的实体(简单版本)"""
136
+ entities = {}
137
+
138
+ if not entity_types:
139
+ entity_types = ["email", "url", "phone", "date"]
140
+
141
+ patterns = {
142
+ "email": r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
143
+ "url": r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+',
144
+ "phone": r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b',
145
+ "date": r'\b\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b'
146
+ }
147
+
148
+ for entity_type in entity_types:
149
+ if entity_type in patterns:
150
+ matches = re.findall(patterns[entity_type], text)
151
+ entities[entity_type] = matches
152
+
153
+ return entities
154
+
155
+ class ResponseParser:
156
+ """响应解析器"""
157
+
158
+ @staticmethod
159
+ def parse_structured_response(response: str, expected_format: str = "json") -> Any:
160
+ """解析结构化响应"""
161
+ if expected_format == "json":
162
+ return TextProcessor.extract_json_from_text(response)
163
+ elif expected_format == "code":
164
+ return TextProcessor.extract_code_blocks(response)
165
+ else:
166
+ return response
167
+
168
+ @staticmethod
169
+ def parse_classification_response(response: str, categories: List[str]) -> Tuple[str, float]:
170
+ """解析分类响应,返回类别和置信度"""
171
+ response_lower = response.lower()
172
+
173
+ # 查找匹配的类别
174
+ for category in categories:
175
+ if category.lower() in response_lower:
176
+ # 尝试提取置信度
177
+ confidence_pattern = r'(\d+(?:\.\d+)?)%|confidence[:\s]*(\d+(?:\.\d+)?)'
178
+ match = re.search(confidence_pattern, response_lower)
179
+ confidence = float(match.group(1) or match.group(2)) / 100 if match else 0.8
180
+ return category, confidence
181
+
182
+ # 如果没有找到匹配的类别,返回最可能的一个
183
+ return categories[0] if categories else "unknown", 0.5
184
+
185
+ @staticmethod
186
+ def parse_sentiment_response(response: str) -> Tuple[str, float]:
187
+ """解析情感分析响应"""
188
+ response_lower = response.lower()
189
+
190
+ # 定义情感关键词
191
+ sentiments = {
192
+ "positive": ["positive", "good", "great", "excellent", "happy", "pleased"],
193
+ "negative": ["negative", "bad", "terrible", "awful", "sad", "disappointed"],
194
+ "neutral": ["neutral", "okay", "average", "mixed", "unclear"]
195
+ }
196
+
197
+ # 查找匹配的情感
198
+ for sentiment, keywords in sentiments.items():
199
+ for keyword in keywords:
200
+ if keyword in response_lower:
201
+ # 尝试提取置信度
202
+ confidence_pattern = r'(\d+(?:\.\d+)?)%|confidence[:\s]*(\d+(?:\.\d+)?)'
203
+ match = re.search(confidence_pattern, response_lower)
204
+ confidence = float(match.group(1) or match.group(2)) / 100 if match else 0.7
205
+ return sentiment, confidence
206
+
207
+ return "neutral", 0.5
208
+
209
+ class LLMMetrics:
210
+ """
211
+ LLM性能指标计算工具
212
+
213
+ 注意:计费和使用跟踪功能已经统一到BaseService中的_track_usage()方法。
214
+ LLM服务应该使用BaseLLMService中的_track_llm_usage()方法来跟踪使用情况。
215
+ """
216
+
217
+ @staticmethod
218
+ def calculate_latency_metrics(start_time: float, end_time: float, token_count: int) -> Dict[str, float]:
219
+ """计算延迟指标"""
220
+ total_time = end_time - start_time
221
+ tokens_per_second = token_count / total_time if total_time > 0 else 0
222
+
223
+ return {
224
+ "total_time": total_time,
225
+ "tokens_per_second": tokens_per_second,
226
+ "ms_per_token": (total_time * 1000) / token_count if token_count > 0 else 0
227
+ }
228
+
229
+
230
+ def validate_model_response(response: Any, expected_type: type = str) -> bool:
231
+ """验证模型响应是否符合预期类型"""
232
+ return isinstance(response, expected_type)
233
+
234
+ def format_chat_history(messages: List[Dict[str, str]], max_history: int = 10) -> List[Dict[str, str]]:
235
+ """格式化聊天历史,保留最近的消息"""
236
+ if len(messages) <= max_history:
237
+ return messages
238
+
239
+ # 保留系统消息和最近的消息
240
+ system_messages = [msg for msg in messages if msg.get("role") == "system"]
241
+ other_messages = [msg for msg in messages if msg.get("role") != "system"]
242
+
243
+ recent_messages = other_messages[-max_history+len(system_messages):]
244
+ return system_messages + recent_messages
245
+
246
+ def extract_function_calls(response: str) -> List[Dict[str, Any]]:
247
+ """从响应中提取函数调用"""
248
+ function_calls = []
249
+
250
+ # 查找JSON格式的函数调用
251
+ json_pattern = r'\{[^{}]*"function_name"[^{}]*\}'
252
+ matches = re.findall(json_pattern, response)
253
+
254
+ for match in matches:
255
+ try:
256
+ call = json.loads(match)
257
+ if "function_name" in call:
258
+ function_calls.append(call)
259
+ except json.JSONDecodeError:
260
+ continue
261
+
262
+ return function_calls
263
+
264
+ def merge_streaming_tokens(tokens: List[str]) -> str:
265
+ """合并流式token为完整文本"""
266
+ return ''.join(tokens)
267
+
268
+ def detect_language(text: str) -> str:
269
+ """检测文本语言(简单版本)"""
270
+ # 简单的语言检测,基于字符模式
271
+ if re.search(r'[\u4e00-\u9fff]', text):
272
+ return "zh"
273
+ elif re.search(r'[а-яё]', text, re.IGNORECASE):
274
+ return "ru"
275
+ elif re.search(r'[ひらがなカタカナ]', text):
276
+ return "ja"
277
+ elif re.search(r'[가-힣]', text):
278
+ return "ko"
279
+ else:
280
+ return "en"