sycommon-python-lib 0.1.56b15__py3-none-any.whl → 0.1.56b17__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.
sycommon/llm/get_llm.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import os
2
- from typing import Dict, Type, List, Optional, Callable, Any
2
+ from typing import Dict, Tuple, Type, List, Optional, Callable, Any
3
+ from langfuse import Langfuse, get_client, propagate_attributes
3
4
  from sycommon.config.Config import Config
4
5
  from sycommon.llm.llm_logger import LLMLogger
5
6
  from langchain_core.language_models import BaseChatModel
@@ -12,15 +13,18 @@ from pydantic import BaseModel, ValidationError, Field
12
13
  from sycommon.config.LLMConfig import LLMConfig
13
14
  from sycommon.llm.llm_tokens import TokensCallbackHandler
14
15
  from sycommon.logging.kafka_log import SYLogger
16
+ from sycommon.tools.env import get_env_var
15
17
  from langfuse.langchain import CallbackHandler
16
18
 
17
19
 
18
20
  class StructuredRunnableWithToken(Runnable):
19
21
  """带Token统计的Runnable类"""
20
22
 
21
- def __init__(self, retry_chain: Runnable):
23
+ def __init__(self, retry_chain: Runnable, langfuse: Langfuse):
22
24
  super().__init__()
23
25
  self.retry_chain = retry_chain
26
+ self.langfuse = langfuse
27
+ self.metadata = {"langfuse_session_id": SYLogger.get_trace_id()}
24
28
 
25
29
  def _adapt_input(self, input: Any) -> List[BaseMessage]:
26
30
  """适配输入格式"""
@@ -72,21 +76,28 @@ class StructuredRunnableWithToken(Runnable):
72
76
  # 同步调用
73
77
  def invoke(self, input: Any, config: Optional[RunnableConfig] = None, ** kwargs) -> Dict[str, Any]:
74
78
  try:
75
- processed_config, token_handler = self._get_callback_config(
76
- config)
77
- adapted_input = self._adapt_input(input)
79
+ with self.langfuse.start_as_current_observation(as_type="span", name="invoke") as span:
80
+ with propagate_attributes(session_id=SYLogger.get_trace_id(), user_id=get_env_var('VERSION')):
81
+ processed_config, token_handler = self._get_callback_config(
82
+ config)
83
+ adapted_input = self._adapt_input(input)
78
84
 
79
- structured_result = self.retry_chain.invoke(
80
- {"messages": adapted_input},
81
- config=processed_config,
82
- **kwargs
83
- )
85
+ span.update_trace(
86
+ input={"messages": adapted_input}
87
+ )
84
88
 
85
- # 获取Token统计结果
86
- token_usage = token_handler.usage_metadata
87
- structured_result._token_usage_ = token_usage
89
+ structured_result = self.retry_chain.invoke(
90
+ {"messages": adapted_input},
91
+ config={**processed_config, **self.metadata},
92
+ **kwargs
93
+ )
88
94
 
89
- return structured_result
95
+ span.update_trace(output=structured_result)
96
+
97
+ token_usage = token_handler.usage_metadata
98
+ structured_result._token_usage_ = token_usage
99
+
100
+ return structured_result
90
101
 
91
102
  except Exception as e:
92
103
  SYLogger.error(f"同步LLM调用失败: {str(e)}", exc_info=True)
@@ -95,20 +106,28 @@ class StructuredRunnableWithToken(Runnable):
95
106
  # 异步调用
96
107
  async def ainvoke(self, input: Any, config: Optional[RunnableConfig] = None, ** kwargs) -> Dict[str, Any]:
97
108
  try:
98
- processed_config, token_handler = self._get_callback_config(
99
- config)
100
- adapted_input = self._adapt_input(input)
109
+ with self.langfuse.start_as_current_observation(as_type="span", name="ainvoke") as span:
110
+ with propagate_attributes(session_id=SYLogger.get_trace_id(), user_id=get_env_var('VERSION')):
111
+ processed_config, token_handler = self._get_callback_config(
112
+ config)
113
+ adapted_input = self._adapt_input(input)
114
+
115
+ span.update_trace(
116
+ input={"messages": adapted_input}
117
+ )
118
+
119
+ structured_result = await self.retry_chain.ainvoke(
120
+ {"messages": adapted_input},
121
+ config={**processed_config, **self.metadata},
122
+ **kwargs
123
+ )
101
124
 
102
- structured_result = await self.retry_chain.ainvoke(
103
- {"messages": adapted_input},
104
- config=processed_config,
105
- **kwargs
106
- )
125
+ span.update_trace(output=structured_result)
107
126
 
108
- token_usage = token_handler.usage_metadata
109
- structured_result._token_usage_ = token_usage
127
+ token_usage = token_handler.usage_metadata
128
+ structured_result._token_usage_ = token_usage
110
129
 
