kagent-adk 0.6.9__tar.gz → 0.6.11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kagent-adk might be problematic. Click here for more details.

Files changed (23) hide show
  1. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/PKG-INFO +1 -1
  2. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/pyproject.toml +1 -1
  3. kagent_adk-0.6.11/src/kagent/adk/converters/error_mappings.py +60 -0
  4. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/src/kagent/adk/converters/event_converter.py +6 -3
  5. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/src/kagent/adk/models/_openai.py +33 -16
  6. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/.gitignore +0 -0
  7. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/.python-version +0 -0
  8. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/README.md +0 -0
  9. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/src/kagent/adk/__init__.py +0 -0
  10. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/src/kagent/adk/_a2a.py +0 -0
  11. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/src/kagent/adk/_agent_executor.py +0 -0
  12. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/src/kagent/adk/_session_service.py +0 -0
  13. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/src/kagent/adk/_token.py +0 -0
  14. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/src/kagent/adk/cli.py +0 -0
  15. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/src/kagent/adk/converters/__init__.py +0 -0
  16. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/src/kagent/adk/converters/part_converter.py +0 -0
  17. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/src/kagent/adk/converters/request_converter.py +0 -0
  18. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/src/kagent/adk/models/__init__.py +0 -0
  19. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/src/kagent/adk/types.py +0 -0
  20. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/tests/__init__.py +0 -0
  21. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/tests/unittests/__init__.py +0 -0
  22. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/tests/unittests/models/__init__.py +0 -0
  23. {kagent_adk-0.6.9 → kagent_adk-0.6.11}/tests/unittests/models/test_openai.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kagent-adk
3
- Version: 0.6.9
3
+ Version: 0.6.11
4
4
  Summary: kagent-adk is an sdk for integrating adk agents with kagent
5
5
  Requires-Python: >=3.12.11
6
6
  Requires-Dist: a2a-sdk>=0.3.1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "kagent-adk"
7
- version = "0.6.9"
7
+ version = "0.6.11"
8
8
  description = "kagent-adk is an sdk for integrating adk agents with kagent"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12.11"
@@ -0,0 +1,60 @@
1
+ """Error code to user-friendly message mappings for ADK events.
2
+
3
+ This module provides mappings from Google GenAI finish reasons to user-friendly
4
+ error messages, excluding STOP which is a normal completion reason.
5
+ """
6
+
7
+ from typing import Dict, Optional
8
+
9
+ from google.genai import types as genai_types
10
+
11
+ # Error code to user-friendly message mappings
12
+ # Based on Google GenAI types.py FinishReason enum (excluding STOP)
13
+ ERROR_CODE_MESSAGES: Dict[str, str] = {
14
+ # Length and token limits
15
+ genai_types.FinishReason.MAX_TOKENS: "Response was truncated due to maximum token limit. Try asking a shorter question or breaking it into parts.",
16
+ # Safety and content filtering
17
+ genai_types.FinishReason.SAFETY: "Response was blocked due to safety concerns. Please rephrase your request to avoid potentially harmful content.",
18
+ genai_types.FinishReason.RECITATION: "Response was blocked due to unauthorized citations. Please rephrase your request.",
19
+ genai_types.FinishReason.BLOCKLIST: "Response was blocked due to restricted terminology. Please rephrase your request using different words.",
20
+ genai_types.FinishReason.PROHIBITED_CONTENT: "Response was blocked due to prohibited content. Please rephrase your request.",
21
+ genai_types.FinishReason.SPII: "Response was blocked due to sensitive personal information concerns. Please avoid including personal details.",
22
+ # Function calling errors
23
+ genai_types.FinishReason.MALFORMED_FUNCTION_CALL: "The agent generated an invalid function call. This may be due to complex input data. Try rephrasing your request or breaking it into simpler steps.",
24
+ # Generic fallback
25
+ genai_types.FinishReason.OTHER: "An unexpected error occurred during processing. Please try again or rephrase your request.",
26
+ }
27
+
28
+ # Normal completion reasons that should not be treated as errors
29
+ NORMAL_COMPLETION_REASONS = {
30
+ genai_types.FinishReason.STOP, # Normal completion
31
+ }
32
+
33
+ # Default error message when no specific mapping exists
34
+ DEFAULT_ERROR_MESSAGE = "An error occurred during processing"
35
+
36
+
37
+ def _get_error_message(error_code: Optional[str]) -> str:
38
+ """Get a user-friendly error message for the given error code.
39
+
40
+ Args:
41
+ error_code: The error code from the ADK event (e.g., finish_reason)
42
+
43
+ Returns:
44
+ User-friendly error message string
45
+ """
46
+
47
+ # Return mapped message or default
48
+ return ERROR_CODE_MESSAGES.get(error_code, DEFAULT_ERROR_MESSAGE)
49
+
50
+
51
+ def _is_normal_completion(error_code: Optional[str]) -> bool:
52
+ """Check if the error code represents normal completion rather than an error.
53
+
54
+ Args:
55
+ error_code: The error code to check
56
+
57
+ Returns:
58
+ True if this is a normal completion reason, False otherwise
59
+ """
60
+ return error_code in NORMAL_COMPLETION_REASONS
@@ -20,6 +20,7 @@ from kagent.core.a2a import (
20
20
  get_kagent_metadata_key,
21
21
  )
22
22
 
