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.
- aiq/agent/base.py +171 -8
- aiq/agent/dual_node.py +1 -1
- aiq/agent/react_agent/agent.py +113 -113
- aiq/agent/react_agent/register.py +31 -14
- aiq/agent/rewoo_agent/agent.py +36 -35
- aiq/agent/rewoo_agent/register.py +2 -2
- aiq/agent/tool_calling_agent/agent.py +3 -7
- aiq/authentication/__init__.py +14 -0
- aiq/authentication/api_key/__init__.py +14 -0
- aiq/authentication/api_key/api_key_auth_provider.py +92 -0
- aiq/authentication/api_key/api_key_auth_provider_config.py +124 -0
- aiq/authentication/api_key/register.py +26 -0
- aiq/authentication/exceptions/__init__.py +14 -0
- aiq/authentication/exceptions/api_key_exceptions.py +38 -0
- aiq/authentication/exceptions/auth_code_grant_exceptions.py +86 -0
- aiq/authentication/exceptions/call_back_exceptions.py +38 -0
- aiq/authentication/exceptions/request_exceptions.py +54 -0
- aiq/authentication/http_basic_auth/__init__.py +0 -0
- aiq/authentication/http_basic_auth/http_basic_auth_provider.py +81 -0
- aiq/authentication/http_basic_auth/register.py +30 -0
- aiq/authentication/interfaces.py +93 -0
- aiq/authentication/oauth2/__init__.py +14 -0
- aiq/authentication/oauth2/oauth2_auth_code_flow_provider.py +107 -0
- aiq/authentication/oauth2/oauth2_auth_code_flow_provider_config.py +39 -0
- aiq/authentication/oauth2/register.py +25 -0
- aiq/authentication/register.py +21 -0
- aiq/builder/builder.py +64 -2
- aiq/builder/component_utils.py +16 -3
- aiq/builder/context.py +26 -0
- aiq/builder/eval_builder.py +43 -2
- aiq/builder/function.py +32 -4
- aiq/builder/function_base.py +1 -1
- aiq/builder/intermediate_step_manager.py +6 -8
- aiq/builder/user_interaction_manager.py +3 -0
- aiq/builder/workflow.py +23 -18
- aiq/builder/workflow_builder.py +420 -73
- aiq/cli/commands/info/list_mcp.py +103 -16
- aiq/cli/commands/sizing/__init__.py +14 -0
- aiq/cli/commands/sizing/calc.py +294 -0
- aiq/cli/commands/sizing/sizing.py +27 -0
- aiq/cli/commands/start.py +1 -0
- aiq/cli/entrypoint.py +2 -0
- aiq/cli/register_workflow.py +80 -0
- aiq/cli/type_registry.py +151 -30
- aiq/data_models/api_server.py +117 -11
- aiq/data_models/authentication.py +231 -0
- aiq/data_models/common.py +35 -7
- aiq/data_models/component.py +17 -9
- aiq/data_models/component_ref.py +33 -0
- aiq/data_models/config.py +60 -3
- aiq/data_models/embedder.py +1 -0
- aiq/data_models/function_dependencies.py +8 -0
- aiq/data_models/interactive.py +10 -1
- aiq/data_models/intermediate_step.py +15 -5
- aiq/data_models/its_strategy.py +30 -0
- aiq/data_models/llm.py +1 -0
- aiq/data_models/memory.py +1 -0
- aiq/data_models/object_store.py +44 -0
- aiq/data_models/retry_mixin.py +35 -0
- aiq/data_models/span.py +187 -0
- aiq/data_models/telemetry_exporter.py +2 -2
- aiq/embedder/nim_embedder.py +2 -1
- aiq/embedder/openai_embedder.py +2 -1
- aiq/eval/config.py +19 -1
- aiq/eval/dataset_handler/dataset_handler.py +75 -1
- aiq/eval/evaluate.py +53 -10
- aiq/eval/rag_evaluator/evaluate.py +23 -12
- aiq/eval/remote_workflow.py +7 -2
- aiq/eval/runners/__init__.py +14 -0
- aiq/eval/runners/config.py +39 -0
- aiq/eval/runners/multi_eval_runner.py +54 -0
- aiq/eval/usage_stats.py +6 -0
- aiq/eval/utils/weave_eval.py +5 -1
- aiq/experimental/__init__.py +0 -0
- aiq/experimental/decorators/__init__.py +0 -0
- aiq/experimental/decorators/experimental_warning_decorator.py +130 -0
- aiq/experimental/inference_time_scaling/__init__.py +0 -0
- aiq/experimental/inference_time_scaling/editing/__init__.py +0 -0
- aiq/experimental/inference_time_scaling/editing/iterative_plan_refinement_editor.py +147 -0
- aiq/experimental/inference_time_scaling/editing/llm_as_a_judge_editor.py +204 -0
- aiq/experimental/inference_time_scaling/editing/motivation_aware_summarization.py +107 -0
- aiq/experimental/inference_time_scaling/functions/__init__.py +0 -0
- aiq/experimental/inference_time_scaling/functions/execute_score_select_function.py +105 -0
- aiq/experimental/inference_time_scaling/functions/its_tool_orchestration_function.py +205 -0
- aiq/experimental/inference_time_scaling/functions/its_tool_wrapper_function.py +146 -0
- aiq/experimental/inference_time_scaling/functions/plan_select_execute_function.py +224 -0
- aiq/experimental/inference_time_scaling/models/__init__.py +0 -0
- aiq/experimental/inference_time_scaling/models/editor_config.py +132 -0
- aiq/experimental/inference_time_scaling/models/its_item.py +48 -0
- aiq/experimental/inference_time_scaling/models/scoring_config.py +112 -0
- aiq/experimental/inference_time_scaling/models/search_config.py +120 -0
- aiq/experimental/inference_time_scaling/models/selection_config.py +154 -0
- aiq/experimental/inference_time_scaling/models/stage_enums.py +43 -0
- aiq/experimental/inference_time_scaling/models/strategy_base.py +66 -0
- aiq/experimental/inference_time_scaling/models/tool_use_config.py +41 -0
- aiq/experimental/inference_time_scaling/register.py +36 -0
- aiq/experimental/inference_time_scaling/scoring/__init__.py +0 -0
- aiq/experimental/inference_time_scaling/scoring/llm_based_agent_scorer.py +168 -0
- aiq/experimental/inference_time_scaling/scoring/llm_based_plan_scorer.py +168 -0
- aiq/experimental/inference_time_scaling/scoring/motivation_aware_scorer.py +111 -0
- aiq/experimental/inference_time_scaling/search/__init__.py +0 -0
- aiq/experimental/inference_time_scaling/search/multi_llm_planner.py +128 -0
- aiq/experimental/inference_time_scaling/search/multi_query_retrieval_search.py +122 -0
- aiq/experimental/inference_time_scaling/search/single_shot_multi_plan_planner.py +128 -0
- aiq/experimental/inference_time_scaling/selection/__init__.py +0 -0
- aiq/experimental/inference_time_scaling/selection/best_of_n_selector.py +63 -0
- aiq/experimental/inference_time_scaling/selection/llm_based_agent_output_selector.py +131 -0
- aiq/experimental/inference_time_scaling/selection/llm_based_output_merging_selector.py +159 -0
- aiq/experimental/inference_time_scaling/selection/llm_based_plan_selector.py +128 -0
- aiq/experimental/inference_time_scaling/selection/threshold_selector.py +58 -0
- aiq/front_ends/console/authentication_flow_handler.py +233 -0
- aiq/front_ends/console/console_front_end_plugin.py +11 -2
- aiq/front_ends/fastapi/auth_flow_handlers/__init__.py +0 -0
- aiq/front_ends/fastapi/auth_flow_handlers/http_flow_handler.py +27 -0
- aiq/front_ends/fastapi/auth_flow_handlers/websocket_flow_handler.py +107 -0
- aiq/front_ends/fastapi/fastapi_front_end_config.py +20 -0
- aiq/front_ends/fastapi/fastapi_front_end_controller.py +68 -0
- aiq/front_ends/fastapi/fastapi_front_end_plugin.py +14 -1
- aiq/front_ends/fastapi/fastapi_front_end_plugin_worker.py +353 -31
- aiq/front_ends/fastapi/html_snippets/__init__.py +14 -0
- aiq/front_ends/fastapi/html_snippets/auth_code_grant_success.py +35 -0
- aiq/front_ends/fastapi/main.py +2 -0
- aiq/front_ends/fastapi/message_handler.py +102 -84
- aiq/front_ends/fastapi/step_adaptor.py +2 -1
- aiq/llm/aws_bedrock_llm.py +2 -1
- aiq/llm/nim_llm.py +2 -1
- aiq/llm/openai_llm.py +2 -1
- aiq/object_store/__init__.py +20 -0
- aiq/object_store/in_memory_object_store.py +74 -0
- aiq/object_store/interfaces.py +84 -0
- aiq/object_store/models.py +36 -0
- aiq/object_store/register.py +20 -0
- aiq/observability/__init__.py +14 -0
- aiq/observability/exporter/__init__.py +14 -0
- aiq/observability/exporter/base_exporter.py +449 -0
- aiq/observability/exporter/exporter.py +78 -0
- aiq/observability/exporter/file_exporter.py +33 -0
- aiq/observability/exporter/processing_exporter.py +269 -0
- aiq/observability/exporter/raw_exporter.py +52 -0
- aiq/observability/exporter/span_exporter.py +264 -0
- aiq/observability/exporter_manager.py +335 -0
- aiq/observability/mixin/__init__.py +14 -0
- aiq/observability/mixin/batch_config_mixin.py +26 -0
- aiq/observability/mixin/collector_config_mixin.py +23 -0
- aiq/observability/mixin/file_mixin.py +288 -0
- aiq/observability/mixin/file_mode.py +23 -0
- aiq/observability/mixin/resource_conflict_mixin.py +134 -0
- aiq/observability/mixin/serialize_mixin.py +61 -0
- aiq/observability/mixin/type_introspection_mixin.py +183 -0
- aiq/observability/processor/__init__.py +14 -0
- aiq/observability/processor/batching_processor.py +316 -0
- aiq/observability/processor/intermediate_step_serializer.py +28 -0
- aiq/observability/processor/processor.py +68 -0
- aiq/observability/register.py +32 -116
- aiq/observability/utils/__init__.py +14 -0
- aiq/observability/utils/dict_utils.py +236 -0
- aiq/observability/utils/time_utils.py +31 -0
- aiq/profiler/calc/__init__.py +14 -0
- aiq/profiler/calc/calc_runner.py +623 -0
- aiq/profiler/calc/calculations.py +288 -0
- aiq/profiler/calc/data_models.py +176 -0
- aiq/profiler/calc/plot.py +345 -0
- aiq/profiler/data_models.py +2 -0
- aiq/profiler/profile_runner.py +16 -13
- aiq/runtime/loader.py +8 -2
- aiq/runtime/runner.py +23 -9
- aiq/runtime/session.py +16 -5
- aiq/tool/chat_completion.py +74 -0
- aiq/tool/code_execution/README.md +152 -0
- aiq/tool/code_execution/code_sandbox.py +151 -72
- aiq/tool/code_execution/local_sandbox/.gitignore +1 -0
- aiq/tool/code_execution/local_sandbox/local_sandbox_server.py +139 -24
- aiq/tool/code_execution/local_sandbox/sandbox.requirements.txt +3 -1
- aiq/tool/code_execution/local_sandbox/start_local_sandbox.sh +27 -2
- aiq/tool/code_execution/register.py +7 -3
- aiq/tool/code_execution/test_code_execution_sandbox.py +414 -0
- aiq/tool/mcp/exceptions.py +142 -0
- aiq/tool/mcp/mcp_client.py +17 -3
- aiq/tool/mcp/mcp_tool.py +1 -1
- aiq/tool/register.py +1 -0
- aiq/tool/server_tools.py +2 -2
- aiq/utils/exception_handlers/automatic_retries.py +289 -0
- aiq/utils/exception_handlers/mcp.py +211 -0
- aiq/utils/io/model_processing.py +28 -0
- aiq/utils/log_utils.py +37 -0
- aiq/utils/string_utils.py +38 -0
- aiq/utils/type_converter.py +18 -2
- aiq/utils/type_utils.py +87 -0
- {aiqtoolkit-1.2.0a20250706.dist-info → aiqtoolkit-1.2.0a20250730.dist-info}/METADATA +37 -9
- {aiqtoolkit-1.2.0a20250706.dist-info → aiqtoolkit-1.2.0a20250730.dist-info}/RECORD +195 -80
- {aiqtoolkit-1.2.0a20250706.dist-info → aiqtoolkit-1.2.0a20250730.dist-info}/entry_points.txt +3 -0
- aiq/front_ends/fastapi/websocket.py +0 -153
- aiq/observability/async_otel_listener.py +0 -470
- {aiqtoolkit-1.2.0a20250706.dist-info → aiqtoolkit-1.2.0a20250730.dist-info}/WHEEL +0 -0
- {aiqtoolkit-1.2.0a20250706.dist-info → aiqtoolkit-1.2.0a20250730.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
- {aiqtoolkit-1.2.0a20250706.dist-info → aiqtoolkit-1.2.0a20250730.dist-info}/licenses/LICENSE.md +0 -0
- {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.
|
|
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
|
|
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.
|
|
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[
|
|
65
|
+
self._user_interaction_response: asyncio.Future[HumanResponse] | None = None
|
|
59
66
|
|
|
60
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
return self._background_task
|
|
69
|
+
def set_flow_handler(self, flow_handler: FlowHandlerBase) -> None:
|
|
70
|
+
self._flow_handler = flow_handler
|
|
67
71
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return self._process_messages_task
|
|
72
|
+
async def __aenter__(self) -> "WebSocketMessageHandler":
|
|
73
|
+
await self._socket.accept()
|
|
71
74
|
|
|
72
|
-
|
|
73
|
-
def process_messages_task(self, process_messages_task) -> None:
|
|
74
|
-
self._process_messages_task = process_messages_task
|
|
75
|
+
return self
|
|
75
76
|
|
|
76
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
self._process_out_going_messages_task = process_out_going_messages_task
|
|
79
|
+
# TODO: Handle the exit
|
|
80
|
+
pass
|
|
83
81
|
|
|
84
|
-
async def
|
|
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
|
-
|
|
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.
|
|
96
|
+
await self.process_workflow_request(validated_message)
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
elif isinstance(
|
|
99
99
|
validated_message,
|
|
100
100
|
( # noqa: E131
|
|
101
101
|
WebSocketSystemResponseTokenMessage,
|
|
102
102
|
WebSocketSystemIntermediateStepMessage,
|
|
103
103
|
WebSocketSystemInteractionMessage)):
|
|
104
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
153
|
+
if isinstance(content, TextContent) and (self._running_workflow_task is None):
|
|
151
154
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
234
|
+
Registered human interaction callback that processes human interactions and returns
|
|
235
|
+
responses from websocket connection.
|
|
236
236
|
|
|
237
|
-
:param
|
|
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
|
-
|
|
245
|
-
|
|
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
|
-
|
|
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
|
-
|
|
250
|
-
|
|
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
|
-
|
|
253
|
-
self._websocket_reference.process_response_event.clear()
|
|
253
|
+
if (isinstance(prompt.content, HumanPromptNotification)):
|
|
254
254
|
|
|
255
|
-
|
|
256
|
-
self._user_interaction_response = asyncio.Future()
|
|
255
|
+
return HumanResponseNotification()
|
|
257
256
|
|
|
258
|
-
|
|
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
|
-
|
|
264
|
-
|
|
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
|
-
|
|
271
|
-
return HumanResponseNotification()
|
|
263
|
+
return interaction_response
|
|
272
264
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
|
-
|
|
278
|
-
|
|
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
|
-
|
|
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
|
aiq/llm/aws_bedrock_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 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
|
aiq/observability/__init__.py
CHANGED
|
@@ -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.
|