hackagent 0.3.1__py3-none-any.whl

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.
Files changed (183) hide show
  1. hackagent/__init__.py +12 -0
  2. hackagent/agent.py +214 -0
  3. hackagent/api/__init__.py +1 -0
  4. hackagent/api/agent/__init__.py +1 -0
  5. hackagent/api/agent/agent_create.py +347 -0
  6. hackagent/api/agent/agent_destroy.py +140 -0
  7. hackagent/api/agent/agent_list.py +242 -0
  8. hackagent/api/agent/agent_partial_update.py +361 -0
  9. hackagent/api/agent/agent_retrieve.py +235 -0
  10. hackagent/api/agent/agent_update.py +361 -0
  11. hackagent/api/apilogs/__init__.py +1 -0
  12. hackagent/api/apilogs/apilogs_list.py +170 -0
  13. hackagent/api/apilogs/apilogs_retrieve.py +162 -0
  14. hackagent/api/attack/__init__.py +1 -0
  15. hackagent/api/attack/attack_create.py +275 -0
  16. hackagent/api/attack/attack_destroy.py +146 -0
  17. hackagent/api/attack/attack_list.py +254 -0
  18. hackagent/api/attack/attack_partial_update.py +289 -0
  19. hackagent/api/attack/attack_retrieve.py +247 -0
  20. hackagent/api/attack/attack_update.py +289 -0
  21. hackagent/api/checkout/__init__.py +1 -0
  22. hackagent/api/checkout/checkout_create.py +225 -0
  23. hackagent/api/generate/__init__.py +1 -0
  24. hackagent/api/generate/generate_create.py +253 -0
  25. hackagent/api/judge/__init__.py +1 -0
  26. hackagent/api/judge/judge_create.py +253 -0
  27. hackagent/api/key/__init__.py +1 -0
  28. hackagent/api/key/key_create.py +179 -0
  29. hackagent/api/key/key_destroy.py +103 -0
  30. hackagent/api/key/key_list.py +170 -0
  31. hackagent/api/key/key_retrieve.py +162 -0
  32. hackagent/api/organization/__init__.py +1 -0
  33. hackagent/api/organization/organization_create.py +208 -0
  34. hackagent/api/organization/organization_destroy.py +104 -0
  35. hackagent/api/organization/organization_list.py +170 -0
  36. hackagent/api/organization/organization_me_retrieve.py +126 -0
  37. hackagent/api/organization/organization_partial_update.py +222 -0
  38. hackagent/api/organization/organization_retrieve.py +163 -0
  39. hackagent/api/organization/organization_update.py +222 -0
  40. hackagent/api/prompt/__init__.py +1 -0
  41. hackagent/api/prompt/prompt_create.py +171 -0
  42. hackagent/api/prompt/prompt_destroy.py +104 -0
  43. hackagent/api/prompt/prompt_list.py +185 -0
  44. hackagent/api/prompt/prompt_partial_update.py +185 -0
  45. hackagent/api/prompt/prompt_retrieve.py +163 -0
  46. hackagent/api/prompt/prompt_update.py +185 -0
  47. hackagent/api/result/__init__.py +1 -0
  48. hackagent/api/result/result_create.py +175 -0
  49. hackagent/api/result/result_destroy.py +106 -0
  50. hackagent/api/result/result_list.py +249 -0
  51. hackagent/api/result/result_partial_update.py +193 -0
  52. hackagent/api/result/result_retrieve.py +167 -0
  53. hackagent/api/result/result_trace_create.py +177 -0
  54. hackagent/api/result/result_update.py +189 -0
  55. hackagent/api/run/__init__.py +1 -0
  56. hackagent/api/run/run_create.py +187 -0
  57. hackagent/api/run/run_destroy.py +112 -0
  58. hackagent/api/run/run_list.py +291 -0
  59. hackagent/api/run/run_partial_update.py +201 -0
  60. hackagent/api/run/run_result_create.py +177 -0
  61. hackagent/api/run/run_retrieve.py +179 -0
  62. hackagent/api/run/run_run_tests_create.py +187 -0
  63. hackagent/api/run/run_update.py +201 -0
  64. hackagent/api/user/__init__.py +1 -0
  65. hackagent/api/user/user_create.py +212 -0
  66. hackagent/api/user/user_destroy.py +106 -0
  67. hackagent/api/user/user_list.py +174 -0
  68. hackagent/api/user/user_me_retrieve.py +126 -0
  69. hackagent/api/user/user_me_update.py +196 -0
  70. hackagent/api/user/user_partial_update.py +226 -0
  71. hackagent/api/user/user_retrieve.py +167 -0
  72. hackagent/api/user/user_update.py +226 -0
  73. hackagent/attacks/AdvPrefix/__init__.py +41 -0
  74. hackagent/attacks/AdvPrefix/completions.py +416 -0
  75. hackagent/attacks/AdvPrefix/config.py +259 -0
  76. hackagent/attacks/AdvPrefix/evaluation.py +745 -0
  77. hackagent/attacks/AdvPrefix/evaluators.py +564 -0
  78. hackagent/attacks/AdvPrefix/generate.py +711 -0
  79. hackagent/attacks/AdvPrefix/utils.py +307 -0
  80. hackagent/attacks/__init__.py +35 -0
  81. hackagent/attacks/advprefix.py +507 -0
  82. hackagent/attacks/base.py +106 -0
  83. hackagent/attacks/strategies.py +906 -0
  84. hackagent/cli/__init__.py +19 -0
  85. hackagent/cli/commands/__init__.py +20 -0
  86. hackagent/cli/commands/agent.py +100 -0
  87. hackagent/cli/commands/attack.py +417 -0
  88. hackagent/cli/commands/config.py +301 -0
  89. hackagent/cli/commands/results.py +327 -0
  90. hackagent/cli/config.py +249 -0
  91. hackagent/cli/main.py +515 -0
  92. hackagent/cli/tui/__init__.py +31 -0
  93. hackagent/cli/tui/actions_logger.py +200 -0
  94. hackagent/cli/tui/app.py +288 -0
  95. hackagent/cli/tui/base.py +137 -0
  96. hackagent/cli/tui/logger.py +318 -0
  97. hackagent/cli/tui/views/__init__.py +33 -0
  98. hackagent/cli/tui/views/agents.py +488 -0
  99. hackagent/cli/tui/views/attacks.py +624 -0
  100. hackagent/cli/tui/views/config.py +244 -0
  101. hackagent/cli/tui/views/dashboard.py +307 -0
  102. hackagent/cli/tui/views/results.py +1210 -0
  103. hackagent/cli/tui/widgets/__init__.py +24 -0
  104. hackagent/cli/tui/widgets/actions.py +346 -0
  105. hackagent/cli/tui/widgets/logs.py +435 -0
  106. hackagent/cli/utils.py +276 -0
  107. hackagent/client.py +286 -0
  108. hackagent/errors.py +37 -0
  109. hackagent/logger.py +83 -0
  110. hackagent/models/__init__.py +109 -0
  111. hackagent/models/agent.py +223 -0
  112. hackagent/models/agent_request.py +129 -0
  113. hackagent/models/api_token_log.py +184 -0
  114. hackagent/models/attack.py +154 -0
  115. hackagent/models/attack_request.py +82 -0
  116. hackagent/models/checkout_session_request_request.py +76 -0
  117. hackagent/models/checkout_session_response.py +59 -0
  118. hackagent/models/choice.py +81 -0
  119. hackagent/models/choice_message.py +67 -0
  120. hackagent/models/evaluation_status_enum.py +14 -0
  121. hackagent/models/generate_error_response.py +59 -0
  122. hackagent/models/generate_request_request.py +212 -0
  123. hackagent/models/generate_success_response.py +115 -0
  124. hackagent/models/generic_error_response.py +70 -0
  125. hackagent/models/message_request.py +67 -0
  126. hackagent/models/organization.py +102 -0
  127. hackagent/models/organization_minimal.py +68 -0
  128. hackagent/models/organization_request.py +71 -0
  129. hackagent/models/paginated_agent_list.py +123 -0
  130. hackagent/models/paginated_api_token_log_list.py +123 -0
  131. hackagent/models/paginated_attack_list.py +123 -0
  132. hackagent/models/paginated_organization_list.py +123 -0
  133. hackagent/models/paginated_prompt_list.py +123 -0
  134. hackagent/models/paginated_result_list.py +123 -0
  135. hackagent/models/paginated_run_list.py +123 -0
  136. hackagent/models/paginated_user_api_key_list.py +123 -0
  137. hackagent/models/paginated_user_profile_list.py +123 -0
  138. hackagent/models/patched_agent_request.py +128 -0
  139. hackagent/models/patched_attack_request.py +92 -0
  140. hackagent/models/patched_organization_request.py +71 -0
  141. hackagent/models/patched_prompt_request.py +125 -0
  142. hackagent/models/patched_result_request.py +237 -0
  143. hackagent/models/patched_run_request.py +138 -0
  144. hackagent/models/patched_user_profile_request.py +99 -0
  145. hackagent/models/prompt.py +220 -0
  146. hackagent/models/prompt_request.py +126 -0
  147. hackagent/models/result.py +294 -0
  148. hackagent/models/result_list_evaluation_status.py +14 -0
  149. hackagent/models/result_request.py +232 -0
  150. hackagent/models/run.py +233 -0
  151. hackagent/models/run_list_status.py +12 -0
  152. hackagent/models/run_request.py +133 -0
  153. hackagent/models/status_enum.py +12 -0
  154. hackagent/models/step_type_enum.py +14 -0
  155. hackagent/models/trace.py +121 -0
  156. hackagent/models/trace_request.py +94 -0
  157. hackagent/models/usage.py +75 -0
  158. hackagent/models/user_api_key.py +201 -0
  159. hackagent/models/user_api_key_request.py +73 -0
  160. hackagent/models/user_profile.py +135 -0
  161. hackagent/models/user_profile_minimal.py +76 -0
  162. hackagent/models/user_profile_request.py +99 -0
  163. hackagent/router/__init__.py +25 -0
  164. hackagent/router/adapters/__init__.py +20 -0
  165. hackagent/router/adapters/base.py +63 -0
  166. hackagent/router/adapters/google_adk.py +671 -0
  167. hackagent/router/adapters/litellm_adapter.py +524 -0
  168. hackagent/router/adapters/openai_adapter.py +426 -0
  169. hackagent/router/router.py +969 -0
  170. hackagent/router/types.py +54 -0
  171. hackagent/tracking/__init__.py +42 -0
  172. hackagent/tracking/context.py +163 -0
  173. hackagent/tracking/decorators.py +299 -0
  174. hackagent/tracking/tracker.py +441 -0
  175. hackagent/types.py +54 -0
  176. hackagent/utils.py +194 -0
  177. hackagent/vulnerabilities/__init__.py +13 -0
  178. hackagent/vulnerabilities/prompts.py +81 -0
  179. hackagent-0.3.1.dist-info/METADATA +122 -0
  180. hackagent-0.3.1.dist-info/RECORD +183 -0
  181. hackagent-0.3.1.dist-info/WHEEL +4 -0
  182. hackagent-0.3.1.dist-info/entry_points.txt +2 -0
  183. hackagent-0.3.1.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,426 @@
