mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-09 12:40:59 +00:00
fix(runtime): stabilize reasoning chat delivery
This commit is contained in:
@@ -132,9 +132,7 @@ class Controller:
|
||||
|
||||
break
|
||||
|
||||
if selected_query: # 找到了
|
||||
queries.remove(selected_query)
|
||||
else: # 没找到 说明:没有请求 或者 所有query对应的session都已达到并发上限
|
||||
if not selected_query: # 没有请求,或所有 query 对应的 session 都已达到并发上限
|
||||
await self.ap.query_pool.condition.wait()
|
||||
continue
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import contextvars
|
||||
import logging
|
||||
import time
|
||||
import typing
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
|
||||
import pydantic
|
||||
@@ -25,6 +26,15 @@ _current_pipeline_uuid: contextvars.ContextVar[str | None] = contextvars.Context
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class WebSocketReplyContext:
|
||||
"""Trusted routing context retained when the originating socket reconnects."""
|
||||
|
||||
scope: WebSocketScope
|
||||
pipeline_uuid: str
|
||||
session_id: str | None
|
||||
|
||||
|
||||
class WebSocketMessage(pydantic.BaseModel):
|
||||
"""WebSocket消息格式"""
|
||||
|
||||
@@ -265,6 +275,11 @@ class WebSocketAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter)
|
||||
embed_target = self._parse_embed_target(sender_id)
|
||||
if embed_target is not None:
|
||||
return embed_target
|
||||
reply_context = getattr(message_source, '_websocket_reply_context', None)
|
||||
if isinstance(reply_context, WebSocketReplyContext):
|
||||
if reply_context.scope != self._scope():
|
||||
raise ValueError('WebSocket reply context does not match this adapter scope')
|
||||
return reply_context.pipeline_uuid, reply_context.session_id
|
||||
raise ValueError('WebSocket reply target is not bound to this adapter scope')
|
||||
|
||||
async def send_message(
|
||||
@@ -685,6 +700,16 @@ class WebSocketAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter)
|
||||
|
||||
# 异步触发事件处理
|
||||
# Use owner_bot's listeners if available, otherwise fall back to proxy bot
|
||||
object.__setattr__(
|
||||
event,
|
||||
'_websocket_reply_context',
|
||||
WebSocketReplyContext(
|
||||
scope=connection.scope,
|
||||
pipeline_uuid=pipeline_uuid,
|
||||
session_id=connection.session_id,
|
||||
),
|
||||
)
|
||||
|
||||
listeners = (
|
||||
owner_bot.adapter.listeners
|
||||
if (owner_bot and hasattr(owner_bot.adapter, 'listeners') and owner_bot.adapter.listeners)
|
||||
@@ -707,28 +732,31 @@ class WebSocketAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter)
|
||||
if len(listener_tasks) >= 100:
|
||||
await self.logger.warning('WebSocket inbound listener capacity reached; dropping message')
|
||||
return
|
||||
token = _current_pipeline_uuid.set(pipeline_uuid)
|
||||
try:
|
||||
task_manager = getattr(self.ap, 'task_mgr', None)
|
||||
if task_manager is None or not isinstance(getattr(task_manager, 'tasks', None), list):
|
||||
listener_task = asyncio.create_task(listeners[event.__class__](event, callback_adapter))
|
||||
else:
|
||||
listener_task = task_manager.create_task(
|
||||
listeners[event.__class__](event, callback_adapter),
|
||||
kind='websocket-message',
|
||||
name=f'websocket-message-{connection.connection_id}',
|
||||
scopes=[
|
||||
core_entities.LifecycleControlScope.APPLICATION,
|
||||
core_entities.LifecycleControlScope.PLATFORM,
|
||||
],
|
||||
instance_uuid=connection.instance_uuid,
|
||||
workspace_uuid=connection.workspace_uuid,
|
||||
placement_generation=connection.placement_generation,
|
||||
).task
|
||||
listener_tasks.add(listener_task)
|
||||
listener_task.add_done_callback(self._listener_task_done)
|
||||
finally:
|
||||
_current_pipeline_uuid.reset(token)
|
||||
async def run_listener() -> None:
|
||||
token = _current_pipeline_uuid.set(pipeline_uuid)
|
||||
try:
|
||||
await listeners[event.__class__](event, callback_adapter)
|
||||
finally:
|
||||
_current_pipeline_uuid.reset(token)
|
||||
|
||||
task_manager = getattr(self.ap, 'task_mgr', None)
|
||||
if task_manager is None or not isinstance(getattr(task_manager, 'tasks', None), list):
|
||||
listener_task = asyncio.create_task(run_listener())
|
||||
else:
|
||||
listener_task = task_manager.create_task(
|
||||
run_listener(),
|
||||
kind='websocket-message',
|
||||
name=f'websocket-message-{connection.connection_id}',
|
||||
scopes=[
|
||||
core_entities.LifecycleControlScope.APPLICATION,
|
||||
core_entities.LifecycleControlScope.PLATFORM,
|
||||
],
|
||||
instance_uuid=connection.instance_uuid,
|
||||
workspace_uuid=connection.workspace_uuid,
|
||||
placement_generation=connection.placement_generation,
|
||||
).task
|
||||
listener_tasks.add(listener_task)
|
||||
listener_task.add_done_callback(self._listener_task_done)
|
||||
|
||||
def get_websocket_messages(
|
||||
self,
|
||||
|
||||
@@ -466,6 +466,8 @@ class LiteLLMRequester(requester.ProviderAPIRequester):
|
||||
|
||||
provider = self._reasoning_provider(model.model_entity.name)
|
||||
if level == 'disabled':
|
||||
if provider == 'deepseek':
|
||||
return {'extra_body': {'thinking': {'type': 'disabled'}}}
|
||||
if provider == 'volcengine':
|
||||
return {'thinking': {'type': 'disabled'}}
|
||||
return {'reasoning_effort': 'none'}
|
||||
@@ -844,7 +846,15 @@ class LiteLLMRequester(requester.ProviderAPIRequester):
|
||||
raise errors.RequesterError(
|
||||
'reasoning_config conflicts with advanced parameters: ' + ', '.join(dict.fromkeys(conflicts))
|
||||
)
|
||||
args.update(reasoning_args)
|
||||
reasoning_extra_body = reasoning_args.get('extra_body')
|
||||
if isinstance(reasoning_extra_body, dict):
|
||||
existing_extra_body = args.get('extra_body') or {}
|
||||
if not isinstance(existing_extra_body, dict):
|
||||
raise errors.RequesterError('extra_body must be an object')
|
||||
args.update({key: value for key, value in reasoning_args.items() if key != 'extra_body'})
|
||||
args['extra_body'] = {**existing_extra_body, **reasoning_extra_body}
|
||||
else:
|
||||
args.update(reasoning_args)
|
||||
if 'reasoning_effort' in reasoning_args and self._reasoning_provider(model.model_entity.name) == 'openai':
|
||||
allowed_openai_params = args.get('allowed_openai_params') or []
|
||||
if not isinstance(allowed_openai_params, (list, tuple, set)):
|
||||
|
||||
Reference in New Issue
Block a user