sycommon-python-lib 0.2.5a2__py3-none-any.whl → 0.2.5a3__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.
@@ -484,6 +484,9 @@ class DeepAgent:
484
484
  SYLogger.warning(f"[DeepAgent] Token 兜底统计失败: {te}")
485
485
 
486
486
  event = ChatEventBuilder.done()
487
+ if total_input_tokens > 0 or total_output_tokens > 0:
488
+ event.data["input_tokens"] = total_input_tokens
489
+ event.data["output_tokens"] = total_output_tokens
487
490
  if on_event:
488
491
  await on_event(event)
489
492
  yield event
@@ -0,0 +1,290 @@
1
+ """
2
+ MiniMax2.5 模型推理内容(reasoning_content)测试
3
+
4
+ 测试 MiniMax2.5 模型的流式输出是否正确返回 reasoning_content,
5
+ 以及 deep_agent 是否正确将其包装为 AI_CHUNK 事件。
6
+
7
+ 使用方式:
8
+ cd sycommon-python-lib
9
+ PYTHONPATH=src python -m src.sycommon.tests.test_minimax_reasoning
10
+ """
11
+
12
+ import asyncio
13
+ import json
14
+ from langchain.chat_models import init_chat_model
15
+ from langchain_core.messages import HumanMessage, SystemMessage
16
+
17
+ # MiniMax2.5 模型配置
18
+ MODEL = "MiniMax2.5"
19
+ PROVIDER = "openai"
20
+ BASE_URL = "http://221.130.59.59:8888/8vllm/v1"
21
+ API_KEY = "-"
22
+ MAX_TOKENS = 128000
23
+
24
+
25
+ def create_llm(streaming: bool = False, thinking: bool = True):
26
+ """创建 MiniMax2.5 LLM 实例"""
27
+ extra_body = {
28
+ "top_k": 20,
29
+ "min_p": 0.0,
30
+ "repetition_penalty": 1.0,
31
+ }
32
+
33
+ if thinking:
34
+ extra_body["chat_template_kwargs"] = {"enable_thinking": True}
35
+ temperature = 0.6
36
+ top_p = 0.95
37
+ presence_penalty = 0.0
38
+ else:
39
+ extra_body["chat_template_kwargs"] = {"enable_thinking": False}
40
+ temperature = 0.7
41
+ top_p = 0.8
42
+ presence_penalty = 1.5
43
+
44
+ if streaming:
45
+ extra_body["stream_options"] = {"include_usage": True}
46
+
47
+ return init_chat_model(
48
+ model_provider=PROVIDER,
49
+ model=MODEL,
50
+ base_url=BASE_URL,
51
+ api_key=API_KEY,
52
+ max_tokens=MAX_TOKENS,
53
+ streaming=streaming,
54
+ temperature=temperature,
55
+ top_p=top_p,
56
+ presence_penalty=presence_penalty,
57
+ extra_body=extra_body,
58
+ timeout=60,
59
+ )
60
+
61
+
62
+ async def test_non_stream_basic():
63
+ """测试非流式基本调用"""
64
+ print("\n" + "=" * 60)
65
+ print("测试1: 非流式基本调用(thinking=True)")
66
+ print("=" * 60)
67
+
68
+ llm = create_llm(streaming=False, thinking=True)
69
+ messages = [
70
+ SystemMessage(content="你是一个有用的助手,使用中文回答。"),
71
+ HumanMessage(content="1+1等于几?请简短回答。"),
72
+ ]
73
+
74
+ response = await llm.ainvoke(messages)
75
+
76
+ print(f"类型: {type(response).__name__}")
77
+ print(f"内容: {response.content[:500]}")
78
+
79
+ # 检查 reasoning_content
80
+ reasoning = getattr(response, "reasoning_content", None)
81
+ kwargs_reasoning = getattr(response, "additional_kwargs", {}).get(
82
+ "reasoning_content", None
83
+ )
84
+ print(f"reasoning_content 属性: {repr(reasoning)[:200] if reasoning else 'None'}")
85
+ print(f"additional_kwargs.reasoning_content: {repr(kwargs_reasoning)[:200] if kwargs_reasoning else 'None'}")
86
+
87
+ # 检查 usage
88
+ usage = getattr(response, "usage_metadata", None)
89
+ print(f"usage_metadata: {usage}")
90
+
91
+ assert response.content, "响应内容为空"
92
+ print("[PASS] 非流式调用成功\n")
93
+
94
+
95
+ async def test_non_stream_no_thinking():
96
+ """测试非流式无思考模式"""
97
+ print("=" * 60)
98
+ print("测试2: 非流式调用(thinking=False)")
99
+ print("=" * 60)
100
+
101
+ llm = create_llm(streaming=False, thinking=False)
102
+ messages = [HumanMessage(content="今天星期几?简短回答。")]
103
+
104
+ response = await llm.ainvoke(messages)
105
+
106
+ print(f"内容: {response.content[:500]}")
107
+
108
+ reasoning = getattr(response, "reasoning_content", None)
109
+ kwargs_reasoning = getattr(response, "additional_kwargs", {}).get(
110
+ "reasoning_content", None
111
+ )
112
+ print(f"reasoning_content: {repr(reasoning)[:200] if reasoning else 'None'}")
113
+ print(f"additional_kwargs.reasoning_content: {repr(kwargs_reasoning)[:200] if kwargs_reasoning else 'None'}")
114
+
115
+ print("[PASS] 无思考模式调用成功\n")
116
+
117
+
118
+ async def test_stream_with_reasoning():
119
+ """测试流式输出 + reasoning_content"""
120
+ print("=" * 60)
121
+ print("测试3: 流式调用(thinking=True)- 检查 reasoning_content")
122
+ print("=" * 60)
123
+
124
+ llm = create_llm(streaming=True, thinking=True)
125
+ messages = [
126
+ SystemMessage(content="你是一个有用的助手,使用中文回答。"),
127
+ HumanMessage(content="请思考一下:一个笼子里有鸡和兔,共10个头,26条腿,鸡和兔各几只?"),
128
+ ]
129
+
130
+ reasoning_chunks = []
131
+ content_chunks = []
132
+ chunk_types = {}
133
+
134
+ async for chunk in llm.astream(messages):
135
+ chunk_type = type(chunk).__name__
136
+
137
+ # 统计 chunk 类型
138
+ chunk_types[chunk_type] = chunk_types.get(chunk_type, 0) + 1
139
+
140
+ # 提取 reasoning_content
141
+ reasoning = getattr(chunk, "reasoning_content", None)
142
+ kwargs_reasoning = getattr(chunk, "additional_kwargs", {}).get(
143
+ "reasoning_content", None
144
+ )
145
+
146
+ if reasoning:
147
+ reasoning_chunks.append(reasoning)
148
+ # 只打印前几条
149
+ if len(reasoning_chunks) <= 5:
150
+ print(f" [reasoning] {repr(reasoning[:100])}")
151
+
152
+ if kwargs_reasoning:
153
+ reasoning_chunks.append(kwargs_reasoning)
154
+ if len(reasoning_chunks) <= 5:
155
+ print(f" [kwargs.reasoning] {repr(kwargs_reasoning[:100])}")
156
+
157
+ # 提取内容
158
+ if chunk.content:
159
+ content_chunks.append(chunk.content)
160
+
161
+ print(f"\n--- 流式统计 ---")
162
+ print(f"chunk 类型分布: {chunk_types}")
163
+ print(f"reasoning chunks 数量: {len(reasoning_chunks)}")
164
+ print(f"content chunks 数量: {len(content_chunks)}")
165
+
166
+ if reasoning_chunks:
167
+ full_reasoning = "".join(reasoning_chunks)
168
+ print(f"完整 reasoning (前500字): {full_reasoning[:500]}")
169
+ else:
170
+ print("WARNING: 未收到 reasoning_content")
171
+
172
+ full_content = "".join(content_chunks)
173
+ print(f"完整 content (前500字): {full_content[:500]}")
174
+
175
+ print("[PASS] 流式调用完成\n")
176
+
177
+
178
+ async def test_stream_no_thinking():
179
+ """测试流式输出无思考模式"""
180
+ print("=" * 60)
181
+ print("测试4: 流式调用(thinking=False)")
182
+ print("=" * 60)
183
+
184
+ llm = create_llm(streaming=True, thinking=False)
185
+ messages = [HumanMessage(content="用一句话介绍北京。")]
186
+
187
+ reasoning_chunks = []
188
+ content_chunks = []
189
+
190
+ async for chunk in llm.astream(messages):
191
+ reasoning = getattr(chunk, "reasoning_content", None) or getattr(
192
+ chunk, "additional_kwargs", {}
193
+ ).get("reasoning_content", None)
194
+
195
+ if reasoning:
196
+ reasoning_chunks.append(reasoning)
197
+ if chunk.content:
198
+ content_chunks.append(chunk.content)
199
+
200
+ full_content = "".join(content_chunks)
201
+ print(f"content: {full_content[:500]}")
202
+ print(f"reasoning chunks: {len(reasoning_chunks)} (应为 0)")
203
+
204
+ assert content_chunks, "没有收到内容"
205
+ print("[PASS] 无思考模式流式调用成功\n")
206
+
207
+
208
+ async def test_reasoning_to_chat_event():
209
+ """测试将 reasoning_content 转换为 ChatEvent(模拟 deep_agent 逻辑)"""
210
+ print("=" * 60)
211
+ print("测试5: reasoning_content -> ChatEvent 转换")
212
+ print("=" * 60)
213
+
214
+ from sycommon.agent.chat_events import EventType, ChatEvent
215
+
216
+ llm = create_llm(streaming=True, thinking=True)
217
+ messages = [HumanMessage(content="9.11 和 9.9 哪个大?请仔细思考。")]
218
+
219
+ events = []
220
+
221
+ async for msg in llm.astream(messages):
222
+ msg_type = msg.__class__.__name__
223
+ content = msg.content or ""
224
+
225
+ if msg_type in ("AIMessage", "AIMessageChunk"):
226
+ # 模拟 deep_agent 的 reasoning_content 提取逻辑
227
+ reasoning_content = (
228
+ getattr(msg, "reasoning_content", None)
229
+ or getattr(msg, "additional_kwargs", {}).get(
230
+ "reasoning_content", None
231
+ )
232
+ )
233
+
234
+ if reasoning_content:
235
+ event = ChatEvent(
236
+ EventType.AI_CHUNK,
237
+ {"content": reasoning_content, "reasoning": True},
238
+ )
239
+ events.append(event)
240
+
241
+ if content:
242
+ event = ChatEvent(
243
+ EventType.AI_CHUNK,
244
+ {"content": content, "reasoning": False},
245
+ )
246
+ events.append(event)
247
+
248
+ reasoning_events = [e for e in events if e.data.get("reasoning")]
249
+ content_events = [e for e in events if not e.data.get("reasoning")]
250
+
251
+ print(f"总事件数: {len(events)}")
252
+ print(f"reasoning 事件: {len(reasoning_events)}")
253
+ print(f"content 事件: {len(content_events)}")
254
+
255
+ if reasoning_events:
256
+ sample = reasoning_events[0]
257
+ print(f"reasoning 事件示例: type={sample.type}, data={repr(sample.data)[:200]}")
258
+ if content_events:
259
+ sample = content_events[0]
260
+ print(f"content 事件示例: type={sample.type}, data={repr(sample.data)[:200]}")
261
+
262
+ # 验证事件可以序列化
263
+ if events:
264
+ serialized = events[0].to_dict()
265
+ print(f"序列化示例: {json.dumps(serialized, ensure_ascii=False)[:300]}")
266
+
267
+ assert events, "没有生成任何事件"
268
+ print("[PASS] ChatEvent 转换成功\n")
269
+
270
+
271
+ async def run_all():
272
+ """运行所有测试"""
273
+ print("=" * 60)
274
+ print(f"MiniMax2.5 推理内容测试")
275
+ print(f"模型: {MODEL} | 地址: {BASE_URL}")
276
+ print("=" * 60)
277
+
278
+ await test_non_stream_basic()
279
+ await test_non_stream_no_thinking()
280
+ await test_stream_with_reasoning()
281
+ await test_stream_no_thinking()
282
+ await test_reasoning_to_chat_event()
283
+
284
+ print("=" * 60)
285
+ print("全部测试完成!")
286
+ print("=" * 60)
287
+
288
+
289
+ if __name__ == "__main__":
290
+ asyncio.run(run_all())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.2.5a2
3
+ Version: 0.2.5a3
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -129,7 +129,7 @@ sycommon/services.py,sha256=UtWn36dPmB9g1CTKkNgzbU8M28iOkZd2BtdhvDZLfbk,22214
129
129
  sycommon/agent/__init__.py,sha256=mxceAeUifQ-DKvWp7ZEJIFlmOCb5wpYHPGQw3rwEN8I,4378