111
- return structured_result
130
+ return structured_result
112
131
 
113
132
  except Exception as e:
114
133
  SYLogger.error(f"异步LLM调用失败: {str(e)}", exc_info=True)
@@ -118,9 +137,10 @@ class StructuredRunnableWithToken(Runnable):
118
137
  class LLMWithAutoTokenUsage(BaseChatModel):
119
138
  """自动为结构化调用返回token_usage的LLM包装类"""
120
139
  llm: BaseChatModel = Field(default=None)
140
+ langfuse: Optional[Langfuse] = Field(default=None, exclude=True)
121
141
 
122
- def __init__(self, llm: BaseChatModel, **kwargs):
123
- super().__init__(llm=llm, ** kwargs)
142
+ def __init__(self, llm: BaseChatModel, langfuse: Langfuse, **kwargs):
143
+ super().__init__(llm=llm, langfuse=langfuse, **kwargs)
124
144
 
125
145
  def with_structured_output(
126
146
  self,
@@ -211,7 +231,7 @@ class LLMWithAutoTokenUsage(BaseChatModel):
211
231
  "initial": 0.1, "max": 3.0, "exp_base": 2.0, "jitter": 1.0}
212
232
  )
213
233
 
214
- return StructuredRunnableWithToken(retry_chain)
234
+ return StructuredRunnableWithToken(retry_chain, self.langfuse)
215
235
 
216
236
  # ========== 实现BaseChatModel抽象方法 ==========
217
237
  def _generate(self, messages, stop=None, run_manager=None, ** kwargs):
@@ -222,6 +242,43 @@ class LLMWithAutoTokenUsage(BaseChatModel):
222
242
  return self.llm._llm_type
223
243
 
224
244
 
