sycommon-python-lib 0.1.57b2__tar.gz → 0.1.57b3__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.
Files changed (95) hide show
  1. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/PKG-INFO +1 -1
  2. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/pyproject.toml +1 -1
  3. sycommon_python_lib-0.1.57b3/src/sycommon/llm/embedding.py +368 -0
  4. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon_python_lib.egg-info/PKG-INFO +1 -1
  5. sycommon_python_lib-0.1.57b2/src/sycommon/llm/embedding.py +0 -204
  6. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/README.md +0 -0
  7. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/setup.cfg +0 -0
  8. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/command/cli.py +0 -0
  9. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/__init__.py +0 -0
  10. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/config/Config.py +0 -0
  11. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/config/DatabaseConfig.py +0 -0
  12. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/config/EmbeddingConfig.py +0 -0
  13. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/config/LLMConfig.py +0 -0
  14. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/config/LangfuseConfig.py +0 -0
  15. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/config/MQConfig.py +0 -0
  16. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/config/RerankerConfig.py +0 -0
  17. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/config/SentryConfig.py +0 -0
  18. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/config/__init__.py +0 -0
  19. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/database/async_base_db_service.py +0 -0
  20. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/database/async_database_service.py +0 -0
  21. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/database/base_db_service.py +0 -0
  22. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/database/database_service.py +0 -0
  23. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/health/__init__.py +0 -0
  24. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/health/health_check.py +0 -0
  25. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/health/metrics.py +0 -0
  26. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/health/ping.py +0 -0
  27. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/llm/__init__.py +0 -0
  28. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/llm/get_llm.py +0 -0
  29. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/llm/llm_logger.py +0 -0
  30. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/llm/llm_tokens.py +0 -0
  31. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/llm/struct_token.py +0 -0
  32. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/llm/sy_langfuse.py +0 -0
  33. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/llm/usage_token.py +0 -0
  34. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/logging/__init__.py +0 -0
  35. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/logging/async_sql_logger.py +0 -0
  36. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/logging/kafka_log.py +0 -0
  37. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/logging/logger_levels.py +0 -0
  38. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/logging/logger_wrapper.py +0 -0
  39. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/logging/sql_logger.py +0 -0
  40. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/middleware/__init__.py +0 -0
  41. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/middleware/context.py +0 -0
  42. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/middleware/cors.py +0 -0
  43. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/middleware/docs.py +0 -0
  44. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/middleware/exception.py +0 -0
  45. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/middleware/middleware.py +0 -0
  46. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/middleware/monitor_memory.py +0 -0
  47. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/middleware/mq.py +0 -0
  48. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/middleware/timeout.py +0 -0
  49. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/middleware/traceid.py +0 -0
  50. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/models/__init__.py +0 -0
  51. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/models/base_http.py +0 -0
  52. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/models/log.py +0 -0
  53. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/models/mqlistener_config.py +0 -0
  54. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/models/mqmsg_model.py +0 -0
  55. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/models/mqsend_config.py +0 -0
  56. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/models/sso_user.py +0 -0
  57. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/notice/__init__.py +0 -0
  58. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/notice/uvicorn_monitor.py +0 -0
  59. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/rabbitmq/rabbitmq_client.py +0 -0
  60. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -0
  61. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/rabbitmq/rabbitmq_service.py +0 -0
  62. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/rabbitmq/rabbitmq_service_client_manager.py +0 -0
  63. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +0 -0
  64. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +0 -0
  65. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/rabbitmq/rabbitmq_service_core.py +0 -0
  66. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/rabbitmq/rabbitmq_service_producer_manager.py +0 -0
  67. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/sentry/__init__.py +0 -0
  68. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/sentry/sy_sentry.py +0 -0
  69. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/services.py +0 -0
  70. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/sse/__init__.py +0 -0
  71. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/sse/event.py +0 -0
  72. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/sse/sse.py +0 -0
  73. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/synacos/__init__.py +0 -0
  74. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/synacos/example.py +0 -0
  75. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/synacos/example2.py +0 -0
  76. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/synacos/feign.py +0 -0
  77. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/synacos/feign_client.py +0 -0
  78. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/synacos/nacos_client_base.py +0 -0
  79. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/synacos/nacos_config_manager.py +0 -0
  80. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/synacos/nacos_heartbeat_manager.py +0 -0
  81. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/synacos/nacos_service.py +0 -0
  82. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/synacos/nacos_service_discovery.py +0 -0
  83. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/synacos/nacos_service_registration.py +0 -0
  84. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/synacos/param.py +0 -0
  85. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/tools/__init__.py +0 -0
  86. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/tools/docs.py +0 -0
  87. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/tools/env.py +0 -0
  88. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/tools/merge_headers.py +0 -0
  89. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/tools/snowflake.py +0 -0
  90. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon/tools/timing.py +0 -0
  91. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon_python_lib.egg-info/SOURCES.txt +0 -0
  92. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
  93. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
  94. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon_python_lib.egg-info/requires.txt +0 -0
  95. {sycommon_python_lib-0.1.57b2 → sycommon_python_lib-0.1.57b3}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.1.57b2