130
130
  sycommon/agent/agent_manager.py,sha256=UhhaekEumT7g4v_Z1UB4jTp13X0n8M8erYaQdkGGWkA,13620
131
131
  sycommon/agent/chat_events.py,sha256=bWAMWYIZ2L_yqUcn5jq9ius_lQxLHEv4zQLEqX6UaeM,13190
132
- sycommon/agent/deep_agent.py,sha256=WTGKWx0MxmiIbbrKPTbQl6on2w4LQRWtH0hdiViA-dE,31709
132
+ sycommon/agent/deep_agent.py,sha256=n2LCUF8RDAqwjpY-PP9-L_URkqXuxBSRHO4ec07x108,31905
133
133
  sycommon/agent/multi_agent_team.py,sha256=NHmsUNwe3huguUUzbeoiOjgo9wh4-eXH0AjPDvm1dP4,26781
134
134
  sycommon/agent/summarization_utils.py,sha256=PRCIFtYBrH0bbSxsIc-qpC4iEXJzk72UuR7u5mQTt2w,7360
135
135
  sycommon/agent/mcp/__init__.py,sha256=iKrdDhIrFsNIkqG_kgcwNe-nOiM6uVfolKv44LfQ-FQ,636
@@ -254,6 +254,7 @@ sycommon/synacos/param.py,sha256=KcfSkxnXOa0TGmCjY8hdzU9pzUsA8-4PeyBKWI2-568,176
254
254
  sycommon/tests/deep_agent_server.py,sha256=t7snT2J6KWZrjJsYVi4TU5jpfvXNz4iuzMk4cc-kQGM,16696