1
+ # Copyright 2025 - AI4I. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ import logging
17
+ import os
18
+ from typing import Any, Dict, List, Optional
19
+
20
+ # Attempt to import openai, but catch ImportError if not installed.
21
+ try:
22
+ from openai import (
23
+ APIConnectionError,
24
+ APITimeoutError,
25
+ OpenAI,
26
+ OpenAIError,
27
+ RateLimitError,
28
+ )
29
+
30
+ OPENAI_AVAILABLE = True
31
+ except ImportError:
32
+ OpenAI = None # type: ignore
33
+
34
+ # Define dummy exceptions if openai is not available
35
+ class OpenAIError(Exception):
36
+ pass
37
+
38
+ class APIConnectionError(Exception):
39
+ pass
40
+
41
+ class RateLimitError(Exception):
42
+ pass
43
+
44
+ class APITimeoutError(Exception):
45
+ pass
46
+
47
+ OPENAI_AVAILABLE = False
48
+
49
+
50
+ from .base import Agent
51
+
52
+
53
+ # --- Custom Exceptions ---
54
+ class OpenAIConfigurationError(Exception):
55
+ """Custom exception for OpenAI adapter configuration issues."""
56
+
57
+ pass
58
+
59
+
60
+ logger = logging.getLogger(__name__) # Module-level logger
61
+
62
+
63
+ class OpenAIAgentAdapter(Agent):
64
+ """
65
+ Adapter for interacting with AI agents built using the OpenAI SDK.
66
+
67
+ This adapter supports OpenAI's chat completions API, including support for
68
+ function calling and tool use, which are common patterns in agent implementations.
69
+ """
70
+
71
+ def __init__(self, id: str, config: Dict[str, Any]):
72
+ """
73
+ Initializes the OpenAIAgentAdapter.
74
+
75
+ Args:
76
+ id: The unique identifier for this OpenAI agent instance.
77
+ config: Configuration dictionary for the OpenAI agent.
78
+ Expected keys:
79
+ - 'name': Model name (e.g., "gpt-4", "gpt-3.5-turbo").
80
+ - 'endpoint' (optional): Base URL for the API (for custom endpoints).
81
+ - 'api_key' (optional): Name of the environment variable holding the API key,
82
+ or the API key itself. Defaults to OPENAI_API_KEY env var.
83
+ - 'max_tokens' (optional): Default max tokens for generation.
84
+ - 'temperature' (optional): Default temperature (defaults to 1.0).
85
+ - 'tools' (optional): List of tool/function definitions for function calling.
86
+ - 'tool_choice' (optional): Controls which tools the model can call.
87
+ """
88
+ super().__init__(id, config)
89
+ # Use hierarchical logger name for TUI handler inheritance
90
+ self.logger = logging.getLogger(
91
+ f"hackagent.router.adapters.OpenAIAgentAdapter.{self.id}"
92
+ )
93
+
94
+ if not OPENAI_AVAILABLE:
95
+ msg = (
96
+ f"OpenAI SDK is not installed. Please install it with: pip install openai. "
97
+ f"OpenAIAgentAdapter: {self.id}"
98
+ )
99
+ self.logger.error(msg)
100
+ raise OpenAIConfigurationError(msg)
101
+
102
+ if "name" not in self.config:
103
+ msg = f"Missing required configuration key 'name' (for model string) for OpenAIAgentAdapter: {self.id}"
104
+ self.logger.error(msg)
105
+ raise OpenAIConfigurationError(msg)
106
+
107
+ self.model_name: str = self.config["name"]
108
+ self.api_base_url: Optional[str] = self.config.get("endpoint")
109
+
110
+ # Handle API key: can be env var name or the key itself
111
+ api_key_config: Optional[str] = self.config.get("api_key")
112
+ if api_key_config:
113
+ # Try as environment variable first
114
+ self.actual_api_key: Optional[str] = os.environ.get(
115
+ api_key_config, api_key_config
116
+ )
117
+ else:
118
+ # Default to OPENAI_API_KEY environment variable
119
+ self.actual_api_key = os.environ.get("OPENAI_API_KEY")
120
+
121
+ # Initialize OpenAI client
122
+ client_kwargs = {}
123
+ if self.actual_api_key:
124
+ client_kwargs["api_key"] = self.actual_api_key
125
+ if self.api_base_url:
126
+ client_kwargs["base_url"] = self.api_base_url
127
+
128
+ self.client = OpenAI(**client_kwargs)
129
+
130
+ self.logger.info(
131
+ f"OpenAIAgentAdapter '{self.id}' initialized for model: '{self.model_name}'"
132
+ + (f" API Base: '{self.api_base_url}'" if self.api_base_url else "")
133
+ )
134
+
135
+ # Store default generation parameters
136
+ self.default_max_tokens = self.config.get("max_tokens")
137
+ self.default_temperature = self.config.get("temperature", 1.0)
138
+ self.default_tools = self.config.get("tools")
139
+ self.default_tool_choice = self.config.get("tool_choice")
140
+
141
+ def _execute_openai_completion(
142
+ self,
143
+ messages: List[Dict[str, str]],
144
+ max_tokens: Optional[int],
145
+ temperature: float,
146
+ tools: Optional[List[Dict[str, Any]]] = None,
147
+ tool_choice: Optional[str] = None,
148
+ **kwargs,
149
+ ) -> Dict[str, Any]:
150
+ """
151
+ Internal method to generate completions using OpenAI's chat completions API.
152
+
153
+ Args:
154
+ messages: List of message dictionaries with 'role' and 'content'.
155
+ max_tokens: Maximum tokens to generate.
156
+ temperature: Sampling temperature.
157
+ tools: Optional list of tool/function definitions.
158
+ tool_choice: Optional tool choice parameter.
159
+ **kwargs: Additional parameters to pass to the API.
160
+
161
+ Returns:
162
+ A dictionary containing the response data and metadata.
163
+ """
164
+ self.logger.info(
165
+ f"Sending request to OpenAI model '{self.model_name}' with {len(messages)} messages..."
166
+ )
167
+
168
+ try:
169
+ openai_params = {
170
+ "model": self.model_name,
171
+ "messages": messages,
172
+ "temperature": temperature,
173
+ }
174
+
175
+ if max_tokens is not None:
176
+ openai_params["max_tokens"] = max_tokens
177
+
178
+ if tools:
179
+ openai_params["tools"] = tools
180
+ if tool_choice:
181
+ openai_params["tool_choice"] = tool_choice
182
+
183
+ # Add any additional kwargs
184
+ openai_params.update(kwargs)
185
+
186
+ # Make the API call
187
+ response = self.client.chat.completions.create(**openai_params)
188
+
189
+ # Extract response data
190
+ result = {
191
+ "success": True,
192
+ "message": response.choices[0].message,
193
+ "finish_reason": response.choices[0].finish_reason,
194
+ "usage": response.usage.model_dump() if response.usage else None,
195
+ "model": response.model,
196
+ "raw_response": response,
197
+ }
198
+
199
+ self.logger.info(
200
+ f"Successfully received response from OpenAI model '{self.model_name}'. "
201
+ f"Finish reason: {result['finish_reason']}"
202
+ )
203
+
204
+ return result
205
+
206
+ except APITimeoutError as e:
207
+ self.logger.error(
208
+ f"OpenAI API timeout for model '{self.model_name}': {e}", exc_info=True
209
+ )
210
+ return {
211
+ "success": False,
212
+ "error_type": "timeout",
213
+ "error_message": str(e),
214
+ }
215
+ except RateLimitError as e:
216
+ self.logger.error(
217
+ f"OpenAI rate limit exceeded for model '{self.model_name}': {e}",
218
+ exc_info=True,
219
+ )
220
+ return {
221
+ "success": False,
222
+ "error_type": "rate_limit",
223
+ "error_message": str(e),
224
+ }
225
+ except APIConnectionError as e:
226
+ self.logger.error(
227
+ f"OpenAI API connection error for model '{self.model_name}': {e}",
228
+ exc_info=True,
229
+ )
230
+ return {
231
+ "success": False,
232
+ "error_type": "connection",
233
+ "error_message": str(e),
234
+ }
235
+ except OpenAIError as e:
236
+ self.logger.error(
237
+ f"OpenAI API error for model '{self.model_name}': {e}", exc_info=True
238
+ )
239
+ return {
240
+ "success": False,
241
+ "error_type": "api_error",
242
+ "error_message": str(e),
243
+ }
244
+ except Exception as e:
245
+ self.logger.exception(
246
+ f"Unexpected error during OpenAI completion for model '{self.model_name}': {e}"
247
+ )
248
+ return {
249
+ "success": False,
250
+ "error_type": "unexpected",
251
+ "error_message": f"{type(e).__name__}: {str(e)}",
252
+ }
253
+
254
+ def handle_request(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
255
+ """
256
+ Handles an incoming request by processing it through the OpenAI API.
257
+
258
+ Args:
259
+ request_data: A dictionary containing the request data.
260
+ Expected keys:
261
+ - 'prompt': The text prompt to send to the model (will be converted to messages).
262
+ - 'messages' (optional): Pre-formatted messages list (takes precedence over prompt).
263
+ - 'max_tokens' (optional): Override default max tokens.
264
+ - 'temperature' (optional): Override default temperature.
265
+ - 'tools' (optional): Override default tools.
266
+ - 'tool_choice' (optional): Override default tool choice.
267
+ - Any other kwargs to pass to the OpenAI API.
268
+
269
+ Returns:
270
+ A dictionary representing the agent's response or an error.
271
+ """
272
+ # Get messages or convert prompt to messages
273
+ messages = request_data.get("messages")
274
+ prompt_text = request_data.get("prompt")
275
+
276
+ if not messages and not prompt_text:
277
+ self.logger.warning("No 'messages' or 'prompt' found in request_data.")
278
+ return self._build_error_response(
279
+ error_message="Request data must include either 'messages' or 'prompt' field.",
280
+ status_code=400,
281
+ raw_request=request_data,
282
+ )
283
+
284
+ # Convert prompt to messages if messages not provided
285
+ if not messages:
286
+ messages = [{"role": "user", "content": prompt_text}]
287
+
288
+ self.logger.info(
289
+ f"Handling request for OpenAI adapter {self.id} with {len(messages)} messages"
290
+ )
291
+
292
+ # Get parameters with defaults
293
+ max_tokens = request_data.get("max_tokens", self.default_max_tokens)
294
+ temperature = request_data.get("temperature", self.default_temperature)
295
+ tools = request_data.get("tools", self.default_tools)
296
+ tool_choice = request_data.get("tool_choice", self.default_tool_choice)
297
+
298
+ # Get additional kwargs (exclude known parameters)
299
+ excluded_keys = {
300
+ "prompt",
301
+ "messages",
302
+ "max_tokens",
303
+ "temperature",
304
+ "tools",
305
+ "tool_choice",
306
+ }
307
+ additional_kwargs = {
308
+ k: v for k, v in request_data.items() if k not in excluded_keys
309
+ }
310
+
311
+ try:
312
+ # Execute the completion
313
+ result = self._execute_openai_completion(
314
+ messages=messages,
315
+ max_tokens=max_tokens,
316
+ temperature=temperature,
317
+ tools=tools,
318
+ tool_choice=tool_choice,
319
+ **additional_kwargs,
320
+ )
321
+
322
+ if not result.get("success"):
323
+ # Handle API errors
324
+ return self._build_error_response(
325
+ error_message=f"OpenAI API error ({result.get('error_type')}): {result.get('error_message')}",
326
+ status_code=500,
327
+ raw_request=request_data,
328
+ )
329
+
330
+ # Extract the generated text
331
+ message = result["message"]
332
+ content = message.content if message.content else ""
333
+
334
+ # For reasoning models (e.g., o1-preview, o1-mini), check reasoning field
335
+ if not content and hasattr(message, "reasoning") and message.reasoning:
336
+ generated_text = message.reasoning
337
+ self.logger.info(
338
+ f"OpenAI extracted text from 'reasoning' field (reasoning model) for '{self.model_name}'"
339
+ )
340
+ else:
341
+ generated_text = content
342
+
343
+ # Check if there are tool calls
344
+ tool_calls = None
345
+ if hasattr(message, "tool_calls") and message.tool_calls:
346
+ tool_calls = [
347
+ {
348
+ "id": tc.id,
349
+ "type": tc.type,
350
+ "function": {
351
+ "name": tc.function.name,
352
+ "arguments": tc.function.arguments,
353
+ },
354
+ }
355
+ for tc in message.tool_calls
356
+ ]
357
+
358
+ self.logger.info(
359
+ f"Successfully processed request for OpenAI adapter {self.id}."
360
+ )
361
+
362
+ return {
363
+ "raw_request": request_data,
364
+ "generated_text": generated_text,
365
+ "processed_response": generated_text,
366
+ "status_code": 200,
367
+ "raw_response_headers": None,
368
+ "raw_response_body": None,
369
+ "agent_specific_data": {
370
+ "model_name": self.model_name,
371
+ "finish_reason": result["finish_reason"],
372
+ "usage": result.get("usage"),
373
+ "tool_calls": tool_calls,
374
+ "invoked_parameters": {
375
+ "max_tokens": max_tokens,
376
+ "temperature": temperature,
377
+ "tools_provided": tools is not None,
378
+ **additional_kwargs,
379
+ },
380
+ },
381
+ "error_message": None,
382
+ "agent_id": self.id,
383
+ "adapter_type": "OpenAIAgentAdapter",
384
+ }
385
+
386
+ except Exception as e:
387
+ self.logger.exception(
388
+ f"Unexpected error in OpenAIAgentAdapter handle_request for agent {self.id}: {e}"
389
+ )
390
+ return self._build_error_response(
391
+ error_message=f"Unexpected adapter error: {type(e).__name__} - {str(e)}",
392
+ status_code=500,
393
+ raw_request=request_data,
394
+ )
395
+
396
+ def _build_error_response(
397
+ self,
398
+ error_message: str,
399
+ status_code: Optional[int],
400
+ raw_request: Optional[Dict[str, Any]] = None,
401
+ ) -> Dict[str, Any]:
402
+ """
403
+ Constructs a standardized error response dictionary for the adapter.
404
+
405
+ Args:
406
+ error_message: The primary error message string.
407
+ status_code: The HTTP status code associated with the error.
408
+ raw_request: The original request data that led to the error.
409
+
410
+ Returns:
411
+ A dictionary representing a standardized error response.
412
+ """
413
+ return {
414
+ "raw_request": raw_request,
415
+ "processed_response": None,
416
+ "generated_text": None,
417
+ "status_code": status_code if status_code is not None else 500,
418
+ "raw_response_headers": None,
419
+ "raw_response_body": None,
420
+ "agent_specific_data": {
421
+ "model_name": self.model_name if hasattr(self, "model_name") else "N/A"
422
+ },
423
+ "error_message": error_message,
424
+ "agent_id": self.id,
425
+ "adapter_type": "OpenAIAgentAdapter",
426
+ }