Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions backend/app/services/agent_runtime/model_step_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,16 @@ def append_history(raw: Mapping[str, object]) -> None:
)
if not isinstance(raw_provider_call_ids, Mapping):
raw_provider_call_ids = {}
message_additional_kwargs = raw.get("additional_kwargs")
raw_provider_signatures = raw.get("provider_thought_signatures")
if not isinstance(raw_provider_signatures, Mapping):
raw_provider_signatures = (
message_additional_kwargs.get("provider_thought_signatures")
if isinstance(message_additional_kwargs, Mapping)
else {}
)
if not isinstance(raw_provider_signatures, Mapping):
raw_provider_signatures = {}
if isinstance(raw_tool_calls, list):
provider_tool_calls = []
for raw_call in raw_tool_calls:
Expand All @@ -916,6 +926,12 @@ def append_history(raw: Mapping[str, object]) -> None:
):
provider_call_ids[call_instance_id] = provider_call_id
call["id"] = provider_call_id
if isinstance(call_instance_id, str):
thought_signature = raw_provider_signatures.get(
call_instance_id
)
if isinstance(thought_signature, str) and thought_signature:
call["_gemini_thought_signature"] = thought_signature
provider_tool_calls.append(call)
raw_tool_call_id = raw.get("tool_call_id")
provider_tool_call_id = (
Expand Down Expand Up @@ -1034,6 +1050,7 @@ def _with_call_instances(
run_id = uuid.UUID(context.run_id)
calls: list[JsonObject] = []
provider_call_ids: dict[str, str] = {}
provider_thought_signatures: dict[str, str] = {}
for index, raw_call in enumerate(result.tool_calls):
provider_call_id = raw_call.get("id")
if not isinstance(provider_call_id, str) or not provider_call_id.strip():
Expand All @@ -1047,15 +1064,27 @@ def _with_call_instances(
)
call["provider_call_id"] = provider_call_id.strip()
provider_call_ids[cast(str, call["id"])] = provider_call_id.strip()
thought_signature = raw_call.get("_gemini_thought_signature")
if isinstance(thought_signature, str) and thought_signature:
provider_thought_signatures[cast(str, call["id"])] = thought_signature
calls.append(call)
assistant_message = cast(JsonObject, deepcopy(result.assistant_message))
assistant_message["tool_calls"] = [
{key: value for key, value in call.items() if key != "provider_call_id"}
{
key: value
for key, value in call.items()
if key not in {"provider_call_id", "_gemini_thought_signature"}
}
for call in calls
]
assistant_message["additional_kwargs"] = {
additional_kwargs: dict[str, object] = {
"provider_call_ids": provider_call_ids,
}
if provider_thought_signatures:
additional_kwargs["provider_thought_signatures"] = (
provider_thought_signatures
)
assistant_message["additional_kwargs"] = additional_kwargs
return replace(
result,
assistant_message=assistant_message,
Expand Down
2 changes: 2 additions & 0 deletions backend/app/services/llm/caller.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ def _sanitize_tool_calls_for_context(
}
if "_gemini_extra" in tc:
new_tc["_gemini_extra"] = tc["_gemini_extra"]
if "_gemini_thought_signature" in tc:
new_tc["_gemini_thought_signature"] = tc["_gemini_thought_signature"]
sanitized.append(new_tc)

return sanitized, None, None
Expand Down
27 changes: 19 additions & 8 deletions backend/app/services/llm/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1665,9 +1665,13 @@ def _build_payload(
if "_gemini_extra" in tc:
func_call_dict.update(tc["_gemini_extra"])

parts.append({
call_part: dict[str, Any] = {
"functionCall": func_call_dict
})
}
thought_signature = tc.get("_gemini_thought_signature")
if isinstance(thought_signature, str) and thought_signature:
call_part["thoughtSignature"] = thought_signature
parts.append(call_part)
if parts:
contents.append({"role": "model", "parts": parts})
continue
Expand Down Expand Up @@ -1767,16 +1771,20 @@ def _parse_response_data(self, data: dict[str, Any]) -> LLMResponse:
seen_tool_calls.add(dedup_key)

extra = {k: v for k, v in function_call.items() if k not in ["name", "args"]}
tool_calls.append({

tool_call: dict[str, Any] = {
"id": f"call_{len(tool_calls) + 1}",
"type": "function",
"function": {
"name": name,
"arguments": args_str,
},
"_gemini_extra": extra,
})
}
signature = part.get("thoughtSignature")
if isinstance(signature, str) and signature:
tool_call["_gemini_thought_signature"] = signature
tool_calls.append(tool_call)

usage = self._normalize_usage(data.get("usageMetadata"))

Expand Down Expand Up @@ -1928,16 +1936,19 @@ async def stream(
seen_tool_calls.add(dedup_key)

extra = {k: v for k, v in function_call.items() if k not in ["name", "args"]}
tool_calls.append({

streamed_call: dict[str, Any] = {
"id": f"call_{len(tool_calls) + 1}",
"type": "function",
"function": {
"name": name,
"arguments": args_str,
},
"_gemini_extra": extra,
})
}
if isinstance(signature, str) and signature:
streamed_call["_gemini_thought_signature"] = signature
tool_calls.append(streamed_call)

except (httpx.ConnectError, httpx.ReadError, httpx.ConnectTimeout) as e:
raise LLMError(f"Connection failed: {e}")
Expand Down
Loading