255
255
  sycommon/tests/test_deep_agent.py,sha256=34gP2KL8SSmtqqhaA9OV97OAl_I0T4TfEutZrR_0srM,5874
256
256
  sycommon/tests/test_email.py,sha256=-oPtYVGQzJ3Cv-op3ZNRMeyYOF-UNidGJC35CHRgjGQ,5442
257
+ sycommon/tests/test_minimax_reasoning.py,sha256=IKrPEf_Ybxg_vz4AITStkb-GKo5NooaLiLK3RbmW_Cw,9354
257
258
  sycommon/tests/test_mq.py,sha256=Gpr9Eep-osRkcnlwGeshROEf83Ai3qYbAMHwpoML68o,4366
258
259
  sycommon/tests/test_oa_login.py,sha256=5psNnmUst20x-LdjPa_liunhMLGky2uTDVpQzefBnQE,6239
259
260
  sycommon/tests/test_real_summarization.py,sha256=7B89es7-UwULk-kq9xUiWH1ylXUO3QDJm4oZWzJNPk0,6193
@@ -269,8 +270,8 @@ sycommon/tools/syemail.py,sha256=BDFhgf7WDOQeTcjxJEQdu0dQhnHFPO_p3eI0-Ni3LhQ,561
269
270
  sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
270
271
  sycommon/xxljob/__init__.py,sha256=7eoBlQxv-B39IfRSCY2bkqdGYs1QRe1umAWd88VMEEM,86
