sycommon-python-lib 0.1.56b8__tar.gz → 0.1.57b5__tar.gz
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_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/PKG-INFO +10 -9
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/pyproject.toml +10 -9
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/config/Config.py +7 -0
- sycommon_python_lib-0.1.57b5/src/sycommon/config/LangfuseConfig.py +15 -0
- sycommon_python_lib-0.1.57b5/src/sycommon/llm/embedding.py +353 -0
- sycommon_python_lib-0.1.57b5/src/sycommon/llm/get_llm.py +37 -0
- sycommon_python_lib-0.1.57b5/src/sycommon/llm/struct_token.py +192 -0
- sycommon_python_lib-0.1.57b5/src/sycommon/llm/sy_langfuse.py +103 -0
- sycommon_python_lib-0.1.57b5/src/sycommon/llm/usage_token.py +117 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/logging/kafka_log.py +2 -1
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/middleware/traceid.py +19 -20
- sycommon_python_lib-0.1.57b5/src/sycommon/rabbitmq/rabbitmq_client.py +460 -0
- sycommon_python_lib-0.1.57b5/src/sycommon/rabbitmq/rabbitmq_pool.py +398 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/rabbitmq/rabbitmq_service_client_manager.py +52 -53
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +3 -1
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/rabbitmq/rabbitmq_service_core.py +2 -2
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/rabbitmq/rabbitmq_service_producer_manager.py +8 -5
- sycommon_python_lib-0.1.57b5/src/sycommon/sentry/sy_sentry.py +35 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/services.py +118 -96
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/synacos/nacos_client_base.py +3 -1
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/synacos/nacos_config_manager.py +2 -1
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/synacos/nacos_heartbeat_manager.py +3 -1
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/synacos/nacos_service.py +4 -3
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/synacos/nacos_service_discovery.py +21 -2
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/synacos/nacos_service_registration.py +28 -10
- sycommon_python_lib-0.1.57b5/src/sycommon/tools/env.py +62 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/tools/merge_headers.py +20 -0
- sycommon_python_lib-0.1.57b5/src/sycommon/tools/snowflake.py +248 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon_python_lib.egg-info/PKG-INFO +10 -9
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon_python_lib.egg-info/SOURCES.txt +5 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon_python_lib.egg-info/requires.txt +9 -8
- sycommon_python_lib-0.1.56b8/src/sycommon/llm/embedding.py +0 -149
- sycommon_python_lib-0.1.56b8/src/sycommon/llm/get_llm.py +0 -246
- sycommon_python_lib-0.1.56b8/src/sycommon/rabbitmq/rabbitmq_client.py +0 -514
- sycommon_python_lib-0.1.56b8/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -370
- sycommon_python_lib-0.1.56b8/src/sycommon/sentry/sy_sentry.py +0 -34
- sycommon_python_lib-0.1.56b8/src/sycommon/tools/snowflake.py +0 -300
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/README.md +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/setup.cfg +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/command/cli.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/config/DatabaseConfig.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/config/EmbeddingConfig.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/config/LLMConfig.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/config/MQConfig.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/config/RerankerConfig.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/config/SentryConfig.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/config/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/database/async_base_db_service.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/database/async_database_service.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/database/base_db_service.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/database/database_service.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/health/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/health/health_check.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/health/metrics.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/health/ping.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/llm/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/llm/llm_logger.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/llm/llm_tokens.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/logging/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/logging/async_sql_logger.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/logging/logger_levels.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/logging/logger_wrapper.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/logging/sql_logger.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/middleware/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/middleware/context.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/middleware/cors.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/middleware/docs.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/middleware/exception.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/middleware/middleware.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/middleware/monitor_memory.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/middleware/mq.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/middleware/timeout.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/models/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/models/base_http.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/models/log.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/models/mqlistener_config.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/models/mqmsg_model.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/models/mqsend_config.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/models/sso_user.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/notice/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/notice/uvicorn_monitor.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/rabbitmq/rabbitmq_service.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/sentry/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/sse/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/sse/event.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/sse/sse.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/synacos/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/synacos/example.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/synacos/example2.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/synacos/feign.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/synacos/feign_client.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/synacos/param.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/tools/__init__.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/tools/docs.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon/tools/timing.py +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.56b8 → sycommon_python_lib-0.1.57b5}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sycommon-python-lib
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.57b5
|
|
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.
|
|
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.
|
|
11
|
+
Requires-Dist: fastapi>=0.128.0
|
|
12
12
|
Requires-Dist: kafka-python>=2.3.0
|
|
13
|
-
Requires-Dist: langchain>=1.2.
|
|
14
|
-
Requires-Dist: langchain-core>=1.2.
|
|
15
|
-
Requires-Dist: langchain-openai>=1.1.
|
|
16
|
-
Requires-Dist:
|
|
13
|
+
Requires-Dist: langchain>=1.2.3
|
|
14
|
+
Requires-Dist: langchain-core>=1.2.7
|
|
15
|
+
Requires-Dist: langchain-openai>=1.1.7
|
|
16
|
+
Requires-Dist: langfuse>=3.11.2
|
|
17
|
+
Requires-Dist: langgraph>=1.0.6
|
|
17
18
|
Requires-Dist: loguru>=0.7.3
|
|
18
19
|
Requires-Dist: mysql-connector-python>=9.5.0
|
|
19
20
|
Requires-Dist: nacos-sdk-python<3.0,>=2.0.9
|
|
20
|
-
Requires-Dist: psutil>=7.1
|
|
21
|
+
Requires-Dist: psutil>=7.2.1
|
|
21
22
|
Requires-Dist: pydantic>=2.12.5
|
|
22
23
|
Requires-Dist: python-dotenv>=1.2.1
|
|
23
24
|
Requires-Dist: pyyaml>=6.0.3
|
|
24
|
-
Requires-Dist: sentry-sdk[fastapi]>=2.
|
|
25
|
+
Requires-Dist: sentry-sdk[fastapi]>=2.49.0
|
|
25
26
|
Requires-Dist: sqlalchemy[asyncio]>=2.0.45
|
|
26
27
|
Requires-Dist: starlette>=0.50.0
|
|
27
28
|
Requires-Dist: uvicorn>=0.40.0
|
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sycommon-python-lib"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.57b5"
|
|
4
4
|
description = "Add your description here"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
7
|
dependencies = [
|
|
8
8
|
"aio-pika>=9.5.8",
|
|
9
|
-
"aiohttp>=3.13.
|
|
9
|
+
"aiohttp>=3.13.3",
|
|
10
10
|
"aiomysql>=0.3.2",
|
|
11
11
|
"decorator>=5.2.1",
|
|
12
|
-
"fastapi>=0.
|
|
12
|
+
"fastapi>=0.128.0",
|
|
13
13
|
"kafka-python>=2.3.0",
|
|
14
|
-
"langchain>=1.2.
|
|
15
|
-
"langchain-core>=1.2.
|
|
16
|
-
"langchain-openai>=1.1.
|
|
17
|
-
"
|
|
14
|
+
"langchain>=1.2.3",
|
|
15
|
+
"langchain-core>=1.2.7",
|
|
16
|
+
"langchain-openai>=1.1.7",
|
|
17
|
+
"langfuse>=3.11.2",
|
|
18
|
+
"langgraph>=1.0.6",
|
|
18
19
|
"loguru>=0.7.3",
|
|
19
20
|
"mysql-connector-python>=9.5.0",
|
|
20
21
|
"nacos-sdk-python>=2.0.9,<3.0",
|
|
21
|
-
"psutil>=7.1
|
|
22
|
+
"psutil>=7.2.1",
|
|
22
23
|
"pydantic>=2.12.5",
|
|
23
24
|
"python-dotenv>=1.2.1",
|
|
24
25
|
"pyyaml>=6.0.3",
|
|
25
|
-
"sentry-sdk[fastapi]>=2.
|
|
26
|
+
"sentry-sdk[fastapi]>=2.49.0",
|
|
26
27
|
"sqlalchemy[asyncio]>=2.0.45",
|
|
27
28
|
"starlette>=0.50.0",
|
|
28
29
|
"uvicorn>=0.40.0",
|
|
@@ -21,6 +21,7 @@ class Config(metaclass=SingletonMeta):
|
|
|
21
21
|
self.embedding_configs = []
|
|
22
22
|
self.reranker_configs = []
|
|
23
23
|
self.sentry_configs = []
|
|
24
|
+
self.langfuse_configs = []
|
|
24
25
|
self._process_config()
|
|
25
26
|
|
|
26
27
|
def get_llm_config(self, model_name):
|
|
@@ -47,6 +48,12 @@ class Config(metaclass=SingletonMeta):
|
|
|
47
48
|
return sentry
|
|
48
49
|
raise ValueError(f"No configuration found for server: {name}")
|
|
49
50
|
|
|
51
|
+
def get_langfuse_config(self, name):
|
|
52
|
+
for langfuse in self.langfuse_configs:
|
|
53
|
+
if langfuse.get('name') == name:
|
|
54
|
+
return langfuse
|
|
55
|
+
raise ValueError(f"No configuration found for server: {name}")
|
|
56
|
+
|
|
50
57
|
def _process_config(self):
|
|
51
58
|
llm_config_list = self.config.get('LLMConfig', [])
|
|
52
59
|
for llm_config in llm_config_list:
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class LangfuseConfig(BaseModel):
|
|
5
|
+
name: str
|
|
6
|
+
secretKey: str
|
|
7
|
+
publicKey: str
|
|
8
|
+
baseUrl: str
|
|
9
|
+
enable: bool
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def from_config(cls, server_name: str):
|
|
13
|
+
from sycommon.config.Config import Config
|
|
14
|
+
langfuse_config = Config().get_langfuse_config(server_name)
|
|
15
|
+
return cls(**langfuse_config)
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import aiohttp
|
|
3
|
+
import atexit
|
|
4
|
+
from typing import Union, List, Optional, Dict
|
|
5
|
+
from sycommon.config.Config import SingletonMeta
|
|
6
|
+
from sycommon.config.EmbeddingConfig import EmbeddingConfig
|
|
7
|
+
from sycommon.config.RerankerConfig import RerankerConfig
|
|
8
|
+
from sycommon.logging.kafka_log import SYLogger
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Embedding(metaclass=SingletonMeta):
|
|
12
|
+
def __init__(self):
|
|
13
|
+
# 1. 并发限制
|
|
14
|
+
self.max_concurrency = 20
|
|
15
|
+
# 保留默认模型名称
|
|
16
|
+
self.default_embedding_model = "bge-large-zh-v1.5"
|
|
17
|
+
self.default_reranker_model = "bge-reranker-large"
|
|
18
|
+
|
|
19
|
+
# 初始化默认模型的基础URL
|
|
20
|
+
self.embeddings_base_url = EmbeddingConfig.from_config(
|
|
21
|
+
self.default_embedding_model).baseUrl
|
|
22
|
+
self.reranker_base_url = RerankerConfig.from_config(
|
|
23
|
+
self.default_reranker_model).baseUrl
|
|
24
|
+
|
|
25
|
+
# [修复] 缓存配置URL,避免高并发下重复读取配置文件
|
|
26
|
+
self._embedding_url_cache: Dict[str, str] = {
|
|
27
|
+
self.default_embedding_model: self.embeddings_base_url
|
|
28
|
+
}
|
|
29
|
+
self._reranker_url_cache: Dict[str, str] = {
|
|
30
|
+
self.default_reranker_model: self.reranker_base_url
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# 并发信号量
|
|
34
|
+
self.semaphore = asyncio.Semaphore(self.max_concurrency)
|
|
35
|
+
self.default_timeout = aiohttp.ClientTimeout(total=None)
|
|
36
|
+
|
|
37
|
+
# 核心优化:创建全局可复用的ClientSession(连接池复用)
|
|
38
|
+
self.session = None
|
|
39
|
+
|
|
40
|
+
# [修复] 注册退出钩子,确保程序结束时关闭连接池
|
|
41
|
+
atexit.register(self._sync_close_session)
|
|
42
|
+
|
|
43
|
+
async def init_session(self):
|
|
44
|
+
"""初始化全局ClientSession(仅创建一次)"""
|
|
45
|
+
if self.session is None or self.session.closed:
|
|
46
|
+
# 配置连接池参数,适配高并发
|
|
47
|
+
connector = aiohttp.TCPConnector(
|
|
48
|
+
limit=self.max_concurrency, # 连接池最大连接数
|
|
49
|
+
limit_per_host=self.max_concurrency, # 每个域名的最大连接数
|
|
50
|
+
ttl_dns_cache=300, # DNS缓存时间
|
|
51
|
+
enable_cleanup_closed=True # 自动清理关闭的连接
|
|
52
|
+
)
|
|
53
|
+
self.session = aiohttp.ClientSession(
|
|
54
|
+
connector=connector,
|
|
55
|
+
timeout=self.default_timeout
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
async def close_session(self):
|
|
59
|
+
"""关闭全局Session(程序退出时调用)"""
|
|
60
|
+
if self.session and not self.session.closed:
|
|
61
|
+
await self.session.close()
|
|
62
|
+
|
|
63
|
+
def _sync_close_session(self):
|
|
64
|
+
"""同步关闭Session的封装,供atexit调用"""
|
|
65
|
+
try:
|
|
66
|
+
loop = asyncio.get_event_loop()
|
|
67
|
+
if loop.is_running():
|
|
68
|
+
# [修复] 修正缩进,确保 create_task 的异常能被捕获
|
|
69
|
+
try:
|
|
70
|
+
loop.create_task(self.close_session())
|
|
71
|
+
except Exception:
|
|
72
|
+
pass
|
|
73
|
+
else:
|
|
74
|
+
try:
|
|
75
|
+
loop.run_until_complete(self.close_session())
|
|
76
|
+
except Exception:
|
|
77
|
+
pass
|
|
78
|
+
except Exception:
|
|
79
|
+
# 捕获获取 loop 时的异常
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
def _get_embedding_url(self, model: str) -> str:
|
|
83
|
+
"""获取Embedding URL(带缓存)"""
|
|
84
|
+
if model not in self._embedding_url_cache:
|
|
85
|
+
self._embedding_url_cache[model] = EmbeddingConfig.from_config(
|
|
86
|
+
model).baseUrl
|
|
87
|
+
return self._embedding_url_cache[model]
|
|
88
|
+
|
|
89
|
+
def _get_reranker_url(self, model: str) -> str:
|
|
90
|
+
"""获取Reranker URL(带缓存)"""
|
|
91
|
+
if model not in self._reranker_url_cache:
|
|
92
|
+
self._reranker_url_cache[model] = RerankerConfig.from_config(
|
|
93
|
+
model).baseUrl
|
|
94
|
+
return self._reranker_url_cache[model]
|
|
95
|
+
|
|
96
|
+
async def _get_embeddings_http_core(
|
|
97
|
+
self,
|
|
98
|
+
input: Union[str, List[str]],
|
|
99
|
+
encoding_format: str = None,
|
|
100
|
+
model: str = None,
|
|
101
|
+
timeout: aiohttp.ClientTimeout = None,
|
|
102
|
+
**kwargs
|
|
103
|
+
):
|
|
104
|
+
"""embedding请求核心逻辑"""
|
|
105
|
+
await self.init_session() # 确保Session已初始化
|
|
106
|
+
async with self.semaphore:
|
|
107
|
+
request_timeout = timeout or self.default_timeout
|
|
108
|
+
target_model = model or self.default_embedding_model
|
|
109
|
+
|
|
110
|
+
# [修复] 使用缓存获取URL
|
|
111
|
+
target_base_url = self._get_embedding_url(target_model)
|
|
112
|
+
url = f"{target_base_url}/v1/embeddings"
|
|
113
|
+
|
|
114
|
+
request_body = {
|
|
115
|
+
"model": target_model,
|
|
116
|
+
"input": input,
|
|
117
|
+
"encoding_format": encoding_format or "float"
|
|
118
|
+
}
|
|
119
|
+
request_body.update(kwargs)
|
|
120
|
+
|
|
121
|
+
# 复用全局Session
|
|
122
|
+
try:
|
|
123
|
+
async with self.session.post(
|
|
124
|
+
url,
|
|
125
|
+
json=request_body,
|
|
126
|
+
timeout=request_timeout
|
|
127
|
+
) as response:
|
|
128
|
+
if response.status != 200:
|
|
129
|
+
error_detail = await response.text()
|
|
130
|
+
# [日志] 记录详细的HTTP错误响应
|
|
131
|
+
SYLogger.error(
|
|
132
|
+
f"Embedding request HTTP Error. Status: {response.status}, "
|
|
133
|
+
f"Model: {target_model}, URL: {url}. Detail: {error_detail}"
|
|
134
|
+
)
|
|
135
|
+
return None
|
|
136
|
+
return await response.json()
|
|
137
|
+
except (aiohttp.ClientConnectionResetError, asyncio.TimeoutError, aiohttp.ClientError) as e:
|
|
138
|
+
# [日志] 记录网络错误
|
|
139
|
+
SYLogger.error(
|
|
140
|
+
f"Embedding request Network Error. Model: {target_model}, URL: {url}. "
|
|
141
|
+
f"Error: {e.__class__.__name__} - {str(e)}"
|
|
142
|
+
)
|
|
143
|
+
return None
|
|
144
|
+
except Exception as e:
|
|
145
|
+
# 记录其他未预期的异常
|
|
146
|
+
SYLogger.error(
|
|
147
|
+
f"Unexpected error in _get_embeddings_http_core: {str(e)}", exc_info=True)
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
async def _get_embeddings_http_async(
|
|
151
|
+
self,
|
|
152
|
+
input: Union[str, List[str]],
|
|
153
|
+
encoding_format: str = None,
|
|
154
|
+
model: str = None,
|
|
155
|
+
timeout: aiohttp.ClientTimeout = None, ** kwargs
|
|
156
|
+
):
|
|
157
|
+
"""对外暴露的embedding请求方法"""
|
|
158
|
+
return await self._get_embeddings_http_core(
|
|
159
|
+
input, encoding_format, model, timeout, ** kwargs
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
async def _get_reranker_http_core(
|
|
163
|
+
self,
|
|
164
|
+
documents: List[str],
|
|
165
|
+
query: str,
|
|
166
|
+
top_n: Optional[int] = None,
|
|
167
|
+
model: str = None,
|
|
168
|
+
max_chunks_per_doc: Optional[int] = None,
|
|
169
|
+
return_documents: Optional[bool] = True,
|
|
170
|
+
return_len: Optional[bool] = True,
|
|
171
|
+
timeout: aiohttp.ClientTimeout = None, ** kwargs
|
|
172
|
+
):
|
|
173
|
+
"""reranker请求核心逻辑"""
|
|
174
|
+
await self.init_session() # 确保Session已初始化
|
|
175
|
+
async with self.semaphore:
|
|
176
|
+
request_timeout = timeout or self.default_timeout
|
|
177
|
+
target_model = model or self.default_reranker_model
|
|
178
|
+
|
|
179
|
+
# [修复] 使用缓存获取URL
|
|
180
|
+
target_base_url = self._get_reranker_url(target_model)
|
|
181
|
+
url = f"{target_base_url}/v1/rerank"
|
|
182
|
+
|
|
183
|
+
request_body = {
|
|
184
|
+
"model": target_model,
|
|
185
|
+
"documents": documents,
|
|
186
|
+
"query": query,
|
|
187
|
+
"top_n": top_n or len(documents),
|
|
188
|
+
"max_chunks_per_doc": max_chunks_per_doc,
|
|
189
|
+
"return_documents": return_documents,
|
|
190
|
+
"return_len": return_len,
|
|
191
|
+
}
|
|
192
|
+
request_body.update(kwargs)
|
|
193
|
+
|
|
194
|
+
# 复用全局Session
|
|
195
|
+
try:
|
|
196
|
+
async with self.session.post(
|
|
197
|
+
url,
|
|
198
|
+
json=request_body,
|
|
199
|
+
timeout=request_timeout
|
|
200
|
+
) as response:
|
|
201
|
+
if response.status != 200:
|
|
202
|
+
error_detail = await response.text()
|
|
203
|
+
# [日志] 记录详细的HTTP错误响应
|
|
204
|
+
SYLogger.error(
|
|
205
|
+
f"Reranker request HTTP Error. Status: {response.status}, "
|
|
206
|
+
f"Model: {target_model}, URL: {url}. Detail: {error_detail}"
|
|
207
|
+
)
|
|
208
|
+
return None
|
|
209
|
+
return await response.json()
|
|
210
|
+
except (aiohttp.ClientConnectionResetError, asyncio.TimeoutError, aiohttp.ClientError) as e:
|
|
211
|
+
# [日志] 记录网络错误
|
|
212
|
+
SYLogger.error(
|
|
213
|
+
f"Reranker request Network Error. Model: {target_model}, URL: {url}. "
|
|
214
|
+
f"Error: {e.__class__.__name__} - {str(e)}"
|
|
215
|
+
)
|
|
216
|
+
return None
|
|
217
|
+
except Exception as e:
|
|
218
|
+
# 记录其他未预期的异常
|
|
219
|
+
SYLogger.error(
|
|
220
|
+
f"Unexpected error in _get_reranker_http_core: {str(e)}", exc_info=True)
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
async def _get_reranker_http_async(
|
|
224
|
+
self,
|
|
225
|
+
documents: List[str],
|
|
226
|
+
query: str,
|
|
227
|
+
top_n: Optional[int] = None,
|
|
228
|
+
model: str = None,
|
|
229
|
+
max_chunks_per_doc: Optional[int] = None,
|
|
230
|
+
return_documents: Optional[bool] = True,
|
|
231
|
+
return_len: Optional[bool] = True,
|
|
232
|
+
timeout: aiohttp.ClientTimeout = None, ** kwargs
|
|
233
|
+
):
|
|
234
|
+
"""对外暴露的reranker请求方法"""
|
|
235
|
+
return await self._get_reranker_http_core(
|
|
236
|
+
documents, query, top_n, model, max_chunks_per_doc,
|
|
237
|
+
return_documents, return_len, timeout, **kwargs
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
def _get_dimension(self, model: str) -> int:
|
|
241
|
+
"""获取模型维度,用于生成兜底零向量"""
|
|
242
|
+
try:
|
|
243
|
+
config = EmbeddingConfig.from_config(model)
|
|
244
|
+
if hasattr(config, 'dimension'):
|
|
245
|
+
return int(config.dimension)
|
|
246
|
+
except Exception:
|
|
247
|
+
pass
|
|
248
|
+
# 默认兜底 1024
|
|
249
|
+
return 1024
|
|
250
|
+
|
|
251
|
+
async def get_embeddings(
|
|
252
|
+
self,
|
|
253
|
+
corpus: List[str],
|
|
254
|
+
model: str = None,
|
|
255
|
+
timeout: Optional[Union[int, float]] = None
|
|
256
|
+
):
|
|
257
|
+
"""
|
|
258
|
+
获取语料库的嵌入向量,结果顺序与输入语料库顺序一致
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
corpus: 待生成嵌入向量的文本列表
|
|
262
|
+
model: 可选,指定使用的embedding模型名称,默认使用bge-large-zh-v1.5
|
|
263
|
+
timeout: 可选,超时时间(秒):
|
|
264
|
+
- 传int/float:表示总超时时间(秒)
|
|
265
|
+
- 不传/None:使用默认永不超时配置
|
|
266
|
+
"""
|
|
267
|
+
request_timeout = None
|
|
268
|
+
if timeout is not None:
|
|
269
|
+
if isinstance(timeout, (int, float)):
|
|
270
|
+
request_timeout = aiohttp.ClientTimeout(total=timeout)
|
|
271
|
+
else:
|
|
272
|
+
SYLogger.warning(
|
|
273
|
+
f"Invalid timeout type: {type(timeout)}, must be int/float, use default timeout")
|
|
274
|
+
|
|
275
|
+
actual_model = model or self.default_embedding_model
|
|
276
|
+
|
|
277
|
+
SYLogger.info(
|
|
278
|
+
f"Requesting embeddings for corpus: {len(corpus)} items (model: {actual_model}, max_concurrency: {self.max_concurrency}, timeout: {timeout or 'None'})")
|
|
279
|
+
|
|
280
|
+
all_vectors = []
|
|
281
|
+
|
|
282
|
+
# [修复] 增加 Chunk 处理逻辑,防止 corpus 过大导致内存溢出或协程过多
|
|
283
|
+
# 每次最多处理 max_concurrency * 2 个请求,避免一次性创建几十万个协程
|
|
284
|
+
batch_size = self.max_concurrency * 2
|
|
285
|
+
|
|
286
|
+
for i in range(0, len(corpus), batch_size):
|
|
287
|
+
batch_texts = corpus[i: i + batch_size]
|
|
288
|
+
|
|
289
|
+
# 给每个异步任务传入模型名称和超时配置
|
|
290
|
+
tasks = [self._get_embeddings_http_async(
|
|
291
|
+
text, model=model, timeout=request_timeout) for text in batch_texts]
|
|
292
|
+
results = await asyncio.gather(*tasks)
|
|
293
|
+
|
|
294
|
+
for result in results:
|
|
295
|
+
if result is None:
|
|
296
|
+
dim = self._get_dimension(actual_model)
|
|
297
|
+
|
|
298
|
+
zero_vector = [0.0] * dim
|
|
299
|
+
all_vectors.append(zero_vector)
|
|
300
|
+
# [日志] 补充日志,明确是补零操作
|
|
301
|
+
SYLogger.warning(
|
|
302
|
+
f"Embedding request failed (returned None), appending zero vector ({dim}D) for model {actual_model}")
|
|
303
|
+
continue
|
|
304
|
+
|
|
305
|
+
# 从返回结果中提取向量
|
|
306
|
+
try:
|
|
307
|
+
for item in result["data"]:
|
|
308
|
+
embedding = item["embedding"]
|
|
309
|
+
all_vectors.append(embedding)
|
|
310
|
+
except (KeyError, TypeError) as e:
|
|
311
|
+
SYLogger.error(f"Failed to parse embedding result: {e}")
|
|
312
|
+
dim = self._get_dimension(actual_model)
|
|
313
|
+
all_vectors.append([0.0] * dim)
|
|
314
|
+
|
|
315
|
+
SYLogger.info(
|
|
316
|
+
f"Embeddings for corpus created: {len(all_vectors)} vectors (model: {actual_model})")
|
|
317
|
+
return all_vectors
|
|
318
|
+
|
|
319
|
+
async def get_reranker(
|
|
320
|
+
self,
|
|
321
|
+
top_results: List[str],
|
|
322
|
+
query: str,
|
|
323
|
+
model: str = None,
|
|
324
|
+
timeout: Optional[Union[int, float]] = None
|
|
325
|
+
):
|
|
326
|
+
"""
|
|
327
|
+
对搜索结果进行重排序
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
top_results: 待重排序的文本列表
|
|
331
|
+
query: 排序参考的查询语句
|
|
332
|
+
model: 可选,指定使用的reranker模型名称,默认使用bge-reranker-large
|
|
333
|
+
timeout: 可选,超时时间(秒):
|
|
334
|
+
- 传int/float:表示总超时时间(秒)
|
|
335
|
+
- 不传/None:使用默认永不超时配置
|
|
336
|
+
"""
|
|
337
|
+
request_timeout = None
|
|
338
|
+
if timeout is not None:
|
|
339
|
+
if isinstance(timeout, (int, float)):
|
|
340
|
+
request_timeout = aiohttp.ClientTimeout(total=timeout)
|
|
341
|
+
else:
|
|
342
|
+
SYLogger.warning(
|
|
343
|
+
f"Invalid timeout type: {type(timeout)}, must be int/float, use default timeout")
|
|
344
|
+
|
|
345
|
+
actual_model = model or self.default_reranker_model
|
|
346
|
+
SYLogger.info(
|
|
347
|
+
f"Requesting reranker for top_results: {top_results} (model: {actual_model}, max_concurrency: {self.max_concurrency}, timeout: {timeout or 'None'})")
|
|
348
|
+
|
|
349
|
+
data = await self._get_reranker_http_async(
|
|
350
|
+
top_results, query, model=model, timeout=request_timeout)
|
|
351
|
+
SYLogger.info(
|
|
352
|
+
f"Reranker for top_results completed (model: {actual_model})")
|
|
353
|
+
return data
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from sycommon.llm.llm_logger import LLMLogger
|
|
2
|
+
from langchain.chat_models import init_chat_model
|
|
3
|
+
from sycommon.config.LLMConfig import LLMConfig
|
|
4
|
+
from sycommon.llm.sy_langfuse import LangfuseInitializer
|
|
5
|
+
from sycommon.llm.usage_token import LLMWithAutoTokenUsage
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_llm(
|
|
9
|
+
model: str = None,
|
|
10
|
+
streaming: bool = False
|
|
11
|
+
) -> LLMWithAutoTokenUsage:
|
|
12
|
+
if not model:
|
|
13
|
+
model = "Qwen2.5-72B"
|
|
14
|
+
|
|
15
|
+
llmConfig = LLMConfig.from_config(model)
|
|
16
|
+
if not llmConfig:
|
|
17
|
+
raise Exception(f"无效的模型配置:{model}")
|
|
18
|
+
|
|
19
|
+
# 初始化Langfuse
|
|
20
|
+
langfuse_callbacks, langfuse = LangfuseInitializer.get()
|
|
21
|
+
|
|
22
|
+
callbacks = [LLMLogger()] + langfuse_callbacks
|
|
23
|
+
|
|
24
|
+
llm = init_chat_model(
|
|
25
|
+
model_provider=llmConfig.provider,
|
|
26
|
+
model=llmConfig.model,
|
|
27
|
+
base_url=llmConfig.baseUrl,
|
|
28
|
+
api_key="-",
|
|
29
|
+
temperature=0.1,
|
|
30
|
+
streaming=streaming,
|
|
31
|
+
callbacks=callbacks
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
if llm is None:
|
|
35
|
+
raise Exception(f"初始化原始LLM实例失败:{model}")
|
|
36
|
+
|
|
37
|
+
return LLMWithAutoTokenUsage(llm, langfuse)
|