sycommon-python-lib 0.1.56b15__py3-none-any.whl → 0.1.56b16__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 +87 -43
- sycommon/tools/snowflake.py +2 -7
- {sycommon_python_lib-0.1.56b15.dist-info → sycommon_python_lib-0.1.56b16.dist-info}/METADATA +1 -1
- {sycommon_python_lib-0.1.56b15.dist-info → sycommon_python_lib-0.1.56b16.dist-info}/RECORD +7 -7
- {sycommon_python_lib-0.1.56b15.dist-info → sycommon_python_lib-0.1.56b16.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.56b15.dist-info → sycommon_python_lib-0.1.56b16.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.56b15.dist-info → sycommon_python_lib-0.1.56b16.dist-info}/top_level.txt +0 -0
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
**kwargs
|
|
83
|
-
)
|
|
85
|
+
span.update_trace(
|
|
86
|
+
input={"messages": adapted_input}
|
|
87
|
+
)
|
|
84
88
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
structured_result = self.retry_chain.invoke(
|
|
90
|
+
{"messages": adapted_input},
|
|
91
|
+
config={**processed_config, **self.metadata},
|
|
92
|
+
**kwargs
|
|
93
|
+
)
|
|
88
94
|
|
|
89
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
103
|
-
{"messages": adapted_input},
|
|
104
|
-
config=processed_config,
|
|
105
|
-
**kwargs
|
|
106
|
-
)
|
|
125
|
+
span.update_trace(output=structured_result)
|
|
107
126
|
|
|
108
|
-
|
|
109
|
-
|
|
127
|
+
token_usage = token_handler.usage_metadata
|
|
128
|
+
structured_result._token_usage_ = token_usage
|
|
110
129
|
|
|
111
|
-
|
|
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, **
|
|
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 =
|
|
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)
|
sycommon/tools/snowflake.py
CHANGED
|
@@ -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])))
|
|
@@ -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=
|
|
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
|
|
@@ -77,10 +77,10 @@ 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=
|
|
80
|
+
sycommon/tools/snowflake.py,sha256=xQlYXwYnI85kSJ1rZ89gMVBhzemP03xrMPVX9vVa3MY,9228
|
|
81
81
|
sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
|
|
82
|
-
sycommon_python_lib-0.1.
|
|
83
|
-
sycommon_python_lib-0.1.
|
|
84
|
-
sycommon_python_lib-0.1.
|
|
85
|
-
sycommon_python_lib-0.1.
|
|
86
|
-
sycommon_python_lib-0.1.
|
|
82
|
+
sycommon_python_lib-0.1.56b16.dist-info/METADATA,sha256=V5zIsx7nwAUlwFSjQXfBa7e5jZwwsjkkXC0IjdgIhQ4,7302
|
|
83
|
+
sycommon_python_lib-0.1.56b16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
84
|
+
sycommon_python_lib-0.1.56b16.dist-info/entry_points.txt,sha256=q_h2nbvhhmdnsOUZEIwpuoDjaNfBF9XqppDEmQn9d_A,46
|
|
85
|
+
sycommon_python_lib-0.1.56b16.dist-info/top_level.txt,sha256=98CJ-cyM2WIKxLz-Pf0AitWLhJyrfXvyY8slwjTXNuc,17
|
|
86
|
+
sycommon_python_lib-0.1.56b16.dist-info/RECORD,,
|
|
File without changes
|
{sycommon_python_lib-0.1.56b15.dist-info → sycommon_python_lib-0.1.56b16.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.56b15.dist-info → sycommon_python_lib-0.1.56b16.dist-info}/top_level.txt
RENAMED
|
File without changes
|