271
272
  sycommon/xxljob/xxljob_service.py,sha256=JIEJaGXhqrTLcyxlyynSrsHg9bBnDNzX-D4qIWLRPUE,6815
272
- sycommon_python_lib-0.2.5a2.dist-info/METADATA,sha256=Al8QcJtx8mGbEePjfphPJ827JvyX_shWGbxwIKY1taU,7879
273
- sycommon_python_lib-0.2.5a2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
274
- sycommon_python_lib-0.2.5a2.dist-info/entry_points.txt,sha256=gsR4SssKxDWjRU8ggidzNcdMXDPRSKRS7UaGyNP84Qg,92
275
- sycommon_python_lib-0.2.5a2.dist-info/top_level.txt,sha256=RgphKrg7nJyZ7irJqbxFr-5H2LUYTvI7ivoWZH2hcD0,29
276
- sycommon_python_lib-0.2.5a2.dist-info/RECORD,,
273
+ sycommon_python_lib-0.2.5a3.dist-info/METADATA,sha256=qlZaDnNw8273-F4OgL7RIsJHkhow7kRWmpf2mHfN5is,7879
274
+ sycommon_python_lib-0.2.5a3.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
275
+ sycommon_python_lib-0.2.5a3.dist-info/entry_points.txt,sha256=gsR4SssKxDWjRU8ggidzNcdMXDPRSKRS7UaGyNP84Qg,92
276
+ sycommon_python_lib-0.2.5a3.dist-info/top_level.txt,sha256=RgphKrg7nJyZ7irJqbxFr-5H2LUYTvI7ivoWZH2hcD0,29
277
+ sycommon_python_lib-0.2.5a3.dist-info/RECORD,,