feat: add emulated tool-calling bridge for Lingma

Add a proxy-side tool emulation layer so Lingma requests can surface stable OpenAI tool_calls and Anthropic tool_use blocks even when upstream tool events are missing or inconsistent.

Constraint: Keep native Lingma tool event bridging as the first path and layer emulation as a fallback

Rejected: Depend exclusively on Lingma native tool/invoke events | tool visibility remains inconsistent across models and transports

Confidence: high

Scope-risk: moderate
This commit is contained in:
mmc
2026-05-07 18:10:01 +08:00
parent 5911e4322e
commit 94a8025ae5
11 changed files with 1808 additions and 4 deletions

View File

@@ -196,6 +196,33 @@ def _extract_tool_calls_from_text(text: str) -> list[dict[str, Any]] | None:
return None
def _extract_hash_tool_call_event_from_text(
text: str,
*,
forced_tool_name: str | None = None,
) -> dict[str, Any] | None:
raw = (text or "").strip()
if not raw:
return None
match = re.search(
r"#Tool Call\s*```([A-Za-z0-9_\-.]+)\s*(\{.*?\})\s*```",
raw,
flags=re.S,
)
if not match:
return None
name = match.group(1).strip()
if forced_tool_name and name != forced_tool_name:
return None
try:
arguments = json.loads(match.group(2))
except Exception:
return None
if not isinstance(arguments, dict):
return None
return {"name": name, "input": arguments}
def _tool_code_single_arg_name(
tools: list[dict[str, Any]] | None, forced_tool_name: str
) -> str | None:
@@ -342,6 +369,45 @@ def _forced_tool_fallback_event(
)
def _declared_tool_names(tools: list[dict[str, Any]] | None) -> list[str]:
if not isinstance(tools, list):
return []
out: list[str] = []
for tool in tools:
name = _openai_tool_name(tool) or _anthropic_tool_name(tool)
if name and name not in out:
out.append(name)
return out
def _infer_tool_event_from_declared_tools(
text: str,
*,
tools: list[dict[str, Any]] | None,
) -> dict[str, Any] | None:
for tool_name in _declared_tool_names(tools):
inferred = _extract_function_call_event_from_text(
text,
forced_tool_name=tool_name,
)
if inferred is not None:
return inferred
inferred = _extract_hash_tool_call_event_from_text(
text,
forced_tool_name=tool_name,
)
if inferred is not None:
return inferred
inferred = _forced_tool_fallback_event(
text,
forced_tool_name=tool_name,
tools=tools,
)
if inferred is not None:
return inferred
return None
def _openai_tool_call(
tool: dict[str, Any], *, forced_id: str | None = None
) -> dict[str, Any]: