aiqtoolkit 1.2.0a20250706__py3-none-any.whl → 1.2.0a20250730__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.

Potentially problematic release.


This version of aiqtoolkit might be problematic. Click here for more details.

Files changed (197) hide show
  1. aiq/agent/base.py +171 -8
  2. aiq/agent/dual_node.py +1 -1
  3. aiq/agent/react_agent/agent.py +113 -113
  4. aiq/agent/react_agent/register.py +31 -14
  5. aiq/agent/rewoo_agent/agent.py +36 -35
  6. aiq/agent/rewoo_agent/register.py +2 -2
  7. aiq/agent/tool_calling_agent/agent.py +3 -7
  8. aiq/authentication/__init__.py +14 -0
  9. aiq/authentication/api_key/__init__.py +14 -0
  10. aiq/authentication/api_key/api_key_auth_provider.py +92 -0
  11. aiq/authentication/api_key/api_key_auth_provider_config.py +124 -0
  12. aiq/authentication/api_key/register.py +26 -0
  13. aiq/authentication/exceptions/__init__.py +14 -0
  14. aiq/authentication/exceptions/api_key_exceptions.py +38 -0
  15. aiq/authentication/exceptions/auth_code_grant_exceptions.py +86 -0
  16. aiq/authentication/exceptions/call_back_exceptions.py +38 -0
  17. aiq/authentication/exceptions/request_exceptions.py +54 -0
  18. aiq/authentication/http_basic_auth/__init__.py +0 -0
  19. aiq/authentication/http_basic_auth/http_basic_auth_provider.py +81 -0
  20. aiq/authentication/http_basic_auth/register.py +30 -0
  21. aiq/authentication/interfaces.py +93 -0
  22. aiq/authentication/oauth2/__init__.py +14 -0
  23. aiq/authentication/oauth2/oauth2_auth_code_flow_provider.py +107 -0
  24. aiq/authentication/oauth2/oauth2_auth_code_flow_provider_config.py +39 -0
  25. aiq/authentication/oauth2/register.py +25 -0
  26. aiq/authentication/register.py +21 -0
  27. aiq/builder/builder.py +64 -2
  28. aiq/builder/component_utils.py +16 -3
  29. aiq/builder/context.py +26 -0
  30. aiq/builder/eval_builder.py +43 -2
  31. aiq/builder/function.py +32 -4
  32. aiq/builder/function_base.py +1 -1
  33. aiq/builder/intermediate_step_manager.py +6 -8
  34. aiq/builder/user_interaction_manager.py +3 -0
  35. aiq/builder/workflow.py +23 -18
  36. aiq/builder/workflow_builder.py +420 -73
  37. aiq/cli/commands/info/list_mcp.py +103 -16
  38. aiq/cli/commands/sizing/__init__.py +14 -0
  39. aiq/cli/commands/sizing/calc.py +294 -0
  40. aiq/cli/commands/sizing/sizing.py +27 -0
  41. aiq/cli/commands/start.py +1 -0
  42. aiq/cli/entrypoint.py +2 -0
  43. aiq/cli/register_workflow.py +80 -0
  44. aiq/cli/type_registry.py +151 -30
  45. aiq/data_models/api_server.py +117 -11
  46. aiq/data_models/authentication.py +231 -0
  47. aiq/data_models/common.py +35 -7
  48. aiq/data_models/component.py +17 -9
  49. aiq/data_models/component_ref.py +33 -0
  50. aiq/data_models/config.py +60 -3
  51. aiq/data_models/embedder.py +1 -0
  52. aiq/data_models/function_dependencies.py +8 -0
  53. aiq/data_models/interactive.py +10 -1
  54. aiq/data_models/intermediate_step.py +15 -5
  55. aiq/data_models/its_strategy.py +30 -0
  56. aiq/data_models/llm.py +1 -0
  57. aiq/data_models/memory.py +1 -0
  58. aiq/data_models/object_store.py +44 -0
  59. aiq/data_models/retry_mixin.py +35 -0
  60. aiq/data_models/span.py +187 -0
  61. aiq/data_models/telemetry_exporter.py +2 -2
  62. aiq/embedder/nim_embedder.py +2 -1
  63. aiq/embedder/openai_embedder.py +2 -1
  64. aiq/eval/config.py +19 -1
  65. aiq/eval/dataset_handler/dataset_handler.py +75 -1
  66. aiq/eval/evaluate.py +53 -10
  67. aiq/eval/rag_evaluator/evaluate.py +23 -12
  68. aiq/eval/remote_workflow.py +7 -2
  69. aiq/eval/runners/__init__.py +14 -0
  70. aiq/eval/runners/config.py +39 -0
  71. aiq/eval/runners/multi_eval_runner.py +54 -0
  72. aiq/eval/usage_stats.py +6 -0
  73. aiq/eval/utils/weave_eval.py +5 -1
  74. aiq/experimental/__init__.py +0 -0
  75. aiq/experimental/decorators/__init__.py +0 -0
  76. aiq/experimental/decorators/experimental_warning_decorator.py +130 -0
  77. aiq/experimental/inference_time_scaling/__init__.py +0 -0
  78. aiq/experimental/inference_time_scaling/editing/__init__.py +0 -0
  79. aiq/experimental/inference_time_scaling/editing/iterative_plan_refinement_editor.py +147 -0
  80. aiq/experimental/inference_time_scaling/editing/llm_as_a_judge_editor.py +204 -0
  81. aiq/experimental/inference_time_scaling/editing/motivation_aware_summarization.py +107 -0
  82. aiq/experimental/inference_time_scaling/functions/__init__.py +0 -0
  83. aiq/experimental/inference_time_scaling/functions/execute_score_select_function.py +105 -0
  84. aiq/experimental/inference_time_scaling/functions/its_tool_orchestration_function.py +205 -0
  85. aiq/experimental/inference_time_scaling/functions/its_tool_wrapper_function.py +146 -0
  86. aiq/experimental/inference_time_scaling/functions/plan_select_execute_function.py +224 -0
  87. aiq/experimental/inference_time_scaling/models/__init__.py +0 -0
  88. aiq/experimental/inference_time_scaling/models/editor_config.py +132 -0
  89. aiq/experimental/inference_time_scaling/models/its_item.py +48 -0
  90. aiq/experimental/inference_time_scaling/models/scoring_config.py +112 -0
  91. aiq/experimental/inference_time_scaling/models/search_config.py +120 -0
  92. aiq/experimental/inference_time_scaling/models/selection_config.py +154 -0
  93. aiq/experimental/inference_time_scaling/models/stage_enums.py +43 -0
  94. aiq/experimental/inference_time_scaling/models/strategy_base.py +66 -0
  95. aiq/experimental/inference_time_scaling/models/tool_use_config.py +41 -0
  96. aiq/experimental/inference_time_scaling/register.py +36 -0
  97. aiq/experimental/inference_time_scaling/scoring/__init__.py +0 -0
  98. aiq/experimental/inference_time_scaling/scoring/llm_based_agent_scorer.py +168 -0
  99. aiq/experimental/inference_time_scaling/scoring/llm_based_plan_scorer.py +168 -0
  100. aiq/experimental/inference_time_scaling/scoring/motivation_aware_scorer.py +111 -0
  101. aiq/experimental/inference_time_scaling/search/__init__.py +0 -0
  102. aiq/experimental/inference_time_scaling/search/multi_llm_planner.py +128 -0
  103. aiq/experimental/inference_time_scaling/search/multi_query_retrieval_search.py +122 -0
  104. aiq/experimental/inference_time_scaling/search/single_shot_multi_plan_planner.py +128 -0
  105. aiq/experimental/inference_time_scaling/selection/__init__.py +0 -0
  106. aiq/experimental/inference_time_scaling/selection/best_of_n_selector.py +63 -0
  107. aiq/experimental/inference_time_scaling/selection/llm_based_agent_output_selector.py +131 -0
  108. aiq/experimental/inference_time_scaling/selection/llm_based_output_merging_selector.py +159 -0
  109. aiq/experimental/inference_time_scaling/selection/llm_based_plan_selector.py +128 -0
  110. aiq/experimental/inference_time_scaling/selection/threshold_selector.py +58 -0
  111. aiq/front_ends/console/authentication_flow_handler.py +233 -0
  112. aiq/front_ends/console/console_front_end_plugin.py +11 -2
  113. aiq/front_ends/fastapi/auth_flow_handlers/__init__.py +0 -0
  114. aiq/front_ends/fastapi/auth_flow_handlers/http_flow_handler.py +27 -0
  115. aiq/front_ends/fastapi/auth_flow_handlers/websocket_flow_handler.py +107 -0
  116. aiq/front_ends/fastapi/fastapi_front_end_config.py +20 -0
  117. aiq/front_ends/fastapi/fastapi_front_end_controller.py +68 -0
  118. aiq/front_ends/fastapi/fastapi_front_end_plugin.py +14 -1
  119. aiq/front_ends/fastapi/fastapi_front_end_plugin_worker.py +353 -31
  120. aiq/front_ends/fastapi/html_snippets/__init__.py +14 -0
  121. aiq/front_ends/fastapi/html_snippets/auth_code_grant_success.py +35 -0
  122. aiq/front_ends/fastapi/main.py +2 -0
  123. aiq/front_ends/fastapi/message_handler.py +102 -84
  124. aiq/front_ends/fastapi/step_adaptor.py +2 -1
  125. aiq/llm/aws_bedrock_llm.py +2 -1
  126. aiq/llm/nim_llm.py +2 -1
  127. aiq/llm/openai_llm.py +2 -1
  128. aiq/object_store/__init__.py +20 -0
  129. aiq/object_store/in_memory_object_store.py +74 -0
  130. aiq/object_store/interfaces.py +84 -0
  131. aiq/object_store/models.py +36 -0
  132. aiq/object_store/register.py +20 -0
  133. aiq/observability/__init__.py +14 -0
  134. aiq/observability/exporter/__init__.py +14 -0
  135. aiq/observability/exporter/base_exporter.py +449 -0
  136. aiq/observability/exporter/exporter.py +78 -0
  137. aiq/observability/exporter/file_exporter.py +33 -0
  138. aiq/observability/exporter/processing_exporter.py +269 -0
  139. aiq/observability/exporter/raw_exporter.py +52 -0
  140. aiq/observability/exporter/span_exporter.py +264 -0
  141. aiq/observability/exporter_manager.py +335 -0
  142. aiq/observability/mixin/__init__.py +14 -0
  143. aiq/observability/mixin/batch_config_mixin.py +26 -0
  144. aiq/observability/mixin/collector_config_mixin.py +23 -0
  145. aiq/observability/mixin/file_mixin.py +288 -0
  146. aiq/observability/mixin/file_mode.py +23 -0
  147. aiq/observability/mixin/resource_conflict_mixin.py +134 -0
  148. aiq/observability/mixin/serialize_mixin.py +61 -0
  149. aiq/observability/mixin/type_introspection_mixin.py +183 -0
  150. aiq/observability/processor/__init__.py +14 -0
  151. aiq/observability/processor/batching_processor.py +316 -0
  152. aiq/observability/processor/intermediate_step_serializer.py +28 -0
  153. aiq/observability/processor/processor.py +68 -0
  154. aiq/observability/register.py +32 -116
  155. aiq/observability/utils/__init__.py +14 -0
  156. aiq/observability/utils/dict_utils.py +236 -0
  157. aiq/observability/utils/time_utils.py +31 -0
  158. aiq/profiler/calc/__init__.py +14 -0
  159. aiq/profiler/calc/calc_runner.py +623 -0
  160. aiq/profiler/calc/calculations.py +288 -0
  161. aiq/profiler/calc/data_models.py +176 -0
  162. aiq/profiler/calc/plot.py +345 -0
  163. aiq/profiler/data_models.py +2 -0
  164. aiq/profiler/profile_runner.py +16 -13
  165. aiq/runtime/loader.py +8 -2
  166. aiq/runtime/runner.py +23 -9
  167. aiq/runtime/session.py +16 -5
  168. aiq/tool/chat_completion.py +74 -0
  169. aiq/tool/code_execution/README.md +152 -0
  170. aiq/tool/code_execution/code_sandbox.py +151 -72
  171. aiq/tool/code_execution/local_sandbox/.gitignore +1 -0
  172. aiq/tool/code_execution/local_sandbox/local_sandbox_server.py +139 -24
  173. aiq/tool/code_execution/local_sandbox/sandbox.requirements.txt +3 -1
  174. aiq/tool/code_execution/local_sandbox/start_local_sandbox.sh +27 -2
  175. aiq/tool/code_execution/register.py +7 -3
  176. aiq/tool/code_execution/test_code_execution_sandbox.py +414 -0
  177. aiq/tool/mcp/exceptions.py +142 -0
  178. aiq/tool/mcp/mcp_client.py +17 -3
  179. aiq/tool/mcp/mcp_tool.py +1 -1
  180. aiq/tool/register.py +1 -0
  181. aiq/tool/server_tools.py +2 -2
  182. aiq/utils/exception_handlers/automatic_retries.py +289 -0
  183. aiq/utils/exception_handlers/mcp.py +211 -0
  184. aiq/utils/io/model_processing.py +28 -0
  185. aiq/utils/log_utils.py +37 -0
  186. aiq/utils/string_utils.py +38 -0
  187. aiq/utils/type_converter.py +18 -2
  188. aiq/utils/type_utils.py +87 -0
  189. {aiqtoolkit-1.2.0a20250706.dist-info → aiqtoolkit-1.2.0a20250730.dist-info}/METADATA +37 -9
  190. {aiqtoolkit-1.2.0a20250706.dist-info → aiqtoolkit-1.2.0a20250730.dist-info}/RECORD +195 -80
  191. {aiqtoolkit-1.2.0a20250706.dist-info → aiqtoolkit-1.2.0a20250730.dist-info}/entry_points.txt +3 -0
  192. aiq/front_ends/fastapi/websocket.py +0 -153
  193. aiq/observability/async_otel_listener.py +0 -470
  194. {aiqtoolkit-1.2.0a20250706.dist-info → aiqtoolkit-1.2.0a20250730.dist-info}/WHEEL +0 -0
  195. {aiqtoolkit-1.2.0a20250706.dist-info → aiqtoolkit-1.2.0a20250730.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
  196. {aiqtoolkit-1.2.0a20250706.dist-info → aiqtoolkit-1.2.0a20250730.dist-info}/licenses/LICENSE.md +0 -0
  197. {aiqtoolkit-1.2.0a20250706.dist-info → aiqtoolkit-1.2.0a20250730.dist-info}/top_level.txt +0 -0
@@ -15,14 +15,19 @@
15
15
 
16
16
  import asyncio
17
17
  import logging
18
+ import typing
18
19
  import uuid
19
20
  from typing import Any
20
21
 
21
22
  from fastapi import WebSocket
22
23
  from pydantic import BaseModel
23
24
  from pydantic import ValidationError
24
- from starlette.endpoints import WebSocketEndpoint
25
+ from starlette.websockets import WebSocketDisconnect
25
26
 
27
+ from aiq.authentication.interfaces import FlowHandlerBase
28
+ from aiq.data_models.api_server import AIQChatResponse
29
+ from aiq.data_models.api_server import AIQResponsePayloadOutput
30
+ from aiq.data_models.api_server import AIQResponseSerializable
26
31
  from aiq.data_models.api_server import Error
27
32
  from aiq.data_models.api_server import ErrorTypes
28
33
  from aiq.data_models.api_server import SystemResponseContent
@@ -39,74 +44,72 @@ from aiq.data_models.interactive import HumanResponse
39
44
  from aiq.data_models.interactive import HumanResponseNotification
40
45
  from aiq.data_models.interactive import InteractionPrompt
41
46
  from aiq.front_ends.fastapi.message_validator import MessageValidator
47
+ from aiq.front_ends.fastapi.response_helpers import generate_streaming_response
48
+ from aiq.front_ends.fastapi.step_adaptor import StepAdaptor
49
+ from aiq.runtime.session import AIQSessionManager
42
50
 
43
51
  logger = logging.getLogger(__name__)
44
52
 
45
53
 
46
- class MessageHandler:
54
+ class WebSocketMessageHandler:
55
+
56
+ def __init__(self, socket: WebSocket, session_manager: AIQSessionManager, step_adaptor: StepAdaptor):
57
+ self._socket: WebSocket = socket
58
+ self._session_manager: AIQSessionManager = session_manager
59
+ self._step_adaptor: StepAdaptor = step_adaptor
47
60
 
48
- def __init__(self, websocket_reference: WebSocketEndpoint):
49
- self._websocket_reference: WebSocketEndpoint = websocket_reference
50
61
  self._message_validator: MessageValidator = MessageValidator()
51
- self._messages_queue: asyncio.Queue[dict[str, str]] = asyncio.Queue()
52
- self._out_going_messages_queue: asyncio.Queue[dict] = asyncio.Queue()
53
- self._process_messages_task: asyncio.Task | None = None
54
- self._process_out_going_messages_task: asyncio.Task = None
55
- self._background_task: asyncio.Task = None
62
+ self._running_workflow_task: asyncio.Task | None = None
56
63
  self._message_parent_id: str = "default_id"
57
64
  self._workflow_schema_type: str = None
58
- self._user_interaction_response: asyncio.Future[TextContent] = asyncio.Future()
65
+ self._user_interaction_response: asyncio.Future[HumanResponse] | None = None
59
66
 
60
- @property
61
- def messages_queue(self) -> asyncio.Queue[dict[str, str]]:
62
- return self._messages_queue
67
+ self._flow_handler: FlowHandlerBase | None = None
63
68
 
64
- @property
65
- def background_task(self) -> asyncio.Task:
66
- return self._background_task
69
+ def set_flow_handler(self, flow_handler: FlowHandlerBase) -> None:
70
+ self._flow_handler = flow_handler
67
71
 
68
- @property
69
- def process_messages_task(self) -> asyncio.Task | None:
70
- return self._process_messages_task
72
+ async def __aenter__(self) -> "WebSocketMessageHandler":
73
+ await self._socket.accept()
71
74
 
72
- @process_messages_task.setter
73
- def process_messages_task(self, process_messages_task) -> None:
74
- self._process_messages_task = process_messages_task
75
+ return self
75
76
 
76
- @property
77
- def process_out_going_messages_task(self) -> asyncio.Task:
78
- return self._process_out_going_messages_task
77
+ async def __aexit__(self, exc_type, exc_value, traceback) -> None:
79
78
 
80
- @process_out_going_messages_task.setter
81
- def process_out_going_messages_task(self, process_out_going_messages_task) -> None:
82
- self._process_out_going_messages_task = process_out_going_messages_task
79
+ # TODO: Handle the exit
80
+ pass
83
81
 
84
- async def process_messages(self) -> None:
82
+ async def run(self) -> None:
85
83
  """
86
84
  Processes received messages from websocket and routes them appropriately.
87
85
  """
88
86
  while True:
89
87
 
90
88
  try:
91
- message: dict[str, Any] = await self._messages_queue.get()
89
+
90
+ message: dict[str, Any] = await self._socket.receive_json()
92
91
 
93
92
  validated_message: BaseModel = await self._message_validator.validate_message(message)
94
93
 
94
+ # Received a request to start a workflow
95
95
  if (isinstance(validated_message, WebSocketUserMessage)):
96
- await self.process_user_message(validated_message)
96
+ await self.process_workflow_request(validated_message)
97
97
 
98
- if isinstance(
98
+ elif isinstance(
99
99
  validated_message,
100
100
  ( # noqa: E131
101
101
  WebSocketSystemResponseTokenMessage,
102
102
  WebSocketSystemIntermediateStepMessage,
103
103
  WebSocketSystemInteractionMessage)):
104
- await self._out_going_messages_queue.put(validated_message.model_dump())
104
+ # These messages are already handled by self.create_websocket_message(data_model=value, …)
105
+ # No further processing is needed here.
106
+ pass
105
107
 
106
- if (isinstance(validated_message, WebSocketUserInteractionResponseMessage)):
108
+ elif (isinstance(validated_message, WebSocketUserInteractionResponseMessage)):
107
109
  user_content = await self.process_user_message_content(validated_message)
108
110
  self._user_interaction_response.set_result(user_content)
109
- except (asyncio.CancelledError):
111
+ except (asyncio.CancelledError, WebSocketDisconnect):
112
+ # TODO: Handle the disconnect
110
113
  break
111
114
 
112
115
  return None
@@ -130,7 +133,7 @@ class MessageHandler:
130
133
 
131
134
  return None
132
135
 
133
- async def process_user_message(self, user_message_as_validated_type: WebSocketUserMessage) -> None:
136
+ async def process_workflow_request(self, user_message_as_validated_type: WebSocketUserMessage) -> None:
134
137
  """
135
138
  Process user messages and routes them appropriately.
136
139
 
@@ -147,13 +150,15 @@ class MessageHandler:
147
150
  if content is None:
148
151
  raise ValueError(f"User message content could not be found: {user_message_as_validated_type}")
149
152
 
150
- if isinstance(content, TextContent) and (self._background_task is None):
153
+ if isinstance(content, TextContent) and (self._running_workflow_task is None):
151
154
 
152
- await self._process_response()
153
- self._background_task = asyncio.create_task(
154
- self._websocket_reference.workflow_schema_type.get(self._workflow_schema_type)(
155
- content.text, conversation_id)).add_done_callback(
156
- lambda task: asyncio.create_task(self._on_process_stream_task_done(task)))
155
+ def _done_callback(task: asyncio.Task):
156
+ self._running_workflow_task = None
157
+
158
+ # await self._process_response()
159
+ self._running_workflow_task = asyncio.create_task(
160
+ self._run_workflow(content.text, conversation_id,
161
+ result_type=AIQChatResponse)).add_done_callback(_done_callback)
157
162
 
158
163
  except ValueError as e:
159
164
  logger.error("User message content not found: %s", str(e), exc_info=True)
@@ -221,60 +226,73 @@ class MessageHandler:
221
226
  content=Error(code=ErrorTypes.UNKNOWN_ERROR, message="default", details=str(e)))
222
227
 
223
228
  finally:
224
- await self._messages_queue.put(message.model_dump())
225
-
226
- async def _on_process_stream_task_done(self, task: asyncio.Task) -> None:
227
- await self.create_websocket_message(data_model=SystemResponseContent(),
228
- message_type=WebSocketMessageType.RESPONSE_MESSAGE,
229
- status=WebSocketMessageStatus.COMPLETE)
229
+ if (message is not None):
230
+ await self._socket.send_json(message.model_dump())
230
231
 
231
- return None
232
-
233
- async def process_out_going_messages(self, websocket: WebSocket) -> None:
232
+ async def human_interaction_callback(self, prompt: InteractionPrompt) -> HumanResponse:
234
233
  """
235
- Spawns out going message processing task.
234
+ Registered human interaction callback that processes human interactions and returns
235
+ responses from websocket connection.
236
236
 
237
- :param websocket: Websocket instance.
237
+ :param prompt: Incoming interaction content data model.
238
+ :return: A Text Content Base Pydantic model.
238
239
  """
239
- while True:
240
- try:
241
- out_going_message = await self._out_going_messages_queue.get()
242
- await self._websocket_reference.on_send(websocket, out_going_message)
243
240
 
244
- except (asyncio.CancelledError, ValidationError):
245
- break
241
+ # First create a future from the loop for the human response
242
+ human_response_future: asyncio.Future[HumanResponse] = asyncio.get_running_loop().create_future()
246
243
 
247
- return None
244
+ # Then add the future to the outstanding human prompts dictionary
245
+ self._user_interaction_response = human_response_future
246
+
247
+ try:
248
248
 
249
- async def _process_response(self):
250
- self._websocket_reference.process_response_event.set()
249
+ await self.create_websocket_message(data_model=prompt.content,
250
+ message_type=WebSocketMessageType.SYSTEM_INTERACTION_MESSAGE,
251
+ status=WebSocketMessageStatus.IN_PROGRESS)
251
252
 
252
- async def _pause_response(self):
253
- self._websocket_reference.process_response_event.clear()
253
+ if (isinstance(prompt.content, HumanPromptNotification)):
254
254
 
255
- async def __reset_user_interaction_response(self):
256
- self._user_interaction_response = asyncio.Future()
255
+ return HumanResponseNotification()
257
256
 
258
- async def human_interaction(self, prompt: InteractionPrompt) -> HumanResponse:
259
- """
260
- Registered human interaction callback that processes human interactions and returns
261
- responses from websocket connection.
257
+ # Wait for the human response future to complete
258
+ interaction_response: HumanResponse = await human_response_future
262
259
 
263
- :param prompt: Incoming interaction content data model.
264
- :return: A Text Content Base Pydantic model.
265
- """
266
- await self.create_websocket_message(data_model=prompt.content,
267
- message_type=WebSocketMessageType.SYSTEM_INTERACTION_MESSAGE,
268
- status=WebSocketMessageStatus.IN_PROGRESS)
260
+ interaction_response: HumanResponse = await self._message_validator.convert_text_content_to_human_response(
261
+ interaction_response, prompt.content)
269
262
 
270
- if (isinstance(prompt.content, HumanPromptNotification)):
271
- return HumanResponseNotification()
263
+ return interaction_response
272
264
 
273
- user_message_repsonse_content: TextContent = await self._user_interaction_response
274
- interaction_response: HumanResponse = await self._message_validator.convert_text_content_to_human_response(
275
- user_message_repsonse_content, prompt.content)
265
+ finally:
266
+ # Delete the future from the outstanding human prompts dictionary
267
+ self._user_interaction_response = None
268
+
269
+ async def _run_workflow(self,
270
+ payload: typing.Any,
271
+ conversation_id: str | None = None,
272
+ result_type: type | None = None,
273
+ output_type: type | None = None) -> None:
274
+
275
+ try:
276
+ async with self._session_manager.session(
277
+ conversation_id=conversation_id,
278
+ request=self._socket,
279
+ user_input_callback=self.human_interaction_callback,
280
+ user_authentication_callback=(self._flow_handler.authenticate
281
+ if self._flow_handler else None)) as session:
276
282
 
277
- await self.__reset_user_interaction_response()
278
- await self._process_response()
283
+ async for value in generate_streaming_response(payload,
284
+ session_manager=session,
285
+ streaming=True,
286
+ step_adaptor=self._step_adaptor,
287
+ result_type=result_type,
288
+ output_type=output_type):
279
289
 
280
- return interaction_response
290
+ if not isinstance(value, AIQResponseSerializable):
291
+ value = AIQResponsePayloadOutput(payload=value)
292
+
293
+ await self.create_websocket_message(data_model=value, status=WebSocketMessageStatus.IN_PROGRESS)
294
+
295
+ finally:
296
+ await self.create_websocket_message(data_model=SystemResponseContent(),
297
+ message_type=WebSocketMessageType.RESPONSE_MESSAGE,
298
+ status=WebSocketMessageStatus.COMPLETE)
@@ -291,7 +291,8 @@ class StepAdaptor:
291
291
 
292
292
  return event
293
293
 
294
- def process(self, step: IntermediateStep) -> AIQResponseSerializable | None:
294
+ def process(self, step: IntermediateStep) -> AIQResponseSerializable | None: # pylint: disable=R1710
295
+
295
296
  # Track the chunk
296
297
  self._history.append(step)
297
298
  payload = step.payload
@@ -21,9 +21,10 @@ from aiq.builder.builder import Builder
21
21
  from aiq.builder.llm import LLMProviderInfo
22
22
  from aiq.cli.register_workflow import register_llm_provider
23
23
  from aiq.data_models.llm import LLMBaseConfig
24
+ from aiq.data_models.retry_mixin import RetryMixin
24
25
 
25
26
 
26
- class AWSBedrockModelConfig(LLMBaseConfig, name="aws_bedrock"):
27
+ class AWSBedrockModelConfig(LLMBaseConfig, RetryMixin, name="aws_bedrock"):
27
28
  """An AWS Bedrock llm provider to be used with an LLM client."""
28
29
 
29
30
  model_config = ConfigDict(protected_namespaces=())
aiq/llm/nim_llm.py CHANGED
@@ -22,9 +22,10 @@ from aiq.builder.builder import Builder
22
22
  from aiq.builder.llm import LLMProviderInfo
23
23
  from aiq.cli.register_workflow import register_llm_provider
24
24
  from aiq.data_models.llm import LLMBaseConfig
25
+ from aiq.data_models.retry_mixin import RetryMixin
25
26
 
26
27
 
27
- class NIMModelConfig(LLMBaseConfig, name="nim"):
28
+ class NIMModelConfig(LLMBaseConfig, RetryMixin, name="nim"):
28
29
  """An NVIDIA Inference Microservice (NIM) llm provider to be used with an LLM client."""
29
30
 
30
31
  model_config = ConfigDict(protected_namespaces=())
aiq/llm/openai_llm.py CHANGED
@@ -21,9 +21,10 @@ from aiq.builder.builder import Builder
21
21
  from aiq.builder.llm import LLMProviderInfo
22
22
  from aiq.cli.register_workflow import register_llm_provider
23
23
  from aiq.data_models.llm import LLMBaseConfig
24
+ from aiq.data_models.retry_mixin import RetryMixin
24
25
 
25
26
 
26
- class OpenAIModelConfig(LLMBaseConfig, name="openai"):
27
+ class OpenAIModelConfig(LLMBaseConfig, RetryMixin, name="openai"):
27
28
  """An OpenAI LLM provider to be used with an LLM client."""
28
29
 
29
30
  model_config = ConfigDict(protected_namespaces=(), extra="allow")
@@ -0,0 +1,20 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """
16
+ AIQ Toolkit Object Store Module
17
+
18
+ This package provides foundational classes and interfaces
19
+ for managing object storage in AIQ Toolkit's LLM-based agents.
20
+ """
@@ -0,0 +1,74 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from aiq.builder.builder import Builder
17
+ from aiq.cli.register_workflow import register_object_store
18
+ from aiq.data_models.object_store import KeyAlreadyExistsError
19
+ from aiq.data_models.object_store import NoSuchKeyError
20
+ from aiq.data_models.object_store import ObjectStoreBaseConfig
21
+ from aiq.utils.type_utils import override
22
+
23
+ from .interfaces import ObjectStore
24
+ from .models import ObjectStoreItem
25
+
26
+
27
+ class InMemoryObjectStoreConfig(ObjectStoreBaseConfig, name="in_memory"):
28
+ """
29
+ Object store that stores objects in memory. Objects are not persisted when the process shuts down.
30
+ """
31
+ pass
32
+
33
+
34
+ class InMemoryObjectStore(ObjectStore):
35
+ """
36
+ Implementation of ObjectStore that stores objects in memory. Objects are not persisted when the process shuts down.
37
+ """
38
+
39
+ def __init__(self) -> None:
40
+ self._store: dict[str, ObjectStoreItem] = {}
41
+
42
+ @override
43
+ async def put_object(self, key: str, item: ObjectStoreItem) -> None:
44
+ if key in self._store:
45
+ raise KeyAlreadyExistsError(key)
46
+
47
+ self._store[key] = item
48
+ return
49
+
50
+ @override
51
+ async def upsert_object(self, key: str, item: ObjectStoreItem) -> None:
52
+ self._store[key] = item
53
+ return
54
+
55
+ @override
56
+ async def get_object(self, key: str) -> ObjectStoreItem:
57
+
58
+ if key not in self._store:
59
+ raise NoSuchKeyError(key)
60
+
61
+ return self._store[key]
62
+
63
+ @override
64
+ async def delete_object(self, key: str) -> None:
65
+ if key not in self._store:
66
+ raise NoSuchKeyError(key)
67
+
68
+ self._store.pop(key)
69
+ return
70
+
71
+
72
+ @register_object_store(config_type=InMemoryObjectStoreConfig)
73
+ async def in_memory_object_store(config: InMemoryObjectStoreConfig, builder: Builder):
74
+ yield InMemoryObjectStore()
@@ -0,0 +1,84 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from abc import ABC
17
+ from abc import abstractmethod
18
+
19
+ from .models import ObjectStoreItem
20
+
21
+
22
+ class ObjectStore(ABC):
23
+ """
24
+ Abstract interface for an object store.
25
+
26
+ Implementations may integrate with various object stores,
27
+ such as S3, MySQL, etc.
28
+ """
29
+
30
+ @abstractmethod
31
+ async def put_object(self, key: str, item: ObjectStoreItem) -> None:
32
+ """
33
+ Save an ObjectStoreItem in the object store with the given key.
34
+ If the key already exists, raise an error.
35
+
36
+ Args:
37
+ key (str): The key to save the item under.
38
+ item (ObjectStoreItem): The item to save.
39
+
40
+ Raises:
41
+ KeyAlreadyExistsError: If the key already exists.
42
+ """
43
+ pass
44
+
45
+ @abstractmethod
46
+ async def upsert_object(self, key: str, item: ObjectStoreItem) -> None:
47
+ """
48
+ Save an ObjectStoreItem in the object store with the given key.
49
+ If the key already exists, update the item.
50
+
51
+ Args:
52
+ key (str): The key to save the item under.
53
+ item (ObjectStoreItem): The item to save.
54
+ """
55
+ pass
56
+
57
+ @abstractmethod
58
+ async def get_object(self, key: str) -> ObjectStoreItem:
59
+ """
60
+ Get an ObjectStoreItem from the object store by key.
61
+
62
+ Args:
63
+ key (str): The key to get the item from.
64
+
65
+ Returns:
66
+ ObjectStoreItem: The item retrieved from the object store.
67
+
68
+ Raises:
69
+ NoSuchKeyError: If the item does not exist.
70
+ """
71
+ pass
72
+
73
+ @abstractmethod
74
+ async def delete_object(self, key: str) -> None:
75
+ """
76
+ Delete an ObjectStoreItem from the object store by key.
77
+
78
+ Args:
79
+ key (str): The key to delete the item from.
80
+
81
+ Raises:
82
+ NoSuchKeyError: If the item does not exist.
83
+ """
84
+ pass
@@ -0,0 +1,36 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from pydantic import BaseModel
17
+ from pydantic import Field
18
+
19
+
20
+ class ObjectStoreItem(BaseModel):
21
+ """
22
+ Represents an object store item consisting of bytes and associated metadata.
23
+
24
+ Attributes
25
+ ----------
26
+ data : bytes
27
+ The data to store in the object store.
28
+ content_type : str | None
29
+ The content type of the data.
30
+ metadata : dict[str, str] | None
31
+ Metadata providing context and utility for management operations.
32
+ """
33
+
34
+ data: bytes = Field(description="The data to store in the object store.")
35
+ content_type: str | None = Field(description="The content type of the data.", default=None)
36
+ metadata: dict[str, str] | None = Field(description="The metadata of the data.", default=None)
@@ -0,0 +1,20 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ # pylint: disable=unused-import
17
+ # flake8: noqa
18
+ # isort:skip_file
19
+
20
+ from . import in_memory_object_store
@@ -0,0 +1,14 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
@@ -0,0 +1,14 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.