1"""Constants and wire-part helpers shared by Gemini request conversion.
2
3The canonical ``ToolCall`` to/from Gemini part helpers live here because
4both conversion directions and the response module consume the same
5wire-part shapes when rebuilding candidate content.
6"""
7
8from __future__ import annotations
9
10from typing import Any
11
12from lexigram.contracts.ai.llm import FunctionCall, ToolCall
13from lexigram.contracts.ai.relay.dto import GeminiPart
14from lexigram.contracts.ai.relay.types import RelayFormat
15from lexigram.serialization import loads_str
16
17_TARGET = RelayFormat.GEMINI
18
19_SAFETY_CATEGORIES = (
20 "HARM_CATEGORY_HARASSMENT",
21 "HARM_CATEGORY_HATE_SPEECH",
22 "HARM_CATEGORY_SEXUALLY_EXPLICIT",
23 "HARM_CATEGORY_DANGEROUS_CONTENT",
24)
25
26_MIME_KEY = "mimeType"
27
28_THOUGHT_SIGNATURE_BYPASS = "context_engineering_is_the_way_to_go"
29
30_SCHEMA_TYPE_MAP = {
31 "string": "STRING",
32 "object": "OBJECT",
33 "array": "ARRAY",
34 "integer": "INTEGER",
35 "number": "NUMBER",
36 "boolean": "BOOLEAN",
37}
38
39
40def _tool_call_from_part(part: GeminiPart) -> ToolCall:
41 """Convert a Gemini ``functionCall`` part into a canonical ``ToolCall``.
42
43 Gemini function calls carry no stable id; the canonical id stays
44 empty so target writers can generate a dialect-appropriate one.
45 """
46 call = part.function_call or {}
47 name = str(call.get("name", ""))
48 args = call.get("args")
49 return ToolCall(
50 id="",
51 type="custom",
52 function=FunctionCall(
53 name=name,
54 arguments=args if isinstance(args, dict) else {},
55 ),
56 )
57
58
59def _tool_call_to_part(tool_call: ToolCall) -> GeminiPart:
60 """Serialize a canonical ``ToolCall`` as a Gemini ``functionCall`` part."""
61 arguments: Any = tool_call.function.arguments if tool_call.function else {}
62 if isinstance(arguments, str):
63 try:
64 arguments = loads_str(arguments)
65 except ValueError:
66 arguments = {}
67 elif not isinstance(arguments, dict):
68 arguments = {}
69 return GeminiPart(
70 function_call={
71 "name": tool_call.function.name if tool_call.function else "",
72 "args": arguments,
73 }
74 )