fix: trace tool forwarding decisions

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
mmc
2026-04-29 06:12:21 +08:00
parent 3c9d419726
commit d9fec3fd74
4 changed files with 168 additions and 0 deletions

View File

@@ -19,6 +19,31 @@ from .logging_config import get_logger
logger = get_logger("lingma_gateway.client")
def _tool_config_summary(tool_config: dict[str, Any] | None) -> dict[str, Any]:
if not isinstance(tool_config, dict):
return {"present": False, "provider": None, "tool_names": [], "tool_choice": None}
tools = tool_config.get("tools")
tool_names: list[str] = []
if isinstance(tools, list):
for tool in tools:
if not isinstance(tool, dict):
continue
if tool.get("type") == "function":
fn = tool.get("function")
if isinstance(fn, dict) and isinstance(fn.get("name"), str) and fn.get("name").strip():
tool_names.append(fn.get("name").strip())
continue
name = tool.get("name")
if isinstance(name, str) and name.strip():
tool_names.append(name.strip())
return {
"present": True,
"provider": tool_config.get("provider"),
"tool_names": tool_names,
"tool_choice": tool_config.get("tool_choice"),
}
# Some callers live on Python 3.10 where asyncio.TimeoutError is a distinct class,
# while 3.11+ unifies it with the builtin TimeoutError. Always catch both.
TIMEOUT_EXCEPTIONS: tuple[type[BaseException], ...] = (
@@ -407,6 +432,12 @@ class LspWsRpcClient:
if method in {"tool/call/sync", "tool/invoke", "tool/call/approve", "tool/invokeResult"}:
tool_event = self._extract_tool_event(params)
logger.info(
"lingma tool event method=%s request_id=%s tool=%s",
method,
params.get("requestId"),
tool_event,
)
stream = self._resolve_tool_stream(method, params, tool_event)
if stream is not None and tool_event is not None:
@@ -433,6 +464,11 @@ class LspWsRpcClient:
if method == "chat/finish":
logger.info(
"lingma finish request_id=%s session_id=%s",
params.get("requestId"),
params.get("sessionId"),
)
req_id = params.get("requestId")
stream = self._chat_streams.get(req_id)
if stream is not None and not stream["done"].is_set():
@@ -936,6 +972,13 @@ class LingmaGatewayClient:
}
if tool_config is not None:
payload["toolConfig"] = tool_config
logger.info(
"lingma payload request_id=%s session_id=%s mode=%s tool_config=%s",
request_id,
session_id,
ask_mode,
_tool_config_summary(tool_config),
)
return payload
async def _kick_chat_ask(self, payload: dict) -> None: