Coverage for mindsdb / api / a2a / utils.py: 0%
50 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-21 00:36 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-21 00:36 +0000
1from typing import Dict, List
2from mindsdb.utilities.log import getLogger
4logger = getLogger(__name__)
7def to_serializable(obj):
8 # Primitives
9 if isinstance(obj, (str, int, float, bool, type(None))):
10 return obj
11 # Pydantic v2
12 if hasattr(obj, "model_dump"):
13 return to_serializable(obj.model_dump(exclude_none=True))
14 # Pydantic v1
15 if hasattr(obj, "dict"):
16 return to_serializable(obj.dict(exclude_none=True))
17 # Custom classes with __dict__
18 if hasattr(obj, "__dict__"):
19 return {k: to_serializable(v) for k, v in vars(obj).items() if not k.startswith("_")}
20 # Dicts
21 if isinstance(obj, dict):
22 return {k: to_serializable(v) for k, v in obj.items()}
23 # Lists, Tuples, Sets
24 if isinstance(obj, (list, tuple, set)):
25 return [to_serializable(v) for v in obj]
26 # Fallback: string
27 return str(obj)
30def convert_a2a_message_to_qa_format(a2a_message: Dict) -> List[Dict[str, str]]:
31 """
32 Convert A2A message format to question/answer format.
34 This is the format that the langchain agent expects and ensure effective multi-turn conversation
36 Args:
37 a2a_message: A2A message containing history and current message parts
39 Returns:
40 List of messages in question/answer format
41 """
42 converted_messages = []
44 # Process conversation history first
45 if "history" in a2a_message and a2a_message["history"] is not None:
46 for hist_msg in a2a_message["history"]:
47 if hist_msg.get("role") == "user":
48 # Extract text from parts
49 text = ""
50 for part in hist_msg.get("parts", []):
51 if part.get("type") == "text":
52 text = part.get("text", "")
53 break
54 # Create question with empty answer initially
55 converted_messages.append({"question": text, "answer": ""})
56 elif hist_msg.get("role") in ["agent", "assistant"]:
57 # Extract text from parts
58 text = ""
59 for part in hist_msg.get("parts", []):
60 if part.get("type") == "text":
61 text = part.get("text", "")
62 break
63 # Pair with the most recent question that has empty answer
64 paired = False
65 for i in range(len(converted_messages) - 1, -1, -1):
66 if converted_messages[i].get("answer") == "":
67 converted_messages[i]["answer"] = text
68 paired = True
69 break
71 if not paired:
72 logger.warning("Could not pair agent response with question (no empty answer found)")
74 logger.debug(f"Converted {len(a2a_message['history'])} A2A history messages to Q&A format")
76 # Add current message as final question with empty answer
77 current_text = ""
78 for part in a2a_message.get("parts", []):
79 if part.get("type") == "text":
80 current_text = part.get("text", "")
81 break
82 converted_messages.append({"question": current_text, "answer": ""})
84 return converted_messages