3
+ Version: 0.1.57b3
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sycommon-python-lib"
3
- version = "0.1.57b2"
3
+ version = "0.1.57b3"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -0,0 +1,368 @@
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._model_dim_cache: Dict[str, int] = {}
35
+
36
+ # 并发信号量
37
+ self.semaphore = asyncio.Semaphore(self.max_concurrency)
38
+ self.default_timeout = aiohttp.ClientTimeout(total=None)
39
+
40
+ # 核心优化:创建全局可复用的ClientSession(连接池复用)
41
+ self.session = None
42
+ # 重试配置(可根据需要调整)
43
+ self.max_retry_attempts = 3 # 最大重试次数
44
+ self.retry_wait_base = 0.5 # 基础等待时间(秒)
45
+
46
+ # [修复] 注册退出钩子,确保程序结束时关闭连接池
47
+ atexit.register(self._sync_close_session)
48
+
49
+ async def init_session(self):
50
+ """初始化全局ClientSession(仅创建一次)"""
51
+ if self.session is None or self.session.closed:
52
+ # 配置连接池参数,适配高并发
53
+ connector = aiohttp.TCPConnector(
54
+ limit=self.max_concurrency * 2, # 连接池最大连接数(建议是并发数的2倍)
55
+ limit_per_host=self.max_concurrency, # 每个域名的最大连接数
56
+ ttl_dns_cache=300, # DNS缓存时间
57
+ enable_cleanup_closed=True # 自动清理关闭的连接
58
+ )
59
+ self.session = aiohttp.ClientSession(
60
+ connector=connector,
61
+ timeout=self.default_timeout
62
+ )
63
+
64
+ async def close_session(self):
65
+ """关闭全局Session(程序退出时调用)"""
66
+ if self.session and not self.session.closed:
67
+ await self.session.close()
68
+
69
+ def _sync_close_session(self):
70
+ """同步关闭Session的封装,供atexit调用"""
71
+ # 注意:atexit在主线程运行,如果当前没有事件循环,这个操作可能会受限
72
+ # 但它能捕获大多数正常退出的场景。对于asyncio程序,建议显式调用cleanup
73
+ try:
74
+ loop = asyncio.get_event_loop()
75
+ if loop.is_running():
76
+ # 如果loop还在跑,创建一个任务去关闭
77
+ loop.create_task(self.close_session())
78
+ else:
79
+ # 如果loop已经停止,尝试运行一次
80
+ loop.run_until_complete(self.close_session())
81
+ except Exception:
82
+ # 静默处理清理失败,避免退出报错
83
+ pass
84
+
85
+ async def _retry_request(self, func, *args, **kwargs):
86
+ """
87
+ 原生异步重试封装函数
88
+ Args:
89
+ func: 待重试的异步函数
90
+ *args: 函数参数
91
+ **kwargs: 函数关键字参数
92
+ Returns:
93
+ 函数执行结果,重试失败返回None
94
+ """
95
+ attempt = 0
96
+ while attempt < self.max_retry_attempts:
97
+ try:
98
+ return await func(*args, **kwargs)
99
+ except (aiohttp.ClientConnectionResetError, asyncio.TimeoutError, aiohttp.ClientError) as e:
100
+ attempt += 1
101
+ if attempt >= self.max_retry_attempts:
102
+ SYLogger.error(
103
+ f"Request failed after {attempt} retries: {str(e)}")
104
+ return None
105
+ # 指数退避等待:0.5s → 1s → 2s(最大不超过5s)
106
+ wait_time = min(self.retry_wait_base * (2 ** (attempt - 1)), 5)
107
+ SYLogger.warning(
108
+ f"Retry {func.__name__} (attempt {attempt}/{self.max_retry_attempts}): {str(e)}, wait {wait_time}s")
109
+ await asyncio.sleep(wait_time)
110
+ except Exception as e:
111
+ # 非重试类异常直接返回None
112
+ SYLogger.error(
113
+ f"Non-retryable error in {func.__name__}: {str(e)}")
114
+ return None
115
+ return None
116
+
117
+ def _get_embedding_url(self, model: str) -> str:
118
+ """获取Embedding URL(带缓存)"""
119
+ if model not in self._embedding_url_cache:
120
+ self._embedding_url_cache[model] = EmbeddingConfig.from_config(
121
+ model).baseUrl
122
+ return self._embedding_url_cache[model]
123
+
124
+ def _get_reranker_url(self, model: str) -> str:
125
+ """获取Reranker URL(带缓存)"""
126
+ if model not in self._reranker_url_cache:
127
+ self._reranker_url_cache[model] = RerankerConfig.from_config(
128
+ model).baseUrl
129
+ return self._reranker_url_cache[model]
130
+
131
+ async def _get_embeddings_http_core(
132
+ self,
133
+ input: Union[str, List[str]],
134
+ encoding_format: str = None,
135
+ model: str = None,
136
+ timeout: aiohttp.ClientTimeout = None,
137
+ **kwargs
138
+ ):
139
+ """embedding请求核心逻辑(剥离重试,供重试封装调用)"""
140
+ await self.init_session() # 确保Session已初始化
141
+ async with self.semaphore:
142
+ request_timeout = timeout or self.default_timeout
143
+ target_model = model or self.default_embedding_model
144
+
145
+ # [修复] 使用缓存获取URL
146
+ target_base_url = self._get_embedding_url(target_model)
147
+ url = f"{target_base_url}/v1/embeddings"
148
+
149
+ request_body = {
150
+ "model": target_model,
151
+ "input": input,
152
+ "encoding_format": encoding_format or "float"
153
+ }
154
+ request_body.update(kwargs)
155
+
156
+ # 复用全局Session
157
+ async with self.session.post(
158
+ url,
159
+ json=request_body,
160
+ timeout=request_timeout
161
+ ) as response:
162
+ if response.status != 200:
163
+ error_detail = await response.text()
164
+ SYLogger.error(
165
+ f"Embedding request failed (model: {target_model}): {error_detail}")
166
+ return None
167
+ return await response.json()
168
+
169
+ async def _get_embeddings_http_async(
170
+ self,
171
+ input: Union[str, List[str]],
172
+ encoding_format: str = None,
173
+ model: str = None,
174
+ timeout: aiohttp.ClientTimeout = None, ** kwargs
175
+ ):
176
+ """对外暴露的embedding请求方法(包含重试)"""
177
+ return await self._retry_request(
178
+ self._get_embeddings_http_core,
179
+ input, encoding_format, model, timeout, ** kwargs
180
+ )
181
+
182
+ async def _get_reranker_http_core(
183
+ self,
184
+ documents: List[str],
185
+ query: str,
186
+ top_n: Optional[int] = None,
187
+ model: str = None,
188
+ max_chunks_per_doc: Optional[int] = None,
189
+ return_documents: Optional[bool] = True,
190
+ return_len: Optional[bool] = True,
191
+ timeout: aiohttp.ClientTimeout = None, ** kwargs
192
+ ):
193
+ """reranker请求核心逻辑(剥离重试,供重试封装调用)"""
194
+ await self.init_session() # 确保Session已初始化
195
+ async with self.semaphore:
196
+ request_timeout = timeout or self.default_timeout
197
+ target_model = model or self.default_reranker_model
198
+
199
+ # [修复] 使用缓存获取URL
200
+ target_base_url = self._get_reranker_url(target_model)
201
+ url = f"{target_base_url}/v1/rerank"
202
+
203
+ request_body = {
204
+ "model": target_model,
205
+ "documents": documents,
206
+ "query": query,
207
+ "top_n": top_n or len(documents),
208
+ "max_chunks_per_doc": max_chunks_per_doc,
209
+ "return_documents": return_documents,
210
+ "return_len": return_len,
211
+ }
212
+ request_body.update(kwargs)
213
+
214
+ # 复用全局Session
215
+ async with self.session.post(
216
+ url,
217
+ json=request_body,
218
+ timeout=request_timeout
219
+ ) as response:
220
+ if response.status != 200:
221
+ error_detail = await response.text()
222
+ SYLogger.error(
223
+ f"Rerank request failed (model: {target_model}): {error_detail}")
224
+ return None
225
+ return await response.json()
226
+
227
+ async def _get_reranker_http_async(
228
+ self,
229
+ documents: List[str],
230
+ query: str,
231
+ top_n: Optional[int] = None,
232
+ model: str = None,
233
+ max_chunks_per_doc: Optional[int] = None,
234
+ return_documents: Optional[bool] = True,
235
+ return_len: Optional[bool] = True,
236
+ timeout: aiohttp.ClientTimeout = None, ** kwargs
237
+ ):
238
+ """对外暴露的reranker请求方法(包含重试)"""
239
+ return await self._retry_request(
240
+ self._get_reranker_http_core,
241
+ documents, query, top_n, model, max_chunks_per_doc,
242
+ return_documents, return_len, timeout, **kwargs
243
+ )
244
+
245
+ async def get_embeddings(
246
+ self,
247
+ corpus: List[str],
248
+ model: str = None,
249
+ timeout: Optional[Union[int, float]] = None
250
+ ):
251
+ """
252
+ 获取语料库的嵌入向量,结果顺序与输入语料库顺序一致
253
+
254
+ Args:
255
+ corpus: 待生成嵌入向量的文本列表
256
+ model: 可选,指定使用的embedding模型名称,默认使用bge-large-zh-v1.5
257
+ timeout: 可选,超时时间(秒):
258
+ - 传int/float:表示总超时时间(秒)
259
+ - 不传/None:使用默认永不超时配置
260
+ """
261
+ request_timeout = None
262
+ if timeout is not None:
263
+ if isinstance(timeout, (int, float)):
264
+ request_timeout = aiohttp.ClientTimeout(total=timeout)
265
+ else:
266
+ SYLogger.warning(
267
+ f"Invalid timeout type: {type(timeout)}, must be int/float, use default timeout")
268
+
269
+ actual_model = model or self.default_embedding_model
270
+
271
+ SYLogger.info(
272
+ f"Requesting embeddings for corpus: {len(corpus)} items (model: {actual_model}, max_concurrency: {self.max_concurrency}, timeout: {timeout or 'None'})")
273
+
274
+ all_vectors = []
275
+
276
+ # [修复] 增加 Chunk 处理逻辑,防止 corpus 过大导致内存溢出或协程过多
277
+ # 每次最多处理 max_concurrency * 2 个请求,避免一次性创建几十万个协程
278
+ batch_size = self.max_concurrency * 2
279
+
280
+ for i in range(0, len(corpus), batch_size):
281
+ batch_texts = corpus[i: i + batch_size]
282
+
283
+ # 给每个异步任务传入模型名称和超时配置
284
+ tasks = [self._get_embeddings_http_async(
285
+ text, model=model, timeout=request_timeout) for text in batch_texts]
286
+ results = await asyncio.gather(*tasks)
287
+
288
+ for result in results:
289
+ if result is None:
290
+ # [修复] 尝试获取真实维度或使用配置兜底,不再硬编码 1024
291
+ dim = self._model_dim_cache.get(actual_model)
292
+
293
+ # 如果缓存中没有维度,尝试从配置对象获取(假设Config类有dimension属性)
294
+ if dim is None:
295
+ try:
296
+ config = EmbeddingConfig.from_config(actual_model)
297
+ if hasattr(config, 'dimension'):
298
+ dim = config.dimension
299
+ else:
300
+ # 最后的兜底:如果配置也没有,必须有一个默认值防止崩溃
301
+ # bge-large 通常是 1024
302
+ dim = 1024
303
+ SYLogger.warning(
304
+ f"Cannot get dimension from config for {actual_model}, use default 1024")
305
+ except Exception:
306
+ dim = 1024
307
+
308
+ zero_vector = [0.0] * dim
309
+ all_vectors.append(zero_vector)
310
+ SYLogger.warning(
311
+ f"Embedding request failed, append zero vector ({dim}D) for model {actual_model}")
312
+ continue
313
+
314
+ # 从返回结果中提取向量并更新维度缓存
315
+ # 正常情况下 result["data"] 是一个列表
316
+ try:
317
+ for item in result["data"]:
318
+ embedding = item["embedding"]
319
+ # [修复] 动态学习并缓存维度
320
+ if actual_model not in self._model_dim_cache:
321
+ self._model_dim_cache[actual_model] = len(
322
+ embedding)
323
+ all_vectors.append(embedding)
324
+ except (KeyError, TypeError) as e:
325
+ SYLogger.error(f"Failed to parse embedding result: {e}")
326
+ # 解析失败也补零
327
+ dim = self._model_dim_cache.get(actual_model, 1024)
328
+ all_vectors.append([0.0] * dim)
329
+
330
+ SYLogger.info(
331
+ f"Embeddings for corpus created: {len(all_vectors)} vectors (model: {actual_model})")
332
+ return all_vectors
333
+
334
+ async def get_reranker(
335
+ self,
336
+ top_results: List[str],
337
+ query: str,
338
+ model: str = None,
339
+ timeout: Optional[Union[int, float]] = None
340
+ ):
341
+ """
342
+ 对搜索结果进行重排序
343
+
344
+ Args:
345
+ top_results: 待重排序的文本列表
346
+ query: 排序参考的查询语句
347
+ model: 可选,指定使用的reranker模型名称,默认使用bge-reranker-large
348
+ timeout: 可选,超时时间(秒):
349
+ - 传int/float:表示总超时时间(秒)
350
+ - 不传/None:使用默认永不超时配置
351
+ """
352
+ request_timeout = None
353
+ if timeout is not None:
354
+ if isinstance(timeout, (int, float)):
355
+ request_timeout = aiohttp.ClientTimeout(total=timeout)
356
+ else:
357
+ SYLogger.warning(
358
+ f"Invalid timeout type: {type(timeout)}, must be int/float, use default timeout")
359
+
360
+ actual_model = model or self.default_reranker_model
361
+ SYLogger.info(
362
+ f"Requesting reranker for top_results: {top_results} (model: {actual_model}, max_concurrency: {self.max_concurrency}, timeout: {timeout or 'None'})")
363
+
364
+ data = await self._get_reranker_http_async(
365
+ top_results, query, model=model, timeout=request_timeout)
366
+ SYLogger.info(
367
+ f"Reranker for top_results completed (model: {actual_model})")
368
+ return data
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.1.57b2
3
+ Version: 0.1.57b3
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,204 +0,0 @@
1
- import asyncio
2
- import json
3
- import aiohttp
4
- from typing import Union, List, Optional
5
-
6
- from sycommon.config.Config import SingletonMeta
7
- from sycommon.config.EmbeddingConfig import EmbeddingConfig
8
- from sycommon.config.RerankerConfig import RerankerConfig
9
- from sycommon.logging.kafka_log import SYLogger
10
-
11
-
12
- class Embedding(metaclass=SingletonMeta):
13
- def __init__(self):
14
- # 1. 并发限制
15
- self.max_concurrency = 20
16
- # 保留默认模型名称
17
- self.default_embedding_model = "bge-large-zh-v1.5"
18
- self.default_reranker_model = "bge-reranker-large"
19
-
20
- # 初始化默认模型的基础URL
21
- self.embeddings_base_url = EmbeddingConfig.from_config(
22
- self.default_embedding_model).baseUrl
23
- self.reranker_base_url = RerankerConfig.from_config(
24
- self.default_reranker_model).baseUrl
25
-
26
- # 并发信号量
27
- self.semaphore = asyncio.Semaphore(self.max_concurrency)
28
- # 全局默认超时:永不超时(None)
29
- self.default_timeout = aiohttp.ClientTimeout(total=None)
30
-
31
- async def _get_embeddings_http_async(
32
- self,
33
- input: Union[str, List[str]],
34
- encoding_format: str = None,
35
- model: str = None,
36
- timeout: aiohttp.ClientTimeout = None,
37
- **kwargs
38
- ):
39
- async with self.semaphore:
40
- # 优先使用传入的超时,无则用全局默认
41
- request_timeout = timeout or self.default_timeout
42
-
43
- # 优先使用传入的模型名,无则用默认值
44
- target_model = model or self.default_embedding_model
45
- target_base_url = EmbeddingConfig.from_config(target_model).baseUrl
46
- url = f"{target_base_url}/v1/embeddings"
47
-
48
- request_body = {
49
- "model": target_model,
50
- "input": input,
51
- "encoding_format": encoding_format or "float"
52
- }
53
- request_body.update(kwargs)
54
-
55
- try:
56
- async with aiohttp.ClientSession(timeout=request_timeout) as session:
57
- async with session.post(url, json=request_body) as response:
58
- if response.status != 200:
59
- error_detail = await response.text()
60
- SYLogger.error(
61
- f"Embedding request failed (model: {target_model}): {error_detail}")
62
- return None
63
- return await response.json()
64
- except asyncio.TimeoutError:
65
- SYLogger.error(
66
- f"Embedding request timeout (model: {target_model})")
67
- return None
68
- except Exception as e:
69
- SYLogger.error(
70
- f"Embedding request unexpected error (model: {target_model}): {str(e)}")
71
- return None
72
-
73
- async def _get_reranker_http_async(
74
- self,
75
- documents: List[str],
76
- query: str,
77
- top_n: Optional[int] = None,
78
- model: str = None,
79
- max_chunks_per_doc: Optional[int] = None,
80
- return_documents: Optional[bool] = True,
81
- return_len: Optional[bool] = True,
82
- timeout: aiohttp.ClientTimeout = None,
83
- **kwargs
84
- ):
85
- async with self.semaphore:
86
- # 优先使用传入的超时,无则用全局默认
87
- request_timeout = timeout or self.default_timeout
88
-
89
- # 优先使用传入的模型名,无则用默认值
90
- target_model = model or self.default_reranker_model
91
- target_base_url = RerankerConfig.from_config(target_model).baseUrl
92
- url = f"{target_base_url}/v1/rerank"
93
-
94
- request_body = {
95
- "model": target_model,
96
- "documents": documents,
97
- "query": query,
98
- "top_n": top_n or len(documents),
99
- "max_chunks_per_doc": max_chunks_per_doc,
100
- "return_documents": return_documents,
101
- "return_len": return_len,
102
- "kwargs": json.dumps(kwargs),
103
- }
104
- request_body.update(kwargs)
105
-
106
- try:
107
- async with aiohttp.ClientSession(timeout=request_timeout) as session:
108
- async with session.post(url, json=request_body) as response:
109
- if response.status != 200:
110
- error_detail = await response.text()
111
- SYLogger.error(
112
- f"Rerank request failed (model: {target_model}): {error_detail}")
113
- return None
114
- return await response.json()
115
- except asyncio.TimeoutError:
116
- SYLogger.error(
117
- f"Rerank request timeout (model: {target_model})")
118
- return None
119
- except Exception as e:
120
- SYLogger.error(
121
- f"Rerank request unexpected error (model: {target_model}): {str(e)}")
122
- return None
123
-
124
- async def get_embeddings(
125
- self,
126
- corpus: List[str],
127
- model: str = None,
128
- timeout: Optional[Union[int, float]] = None
129
- ):
130
- """
131
- 获取语料库的嵌入向量,结果顺序与输入语料库顺序一致
132
-
133
- Args:
134
- corpus: 待生成嵌入向量的文本列表
135
- model: 可选,指定使用的embedding模型名称,默认使用bge-large-zh-v1.5
136
- timeout: 可选,超时时间(秒):
137
- - 传int/float:表示总超时时间(秒)
138
- - 不传/None:使用默认永不超时配置
139
- """
140
- request_timeout = None
141
- if timeout is not None:
142
- if isinstance(timeout, (int, float)):
143
- request_timeout = aiohttp.ClientTimeout(total=timeout)
144
- else:
145
- SYLogger.warning(
146
- f"Invalid timeout type: {type(timeout)}, must be int/float, use default timeout")
147
-
148
- SYLogger.info(
149
- f"Requesting embeddings for corpus: {corpus} (model: {model or self.default_embedding_model}, max_concurrency: {self.max_concurrency}, timeout: {timeout or 'None'})")
150
-
151
- # 给每个异步任务传入模型名称和超时配置
152
- tasks = [self._get_embeddings_http_async(
153
- text, model=model, timeout=request_timeout) for text in corpus]
154
- results = await asyncio.gather(*tasks)
155
-
156
- vectors = []
157
- for result in results:
158
- if result is None:
159
- zero_vector = [0.0] * 1024
160
- vectors.append(zero_vector)
161
- SYLogger.warning(
162
- f"Embedding request failed, append zero vector (1024D)")
163
- continue
164
- for item in result["data"]:
165
- vectors.append(item["embedding"])
166
-
167
- SYLogger.info(
168
- f"Embeddings for corpus: {corpus} created (model: {model or self.default_embedding_model})")
169
- return vectors
170
-
171
- async def get_reranker(
172
- self,
173
- top_results: List[str],
174
- query: str,
175
- model: str = None,
176
- timeout: Optional[Union[int, float]] = None
177
- ):
178
- """
179
- 对搜索结果进行重排序
180
-
181
- Args:
182
- top_results: 待重排序的文本列表
183
- query: 排序参考的查询语句
184
- model: 可选,指定使用的reranker模型名称,默认使用bge-reranker-large
185
- timeout: 可选,超时时间(秒):
186
- - 传int/float:表示总超时时间(秒)
187
- - 不传/None:使用默认永不超时配置
188
- """
189
- request_timeout = None
190
- if timeout is not None:
191
- if isinstance(timeout, (int, float)):
192
- request_timeout = aiohttp.ClientTimeout(total=timeout)
193
- else:
194
- SYLogger.warning(
195
- f"Invalid timeout type: {type(timeout)}, must be int/float, use default timeout")
196
-
197
- SYLogger.info(
198
- f"Requesting reranker for top_results: {top_results} (model: {model or self.default_reranker_model}, max_concurrency: {self.max_concurrency}, timeout: {timeout or 'None'})")
199
-
200
- data = await self._get_reranker_http_async(
201
- top_results, query, model=model, timeout=request_timeout)
202
- SYLogger.info(
203
- f"Reranker for top_results: {top_results} completed (model: {model or self.default_reranker_model})")
204
- return data