aiqtoolkit 1.2.0.dev0__py3-none-any.whl → 1.2.0rc1__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 (220) hide show
  1. aiq/agent/base.py +170 -8
  2. aiq/agent/dual_node.py +1 -1
  3. aiq/agent/react_agent/agent.py +146 -112
  4. aiq/agent/react_agent/prompt.py +1 -6
  5. aiq/agent/react_agent/register.py +36 -35
  6. aiq/agent/rewoo_agent/agent.py +36 -35
  7. aiq/agent/rewoo_agent/register.py +2 -2
  8. aiq/agent/tool_calling_agent/agent.py +3 -7
  9. aiq/agent/tool_calling_agent/register.py +1 -1
  10. aiq/authentication/__init__.py +14 -0
  11. aiq/authentication/api_key/__init__.py +14 -0
  12. aiq/authentication/api_key/api_key_auth_provider.py +92 -0
  13. aiq/authentication/api_key/api_key_auth_provider_config.py +124 -0
  14. aiq/authentication/api_key/register.py +26 -0
  15. aiq/authentication/exceptions/__init__.py +14 -0
  16. aiq/authentication/exceptions/api_key_exceptions.py +38 -0
  17. aiq/authentication/exceptions/auth_code_grant_exceptions.py +86 -0
  18. aiq/authentication/exceptions/call_back_exceptions.py +38 -0
  19. aiq/authentication/exceptions/request_exceptions.py +54 -0
  20. aiq/authentication/http_basic_auth/__init__.py +0 -0
  21. aiq/authentication/http_basic_auth/http_basic_auth_provider.py +81 -0
  22. aiq/authentication/http_basic_auth/register.py +30 -0
  23. aiq/authentication/interfaces.py +93 -0
  24. aiq/authentication/oauth2/__init__.py +14 -0
  25. aiq/authentication/oauth2/oauth2_auth_code_flow_provider.py +107 -0
  26. aiq/authentication/oauth2/oauth2_auth_code_flow_provider_config.py +39 -0
  27. aiq/authentication/oauth2/register.py +25 -0
  28. aiq/authentication/register.py +21 -0
  29. aiq/builder/builder.py +64 -2
  30. aiq/builder/component_utils.py +16 -3
  31. aiq/builder/context.py +37 -0
  32. aiq/builder/eval_builder.py +43 -2
  33. aiq/builder/function.py +44 -12
  34. aiq/builder/function_base.py +1 -1
  35. aiq/builder/intermediate_step_manager.py +6 -8
  36. aiq/builder/user_interaction_manager.py +3 -0
  37. aiq/builder/workflow.py +23 -18
  38. aiq/builder/workflow_builder.py +421 -61
  39. aiq/cli/commands/info/list_mcp.py +103 -16
  40. aiq/cli/commands/sizing/__init__.py +14 -0
  41. aiq/cli/commands/sizing/calc.py +294 -0
  42. aiq/cli/commands/sizing/sizing.py +27 -0
  43. aiq/cli/commands/start.py +2 -1
  44. aiq/cli/entrypoint.py +2 -0
  45. aiq/cli/register_workflow.py +80 -0
  46. aiq/cli/type_registry.py +151 -30
  47. aiq/data_models/api_server.py +124 -12
  48. aiq/data_models/authentication.py +231 -0
  49. aiq/data_models/common.py +35 -7
  50. aiq/data_models/component.py +17 -9
  51. aiq/data_models/component_ref.py +33 -0
  52. aiq/data_models/config.py +60 -3
  53. aiq/data_models/dataset_handler.py +2 -1
  54. aiq/data_models/embedder.py +1 -0
  55. aiq/data_models/evaluate.py +23 -0
  56. aiq/data_models/function_dependencies.py +8 -0
  57. aiq/data_models/interactive.py +10 -1
  58. aiq/data_models/intermediate_step.py +38 -5
  59. aiq/data_models/its_strategy.py +30 -0
  60. aiq/data_models/llm.py +1 -0
  61. aiq/data_models/memory.py +1 -0
  62. aiq/data_models/object_store.py +44 -0
  63. aiq/data_models/profiler.py +1 -0
  64. aiq/data_models/retry_mixin.py +35 -0
  65. aiq/data_models/span.py +187 -0
  66. aiq/data_models/telemetry_exporter.py +2 -2
  67. aiq/embedder/nim_embedder.py +2 -1
  68. aiq/embedder/openai_embedder.py +2 -1
  69. aiq/eval/config.py +19 -1
  70. aiq/eval/dataset_handler/dataset_handler.py +87 -2
  71. aiq/eval/evaluate.py +208 -27
  72. aiq/eval/evaluator/base_evaluator.py +73 -0
  73. aiq/eval/evaluator/evaluator_model.py +1 -0
  74. aiq/eval/intermediate_step_adapter.py +11 -5
  75. aiq/eval/rag_evaluator/evaluate.py +55 -15
  76. aiq/eval/rag_evaluator/register.py +6 -1
  77. aiq/eval/remote_workflow.py +7 -2
  78. aiq/eval/runners/__init__.py +14 -0
  79. aiq/eval/runners/config.py +39 -0
  80. aiq/eval/runners/multi_eval_runner.py +54 -0
  81. aiq/eval/trajectory_evaluator/evaluate.py +22 -65
  82. aiq/eval/tunable_rag_evaluator/evaluate.py +150 -168
  83. aiq/eval/tunable_rag_evaluator/register.py +2 -0
  84. aiq/eval/usage_stats.py +41 -0
  85. aiq/eval/utils/output_uploader.py +10 -1
  86. aiq/eval/utils/weave_eval.py +184 -0
  87. aiq/experimental/__init__.py +0 -0
  88. aiq/experimental/decorators/__init__.py +0 -0
  89. aiq/experimental/decorators/experimental_warning_decorator.py +130 -0
  90. aiq/experimental/inference_time_scaling/__init__.py +0 -0
  91. aiq/experimental/inference_time_scaling/editing/__init__.py +0 -0
  92. aiq/experimental/inference_time_scaling/editing/iterative_plan_refinement_editor.py +147 -0
  93. aiq/experimental/inference_time_scaling/editing/llm_as_a_judge_editor.py +204 -0
  94. aiq/experimental/inference_time_scaling/editing/motivation_aware_summarization.py +107 -0
  95. aiq/experimental/inference_time_scaling/functions/__init__.py +0 -0
  96. aiq/experimental/inference_time_scaling/functions/execute_score_select_function.py +105 -0
  97. aiq/experimental/inference_time_scaling/functions/its_tool_orchestration_function.py +205 -0
  98. aiq/experimental/inference_time_scaling/functions/its_tool_wrapper_function.py +146 -0
  99. aiq/experimental/inference_time_scaling/functions/plan_select_execute_function.py +224 -0
  100. aiq/experimental/inference_time_scaling/models/__init__.py +0 -0
  101. aiq/experimental/inference_time_scaling/models/editor_config.py +132 -0
  102. aiq/experimental/inference_time_scaling/models/its_item.py +48 -0
  103. aiq/experimental/inference_time_scaling/models/scoring_config.py +112 -0
  104. aiq/experimental/inference_time_scaling/models/search_config.py +120 -0
  105. aiq/experimental/inference_time_scaling/models/selection_config.py +154 -0
  106. aiq/experimental/inference_time_scaling/models/stage_enums.py +43 -0
  107. aiq/experimental/inference_time_scaling/models/strategy_base.py +66 -0
  108. aiq/experimental/inference_time_scaling/models/tool_use_config.py +41 -0
  109. aiq/experimental/inference_time_scaling/register.py +36 -0
  110. aiq/experimental/inference_time_scaling/scoring/__init__.py +0 -0
  111. aiq/experimental/inference_time_scaling/scoring/llm_based_agent_scorer.py +168 -0
  112. aiq/experimental/inference_time_scaling/scoring/llm_based_plan_scorer.py +168 -0
  113. aiq/experimental/inference_time_scaling/scoring/motivation_aware_scorer.py +111 -0
  114. aiq/experimental/inference_time_scaling/search/__init__.py +0 -0
  115. aiq/experimental/inference_time_scaling/search/multi_llm_planner.py +128 -0
  116. aiq/experimental/inference_time_scaling/search/multi_query_retrieval_search.py +122 -0
  117. aiq/experimental/inference_time_scaling/search/single_shot_multi_plan_planner.py +128 -0
  118. aiq/experimental/inference_time_scaling/selection/__init__.py +0 -0
  119. aiq/experimental/inference_time_scaling/selection/best_of_n_selector.py +63 -0
  120. aiq/experimental/inference_time_scaling/selection/llm_based_agent_output_selector.py +131 -0
  121. aiq/experimental/inference_time_scaling/selection/llm_based_output_merging_selector.py +159 -0
  122. aiq/experimental/inference_time_scaling/selection/llm_based_plan_selector.py +128 -0
  123. aiq/experimental/inference_time_scaling/selection/threshold_selector.py +58 -0
  124. aiq/front_ends/console/authentication_flow_handler.py +233 -0
  125. aiq/front_ends/console/console_front_end_plugin.py +11 -2
  126. aiq/front_ends/fastapi/auth_flow_handlers/__init__.py +0 -0
  127. aiq/front_ends/fastapi/auth_flow_handlers/http_flow_handler.py +27 -0
  128. aiq/front_ends/fastapi/auth_flow_handlers/websocket_flow_handler.py +107 -0
  129. aiq/front_ends/fastapi/fastapi_front_end_config.py +93 -9
  130. aiq/front_ends/fastapi/fastapi_front_end_controller.py +68 -0
  131. aiq/front_ends/fastapi/fastapi_front_end_plugin.py +14 -1
  132. aiq/front_ends/fastapi/fastapi_front_end_plugin_worker.py +537 -52
  133. aiq/front_ends/fastapi/html_snippets/__init__.py +14 -0
  134. aiq/front_ends/fastapi/html_snippets/auth_code_grant_success.py +35 -0
  135. aiq/front_ends/fastapi/job_store.py +47 -25
  136. aiq/front_ends/fastapi/main.py +2 -0
  137. aiq/front_ends/fastapi/message_handler.py +108 -89
  138. aiq/front_ends/fastapi/step_adaptor.py +2 -1
  139. aiq/llm/aws_bedrock_llm.py +57 -0
  140. aiq/llm/nim_llm.py +2 -1
  141. aiq/llm/openai_llm.py +3 -2
  142. aiq/llm/register.py +1 -0
  143. aiq/meta/pypi.md +12 -12
  144. aiq/object_store/__init__.py +20 -0
  145. aiq/object_store/in_memory_object_store.py +74 -0
  146. aiq/object_store/interfaces.py +84 -0
  147. aiq/object_store/models.py +36 -0
  148. aiq/object_store/register.py +20 -0
  149. aiq/observability/__init__.py +14 -0
  150. aiq/observability/exporter/__init__.py +14 -0
  151. aiq/observability/exporter/base_exporter.py +449 -0
  152. aiq/observability/exporter/exporter.py +78 -0
  153. aiq/observability/exporter/file_exporter.py +33 -0
  154. aiq/observability/exporter/processing_exporter.py +269 -0
  155. aiq/observability/exporter/raw_exporter.py +52 -0
  156. aiq/observability/exporter/span_exporter.py +264 -0
  157. aiq/observability/exporter_manager.py +335 -0
  158. aiq/observability/mixin/__init__.py +14 -0
  159. aiq/observability/mixin/batch_config_mixin.py +26 -0
  160. aiq/observability/mixin/collector_config_mixin.py +23 -0
  161. aiq/observability/mixin/file_mixin.py +288 -0
  162. aiq/observability/mixin/file_mode.py +23 -0
  163. aiq/observability/mixin/resource_conflict_mixin.py +134 -0
  164. aiq/observability/mixin/serialize_mixin.py +61 -0
  165. aiq/observability/mixin/type_introspection_mixin.py +183 -0
  166. aiq/observability/processor/__init__.py +14 -0
  167. aiq/observability/processor/batching_processor.py +316 -0
  168. aiq/observability/processor/intermediate_step_serializer.py +28 -0
  169. aiq/observability/processor/processor.py +68 -0
  170. aiq/observability/register.py +36 -39
  171. aiq/observability/utils/__init__.py +14 -0
  172. aiq/observability/utils/dict_utils.py +236 -0
  173. aiq/observability/utils/time_utils.py +31 -0
  174. aiq/profiler/calc/__init__.py +14 -0
  175. aiq/profiler/calc/calc_runner.py +623 -0
  176. aiq/profiler/calc/calculations.py +288 -0
  177. aiq/profiler/calc/data_models.py +176 -0
  178. aiq/profiler/calc/plot.py +345 -0
  179. aiq/profiler/callbacks/langchain_callback_handler.py +22 -10
  180. aiq/profiler/data_models.py +24 -0
  181. aiq/profiler/inference_metrics_model.py +3 -0
  182. aiq/profiler/inference_optimization/bottleneck_analysis/nested_stack_analysis.py +8 -0
  183. aiq/profiler/inference_optimization/data_models.py +2 -2
  184. aiq/profiler/inference_optimization/llm_metrics.py +2 -2
  185. aiq/profiler/profile_runner.py +61 -21
  186. aiq/runtime/loader.py +9 -3
  187. aiq/runtime/runner.py +23 -9
  188. aiq/runtime/session.py +25 -7
  189. aiq/runtime/user_metadata.py +2 -3
  190. aiq/tool/chat_completion.py +74 -0
  191. aiq/tool/code_execution/README.md +152 -0
  192. aiq/tool/code_execution/code_sandbox.py +151 -72
  193. aiq/tool/code_execution/local_sandbox/.gitignore +1 -0
  194. aiq/tool/code_execution/local_sandbox/local_sandbox_server.py +139 -24
  195. aiq/tool/code_execution/local_sandbox/sandbox.requirements.txt +3 -1
  196. aiq/tool/code_execution/local_sandbox/start_local_sandbox.sh +27 -2
  197. aiq/tool/code_execution/register.py +7 -3
  198. aiq/tool/code_execution/test_code_execution_sandbox.py +414 -0
  199. aiq/tool/mcp/exceptions.py +142 -0
  200. aiq/tool/mcp/mcp_client.py +41 -6
  201. aiq/tool/mcp/mcp_tool.py +3 -2
  202. aiq/tool/register.py +1 -0
  203. aiq/tool/server_tools.py +6 -3
  204. aiq/utils/exception_handlers/automatic_retries.py +289 -0
  205. aiq/utils/exception_handlers/mcp.py +211 -0
  206. aiq/utils/io/model_processing.py +28 -0
  207. aiq/utils/log_utils.py +37 -0
  208. aiq/utils/string_utils.py +38 -0
  209. aiq/utils/type_converter.py +18 -2
  210. aiq/utils/type_utils.py +87 -0
  211. {aiqtoolkit-1.2.0.dev0.dist-info → aiqtoolkit-1.2.0rc1.dist-info}/METADATA +53 -21
  212. aiqtoolkit-1.2.0rc1.dist-info/RECORD +436 -0
  213. {aiqtoolkit-1.2.0.dev0.dist-info → aiqtoolkit-1.2.0rc1.dist-info}/WHEEL +1 -1
  214. {aiqtoolkit-1.2.0.dev0.dist-info → aiqtoolkit-1.2.0rc1.dist-info}/entry_points.txt +3 -0
  215. aiq/front_ends/fastapi/websocket.py +0 -148
  216. aiq/observability/async_otel_listener.py +0 -429
  217. aiqtoolkit-1.2.0.dev0.dist-info/RECORD +0 -316
  218. {aiqtoolkit-1.2.0.dev0.dist-info → aiqtoolkit-1.2.0rc1.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
  219. {aiqtoolkit-1.2.0.dev0.dist-info → aiqtoolkit-1.2.0rc1.dist-info}/licenses/LICENSE.md +0 -0
  220. {aiqtoolkit-1.2.0.dev0.dist-info → aiqtoolkit-1.2.0rc1.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.async_otel_listener import AsyncOtelSpanListener
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, input_message: typing.Any, entry_fn: Function, context_state: AIQContextState):
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._span_manager = AsyncOtelSpanListener(context_state=context_state)
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._span_manager.start():
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().on_complete()
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().on_complete()
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._span_manager.start():
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().on_complete()
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().on_complete()
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 fastapi import Request
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: Request = None,
90
- user_input_callback: Callable[[InteractionPrompt], Awaitable[HumanResponse]] = None):
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
- self.set_request_attributes(request)
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 set_request_attributes(self, request: Request) -> None:
138
+ def set_metadata_from_http_request(self, request: HTTPConnection | None) -> None:
124
139
  """
125
- Extracts and sets request attributes from an HTTP request.
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.method
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"])
@@ -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-defined
25
- metadata and attributes. It provides a way to store and
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