245
+ def _init_langfuse() -> Tuple[List[CallbackHandler], Optional[Langfuse]]:
246
+ """
247
+ 初始化 Langfuse 组件的辅助函数
248
+ """
249
+ callbacks = []
250
+ langfuse = None
251
+
252
+ # 基础日志回调
253
+ callbacks.append(LLMLogger())
254
+ config_dict = Config().config
255
+
256
+ server_name = config_dict.get('Name', '')
257
+ langfuse_configs = config_dict.get('LangfuseConfig', [])
258
+ environment = config_dict.get('Nacos', {}).get('namespaceId', '')
259
+
260
+ # 查找当前服务对应的 Langfuse 配置
261
+ target_config = next(
262
+ (item for item in langfuse_configs if item.get('name') == server_name), None
263
+ )
264
+
265
+ if target_config and target_config.get('enable', False):
266
+ # 设置环境变量
267
+ os.environ["LANGFUSE_SECRET_KEY"] = target_config.get('secretKey', '')
268
+ os.environ["LANGFUSE_PUBLIC_KEY"] = target_config.get('publicKey', '')
269
+ os.environ["LANGFUSE_BASE_URL"] = target_config.get('baseUrl', '')
270
+ os.environ["LANGFUSE_TRACING_ENVIRONMENT"] = environment
271
+
272
+ # 创建 Langfuse Handler 和 Client
273
+ langfuse_handler = CallbackHandler(
274
+ trace_context={"trace_id": SYLogger.get_trace_id()}
275
+ )
276
+ callbacks.append(langfuse_handler)
277
+ langfuse = get_client()
278
+
279
+ return callbacks, langfuse
280
+
281
+
225
282
  def get_llm(
226
283
  model: str = None,
227
284
  streaming: bool = False
@@ -233,20 +290,7 @@ def get_llm(
233
290
  if not llmConfig:
234
291
  raise Exception(f"无效的模型配置:{model}")
235
292
 
236
- callbacks = [LLMLogger()]
237
-
238
- config = Config().config
239
- server_name = config.get('Name', '')
240
- langfuse_configs = config.get('LangfuseConfig', [])
241
- target_config = next(
242
- (item for item in langfuse_configs if item.get('name') == server_name), None)
243
- if target_config and target_config.get('enable', False):
244
- os.environ["LANGFUSE_SECRET_KEY"] = target_config.get('secretKey', '')
245
- os.environ["LANGFUSE_PUBLIC_KEY"] = target_config.get('publicKey', '')
246
- os.environ["LANGFUSE_BASE_URL"] = target_config.get('baseUrl', '')
247
-
248
- langfuse_handler = CallbackHandler()
249
- callbacks += [langfuse_handler]
293
+ callbacks, langfuse = _init_langfuse()
250
294
 
251
295
  llm = init_chat_model(
252
296
  model_provider=llmConfig.provider,
@@ -261,4 +305,4 @@ def get_llm(
261
305
  if llm is None:
262
306
  raise Exception(f"初始化原始LLM实例失败:{model}")
263
307
 
264
- return LLMWithAutoTokenUsage(llm)
308
+ return LLMWithAutoTokenUsage(llm, langfuse)
@@ -78,6 +78,23 @@ class NacosServiceDiscovery:
78
78
  SYLogger.info(
79
79
  f"nacos:未找到相同版本({version_to_use})的实例,筛选出 {len(no_version_instances)} 个无版本号的实例")
80
80
  return no_version_instances
81
+ else:
82
+ no_version_instances = [
83
+ instance for instance in all_instances
84
+ if 'version' not in instance.get('metadata', {})
85
+ ]
86
+
87
+ if no_version_instances:
88
+ # 从通用实例中轮询
89
+ with self._round_robin_lock:
90
+ selected_index = self._round_robin_index % len(
91
+ no_version_instances)
92
+ self._round_robin_index = (
93
+ selected_index + 1) % len(no_version_instances)
94
+
95
+ SYLogger.info(
96
+ f"nacos:无版本请求,从 {len(no_version_instances)} 个通用实例中选择")
97
+ return [no_version_instances[selected_index]]
81
98
 
82
99
  SYLogger.info(
83
100
  f"nacos:使用轮询方式从 {len(all_instances)} 个实例中选择")
@@ -153,13 +153,6 @@ class Snowflake:
153
153
  # 同一毫秒内
154
154
  sequence = (sequence + 1) & self.MAX_SEQUENCE
155
155
  if sequence == 0:
156
- # 【关键优化】:序列号溢出!
157
- # 此时必须等待下一毫秒。为了不阻塞其他线程,我们:
158
- # 1. 更新 last_timestamp 标记进入新毫秒 (可选,但这里直接 release 更安全)
159
- # 2. 释放锁
160
- # 3. 锁外自旋
161
- # 4. 下一轮循环重新抢锁
162
-
163
156
  self.lock.release() # 手动释放锁
164
157
 
165
158
  # 锁外自旋等待下一毫秒
@@ -251,3 +244,5 @@ if __name__ == "__main__":
251
244
  print(f"是否有重复: {'是 ❌' if unique_count != len(ids) else '否 ✅'}")
252
245
  print(f"总耗时: {duration:.4f} 秒")
253
246
  print(f"吞吐量 (QPS): {len(ids) / duration:,.2f}")
247
+ print("\n最后一个ID解析:")
248
+ print(Snowflake.parse_id(int(ids[-1])))
@@ -1,28 +1,28 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.1.56b15
3
+ Version: 0.1.56b17
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
7
7
  Requires-Dist: aio-pika>=9.5.8
8
- Requires-Dist: aiohttp>=3.13.2
8
+ Requires-Dist: aiohttp>=3.13.3
9
9
  Requires-Dist: aiomysql>=0.3.2
10
10
  Requires-Dist: decorator>=5.2.1
11
- Requires-Dist: fastapi>=0.127.0
11
+ Requires-Dist: fastapi>=0.128.0
12
12
  Requires-Dist: kafka-python>=2.3.0
13
- Requires-Dist: langchain>=1.2.0
14
- Requires-Dist: langchain-core>=1.2.6
15
- Requires-Dist: langchain-openai>=1.1.6
13
+ Requires-Dist: langchain>=1.2.3
14
+ Requires-Dist: langchain-core>=1.2.7
15
+ Requires-Dist: langchain-openai>=1.1.7
16
16
  Requires-Dist: langfuse>=3.11.2
17
- Requires-Dist: langgraph>=1.0.5
17
+ Requires-Dist: langgraph>=1.0.6
18
18
  Requires-Dist: loguru>=0.7.3
19
19
  Requires-Dist: mysql-connector-python>=9.5.0
20
20
  Requires-Dist: nacos-sdk-python<3.0,>=2.0.9
21
- Requires-Dist: psutil>=7.1.3
21
+ Requires-Dist: psutil>=7.2.1
22
22
  Requires-Dist: pydantic>=2.12.5
23
23
  Requires-Dist: python-dotenv>=1.2.1
24
24
  Requires-Dist: pyyaml>=6.0.3
25
- Requires-Dist: sentry-sdk[fastapi]>=2.48.0
25
+ Requires-Dist: sentry-sdk[fastapi]>=2.49.0
26
26
  Requires-Dist: sqlalchemy[asyncio]>=2.0.45
27
27
  Requires-Dist: starlette>=0.50.0
28
28
  Requires-Dist: uvicorn>=0.40.0
@@ -20,7 +20,7 @@ sycommon/health/metrics.py,sha256=fHqO73JuhoZkNPR-xIlxieXiTCvttq-kG-tvxag1s1s,26
20
20
  sycommon/health/ping.py,sha256=FTlnIKk5y1mPfS1ZGOeT5IM_2udF5aqVLubEtuBp18M,250
21
21
  sycommon/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  sycommon/llm/embedding.py,sha256=HknwDqXmRQcAZ8-6d8wZ6n7Bv7HtxTajDt1vvzHGeFQ,8411
23
- sycommon/llm/get_llm.py,sha256=vPwGNm2DaF4-ZzhfRUcDFJY756IK93pvGjAnuhP_Fec,10179
23
+ sycommon/llm/get_llm.py,sha256=0-sqC-2lxmvfBttYvPF7Vc9qIxzCvxzOTeSfc2x4Lw4,12194
24
24
  sycommon/llm/llm_logger.py,sha256=n4UeNy_-g4oHQOsw-VUzF4uo3JVRLtxaMp1FcI8FiEo,5437
25
25
  sycommon/llm/llm_tokens.py,sha256=-udDyFcmyzx6UAwIi6_d_wwI5kMd5w0-WcS2soVPQxg,4309
26
26
  sycommon/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -70,17 +70,17 @@ sycommon/synacos/nacos_client_base.py,sha256=l5jpall6nEt0Hy07Wk-PVU0VN0BmD_Mmtld
70
70
  sycommon/synacos/nacos_config_manager.py,sha256=Cff-4gpp0aD7sQVi-nEvDO4BWqK9abEDDDJ9qXKFQgs,4399
71
71
  sycommon/synacos/nacos_heartbeat_manager.py,sha256=G80_pOn37WdO_HpYUiAfpwMqAxW0ff0Bnw0NEuge9v0,5568
72
72
  sycommon/synacos/nacos_service.py,sha256=BezQ1eDIYwBPE567Po_Qh1Ki_z9WmhZy1J1NiTPbdHY,6118
73
- sycommon/synacos/nacos_service_discovery.py,sha256=vE72BCLBv4fQ1H9CQJ8efC6R2SPMQlAWPPTb8Ae7i2g,6024
73
+ sycommon/synacos/nacos_service_discovery.py,sha256=O0M4facMa3I9YDFaovrfiTnCMtJWTuewbt9ce4a2-CA,6828
74
74
  sycommon/synacos/nacos_service_registration.py,sha256=plg2PmH8CWgmVnPtiIXBxtj-3BpyMdSzKr1wyWRdzh4,10968
75
75
  sycommon/synacos/param.py,sha256=KcfSkxnXOa0TGmCjY8hdzU9pzUsA8-4PeyBKWI2-568,1765
76
76
  sycommon/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
77
  sycommon/tools/docs.py,sha256=OPj2ETheuWjXLyaXtaZPbwmJKfJaYXV5s4XMVAUNrms,1607
78
78
  sycommon/tools/env.py,sha256=Ah-tBwG2C0_hwLGFebVQgKdWWXCjTzBuF23gCkLHYy4,2437
79
79
  sycommon/tools/merge_headers.py,sha256=HV_i52Q-9se3SP8qh7ZGYl8bP7Fxtal4CGVkyMwEdM8,4373
80
- sycommon/tools/snowflake.py,sha256=MUUkBeCm3Tp9SLBU6oNjLsIPAGfn7OmhrR9YvWWhYm8,9569
80
+ sycommon/tools/snowflake.py,sha256=xQlYXwYnI85kSJ1rZ89gMVBhzemP03xrMPVX9vVa3MY,9228
81
81
  sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
82
- sycommon_python_lib-0.1.56b15.dist-info/METADATA,sha256=JGFO7mcs8XAlW3izcvAQEpESd-pK275n-FqUzl1FJfA,7302
83
- sycommon_python_lib-0.1.56b15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
84
- sycommon_python_lib-0.1.56b15.dist-info/entry_points.txt,sha256=q_h2nbvhhmdnsOUZEIwpuoDjaNfBF9XqppDEmQn9d_A,46
85
- sycommon_python_lib-0.1.56b15.dist-info/top_level.txt,sha256=98CJ-cyM2WIKxLz-Pf0AitWLhJyrfXvyY8slwjTXNuc,17
86
- sycommon_python_lib-0.1.56b15.dist-info/RECORD,,
82
+ sycommon_python_lib-0.1.56b17.dist-info/METADATA,sha256=zz5lBYksOaYleWnVs0ZDl8NH1aqGwJ3BS5SNeu4Lv3s,7302
83
+ sycommon_python_lib-0.1.56b17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
84
+ sycommon_python_lib-0.1.56b17.dist-info/entry_points.txt,sha256=q_h2nbvhhmdnsOUZEIwpuoDjaNfBF9XqppDEmQn9d_A,46
85
+ sycommon_python_lib-0.1.56b17.dist-info/top_level.txt,sha256=98CJ-cyM2WIKxLz-Pf0AitWLhJyrfXvyY8slwjTXNuc,17
86
+ sycommon_python_lib-0.1.56b17.dist-info/RECORD,,