mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-09-25 10:46:51 +03:00
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
CI / Jetson Jetpack 6 (push) Waiting to run
* Pin ruff * Add python upgrade fixes This enables python upgrade checks in ruff to look for deprecated types and patterns. This namely fixes: - usage of deprecated `Typing` which is now built in - some specific exceptions which are caught and have new aliases Some specific UP checks were also ignored as they are stylistic / unimportant and likely to cause bugs * Remove async blocking calls Use asyncio.to_thread on two remaining blocking calls to fix hanging event thread loop. Enable this specific rule to block it in the future. * Use proper logging mechanism * Correctly format logs * Raise with context When raising an exception include the from context to improve debugging * Cleanup
84 lines
2.8 KiB
Python
84 lines
2.8 KiB
Python
"""Shared helpers for GenAI providers and chat (OpenAI-style messages, tool call parsing)."""
|
|
|
|
import json
|
|
import logging
|
|
from typing import Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def parse_tool_calls_from_message(
|
|
message: dict[str, Any],
|
|
) -> list[dict[str, Any]] | None:
|
|
"""
|
|
Parse tool_calls from an OpenAI-style message dict.
|
|
|
|
Message may have "tool_calls" as a list of:
|
|
{"id": str, "function": {"name": str, "arguments": str}, ...}
|
|
|
|
Returns a list of {"id", "name", "arguments"} with arguments parsed as dict,
|
|
or None if no tool_calls. Used by Ollama and LlamaCpp (non-stream) responses.
|
|
"""
|
|
raw = message.get("tool_calls")
|
|
if not raw or not isinstance(raw, list):
|
|
return None
|
|
result = []
|
|
for idx, tool_call in enumerate(raw):
|
|
function_data = tool_call.get("function") or {}
|
|
raw_arguments = function_data.get("arguments") or {}
|
|
if isinstance(raw_arguments, dict):
|
|
arguments = raw_arguments
|
|
elif isinstance(raw_arguments, str):
|
|
try:
|
|
arguments = json.loads(raw_arguments)
|
|
except (json.JSONDecodeError, KeyError, TypeError) as e:
|
|
logger.warning(
|
|
"Failed to parse tool call arguments: %s, tool: %s",
|
|
e,
|
|
function_data.get("name", "unknown"),
|
|
)
|
|
arguments = {}
|
|
else:
|
|
arguments = {}
|
|
result.append(
|
|
{
|
|
"id": tool_call.get("id", "") or f"call_{idx}",
|
|
"name": function_data.get("name", ""),
|
|
"arguments": arguments,
|
|
}
|
|
)
|
|
return result if result else None
|
|
|
|
|
|
def build_assistant_message_for_conversation(
|
|
content: Any,
|
|
tool_calls_raw: list[dict[str, Any]] | None,
|
|
) -> dict[str, Any]:
|
|
"""
|
|
Build the assistant message dict in OpenAI format for appending to a conversation.
|
|
|
|
tool_calls_raw: list of {"id", "name", "arguments"} (arguments as dict), or None.
|
|
"""
|
|
msg: dict[str, Any] = {"role": "assistant", "content": content}
|
|
if tool_calls_raw:
|
|
msg["tool_calls"] = [
|
|
{
|
|
"id": tc["id"],
|
|
"type": "function",
|
|
"function": {
|
|
"name": tc["name"],
|
|
"arguments": json.dumps(tc.get("arguments") or {}),
|
|
},
|
|
# Gemini-only: opaque signature that must be echoed back on
|
|
# the same functionCall part in the next turn. Other providers
|
|
# do not set or read this.
|
|
**(
|
|
{"thought_signature": tc["thought_signature"]}
|
|
if tc.get("thought_signature")
|
|
else {}
|
|
),
|
|
}
|
|
for tc in tool_calls_raw
|
|
]
|
|
return msg
|