🐛 Bug: Fixed the bug with limited concurrency and removed unnecessary asynchronous mutex locks.
Browse files💰 Sponsors: Thanks to @PowerHunter for the ¥1000 sponsorship, sponsorship information has been added to the README.
- README.md +2 -2
- README_CN.md +2 -2
- main.py +33 -46
README.md
CHANGED
@@ -315,8 +315,8 @@ curl -X POST http://127.0.0.1:8000/v1/chat/completions \
|
|
315 |
## Sponsors
|
316 |
|
317 |
We thank the following sponsors for their support:
|
318 |
-
<!-- ¥
|
319 |
-
- @PowerHunter: ¥
|
320 |
|
321 |
## How to sponsor us
|
322 |
|
|
|
315 |
## Sponsors
|
316 |
|
317 |
We thank the following sponsors for their support:
|
318 |
+
<!-- ¥1000 -->
|
319 |
+
- @PowerHunter: ¥1000
|
320 |
|
321 |
## How to sponsor us
|
322 |
|
README_CN.md
CHANGED
@@ -315,8 +315,8 @@ curl -X POST http://127.0.0.1:8000/v1/chat/completions \
|
|
315 |
## 赞助商
|
316 |
|
317 |
我们感谢以下赞助商的支持:
|
318 |
-
<!-- ¥
|
319 |
-
- @PowerHunter:¥
|
320 |
|
321 |
## 如何赞助我们
|
322 |
|
|
|
315 |
## 赞助商
|
316 |
|
317 |
我们感谢以下赞助商的支持:
|
318 |
+
<!-- ¥1000 -->
|
319 |
+
- @PowerHunter:¥1000
|
320 |
|
321 |
## 如何赞助我们
|
322 |
|
main.py
CHANGED
@@ -160,30 +160,24 @@ async def parse_request_body(request: Request):
|
|
160 |
return None
|
161 |
|
162 |
class ChannelManager:
|
163 |
-
def __init__(self, cooldown_period
|
164 |
-
self._excluded_models
|
165 |
-
self._lock = asyncio.Lock()
|
166 |
self.cooldown_period = cooldown_period
|
167 |
|
168 |
async def exclude_model(self, provider: str, model: str):
|
169 |
-
""
|
170 |
-
|
171 |
-
model_key = f"{provider}/{model}"
|
172 |
-
self._excluded_models[model_key] = datetime.now()
|
173 |
|
174 |
async def is_model_excluded(self, provider: str, model: str) -> bool:
|
175 |
-
""
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
del self._excluded_models[model_key]
|
185 |
-
return False
|
186 |
-
return True
|
187 |
|
188 |
async def get_available_providers(self, providers: list) -> list:
|
189 |
"""过滤出可用的providers,仅排除不可用的模型"""
|
@@ -541,39 +535,32 @@ class ClientManager:
|
|
541 |
def __init__(self, pool_size=100):
|
542 |
self.pool_size = pool_size
|
543 |
self.clients = {} # {timeout_value: AsyncClient}
|
544 |
-
self.locks = {} # {timeout_value: Lock}
|
545 |
|
546 |
async def init(self, default_config):
|
547 |
self.default_config = default_config
|
548 |
|
549 |
@asynccontextmanager
|
550 |
async def get_client(self, timeout_value):
|
551 |
-
#
|
552 |
-
if timeout_value not in self.
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
self.clients[timeout_value] = httpx.AsyncClient(
|
565 |
-
timeout=timeout,
|
566 |
-
limits=httpx.Limits(max_connections=self.pool_size),
|
567 |
-
**self.default_config
|
568 |
-
)
|
569 |
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
raise e
|
577 |
|
578 |
async def close(self):
|
579 |
for client in self.clients.values():
|
@@ -791,7 +778,7 @@ def lottery_scheduling(weights):
|
|
791 |
def get_provider_rules(model_rule, config, request_model):
|
792 |
provider_rules = []
|
793 |
if model_rule == "all":
|
794 |
-
#
|
795 |
for provider in config["providers"]:
|
796 |
model_dict = get_model_dict(provider)
|
797 |
for model in model_dict.keys():
|
|
|
160 |
return None
|
161 |
|
162 |
class ChannelManager:
|
163 |
+
def __init__(self, cooldown_period=300):
|
164 |
+
self._excluded_models = defaultdict(lambda: None)
|
|
|
165 |
self.cooldown_period = cooldown_period
|
166 |
|
167 |
async def exclude_model(self, provider: str, model: str):
|
168 |
+
model_key = f"{provider}/{model}"
|
169 |
+
self._excluded_models[model_key] = datetime.now()
|
|
|
|
|
170 |
|
171 |
async def is_model_excluded(self, provider: str, model: str) -> bool:
|
172 |
+
model_key = f"{provider}/{model}"
|
173 |
+
excluded_time = self._excluded_models[model_key]
|
174 |
+
if not excluded_time:
|
175 |
+
return False
|
176 |
+
|
177 |
+
if datetime.now() - excluded_time > timedelta(seconds=self.cooldown_period):
|
178 |
+
del self._excluded_models[model_key]
|
179 |
+
return False
|
180 |
+
return True
|
|
|
|
|
|
|
181 |
|
182 |
async def get_available_providers(self, providers: list) -> list:
|
183 |
"""过滤出可用的providers,仅排除不可用的模型"""
|
|
|
535 |
def __init__(self, pool_size=100):
|
536 |
self.pool_size = pool_size
|
537 |
self.clients = {} # {timeout_value: AsyncClient}
|
|
|
538 |
|
539 |
async def init(self, default_config):
|
540 |
self.default_config = default_config
|
541 |
|
542 |
@asynccontextmanager
|
543 |
async def get_client(self, timeout_value):
|
544 |
+
# 直接获取或创建客户端,不使用锁
|
545 |
+
if timeout_value not in self.clients:
|
546 |
+
timeout = httpx.Timeout(
|
547 |
+
connect=15.0,
|
548 |
+
read=timeout_value,
|
549 |
+
write=30.0,
|
550 |
+
pool=self.pool_size
|
551 |
+
)
|
552 |
+
self.clients[timeout_value] = httpx.AsyncClient(
|
553 |
+
timeout=timeout,
|
554 |
+
limits=httpx.Limits(max_connections=self.pool_size),
|
555 |
+
**self.default_config
|
556 |
+
)
|
|
|
|
|
|
|
|
|
|
|
557 |
|
558 |
+
try:
|
559 |
+
yield self.clients[timeout_value]
|
560 |
+
except Exception as e:
|
561 |
+
await self.clients[timeout_value].aclose()
|
562 |
+
del self.clients[timeout_value]
|
563 |
+
raise e
|
|
|
564 |
|
565 |
async def close(self):
|
566 |
for client in self.clients.values():
|
|
|
778 |
def get_provider_rules(model_rule, config, request_model):
|
779 |
provider_rules = []
|
780 |
if model_rule == "all":
|
781 |
+
# 如���模型名为 all,则返回所有模型
|
782 |
for provider in config["providers"]:
|
783 |
model_dict = get_model_dict(provider)
|
784 |
for model in model_dict.keys():
|