23
+ from .error_mappings import _get_error_message, _is_normal_completion
23
24
  from .part_converter import (
24
25
  convert_genai_part_to_a2a_part,
25
26
  )
@@ -27,7 +28,6 @@ from .part_converter import (
27
28
  # Constants
28
29
 
29
30
  ARTIFACT_ID_SEPARATOR = "-"
30
- DEFAULT_ERROR_MESSAGE = "An error occurred during processing"
31
31
 
32
32
  # Logger
33
33
  logger = logging.getLogger("kagent_adk." + __name__)
@@ -191,13 +191,16 @@ def _create_error_status_event(
191
191
  Returns:
192
192
  A TaskStatusUpdateEvent with FAILED state.
193
193
  """
194
- error_message = getattr(event, "error_message", None) or DEFAULT_ERROR_MESSAGE
194
+ error_message = getattr(event, "error_message", None)
195
195
 
196
196
  # Get context metadata and add error code
197
197
  event_metadata = _get_context_metadata(event, invocation_context)
198
198
  if event.error_code:
199
199
  event_metadata[get_kagent_metadata_key("error_code")] = str(event.error_code)
200
200
 
201
+ if not error_message:
202
+ error_message = _get_error_message(event.error_code)
203
+
201
204
  return TaskStatusUpdateEvent(
202
205
  task_id=task_id,
203
206
  context_id=context_id,
@@ -298,7 +301,7 @@ def convert_event_to_a2a_events(
298
301
 
299
302
  try:
300
303
  # Handle error scenarios
301
- if event.error_code:
304
+ if event.error_code and not _is_normal_completion(event.error_code):
302
305
  error_event = _create_error_status_event(event, invocation_context, task_id, context_id)
303
306
  a2a_events.append(error_event)
304
307
 
@@ -55,8 +55,13 @@ def _convert_content_to_openai_messages(
55
55
  system_message: ChatCompletionSystemMessageParam = {"role": "system", "content": system_instruction}
56
56
  messages.append(system_message)
57
57
 
58
- # Track tool calls to ensure proper flow
59
- pending_tool_calls = set()
58
+ # First pass: collect all function responses to match with tool calls
59
+ all_function_responses = {}
60
+ for content in contents:
61
+ for part in content.parts or []:
62
+ if part.function_response:
63
+ tool_call_id = part.function_response.id or "call_1"
64
+ all_function_responses[tool_call_id] = part.function_response
60
65
 
61
66
  for content in contents:
62
67
  role = _convert_role_to_openai(content.role)
@@ -83,23 +88,14 @@ def _convert_content_to_openai_messages(
83
88
  }
84
89
  image_parts.append(image_part)
85
90
 
86
- # Handle function responses first (they should be tool messages)
87
- for func_response in function_responses:
88
- tool_call_id = func_response.function_response.id or "call_1"
89
- if tool_call_id in pending_tool_calls:
90
- tool_message: ChatCompletionToolMessageParam = {
91
- "role": "tool",
92
- "tool_call_id": tool_call_id,
93
- "content": str(func_response.function_response.response.get("result", ""))
94
- if func_response.function_response.response
95
- else "",
96
- }
97
- messages.append(tool_message)
98
- pending_tool_calls.discard(tool_call_id)
91
+ # Function responses are now handled together with function calls
92
+ # This ensures proper pairing and prevents orphaned tool messages
99
93
 
100
94
  # Handle function calls (assistant messages with tool_calls)
101
95
  if function_calls:
102
96
  tool_calls = []
97
+ tool_response_messages = []
98
+
103
99
  for func_call in function_calls:
104
100
  tool_call_function: ToolCallFunction = {
105
101
  "name": func_call.function_call.name or "",
@@ -112,7 +108,25 @@ def _convert_content_to_openai_messages(
112
108
  "function": tool_call_function,
113
109
  }
114
110
  tool_calls.append(tool_call)
115
- pending_tool_calls.add(tool_call_id)
111
+
112
+ # Check if we have a response for this tool call
113
+ if tool_call_id in all_function_responses:
114
+ func_response = all_function_responses[tool_call_id]
115
+ tool_message: ChatCompletionToolMessageParam = {
116
+ "role": "tool",
117
+ "tool_call_id": tool_call_id,
118
+ "content": str(func_response.response.get("result", "")) if func_response.response else "",
119
+ }
120
+ tool_response_messages.append(tool_message)
121
+ else:
122
+ # If no response is available, create a placeholder response
123
+ # This prevents the OpenAI API error
124
+ tool_message: ChatCompletionToolMessageParam = {
125
+ "role": "tool",
126
+ "tool_call_id": tool_call_id,
127
+ "content": "No response available for this function call.",
128
+ }
129
+ tool_response_messages.append(tool_message)
116
130
 
117
131
  # Create assistant message with tool calls
118
132
  text_content = "\n".join(text_parts) if text_parts else None
@@ -123,6 +137,9 @@ def _convert_content_to_openai_messages(
123
137
  }
124
138
  messages.append(assistant_message)
125
139
 
140
+ # Add all tool response messages immediately after the assistant message
141
+ messages.extend(tool_response_messages)
142
+
126
143
  # Handle regular text/image messages (only if no function calls)
127
144
  elif text_parts or image_parts:
128
145
  if role == "user":
File without changes
File without changes
File without changes