aiqtoolkit 1.2.0.dev0__py3-none-any.whl → 1.2.0rc2__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 +170 -8
- aiq/agent/dual_node.py +1 -1
- aiq/agent/react_agent/agent.py +146 -112
- aiq/agent/react_agent/prompt.py +1 -6
- aiq/agent/react_agent/register.py +36 -35
- 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/agent/tool_calling_agent/register.py +1 -1
- 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 +37 -0
- aiq/builder/eval_builder.py +43 -2
- aiq/builder/function.py +44 -12
- 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 +421 -61
- 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 +2 -1
- 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 +124 -12
- 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/dataset_handler.py +2 -1
- aiq/data_models/embedder.py +1 -0
- aiq/data_models/evaluate.py +23 -0
- aiq/data_models/function_dependencies.py +8 -0
- aiq/data_models/interactive.py +10 -1
- aiq/data_models/intermediate_step.py +38 -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/profiler.py +1 -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 +87 -2
- aiq/eval/evaluate.py +208 -27
- aiq/eval/evaluator/base_evaluator.py +73 -0
- aiq/eval/evaluator/evaluator_model.py +1 -0
- aiq/eval/intermediate_step_adapter.py +11 -5
- aiq/eval/rag_evaluator/evaluate.py +55 -15
- aiq/eval/rag_evaluator/register.py +6 -1
- 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/trajectory_evaluator/evaluate.py +22 -65
- aiq/eval/tunable_rag_evaluator/evaluate.py +150 -168
- aiq/eval/tunable_rag_evaluator/register.py +2 -0
- aiq/eval/usage_stats.py +41 -0
- aiq/eval/utils/output_uploader.py +10 -1
- aiq/eval/utils/weave_eval.py +184 -0
- 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 +93 -9
- 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 +537 -52
- 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/job_store.py +47 -25
- aiq/front_ends/fastapi/main.py +2 -0
- aiq/front_ends/fastapi/message_handler.py +108 -89
- aiq/front_ends/fastapi/step_adaptor.py +2 -1
- aiq/llm/aws_bedrock_llm.py +57 -0
- aiq/llm/nim_llm.py +2 -1
- aiq/llm/openai_llm.py +3 -2
- aiq/llm/register.py +1 -0
- aiq/meta/pypi.md +12 -12
- 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 +36 -39
- 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/callbacks/langchain_callback_handler.py +22 -10
- aiq/profiler/data_models.py +24 -0
- aiq/profiler/inference_metrics_model.py +3 -0
- aiq/profiler/inference_optimization/bottleneck_analysis/nested_stack_analysis.py +8 -0
- aiq/profiler/inference_optimization/data_models.py +2 -2
- aiq/profiler/inference_optimization/llm_metrics.py +2 -2
- aiq/profiler/profile_runner.py +61 -21
- aiq/runtime/loader.py +9 -3
- aiq/runtime/runner.py +23 -9
- aiq/runtime/session.py +25 -7
- aiq/runtime/user_metadata.py +2 -3
- 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 +41 -6
- aiq/tool/mcp/mcp_tool.py +3 -2
- aiq/tool/register.py +1 -0
- aiq/tool/server_tools.py +6 -3
- 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.0.dev0.dist-info → aiqtoolkit-1.2.0rc2.dist-info}/METADATA +53 -21
- aiqtoolkit-1.2.0rc2.dist-info/RECORD +436 -0
- {aiqtoolkit-1.2.0.dev0.dist-info → aiqtoolkit-1.2.0rc2.dist-info}/WHEEL +1 -1
- {aiqtoolkit-1.2.0.dev0.dist-info → aiqtoolkit-1.2.0rc2.dist-info}/entry_points.txt +3 -0
- aiq/front_ends/fastapi/websocket.py +0 -148
- aiq/observability/async_otel_listener.py +0 -429
- aiqtoolkit-1.2.0.dev0.dist-info/RECORD +0 -316
- {aiqtoolkit-1.2.0.dev0.dist-info → aiqtoolkit-1.2.0rc2.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
- {aiqtoolkit-1.2.0.dev0.dist-info → aiqtoolkit-1.2.0rc2.dist-info}/licenses/LICENSE.md +0 -0
- {aiqtoolkit-1.2.0.dev0.dist-info → aiqtoolkit-1.2.0rc2.dist-info}/top_level.txt +0 -0
aiq/runtime/loader.py
CHANGED
|
@@ -48,14 +48,18 @@ class PluginTypes(IntFlag):
|
|
|
48
48
|
"""
|
|
49
49
|
A plugin that is an evaluator for the workflow. This includes evaluators like RAGAS, SWE-bench, etc.
|
|
50
50
|
"""
|
|
51
|
+
AUTHENTICATION = auto()
|
|
52
|
+
"""
|
|
53
|
+
A plugin that is an API authentication provider for the workflow. This includes Oauth2, API Key, etc.
|
|
54
|
+
"""
|
|
51
55
|
REGISTRY_HANDLER = auto()
|
|
52
56
|
|
|
53
57
|
# Convenience flag for groups of plugin types
|
|
54
|
-
CONFIG_OBJECT = COMPONENT | FRONT_END | EVALUATOR
|
|
58
|
+
CONFIG_OBJECT = COMPONENT | FRONT_END | EVALUATOR | AUTHENTICATION
|
|
55
59
|
"""
|
|
56
60
|
Any plugin that can be specified in the AIQ Toolkit configuration file.
|
|
57
61
|
"""
|
|
58
|
-
ALL = COMPONENT | FRONT_END | EVALUATOR | REGISTRY_HANDLER
|
|
62
|
+
ALL = COMPONENT | FRONT_END | EVALUATOR | REGISTRY_HANDLER | AUTHENTICATION
|
|
59
63
|
"""
|
|
60
64
|
All plugin types
|
|
61
65
|
"""
|
|
@@ -130,9 +134,11 @@ def discover_entrypoints(plugin_type: PluginTypes):
|
|
|
130
134
|
plugin_groups.append("aiq.registry_handlers")
|
|
131
135
|
if (plugin_type & PluginTypes.EVALUATOR):
|
|
132
136
|
plugin_groups.append("aiq.evaluators")
|
|
137
|
+
if (plugin_type & PluginTypes.AUTHENTICATION):
|
|
138
|
+
plugin_groups.append("aiq.authentication_providers")
|
|
133
139
|
|
|
134
140
|
# Get the entry points for the specified groups
|
|
135
|
-
aiq_plugins = reduce(lambda x, y: x + y, [entry_points.select(group=y) for y in plugin_groups])
|
|
141
|
+
aiq_plugins = reduce(lambda x, y: list(x) + list(y), [entry_points.select(group=y) for y in plugin_groups])
|
|
136
142
|
|
|
137
143
|
return aiq_plugins
|
|
138
144
|
|
aiq/runtime/runner.py
CHANGED
|
@@ -21,7 +21,7 @@ from aiq.builder.context import AIQContext
|
|
|
21
21
|
from aiq.builder.context import AIQContextState
|
|
22
22
|
from aiq.builder.function import Function
|
|
23
23
|
from aiq.data_models.invocation_node import InvocationNode
|
|
24
|
-
from aiq.observability.
|
|
24
|
+
from aiq.observability.exporter_manager import ExporterManager
|
|
25
25
|
from aiq.utils.reactive.subject import Subject
|
|
26
26
|
|
|
27
27
|
logger = logging.getLogger(__name__)
|
|
@@ -44,7 +44,11 @@ _T = typing.TypeVar("_T")
|
|
|
44
44
|
|
|
45
45
|
class AIQRunner:
|
|
46
46
|
|
|
47
|
-
def __init__(self,
|
|
47
|
+
def __init__(self,
|
|
48
|
+
input_message: typing.Any,
|
|
49
|
+
entry_fn: Function,
|
|
50
|
+
context_state: AIQContextState,
|
|
51
|
+
exporter_manager: ExporterManager):
|
|
48
52
|
"""
|
|
49
53
|
The AIQRunner class is used to run a workflow. It handles converting input and output data types and running the
|
|
50
54
|
workflow with the specified concurrency.
|
|
@@ -57,6 +61,8 @@ class AIQRunner:
|
|
|
57
61
|
The entry function to the workflow
|
|
58
62
|
context_state : AIQContextState
|
|
59
63
|
The context state to use
|
|
64
|
+
exporter_manager : ExporterManager
|
|
65
|
+
The exporter manager to use
|
|
60
66
|
"""
|
|
61
67
|
|
|
62
68
|
if (entry_fn is None):
|
|
@@ -73,7 +79,7 @@ class AIQRunner:
|
|
|
73
79
|
# Before we start, we need to convert the input message to the workflow input type
|
|
74
80
|
self._input_message = input_message
|
|
75
81
|
|
|
76
|
-
self.
|
|
82
|
+
self._exporter_manager = exporter_manager
|
|
77
83
|
|
|
78
84
|
@property
|
|
79
85
|
def context(self) -> AIQContext:
|
|
@@ -130,19 +136,23 @@ class AIQRunner:
|
|
|
130
136
|
if (not self._entry_fn.has_single_output):
|
|
131
137
|
raise ValueError("Workflow does not support single output")
|
|
132
138
|
|
|
133
|
-
async with self.
|
|
139
|
+
async with self._exporter_manager.start(context_state=self._context_state):
|
|
134
140
|
# Run the workflow
|
|
135
141
|
result = await self._entry_fn.ainvoke(self._input_message, to_type=to_type)
|
|
136
142
|
|
|
137
143
|
# Close the intermediate stream
|
|
138
|
-
self._context_state.event_stream.get()
|
|
144
|
+
event_stream = self._context_state.event_stream.get()
|
|
145
|
+
if event_stream:
|
|
146
|
+
event_stream.on_complete()
|
|
139
147
|
|
|
140
148
|
self._state = AIQRunnerState.COMPLETED
|
|
141
149
|
|
|
142
150
|
return result
|
|
143
151
|
except Exception as e:
|
|
144
152
|
logger.exception("Error running workflow: %s", e)
|
|
145
|
-
self._context_state.event_stream.get()
|
|
153
|
+
event_stream = self._context_state.event_stream.get()
|
|
154
|
+
if event_stream:
|
|
155
|
+
event_stream.on_complete()
|
|
146
156
|
self._state = AIQRunnerState.FAILED
|
|
147
157
|
|
|
148
158
|
raise
|
|
@@ -159,18 +169,22 @@ class AIQRunner:
|
|
|
159
169
|
raise ValueError("Workflow does not support streaming output")
|
|
160
170
|
|
|
161
171
|
# Run the workflow
|
|
162
|
-
async with self.
|
|
172
|
+
async with self._exporter_manager.start(context_state=self._context_state):
|
|
163
173
|
async for m in self._entry_fn.astream(self._input_message, to_type=to_type):
|
|
164
174
|
yield m
|
|
165
175
|
|
|
166
176
|
self._state = AIQRunnerState.COMPLETED
|
|
167
177
|
|
|
168
178
|
# Close the intermediate stream
|
|
169
|
-
self._context_state.event_stream.get()
|
|
179
|
+
event_stream = self._context_state.event_stream.get()
|
|
180
|
+
if event_stream:
|
|
181
|
+
event_stream.on_complete()
|
|
170
182
|
|
|
171
183
|
except Exception as e:
|
|
172
184
|
logger.exception("Error running workflow: %s", e)
|
|
173
|
-
self._context_state.event_stream.get()
|
|
185
|
+
event_stream = self._context_state.event_stream.get()
|
|
186
|
+
if event_stream:
|
|
187
|
+
event_stream.on_complete()
|
|
174
188
|
self._state = AIQRunnerState.FAILED
|
|
175
189
|
|
|
176
190
|
raise
|
aiq/runtime/session.py
CHANGED
|
@@ -21,11 +21,14 @@ from collections.abc import Callable
|
|
|
21
21
|
from contextlib import asynccontextmanager
|
|
22
22
|
from contextlib import nullcontext
|
|
23
23
|
|
|
24
|
-
from
|
|
24
|
+
from starlette.requests import HTTPConnection
|
|
25
25
|
|
|
26
26
|
from aiq.builder.context import AIQContext
|
|
27
27
|
from aiq.builder.context import AIQContextState
|
|
28
28
|
from aiq.builder.workflow import Workflow
|
|
29
|
+
from aiq.data_models.authentication import AuthenticatedContext
|
|
30
|
+
from aiq.data_models.authentication import AuthFlowType
|
|
31
|
+
from aiq.data_models.authentication import AuthProviderBaseConfig
|
|
29
32
|
from aiq.data_models.config import AIQConfig
|
|
30
33
|
from aiq.data_models.interactive import HumanResponse
|
|
31
34
|
from aiq.data_models.interactive import InteractionPrompt
|
|
@@ -86,8 +89,11 @@ class AIQSessionManager:
|
|
|
86
89
|
@asynccontextmanager
|
|
87
90
|
async def session(self,
|
|
88
91
|
user_manager=None,
|
|
89
|
-
request:
|
|
90
|
-
|
|
92
|
+
request: HTTPConnection | None = None,
|
|
93
|
+
conversation_id: str | None = None,
|
|
94
|
+
user_input_callback: Callable[[InteractionPrompt], Awaitable[HumanResponse]] = None,
|
|
95
|
+
user_authentication_callback: Callable[[AuthProviderBaseConfig, AuthFlowType],
|
|
96
|
+
Awaitable[AuthenticatedContext | None]] = None):
|
|
91
97
|
|
|
92
98
|
token_user_input = None
|
|
93
99
|
if user_input_callback is not None:
|
|
@@ -97,7 +103,14 @@ class AIQSessionManager:
|
|
|
97
103
|
if user_manager is not None:
|
|
98
104
|
token_user_manager = self._context_state.user_manager.set(user_manager)
|
|
99
105
|
|
|
100
|
-
|
|
106
|
+
token_user_authentication = None
|
|
107
|
+
if user_authentication_callback is not None:
|
|
108
|
+
token_user_authentication = self._context_state.user_auth_callback.set(user_authentication_callback)
|
|
109
|
+
|
|
110
|
+
if conversation_id is not None and request is None:
|
|
111
|
+
self._context_state.conversation_id.set(conversation_id)
|
|
112
|
+
|
|
113
|
+
self.set_metadata_from_http_request(request)
|
|
101
114
|
|
|
102
115
|
try:
|
|
103
116
|
yield self
|
|
@@ -106,6 +119,8 @@ class AIQSessionManager:
|
|
|
106
119
|
self._context_state.user_manager.reset(token_user_manager)
|
|
107
120
|
if token_user_input is not None:
|
|
108
121
|
self._context_state.user_input_callback.reset(token_user_input)
|
|
122
|
+
if token_user_authentication is not None:
|
|
123
|
+
self._context_state.user_auth_callback.reset(token_user_authentication)
|
|
109
124
|
|
|
110
125
|
@asynccontextmanager
|
|
111
126
|
async def run(self, message):
|
|
@@ -120,15 +135,15 @@ class AIQSessionManager:
|
|
|
120
135
|
async with self._workflow.run(message) as runner:
|
|
121
136
|
yield runner
|
|
122
137
|
|
|
123
|
-
def
|
|
138
|
+
def set_metadata_from_http_request(self, request: HTTPConnection | None) -> None:
|
|
124
139
|
"""
|
|
125
|
-
Extracts and sets request attributes from
|
|
140
|
+
Extracts and sets user metadata request attributes from a HTTP request.
|
|
126
141
|
If request is None, no attributes are set.
|
|
127
142
|
"""
|
|
128
143
|
if request is None:
|
|
129
144
|
return
|
|
130
145
|
|
|
131
|
-
self._context.metadata._request.method = request
|
|
146
|
+
self._context.metadata._request.method = getattr(request, "method", None)
|
|
132
147
|
self._context.metadata._request.url_path = request.url.path
|
|
133
148
|
self._context.metadata._request.url_port = request.url.port
|
|
134
149
|
self._context.metadata._request.url_scheme = request.url.scheme
|
|
@@ -138,3 +153,6 @@ class AIQSessionManager:
|
|
|
138
153
|
self._context.metadata._request.client_host = request.client.host
|
|
139
154
|
self._context.metadata._request.client_port = request.client.port
|
|
140
155
|
self._context.metadata._request.cookies = request.cookies
|
|
156
|
+
|
|
157
|
+
if request.headers.get("conversation-id"):
|
|
158
|
+
self._context_state.conversation_id.set(request.headers["conversation-id"])
|
aiq/runtime/user_metadata.py
CHANGED
|
@@ -21,9 +21,8 @@ from aiq.data_models.api_server import Request
|
|
|
21
21
|
|
|
22
22
|
class RequestAttributes:
|
|
23
23
|
"""
|
|
24
|
-
The RequestAttributes class is responsible for managing user
|
|
25
|
-
metadata
|
|
26
|
-
expose user-defined attributes to workflow tools.
|
|
24
|
+
The RequestAttributes class is responsible for managing user http and webscoket session
|
|
25
|
+
metadata. It provides a way to store and expose session attributes to workflow tools.
|
|
27
26
|
"""
|
|
28
27
|
|
|
29
28
|
def __init__(self) -> None:
|
|
@@ -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
|
+
Simple Completion Function for AIQ Toolkit
|
|
17
|
+
|
|
18
|
+
This module provides a simple completion function that can handle
|
|
19
|
+
natural language queries and perform basic text completion tasks.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from pydantic import Field
|
|
23
|
+
|
|
24
|
+
from aiq.builder.builder import Builder
|
|
25
|
+
from aiq.builder.framework_enum import LLMFrameworkEnum
|
|
26
|
+
from aiq.cli.register_workflow import register_function
|
|
27
|
+
from aiq.data_models.component_ref import LLMRef
|
|
28
|
+
from aiq.data_models.function import FunctionBaseConfig
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ChatCompletionConfig(FunctionBaseConfig, name="chat_completion"):
|
|
32
|
+
"""Configuration for the Chat Completion Function."""
|
|
33
|
+
|
|
34
|
+
system_prompt: str = Field(("You are a helpful AI assistant. Provide clear, accurate, and helpful "
|
|
35
|
+
"responses to user queries. You can give general advice, recommendations, "
|
|
36
|
+
"tips, and engage in conversation. Be helpful and informative."),
|
|
37
|
+
description="The system prompt to use for chat completion.")
|
|
38
|
+
|
|
39
|
+
llm_name: LLMRef = Field(description="The LLM to use for generating responses.")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@register_function(config_type=ChatCompletionConfig)
|
|
43
|
+
async def register_chat_completion(config: ChatCompletionConfig, builder: Builder):
|
|
44
|
+
"""Registers a chat completion function that can handle natural language queries."""
|
|
45
|
+
|
|
46
|
+
# Get the LLM from the builder context using the configured LLM reference
|
|
47
|
+
# Use LangChain framework wrapper since we're using LangChain-based LLM
|
|
48
|
+
llm = await builder.get_llm(config.llm_name, wrapper_type=LLMFrameworkEnum.LANGCHAIN)
|
|
49
|
+
|
|
50
|
+
async def _chat_completion(query: str) -> str:
|
|
51
|
+
"""A simple chat completion function that responds to natural language queries.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
query: The user's natural language query
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
A helpful response to the query
|
|
58
|
+
"""
|
|
59
|
+
try:
|
|
60
|
+
# Create a simple prompt with the system message and user query
|
|
61
|
+
prompt = f"{config.system_prompt}\n\nUser: {query}\n\nAssistant:"
|
|
62
|
+
|
|
63
|
+
# Generate response using the LLM
|
|
64
|
+
response = await llm.ainvoke(prompt)
|
|
65
|
+
|
|
66
|
+
return response
|
|
67
|
+
|
|
68
|
+
except Exception as e:
|
|
69
|
+
# Fallback response if LLM call fails
|
|
70
|
+
return (f"I apologize, but I encountered an error while processing your "
|
|
71
|
+
f"query: '{query}'. Please try rephrasing your question or try "
|
|
72
|
+
f"again later. Error: {str(e)}")
|
|
73
|
+
|
|
74
|
+
yield _chat_completion
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
3
|
+
SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
you may not use this file except in compliance with the License.
|
|
7
|
+
You may obtain a copy of the License at
|
|
8
|
+
|
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
|
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
See the License for the specific language governing permissions and
|
|
15
|
+
limitations under the License.
|
|
16
|
+
-->
|
|
17
|
+
|
|
18
|
+
# Code Execution Sandbox
|
|
19
|
+
|
|
20
|
+
A secure, containerized Python code execution environment that allows safe execution of Python code with comprehensive error handling and debugging capabilities.
|
|
21
|
+
|
|
22
|
+
## Overview
|
|
23
|
+
|
|
24
|
+
The Code Execution Sandbox provides:
|
|
25
|
+
- **Secure code execution** in isolated Docker containers
|
|
26
|
+
- **Comprehensive error handling** with detailed stdout/stderr capture
|
|
27
|
+
- **Multiple input formats** including raw code, dictionary format, and markdown
|
|
28
|
+
- **Dependency management** with pre-installed libraries
|
|
29
|
+
- **Flexible configuration** with customizable timeouts and output limits
|
|
30
|
+
- **Robust debugging** with extensive logging and error reporting
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
### Step 1: Start the Sandbox Server
|
|
35
|
+
|
|
36
|
+
Navigate to the local sandbox directory and start the server:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
cd src/aiq/tool/code_execution/local_sandbox
|
|
40
|
+
./start_local_sandbox.sh
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The script will:
|
|
44
|
+
- Build the Docker image if it doesn't exist
|
|
45
|
+
- Start the sandbox server on port 6000
|
|
46
|
+
- Mount your working directory for file operations
|
|
47
|
+
|
|
48
|
+
#### Advanced Usage:
|
|
49
|
+
```bash
|
|
50
|
+
# Custom container name
|
|
51
|
+
./start_local_sandbox.sh my-sandbox
|
|
52
|
+
|
|
53
|
+
# Custom output directory
|
|
54
|
+
./start_local_sandbox.sh my-sandbox /path/to/output
|
|
55
|
+
|
|
56
|
+
# Using environment variable
|
|
57
|
+
export OUTPUT_DATA_PATH=/path/to/output
|
|
58
|
+
./start_local_sandbox.sh
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Step 2: Test the Installation
|
|
62
|
+
|
|
63
|
+
Run the comprehensive test suite to verify everything is working:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
cd src/aiq/tool/code_execution
|
|
67
|
+
pytest test_code_execution_sandbox.py
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Note: a running instance of a local sandbox is required.
|
|
71
|
+
|
|
72
|
+
## Using the Code Execution Tool
|
|
73
|
+
|
|
74
|
+
### Basic Usage
|
|
75
|
+
|
|
76
|
+
The sandbox accepts HTTP POST requests to `http://localhost:6000/execute` with JSON payloads:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
curl -X POST \
|
|
80
|
+
-H "Content-Type: application/json" \
|
|
81
|
+
-d '{
|
|
82
|
+
"generated_code": "print(\"Hello, World!\")",
|
|
83
|
+
"timeout": 30,
|
|
84
|
+
"language": "python"
|
|
85
|
+
}' \
|
|
86
|
+
http://localhost:6000/execute
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Supported Input Formats
|
|
90
|
+
|
|
91
|
+
#### 1. Raw Python Code
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"generated_code": "import numpy as np\nprint(np.array([1, 2, 3]))",
|
|
95
|
+
"timeout": 30,
|
|
96
|
+
"language": "python"
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### 2. Dictionary Format
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"generated_code": "{'generated_code': 'print(\"Hello from dict format\")'}",
|
|
104
|
+
"timeout": 30,
|
|
105
|
+
"language": "python"
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### 3. Markdown Code Blocks
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"generated_code": "```python\nprint('Hello from markdown')\n```",
|
|
113
|
+
"timeout": 30,
|
|
114
|
+
"language": "python"
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Response Format
|
|
119
|
+
|
|
120
|
+
The sandbox returns JSON responses with the following structure:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"process_status": "completed|error|timeout",
|
|
125
|
+
"stdout": "Standard output content",
|
|
126
|
+
"stderr": "Standard error content"
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Configuration Options
|
|
131
|
+
|
|
132
|
+
### Sandbox Configuration
|
|
133
|
+
|
|
134
|
+
- **URI**: Default `http://127.0.0.1:6000`
|
|
135
|
+
- **Timeout**: Default 10 seconds (configurable)
|
|
136
|
+
- **Max Output Characters**: Default 1000 characters
|
|
137
|
+
- **Memory Limit**: 10GB (configurable in Docker)
|
|
138
|
+
- **Working Directory**: Mounted volume for file operations
|
|
139
|
+
|
|
140
|
+
### Environment Variables
|
|
141
|
+
|
|
142
|
+
- `OUTPUT_DATA_PATH`: Custom path for file operations
|
|
143
|
+
- `SANDBOX_HOST`: Custom sandbox host
|
|
144
|
+
- `SANDBOX_PORT`: Custom sandbox port
|
|
145
|
+
|
|
146
|
+
## Security Considerations
|
|
147
|
+
|
|
148
|
+
- **Isolated execution**: All code runs in Docker containers
|
|
149
|
+
- **Resource limits**: Memory and CPU limits prevent resource exhaustion
|
|
150
|
+
- **Network isolation**: Containers have limited network access
|
|
151
|
+
- **File system isolation**: Mounted volumes provide controlled file access
|
|
152
|
+
- **Process isolation**: Each execution runs in a separate process
|