aiqtoolkit 1.1.0rc6__py3-none-any.whl → 1.2.0__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.
- aiqtoolkit-1.2.0.dist-info/METADATA +29 -0
- aiqtoolkit-1.2.0.dist-info/RECORD +4 -0
- {aiqtoolkit-1.1.0rc6.dist-info → aiqtoolkit-1.2.0.dist-info}/WHEEL +1 -1
- aiqtoolkit-1.2.0.dist-info/top_level.txt +1 -0
- aiq/agent/__init__.py +0 -0
- aiq/agent/base.py +0 -76
- aiq/agent/dual_node.py +0 -67
- aiq/agent/react_agent/__init__.py +0 -0
- aiq/agent/react_agent/agent.py +0 -322
- aiq/agent/react_agent/output_parser.py +0 -104
- aiq/agent/react_agent/prompt.py +0 -46
- aiq/agent/react_agent/register.py +0 -148
- aiq/agent/reasoning_agent/__init__.py +0 -0
- aiq/agent/reasoning_agent/reasoning_agent.py +0 -224
- aiq/agent/register.py +0 -23
- aiq/agent/rewoo_agent/__init__.py +0 -0
- aiq/agent/rewoo_agent/agent.py +0 -410
- aiq/agent/rewoo_agent/prompt.py +0 -108
- aiq/agent/rewoo_agent/register.py +0 -158
- aiq/agent/tool_calling_agent/__init__.py +0 -0
- aiq/agent/tool_calling_agent/agent.py +0 -123
- aiq/agent/tool_calling_agent/register.py +0 -105
- aiq/builder/__init__.py +0 -0
- aiq/builder/builder.py +0 -223
- aiq/builder/component_utils.py +0 -303
- aiq/builder/context.py +0 -227
- aiq/builder/embedder.py +0 -24
- aiq/builder/eval_builder.py +0 -120
- aiq/builder/evaluator.py +0 -29
- aiq/builder/framework_enum.py +0 -24
- aiq/builder/front_end.py +0 -73
- aiq/builder/function.py +0 -297
- aiq/builder/function_base.py +0 -376
- aiq/builder/function_info.py +0 -627
- aiq/builder/intermediate_step_manager.py +0 -176
- aiq/builder/llm.py +0 -25
- aiq/builder/retriever.py +0 -25
- aiq/builder/user_interaction_manager.py +0 -71
- aiq/builder/workflow.py +0 -143
- aiq/builder/workflow_builder.py +0 -757
- aiq/cli/__init__.py +0 -14
- aiq/cli/cli_utils/__init__.py +0 -0
- aiq/cli/cli_utils/config_override.py +0 -231
- aiq/cli/cli_utils/validation.py +0 -37
- aiq/cli/commands/__init__.py +0 -0
- aiq/cli/commands/configure/__init__.py +0 -0
- aiq/cli/commands/configure/channel/__init__.py +0 -0
- aiq/cli/commands/configure/channel/add.py +0 -28
- aiq/cli/commands/configure/channel/channel.py +0 -36
- aiq/cli/commands/configure/channel/remove.py +0 -30
- aiq/cli/commands/configure/channel/update.py +0 -30
- aiq/cli/commands/configure/configure.py +0 -33
- aiq/cli/commands/evaluate.py +0 -139
- aiq/cli/commands/info/__init__.py +0 -14
- aiq/cli/commands/info/info.py +0 -39
- aiq/cli/commands/info/list_channels.py +0 -32
- aiq/cli/commands/info/list_components.py +0 -129
- aiq/cli/commands/info/list_mcp.py +0 -126
- aiq/cli/commands/registry/__init__.py +0 -14
- aiq/cli/commands/registry/publish.py +0 -88
- aiq/cli/commands/registry/pull.py +0 -118
- aiq/cli/commands/registry/registry.py +0 -38
- aiq/cli/commands/registry/remove.py +0 -108
- aiq/cli/commands/registry/search.py +0 -155
- aiq/cli/commands/start.py +0 -250
- aiq/cli/commands/uninstall.py +0 -83
- aiq/cli/commands/validate.py +0 -47
- aiq/cli/commands/workflow/__init__.py +0 -14
- aiq/cli/commands/workflow/templates/__init__.py.j2 +0 -0
- aiq/cli/commands/workflow/templates/config.yml.j2 +0 -16
- aiq/cli/commands/workflow/templates/pyproject.toml.j2 +0 -22
- aiq/cli/commands/workflow/templates/register.py.j2 +0 -5
- aiq/cli/commands/workflow/templates/workflow.py.j2 +0 -36
- aiq/cli/commands/workflow/workflow.py +0 -37
- aiq/cli/commands/workflow/workflow_commands.py +0 -313
- aiq/cli/entrypoint.py +0 -133
- aiq/cli/main.py +0 -44
- aiq/cli/register_workflow.py +0 -408
- aiq/cli/type_registry.py +0 -879
- aiq/data_models/__init__.py +0 -14
- aiq/data_models/api_server.py +0 -588
- aiq/data_models/common.py +0 -143
- aiq/data_models/component.py +0 -46
- aiq/data_models/component_ref.py +0 -135
- aiq/data_models/config.py +0 -349
- aiq/data_models/dataset_handler.py +0 -122
- aiq/data_models/discovery_metadata.py +0 -286
- aiq/data_models/embedder.py +0 -26
- aiq/data_models/evaluate.py +0 -104
- aiq/data_models/evaluator.py +0 -26
- aiq/data_models/front_end.py +0 -26
- aiq/data_models/function.py +0 -30
- aiq/data_models/function_dependencies.py +0 -64
- aiq/data_models/interactive.py +0 -237
- aiq/data_models/intermediate_step.py +0 -269
- aiq/data_models/invocation_node.py +0 -38
- aiq/data_models/llm.py +0 -26
- aiq/data_models/logging.py +0 -26
- aiq/data_models/memory.py +0 -26
- aiq/data_models/profiler.py +0 -53
- aiq/data_models/registry_handler.py +0 -26
- aiq/data_models/retriever.py +0 -30
- aiq/data_models/step_adaptor.py +0 -64
- aiq/data_models/streaming.py +0 -33
- aiq/data_models/swe_bench_model.py +0 -54
- aiq/data_models/telemetry_exporter.py +0 -26
- aiq/embedder/__init__.py +0 -0
- aiq/embedder/langchain_client.py +0 -41
- aiq/embedder/nim_embedder.py +0 -58
- aiq/embedder/openai_embedder.py +0 -42
- aiq/embedder/register.py +0 -24
- aiq/eval/__init__.py +0 -14
- aiq/eval/config.py +0 -42
- aiq/eval/dataset_handler/__init__.py +0 -0
- aiq/eval/dataset_handler/dataset_downloader.py +0 -106
- aiq/eval/dataset_handler/dataset_filter.py +0 -52
- aiq/eval/dataset_handler/dataset_handler.py +0 -169
- aiq/eval/evaluate.py +0 -325
- aiq/eval/evaluator/__init__.py +0 -14
- aiq/eval/evaluator/evaluator_model.py +0 -44
- aiq/eval/intermediate_step_adapter.py +0 -93
- aiq/eval/rag_evaluator/__init__.py +0 -0
- aiq/eval/rag_evaluator/evaluate.py +0 -138
- aiq/eval/rag_evaluator/register.py +0 -138
- aiq/eval/register.py +0 -23
- aiq/eval/remote_workflow.py +0 -128
- aiq/eval/runtime_event_subscriber.py +0 -52
- aiq/eval/swe_bench_evaluator/__init__.py +0 -0
- aiq/eval/swe_bench_evaluator/evaluate.py +0 -215
- aiq/eval/swe_bench_evaluator/register.py +0 -36
- aiq/eval/trajectory_evaluator/__init__.py +0 -0
- aiq/eval/trajectory_evaluator/evaluate.py +0 -118
- aiq/eval/trajectory_evaluator/register.py +0 -40
- aiq/eval/tunable_rag_evaluator/__init__.py +0 -0
- aiq/eval/tunable_rag_evaluator/evaluate.py +0 -263
- aiq/eval/tunable_rag_evaluator/register.py +0 -50
- aiq/eval/utils/__init__.py +0 -0
- aiq/eval/utils/output_uploader.py +0 -131
- aiq/eval/utils/tqdm_position_registry.py +0 -40
- aiq/front_ends/__init__.py +0 -14
- aiq/front_ends/console/__init__.py +0 -14
- aiq/front_ends/console/console_front_end_config.py +0 -32
- aiq/front_ends/console/console_front_end_plugin.py +0 -107
- aiq/front_ends/console/register.py +0 -25
- aiq/front_ends/cron/__init__.py +0 -14
- aiq/front_ends/fastapi/__init__.py +0 -14
- aiq/front_ends/fastapi/fastapi_front_end_config.py +0 -150
- aiq/front_ends/fastapi/fastapi_front_end_plugin.py +0 -103
- aiq/front_ends/fastapi/fastapi_front_end_plugin_worker.py +0 -607
- aiq/front_ends/fastapi/intermediate_steps_subscriber.py +0 -80
- aiq/front_ends/fastapi/job_store.py +0 -161
- aiq/front_ends/fastapi/main.py +0 -70
- aiq/front_ends/fastapi/message_handler.py +0 -279
- aiq/front_ends/fastapi/message_validator.py +0 -345
- aiq/front_ends/fastapi/register.py +0 -25
- aiq/front_ends/fastapi/response_helpers.py +0 -195
- aiq/front_ends/fastapi/step_adaptor.py +0 -320
- aiq/front_ends/fastapi/websocket.py +0 -148
- aiq/front_ends/mcp/__init__.py +0 -14
- aiq/front_ends/mcp/mcp_front_end_config.py +0 -32
- aiq/front_ends/mcp/mcp_front_end_plugin.py +0 -93
- aiq/front_ends/mcp/register.py +0 -27
- aiq/front_ends/mcp/tool_converter.py +0 -242
- aiq/front_ends/register.py +0 -22
- aiq/front_ends/simple_base/__init__.py +0 -14
- aiq/front_ends/simple_base/simple_front_end_plugin_base.py +0 -52
- aiq/llm/__init__.py +0 -0
- aiq/llm/nim_llm.py +0 -45
- aiq/llm/openai_llm.py +0 -45
- aiq/llm/register.py +0 -22
- aiq/llm/utils/__init__.py +0 -14
- aiq/llm/utils/env_config_value.py +0 -94
- aiq/llm/utils/error.py +0 -17
- aiq/memory/__init__.py +0 -20
- aiq/memory/interfaces.py +0 -183
- aiq/memory/models.py +0 -112
- aiq/meta/module_to_distro.json +0 -3
- aiq/meta/pypi.md +0 -58
- aiq/observability/__init__.py +0 -0
- aiq/observability/async_otel_listener.py +0 -429
- aiq/observability/register.py +0 -99
- aiq/plugins/.namespace +0 -1
- aiq/profiler/__init__.py +0 -0
- aiq/profiler/callbacks/__init__.py +0 -0
- aiq/profiler/callbacks/agno_callback_handler.py +0 -295
- aiq/profiler/callbacks/base_callback_class.py +0 -20
- aiq/profiler/callbacks/langchain_callback_handler.py +0 -278
- aiq/profiler/callbacks/llama_index_callback_handler.py +0 -205
- aiq/profiler/callbacks/semantic_kernel_callback_handler.py +0 -238
- aiq/profiler/callbacks/token_usage_base_model.py +0 -27
- aiq/profiler/data_frame_row.py +0 -51
- aiq/profiler/decorators/__init__.py +0 -0
- aiq/profiler/decorators/framework_wrapper.py +0 -131
- aiq/profiler/decorators/function_tracking.py +0 -254
- aiq/profiler/forecasting/__init__.py +0 -0
- aiq/profiler/forecasting/config.py +0 -18
- aiq/profiler/forecasting/model_trainer.py +0 -75
- aiq/profiler/forecasting/models/__init__.py +0 -22
- aiq/profiler/forecasting/models/forecasting_base_model.py +0 -40
- aiq/profiler/forecasting/models/linear_model.py +0 -196
- aiq/profiler/forecasting/models/random_forest_regressor.py +0 -268
- aiq/profiler/inference_metrics_model.py +0 -25
- aiq/profiler/inference_optimization/__init__.py +0 -0
- aiq/profiler/inference_optimization/bottleneck_analysis/__init__.py +0 -0
- aiq/profiler/inference_optimization/bottleneck_analysis/nested_stack_analysis.py +0 -452
- aiq/profiler/inference_optimization/bottleneck_analysis/simple_stack_analysis.py +0 -258
- aiq/profiler/inference_optimization/data_models.py +0 -386
- aiq/profiler/inference_optimization/experimental/__init__.py +0 -0
- aiq/profiler/inference_optimization/experimental/concurrency_spike_analysis.py +0 -468
- aiq/profiler/inference_optimization/experimental/prefix_span_analysis.py +0 -405
- aiq/profiler/inference_optimization/llm_metrics.py +0 -212
- aiq/profiler/inference_optimization/prompt_caching.py +0 -163
- aiq/profiler/inference_optimization/token_uniqueness.py +0 -107
- aiq/profiler/inference_optimization/workflow_runtimes.py +0 -72
- aiq/profiler/intermediate_property_adapter.py +0 -102
- aiq/profiler/profile_runner.py +0 -433
- aiq/profiler/utils.py +0 -184
- aiq/registry_handlers/__init__.py +0 -0
- aiq/registry_handlers/local/__init__.py +0 -0
- aiq/registry_handlers/local/local_handler.py +0 -176
- aiq/registry_handlers/local/register_local.py +0 -37
- aiq/registry_handlers/metadata_factory.py +0 -60
- aiq/registry_handlers/package_utils.py +0 -198
- aiq/registry_handlers/pypi/__init__.py +0 -0
- aiq/registry_handlers/pypi/pypi_handler.py +0 -251
- aiq/registry_handlers/pypi/register_pypi.py +0 -40
- aiq/registry_handlers/register.py +0 -21
- aiq/registry_handlers/registry_handler_base.py +0 -157
- aiq/registry_handlers/rest/__init__.py +0 -0
- aiq/registry_handlers/rest/register_rest.py +0 -56
- aiq/registry_handlers/rest/rest_handler.py +0 -237
- aiq/registry_handlers/schemas/__init__.py +0 -0
- aiq/registry_handlers/schemas/headers.py +0 -42
- aiq/registry_handlers/schemas/package.py +0 -68
- aiq/registry_handlers/schemas/publish.py +0 -63
- aiq/registry_handlers/schemas/pull.py +0 -82
- aiq/registry_handlers/schemas/remove.py +0 -36
- aiq/registry_handlers/schemas/search.py +0 -91
- aiq/registry_handlers/schemas/status.py +0 -47
- aiq/retriever/__init__.py +0 -0
- aiq/retriever/interface.py +0 -37
- aiq/retriever/milvus/__init__.py +0 -14
- aiq/retriever/milvus/register.py +0 -81
- aiq/retriever/milvus/retriever.py +0 -228
- aiq/retriever/models.py +0 -74
- aiq/retriever/nemo_retriever/__init__.py +0 -14
- aiq/retriever/nemo_retriever/register.py +0 -60
- aiq/retriever/nemo_retriever/retriever.py +0 -190
- aiq/retriever/register.py +0 -22
- aiq/runtime/__init__.py +0 -14
- aiq/runtime/loader.py +0 -188
- aiq/runtime/runner.py +0 -176
- aiq/runtime/session.py +0 -140
- aiq/runtime/user_metadata.py +0 -131
- aiq/settings/__init__.py +0 -0
- aiq/settings/global_settings.py +0 -318
- aiq/test/.namespace +0 -1
- aiq/tool/__init__.py +0 -0
- aiq/tool/code_execution/__init__.py +0 -0
- aiq/tool/code_execution/code_sandbox.py +0 -188
- aiq/tool/code_execution/local_sandbox/Dockerfile.sandbox +0 -60
- aiq/tool/code_execution/local_sandbox/__init__.py +0 -13
- aiq/tool/code_execution/local_sandbox/local_sandbox_server.py +0 -83
- aiq/tool/code_execution/local_sandbox/sandbox.requirements.txt +0 -4
- aiq/tool/code_execution/local_sandbox/start_local_sandbox.sh +0 -25
- aiq/tool/code_execution/register.py +0 -70
- aiq/tool/code_execution/utils.py +0 -100
- aiq/tool/datetime_tools.py +0 -42
- aiq/tool/document_search.py +0 -141
- aiq/tool/github_tools/__init__.py +0 -0
- aiq/tool/github_tools/create_github_commit.py +0 -133
- aiq/tool/github_tools/create_github_issue.py +0 -87
- aiq/tool/github_tools/create_github_pr.py +0 -106
- aiq/tool/github_tools/get_github_file.py +0 -106
- aiq/tool/github_tools/get_github_issue.py +0 -166
- aiq/tool/github_tools/get_github_pr.py +0 -256
- aiq/tool/github_tools/update_github_issue.py +0 -100
- aiq/tool/mcp/__init__.py +0 -14
- aiq/tool/mcp/mcp_client.py +0 -220
- aiq/tool/mcp/mcp_tool.py +0 -95
- aiq/tool/memory_tools/__init__.py +0 -0
- aiq/tool/memory_tools/add_memory_tool.py +0 -79
- aiq/tool/memory_tools/delete_memory_tool.py +0 -67
- aiq/tool/memory_tools/get_memory_tool.py +0 -72
- aiq/tool/nvidia_rag.py +0 -95
- aiq/tool/register.py +0 -37
- aiq/tool/retriever.py +0 -89
- aiq/tool/server_tools.py +0 -63
- aiq/utils/__init__.py +0 -0
- aiq/utils/data_models/__init__.py +0 -0
- aiq/utils/data_models/schema_validator.py +0 -58
- aiq/utils/debugging_utils.py +0 -43
- aiq/utils/exception_handlers/__init__.py +0 -0
- aiq/utils/exception_handlers/schemas.py +0 -114
- aiq/utils/io/__init__.py +0 -0
- aiq/utils/io/yaml_tools.py +0 -119
- aiq/utils/metadata_utils.py +0 -74
- aiq/utils/optional_imports.py +0 -142
- aiq/utils/producer_consumer_queue.py +0 -178
- aiq/utils/reactive/__init__.py +0 -0
- aiq/utils/reactive/base/__init__.py +0 -0
- aiq/utils/reactive/base/observable_base.py +0 -65
- aiq/utils/reactive/base/observer_base.py +0 -55
- aiq/utils/reactive/base/subject_base.py +0 -79
- aiq/utils/reactive/observable.py +0 -59
- aiq/utils/reactive/observer.py +0 -76
- aiq/utils/reactive/subject.py +0 -131
- aiq/utils/reactive/subscription.py +0 -49
- aiq/utils/settings/__init__.py +0 -0
- aiq/utils/settings/global_settings.py +0 -197
- aiq/utils/type_converter.py +0 -232
- aiq/utils/type_utils.py +0 -397
- aiq/utils/url_utils.py +0 -27
- aiqtoolkit-1.1.0rc6.dist-info/METADATA +0 -331
- aiqtoolkit-1.1.0rc6.dist-info/RECORD +0 -316
- aiqtoolkit-1.1.0rc6.dist-info/entry_points.txt +0 -17
- aiqtoolkit-1.1.0rc6.dist-info/licenses/LICENSE-3rd-party.txt +0 -3686
- aiqtoolkit-1.1.0rc6.dist-info/licenses/LICENSE.md +0 -201
- aiqtoolkit-1.1.0rc6.dist-info/top_level.txt +0 -1
|
@@ -1,607 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 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
|
-
import asyncio
|
|
17
|
-
import logging
|
|
18
|
-
import os
|
|
19
|
-
import typing
|
|
20
|
-
from abc import ABC
|
|
21
|
-
from abc import abstractmethod
|
|
22
|
-
from contextlib import asynccontextmanager
|
|
23
|
-
from functools import partial
|
|
24
|
-
from pathlib import Path
|
|
25
|
-
|
|
26
|
-
from fastapi import BackgroundTasks
|
|
27
|
-
from fastapi import Body
|
|
28
|
-
from fastapi import FastAPI
|
|
29
|
-
from fastapi import Request
|
|
30
|
-
from fastapi import Response
|
|
31
|
-
from fastapi.exceptions import HTTPException
|
|
32
|
-
from fastapi.middleware.cors import CORSMiddleware
|
|
33
|
-
from fastapi.responses import StreamingResponse
|
|
34
|
-
from pydantic import BaseModel
|
|
35
|
-
|
|
36
|
-
from aiq.builder.workflow_builder import WorkflowBuilder
|
|
37
|
-
from aiq.data_models.api_server import AIQChatRequest
|
|
38
|
-
from aiq.data_models.api_server import AIQChatResponse
|
|
39
|
-
from aiq.data_models.api_server import AIQChatResponseChunk
|
|
40
|
-
from aiq.data_models.api_server import AIQResponseIntermediateStep
|
|
41
|
-
from aiq.data_models.config import AIQConfig
|
|
42
|
-
from aiq.eval.config import EvaluationRunOutput
|
|
43
|
-
from aiq.eval.evaluate import EvaluationRun
|
|
44
|
-
from aiq.eval.evaluate import EvaluationRunConfig
|
|
45
|
-
from aiq.front_ends.fastapi.fastapi_front_end_config import AIQEvaluateRequest
|
|
46
|
-
from aiq.front_ends.fastapi.fastapi_front_end_config import AIQEvaluateResponse
|
|
47
|
-
from aiq.front_ends.fastapi.fastapi_front_end_config import AIQEvaluateStatusResponse
|
|
48
|
-
from aiq.front_ends.fastapi.fastapi_front_end_config import FastApiFrontEndConfig
|
|
49
|
-
from aiq.front_ends.fastapi.job_store import JobInfo
|
|
50
|
-
from aiq.front_ends.fastapi.job_store import JobStore
|
|
51
|
-
from aiq.front_ends.fastapi.response_helpers import generate_single_response
|
|
52
|
-
from aiq.front_ends.fastapi.response_helpers import generate_streaming_response_as_str
|
|
53
|
-
from aiq.front_ends.fastapi.response_helpers import generate_streaming_response_full_as_str
|
|
54
|
-
from aiq.front_ends.fastapi.step_adaptor import StepAdaptor
|
|
55
|
-
from aiq.front_ends.fastapi.websocket import AIQWebSocket
|
|
56
|
-
from aiq.runtime.session import AIQSessionManager
|
|
57
|
-
|
|
58
|
-
logger = logging.getLogger(__name__)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
class FastApiFrontEndPluginWorkerBase(ABC):
|
|
62
|
-
|
|
63
|
-
def __init__(self, config: AIQConfig):
|
|
64
|
-
self._config = config
|
|
65
|
-
|
|
66
|
-
assert isinstance(config.general.front_end,
|
|
67
|
-
FastApiFrontEndConfig), ("Front end config is not FastApiFrontEndConfig")
|
|
68
|
-
|
|
69
|
-
self._front_end_config = config.general.front_end
|
|
70
|
-
|
|
71
|
-
@property
|
|
72
|
-
def config(self) -> AIQConfig:
|
|
73
|
-
return self._config
|
|
74
|
-
|
|
75
|
-
@property
|
|
76
|
-
def front_end_config(self) -> FastApiFrontEndConfig:
|
|
77
|
-
|
|
78
|
-
return self._front_end_config
|
|
79
|
-
|
|
80
|
-
def build_app(self) -> FastAPI:
|
|
81
|
-
|
|
82
|
-
# Create the FastAPI app and configure it
|
|
83
|
-
@asynccontextmanager
|
|
84
|
-
async def lifespan(starting_app: FastAPI):
|
|
85
|
-
|
|
86
|
-
logger.debug("Starting AIQ Toolkit server from process %s", os.getpid())
|
|
87
|
-
|
|
88
|
-
async with WorkflowBuilder.from_config(self.config) as builder:
|
|
89
|
-
|
|
90
|
-
await self.configure(starting_app, builder)
|
|
91
|
-
|
|
92
|
-
yield
|
|
93
|
-
|
|
94
|
-
# If a cleanup task is running, cancel it
|
|
95
|
-
cleanup_task = getattr(starting_app.state, "cleanup_task", None)
|
|
96
|
-
if cleanup_task:
|
|
97
|
-
logger.info("Cancelling cleanup task")
|
|
98
|
-
cleanup_task.cancel()
|
|
99
|
-
|
|
100
|
-
logger.debug("Closing AIQ Toolkit server from process %s", os.getpid())
|
|
101
|
-
|
|
102
|
-
aiq_app = FastAPI(lifespan=lifespan)
|
|
103
|
-
|
|
104
|
-
self.set_cors_config(aiq_app)
|
|
105
|
-
|
|
106
|
-
return aiq_app
|
|
107
|
-
|
|
108
|
-
def set_cors_config(self, aiq_app: FastAPI) -> None:
|
|
109
|
-
"""
|
|
110
|
-
Set the cross origin resource sharing configuration.
|
|
111
|
-
"""
|
|
112
|
-
cors_kwargs = {}
|
|
113
|
-
|
|
114
|
-
if self.front_end_config.cors.allow_origins is not None:
|
|
115
|
-
cors_kwargs["allow_origins"] = self.front_end_config.cors.allow_origins
|
|
116
|
-
|
|
117
|
-
if self.front_end_config.cors.allow_origin_regex is not None:
|
|
118
|
-
cors_kwargs["allow_origin_regex"] = self.front_end_config.cors.allow_origin_regex
|
|
119
|
-
|
|
120
|
-
if self.front_end_config.cors.allow_methods is not None:
|
|
121
|
-
cors_kwargs["allow_methods"] = self.front_end_config.cors.allow_methods
|
|
122
|
-
|
|
123
|
-
if self.front_end_config.cors.allow_headers is not None:
|
|
124
|
-
cors_kwargs["allow_headers"] = self.front_end_config.cors.allow_headers
|
|
125
|
-
|
|
126
|
-
if self.front_end_config.cors.allow_credentials is not None:
|
|
127
|
-
cors_kwargs["allow_credentials"] = self.front_end_config.cors.allow_credentials
|
|
128
|
-
|
|
129
|
-
if self.front_end_config.cors.expose_headers is not None:
|
|
130
|
-
cors_kwargs["expose_headers"] = self.front_end_config.cors.expose_headers
|
|
131
|
-
|
|
132
|
-
if self.front_end_config.cors.max_age is not None:
|
|
133
|
-
cors_kwargs["max_age"] = self.front_end_config.cors.max_age
|
|
134
|
-
|
|
135
|
-
aiq_app.add_middleware(
|
|
136
|
-
CORSMiddleware,
|
|
137
|
-
**cors_kwargs,
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
@abstractmethod
|
|
141
|
-
async def configure(self, app: FastAPI, builder: WorkflowBuilder):
|
|
142
|
-
pass
|
|
143
|
-
|
|
144
|
-
@abstractmethod
|
|
145
|
-
def get_step_adaptor(self) -> StepAdaptor:
|
|
146
|
-
pass
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
class RouteInfo(BaseModel):
|
|
150
|
-
|
|
151
|
-
function_name: str | None
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
class FastApiFrontEndPluginWorker(FastApiFrontEndPluginWorkerBase):
|
|
155
|
-
|
|
156
|
-
def get_step_adaptor(self) -> StepAdaptor:
|
|
157
|
-
|
|
158
|
-
return StepAdaptor(self.front_end_config.step_adaptor)
|
|
159
|
-
|
|
160
|
-
async def configure(self, app: FastAPI, builder: WorkflowBuilder):
|
|
161
|
-
|
|
162
|
-
# Do things like setting the base URL and global configuration options
|
|
163
|
-
app.root_path = self.front_end_config.root_path
|
|
164
|
-
|
|
165
|
-
await self.add_routes(app, builder)
|
|
166
|
-
|
|
167
|
-
async def add_routes(self, app: FastAPI, builder: WorkflowBuilder):
|
|
168
|
-
|
|
169
|
-
await self.add_default_route(app, AIQSessionManager(builder.build()))
|
|
170
|
-
await self.add_evaluate_route(app, AIQSessionManager(builder.build()))
|
|
171
|
-
|
|
172
|
-
for ep in self.front_end_config.endpoints:
|
|
173
|
-
|
|
174
|
-
entry_workflow = builder.build(entry_function=ep.function_name)
|
|
175
|
-
|
|
176
|
-
await self.add_route(app, endpoint=ep, session_manager=AIQSessionManager(entry_workflow))
|
|
177
|
-
|
|
178
|
-
async def add_default_route(self, app: FastAPI, session_manager: AIQSessionManager):
|
|
179
|
-
|
|
180
|
-
await self.add_route(app, self.front_end_config.workflow, session_manager)
|
|
181
|
-
|
|
182
|
-
async def add_evaluate_route(self, app: FastAPI, session_manager: AIQSessionManager):
|
|
183
|
-
"""Add the evaluate endpoint to the FastAPI app."""
|
|
184
|
-
|
|
185
|
-
response_500 = {
|
|
186
|
-
"description": "Internal Server Error",
|
|
187
|
-
"content": {
|
|
188
|
-
"application/json": {
|
|
189
|
-
"example": {
|
|
190
|
-
"detail": "Internal server error occurred"
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
},
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
# Create job store for tracking evaluation jobs
|
|
197
|
-
job_store = JobStore()
|
|
198
|
-
# Don't run multiple evaluations at the same time
|
|
199
|
-
evaluation_lock = asyncio.Lock()
|
|
200
|
-
|
|
201
|
-
async def periodic_cleanup(job_store: JobStore):
|
|
202
|
-
while True:
|
|
203
|
-
try:
|
|
204
|
-
job_store.cleanup_expired_jobs()
|
|
205
|
-
logger.debug("Expired jobs cleaned up")
|
|
206
|
-
except Exception as e:
|
|
207
|
-
logger.error("Error during job cleanup: %s", str(e))
|
|
208
|
-
await asyncio.sleep(300) # every 5 minutes
|
|
209
|
-
|
|
210
|
-
def create_cleanup_task():
|
|
211
|
-
# Schedule periodic cleanup of expired jobs on first job creation
|
|
212
|
-
if not hasattr(app.state, "cleanup_task"):
|
|
213
|
-
logger.info("Starting periodic cleanup task")
|
|
214
|
-
app.state.cleanup_task = asyncio.create_task(periodic_cleanup(job_store))
|
|
215
|
-
|
|
216
|
-
async def run_evaluation(job_id: str, config_file: str, reps: int, session_manager: AIQSessionManager):
|
|
217
|
-
"""Background task to run the evaluation."""
|
|
218
|
-
async with evaluation_lock:
|
|
219
|
-
try:
|
|
220
|
-
# Create EvaluationRunConfig using the CLI defaults
|
|
221
|
-
eval_config = EvaluationRunConfig(config_file=Path(config_file), dataset=None, reps=reps)
|
|
222
|
-
|
|
223
|
-
# Create a new EvaluationRun with the evaluation-specific config
|
|
224
|
-
job_store.update_status(job_id, "running")
|
|
225
|
-
eval_runner = EvaluationRun(eval_config)
|
|
226
|
-
output: EvaluationRunOutput = await eval_runner.run_and_evaluate(session_manager=session_manager,
|
|
227
|
-
job_id=job_id)
|
|
228
|
-
if output.workflow_interrupted:
|
|
229
|
-
job_store.update_status(job_id, "interrupted")
|
|
230
|
-
else:
|
|
231
|
-
parent_dir = os.path.dirname(
|
|
232
|
-
output.workflow_output_file) if output.workflow_output_file else None
|
|
233
|
-
|
|
234
|
-
job_store.update_status(job_id, "success", output_path=str(parent_dir))
|
|
235
|
-
except Exception as e:
|
|
236
|
-
logger.error("Error in evaluation job %s: %s", job_id, str(e))
|
|
237
|
-
job_store.update_status(job_id, "failure", error=str(e))
|
|
238
|
-
|
|
239
|
-
async def start_evaluation(request: AIQEvaluateRequest,
|
|
240
|
-
background_tasks: BackgroundTasks,
|
|
241
|
-
http_request: Request):
|
|
242
|
-
"""Handle evaluation requests."""
|
|
243
|
-
|
|
244
|
-
async with session_manager.session(request=http_request):
|
|
245
|
-
|
|
246
|
-
# if job_id is present and already exists return the job info
|
|
247
|
-
if request.job_id:
|
|
248
|
-
job = job_store.get_job(request.job_id)
|
|
249
|
-
if job:
|
|
250
|
-
return AIQEvaluateResponse(job_id=job.job_id, status=job.status)
|
|
251
|
-
|
|
252
|
-
job_id = job_store.create_job(request.config_file, request.job_id, request.expiry_seconds)
|
|
253
|
-
create_cleanup_task()
|
|
254
|
-
background_tasks.add_task(run_evaluation, job_id, request.config_file, request.reps, session_manager)
|
|
255
|
-
|
|
256
|
-
return AIQEvaluateResponse(job_id=job_id, status="submitted")
|
|
257
|
-
|
|
258
|
-
def translate_job_to_response(job: JobInfo) -> AIQEvaluateStatusResponse:
|
|
259
|
-
"""Translate a JobInfo object to an AIQEvaluateStatusResponse."""
|
|
260
|
-
return AIQEvaluateStatusResponse(job_id=job.job_id,
|
|
261
|
-
status=job.status,
|
|
262
|
-
config_file=str(job.config_file),
|
|
263
|
-
error=job.error,
|
|
264
|
-
output_path=str(job.output_path),
|
|
265
|
-
created_at=job.created_at,
|
|
266
|
-
updated_at=job.updated_at,
|
|
267
|
-
expires_at=job_store.get_expires_at(job))
|
|
268
|
-
|
|
269
|
-
async def get_job_status(job_id: str, http_request: Request) -> AIQEvaluateStatusResponse:
|
|
270
|
-
"""Get the status of an evaluation job."""
|
|
271
|
-
logger.info("Getting status for job %s", job_id)
|
|
272
|
-
|
|
273
|
-
async with session_manager.session(request=http_request):
|
|
274
|
-
|
|
275
|
-
job = job_store.get_job(job_id)
|
|
276
|
-
if not job:
|
|
277
|
-
logger.warning("Job %s not found", job_id)
|
|
278
|
-
raise HTTPException(status_code=404, detail=f"Job {job_id} not found")
|
|
279
|
-
logger.info(f"Found job {job_id} with status {job.status}")
|
|
280
|
-
return translate_job_to_response(job)
|
|
281
|
-
|
|
282
|
-
async def get_last_job_status(http_request: Request) -> AIQEvaluateStatusResponse:
|
|
283
|
-
"""Get the status of the last created evaluation job."""
|
|
284
|
-
logger.info("Getting last job status")
|
|
285
|
-
|
|
286
|
-
async with session_manager.session(request=http_request):
|
|
287
|
-
|
|
288
|
-
job = job_store.get_last_job()
|
|
289
|
-
if not job:
|
|
290
|
-
logger.warning("No jobs found when requesting last job status")
|
|
291
|
-
raise HTTPException(status_code=404, detail="No jobs found")
|
|
292
|
-
logger.info("Found last job %s with status %s", job.job_id, job.status)
|
|
293
|
-
return translate_job_to_response(job)
|
|
294
|
-
|
|
295
|
-
async def get_jobs(http_request: Request, status: str | None = None) -> list[AIQEvaluateStatusResponse]:
|
|
296
|
-
"""Get all jobs, optionally filtered by status."""
|
|
297
|
-
|
|
298
|
-
async with session_manager.session(request=http_request):
|
|
299
|
-
|
|
300
|
-
if status is None:
|
|
301
|
-
logger.info("Getting all jobs")
|
|
302
|
-
jobs = job_store.get_all_jobs()
|
|
303
|
-
else:
|
|
304
|
-
logger.info("Getting jobs with status %s", status)
|
|
305
|
-
jobs = job_store.get_jobs_by_status(status)
|
|
306
|
-
logger.info("Found %d jobs", len(jobs))
|
|
307
|
-
return [translate_job_to_response(job) for job in jobs]
|
|
308
|
-
|
|
309
|
-
if self.front_end_config.evaluate.path:
|
|
310
|
-
# Add last job endpoint first (most specific)
|
|
311
|
-
app.add_api_route(
|
|
312
|
-
path=f"{self.front_end_config.evaluate.path}/job/last",
|
|
313
|
-
endpoint=get_last_job_status,
|
|
314
|
-
methods=["GET"],
|
|
315
|
-
response_model=AIQEvaluateStatusResponse,
|
|
316
|
-
description="Get the status of the last created evaluation job",
|
|
317
|
-
responses={
|
|
318
|
-
404: {
|
|
319
|
-
"description": "No jobs found"
|
|
320
|
-
}, 500: response_500
|
|
321
|
-
},
|
|
322
|
-
)
|
|
323
|
-
|
|
324
|
-
# Add specific job endpoint (least specific)
|
|
325
|
-
app.add_api_route(
|
|
326
|
-
path=f"{self.front_end_config.evaluate.path}/job/{{job_id}}",
|
|
327
|
-
endpoint=get_job_status,
|
|
328
|
-
methods=["GET"],
|
|
329
|
-
response_model=AIQEvaluateStatusResponse,
|
|
330
|
-
description="Get the status of an evaluation job",
|
|
331
|
-
responses={
|
|
332
|
-
404: {
|
|
333
|
-
"description": "Job not found"
|
|
334
|
-
}, 500: response_500
|
|
335
|
-
},
|
|
336
|
-
)
|
|
337
|
-
|
|
338
|
-
# Add jobs endpoint with optional status query parameter
|
|
339
|
-
app.add_api_route(
|
|
340
|
-
path=f"{self.front_end_config.evaluate.path}/jobs",
|
|
341
|
-
endpoint=get_jobs,
|
|
342
|
-
methods=["GET"],
|
|
343
|
-
response_model=list[AIQEvaluateStatusResponse],
|
|
344
|
-
description="Get all jobs, optionally filtered by status",
|
|
345
|
-
responses={500: response_500},
|
|
346
|
-
)
|
|
347
|
-
|
|
348
|
-
# Add HTTP endpoint for evaluation
|
|
349
|
-
app.add_api_route(
|
|
350
|
-
path=self.front_end_config.evaluate.path,
|
|
351
|
-
endpoint=start_evaluation,
|
|
352
|
-
methods=[self.front_end_config.evaluate.method],
|
|
353
|
-
response_model=AIQEvaluateResponse,
|
|
354
|
-
description=self.front_end_config.evaluate.description,
|
|
355
|
-
responses={500: response_500},
|
|
356
|
-
)
|
|
357
|
-
|
|
358
|
-
async def add_route(self,
|
|
359
|
-
app: FastAPI,
|
|
360
|
-
endpoint: FastApiFrontEndConfig.EndpointBase,
|
|
361
|
-
session_manager: AIQSessionManager):
|
|
362
|
-
|
|
363
|
-
workflow = session_manager.workflow
|
|
364
|
-
|
|
365
|
-
if (endpoint.websocket_path):
|
|
366
|
-
app.add_websocket_route(endpoint.websocket_path,
|
|
367
|
-
partial(AIQWebSocket, session_manager, self.get_step_adaptor()))
|
|
368
|
-
|
|
369
|
-
GenerateBodyType = workflow.input_schema # pylint: disable=invalid-name
|
|
370
|
-
GenerateStreamResponseType = workflow.streaming_output_schema # pylint: disable=invalid-name
|
|
371
|
-
GenerateSingleResponseType = workflow.single_output_schema # pylint: disable=invalid-name
|
|
372
|
-
|
|
373
|
-
# Ensure that the input is in the body. POD types are treated as query parameters
|
|
374
|
-
if (not issubclass(GenerateBodyType, BaseModel)):
|
|
375
|
-
GenerateBodyType = typing.Annotated[GenerateBodyType, Body()]
|
|
376
|
-
|
|
377
|
-
response_500 = {
|
|
378
|
-
"description": "Internal Server Error",
|
|
379
|
-
"content": {
|
|
380
|
-
"application/json": {
|
|
381
|
-
"example": {
|
|
382
|
-
"detail": "Internal server error occurred"
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
},
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
def get_single_endpoint(result_type: type | None):
|
|
389
|
-
|
|
390
|
-
async def get_single(response: Response, request: Request):
|
|
391
|
-
|
|
392
|
-
response.headers["Content-Type"] = "application/json"
|
|
393
|
-
|
|
394
|
-
async with session_manager.session(request=request):
|
|
395
|
-
|
|
396
|
-
return await generate_single_response(None, session_manager, result_type=result_type)
|
|
397
|
-
|
|
398
|
-
return get_single
|
|
399
|
-
|
|
400
|
-
def get_streaming_endpoint(streaming: bool, result_type: type | None, output_type: type | None):
|
|
401
|
-
|
|
402
|
-
async def get_stream(request: Request):
|
|
403
|
-
|
|
404
|
-
async with session_manager.session(request=request):
|
|
405
|
-
|
|
406
|
-
return StreamingResponse(headers={"Content-Type": "text/event-stream; charset=utf-8"},
|
|
407
|
-
content=generate_streaming_response_as_str(
|
|
408
|
-
None,
|
|
409
|
-
session_manager=session_manager,
|
|
410
|
-
streaming=streaming,
|
|
411
|
-
step_adaptor=self.get_step_adaptor(),
|
|
412
|
-
result_type=result_type,
|
|
413
|
-
output_type=output_type))
|
|
414
|
-
|
|
415
|
-
return get_stream
|
|
416
|
-
|
|
417
|
-
def get_streaming_raw_endpoint(streaming: bool, result_type: type | None, output_type: type | None):
|
|
418
|
-
|
|
419
|
-
async def get_stream(filter_steps: str | None = None):
|
|
420
|
-
|
|
421
|
-
return StreamingResponse(headers={"Content-Type": "text/event-stream; charset=utf-8"},
|
|
422
|
-
content=generate_streaming_response_full_as_str(
|
|
423
|
-
None,
|
|
424
|
-
session_manager=session_manager,
|
|
425
|
-
streaming=streaming,
|
|
426
|
-
result_type=result_type,
|
|
427
|
-
output_type=output_type,
|
|
428
|
-
filter_steps=filter_steps))
|
|
429
|
-
|
|
430
|
-
return get_stream
|
|
431
|
-
|
|
432
|
-
def post_single_endpoint(request_type: type, result_type: type | None):
|
|
433
|
-
|
|
434
|
-
async def post_single(response: Response, request: Request, payload: request_type):
|
|
435
|
-
|
|
436
|
-
response.headers["Content-Type"] = "application/json"
|
|
437
|
-
|
|
438
|
-
async with session_manager.session(request=request):
|
|
439
|
-
|
|
440
|
-
return await generate_single_response(payload, session_manager, result_type=result_type)
|
|
441
|
-
|
|
442
|
-
return post_single
|
|
443
|
-
|
|
444
|
-
def post_streaming_endpoint(request_type: type,
|
|
445
|
-
streaming: bool,
|
|
446
|
-
result_type: type | None,
|
|
447
|
-
output_type: type | None):
|
|
448
|
-
|
|
449
|
-
async def post_stream(request: Request, payload: request_type):
|
|
450
|
-
|
|
451
|
-
async with session_manager.session(request=request):
|
|
452
|
-
|
|
453
|
-
return StreamingResponse(headers={"Content-Type": "text/event-stream; charset=utf-8"},
|
|
454
|
-
content=generate_streaming_response_as_str(
|
|
455
|
-
payload,
|
|
456
|
-
session_manager=session_manager,
|
|
457
|
-
streaming=streaming,
|
|
458
|
-
step_adaptor=self.get_step_adaptor(),
|
|
459
|
-
result_type=result_type,
|
|
460
|
-
output_type=output_type))
|
|
461
|
-
|
|
462
|
-
return post_stream
|
|
463
|
-
|
|
464
|
-
def post_streaming_raw_endpoint(request_type: type,
|
|
465
|
-
streaming: bool,
|
|
466
|
-
result_type: type | None,
|
|
467
|
-
output_type: type | None):
|
|
468
|
-
"""
|
|
469
|
-
Stream raw intermediate steps without any step adaptor translations.
|
|
470
|
-
"""
|
|
471
|
-
|
|
472
|
-
async def post_stream(payload: request_type, filter_steps: str | None = None):
|
|
473
|
-
|
|
474
|
-
return StreamingResponse(headers={"Content-Type": "text/event-stream; charset=utf-8"},
|
|
475
|
-
content=generate_streaming_response_full_as_str(
|
|
476
|
-
payload,
|
|
477
|
-
session_manager=session_manager,
|
|
478
|
-
streaming=streaming,
|
|
479
|
-
result_type=result_type,
|
|
480
|
-
output_type=output_type,
|
|
481
|
-
filter_steps=filter_steps))
|
|
482
|
-
|
|
483
|
-
return post_stream
|
|
484
|
-
|
|
485
|
-
if (endpoint.path):
|
|
486
|
-
if (endpoint.method == "GET"):
|
|
487
|
-
|
|
488
|
-
app.add_api_route(
|
|
489
|
-
path=endpoint.path,
|
|
490
|
-
endpoint=get_single_endpoint(result_type=GenerateSingleResponseType),
|
|
491
|
-
methods=[endpoint.method],
|
|
492
|
-
response_model=GenerateSingleResponseType,
|
|
493
|
-
description=endpoint.description,
|
|
494
|
-
responses={500: response_500},
|
|
495
|
-
)
|
|
496
|
-
|
|
497
|
-
app.add_api_route(
|
|
498
|
-
path=f"{endpoint.path}/stream",
|
|
499
|
-
endpoint=get_streaming_endpoint(streaming=True,
|
|
500
|
-
result_type=GenerateStreamResponseType,
|
|
501
|
-
output_type=GenerateStreamResponseType),
|
|
502
|
-
methods=[endpoint.method],
|
|
503
|
-
response_model=GenerateStreamResponseType,
|
|
504
|
-
description=endpoint.description,
|
|
505
|
-
responses={500: response_500},
|
|
506
|
-
)
|
|
507
|
-
|
|
508
|
-
app.add_api_route(
|
|
509
|
-
path=f"{endpoint.path}/full",
|
|
510
|
-
endpoint=get_streaming_raw_endpoint(streaming=True,
|
|
511
|
-
result_type=GenerateStreamResponseType,
|
|
512
|
-
output_type=GenerateStreamResponseType),
|
|
513
|
-
methods=[endpoint.method],
|
|
514
|
-
description="Stream raw intermediate steps without any step adaptor translations.\n"
|
|
515
|
-
"Use filter_steps query parameter to filter steps by type (comma-separated list) or\
|
|
516
|
-
set to 'none' to suppress all intermediate steps.",
|
|
517
|
-
)
|
|
518
|
-
|
|
519
|
-
elif (endpoint.method == "POST"):
|
|
520
|
-
|
|
521
|
-
app.add_api_route(
|
|
522
|
-
path=endpoint.path,
|
|
523
|
-
endpoint=post_single_endpoint(request_type=GenerateBodyType,
|
|
524
|
-
result_type=GenerateSingleResponseType),
|
|
525
|
-
methods=[endpoint.method],
|
|
526
|
-
response_model=GenerateSingleResponseType,
|
|
527
|
-
description=endpoint.description,
|
|
528
|
-
responses={500: response_500},
|
|
529
|
-
)
|
|
530
|
-
|
|
531
|
-
app.add_api_route(
|
|
532
|
-
path=f"{endpoint.path}/stream",
|
|
533
|
-
endpoint=post_streaming_endpoint(request_type=GenerateBodyType,
|
|
534
|
-
streaming=True,
|
|
535
|
-
result_type=GenerateStreamResponseType,
|
|
536
|
-
output_type=GenerateStreamResponseType),
|
|
537
|
-
methods=[endpoint.method],
|
|
538
|
-
response_model=GenerateStreamResponseType,
|
|
539
|
-
description=endpoint.description,
|
|
540
|
-
responses={500: response_500},
|
|
541
|
-
)
|
|
542
|
-
|
|
543
|
-
app.add_api_route(
|
|
544
|
-
path=f"{endpoint.path}/full",
|
|
545
|
-
endpoint=post_streaming_raw_endpoint(request_type=GenerateBodyType,
|
|
546
|
-
streaming=True,
|
|
547
|
-
result_type=GenerateStreamResponseType,
|
|
548
|
-
output_type=GenerateStreamResponseType),
|
|
549
|
-
methods=[endpoint.method],
|
|
550
|
-
response_model=GenerateStreamResponseType,
|
|
551
|
-
description="Stream raw intermediate steps without any step adaptor translations.\n"
|
|
552
|
-
"Use filter_steps query parameter to filter steps by type (comma-separated list) or \
|
|
553
|
-
set to 'none' to suppress all intermediate steps.",
|
|
554
|
-
responses={500: response_500},
|
|
555
|
-
)
|
|
556
|
-
|
|
557
|
-
else:
|
|
558
|
-
raise ValueError(f"Unsupported method {endpoint.method}")
|
|
559
|
-
|
|
560
|
-
if (endpoint.openai_api_path):
|
|
561
|
-
if (endpoint.method == "GET"):
|
|
562
|
-
|
|
563
|
-
app.add_api_route(
|
|
564
|
-
path=endpoint.openai_api_path,
|
|
565
|
-
endpoint=get_single_endpoint(result_type=AIQChatResponse),
|
|
566
|
-
methods=[endpoint.method],
|
|
567
|
-
response_model=AIQChatResponse,
|
|
568
|
-
description=endpoint.description,
|
|
569
|
-
responses={500: response_500},
|
|
570
|
-
)
|
|
571
|
-
|
|
572
|
-
app.add_api_route(
|
|
573
|
-
path=f"{endpoint.openai_api_path}/stream",
|
|
574
|
-
endpoint=get_streaming_endpoint(streaming=True,
|
|
575
|
-
result_type=AIQChatResponseChunk,
|
|
576
|
-
output_type=AIQChatResponseChunk),
|
|
577
|
-
methods=[endpoint.method],
|
|
578
|
-
response_model=AIQChatResponseChunk,
|
|
579
|
-
description=endpoint.description,
|
|
580
|
-
responses={500: response_500},
|
|
581
|
-
)
|
|
582
|
-
|
|
583
|
-
elif (endpoint.method == "POST"):
|
|
584
|
-
|
|
585
|
-
app.add_api_route(
|
|
586
|
-
path=endpoint.openai_api_path,
|
|
587
|
-
endpoint=post_single_endpoint(request_type=AIQChatRequest, result_type=AIQChatResponse),
|
|
588
|
-
methods=[endpoint.method],
|
|
589
|
-
response_model=AIQChatResponse,
|
|
590
|
-
description=endpoint.description,
|
|
591
|
-
responses={500: response_500},
|
|
592
|
-
)
|
|
593
|
-
|
|
594
|
-
app.add_api_route(
|
|
595
|
-
path=f"{endpoint.openai_api_path}/stream",
|
|
596
|
-
endpoint=post_streaming_endpoint(request_type=AIQChatRequest,
|
|
597
|
-
streaming=True,
|
|
598
|
-
result_type=AIQChatResponseChunk,
|
|
599
|
-
output_type=AIQChatResponseChunk),
|
|
600
|
-
methods=[endpoint.method],
|
|
601
|
-
response_model=AIQChatResponseChunk | AIQResponseIntermediateStep,
|
|
602
|
-
description=endpoint.description,
|
|
603
|
-
responses={500: response_500},
|
|
604
|
-
)
|
|
605
|
-
|
|
606
|
-
else:
|
|
607
|
-
raise ValueError(f"Unsupported method {endpoint.method}")
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 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
|
-
import asyncio
|
|
17
|
-
import logging
|
|
18
|
-
|
|
19
|
-
from aiq.builder.context import AIQContext
|
|
20
|
-
from aiq.data_models.api_server import AIQResponseIntermediateStep
|
|
21
|
-
from aiq.data_models.intermediate_step import IntermediateStep
|
|
22
|
-
|
|
23
|
-
logger = logging.getLogger(__name__)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
async def pull_intermediate(_q, adapter):
|
|
27
|
-
"""
|
|
28
|
-
Subscribes to the runner's event stream (which is now a simplified Observable)
|
|
29
|
-
using direct callbacks. Processes each event with the adapter and enqueues
|
|
30
|
-
results to `_q`.
|
|
31
|
-
"""
|
|
32
|
-
intermediate_done = asyncio.Event()
|
|
33
|
-
context = AIQContext.get()
|
|
34
|
-
loop = asyncio.get_running_loop()
|
|
35
|
-
|
|
36
|
-
async def set_intermediate_done():
|
|
37
|
-
intermediate_done.set()
|
|
38
|
-
|
|
39
|
-
def on_next_cb(item: IntermediateStep):
|
|
40
|
-
"""
|
|
41
|
-
Synchronously called whenever the runner publishes an event.
|
|
42
|
-
We process it, then place it into the async queue (via a small async task).
|
|
43
|
-
If adapter is None, convert the raw IntermediateStep into the complete
|
|
44
|
-
AIQResponseIntermediateStep and place it into the queue.
|
|
45
|
-
"""
|
|
46
|
-
if adapter is None:
|
|
47
|
-
adapted = AIQResponseIntermediateStep(id=item.UUID,
|
|
48
|
-
type=item.event_type,
|
|
49
|
-
name=item.name or "",
|
|
50
|
-
parent_id=item.parent_id,
|
|
51
|
-
payload=item.payload.model_dump_json())
|
|
52
|
-
else:
|
|
53
|
-
adapted = adapter.process(item)
|
|
54
|
-
|
|
55
|
-
if adapted is not None:
|
|
56
|
-
loop.create_task(_q.put(adapted))
|
|
57
|
-
|
|
58
|
-
def on_error_cb(exc: Exception):
|
|
59
|
-
"""
|
|
60
|
-
Called if the runner signals an error. We log it and unblock our wait.
|
|
61
|
-
"""
|
|
62
|
-
logger.error("Hit on_error: %s", exc)
|
|
63
|
-
|
|
64
|
-
loop.create_task(set_intermediate_done())
|
|
65
|
-
|
|
66
|
-
def on_complete_cb():
|
|
67
|
-
"""
|
|
68
|
-
Called once the runner signals no more items. We unblock our wait.
|
|
69
|
-
"""
|
|
70
|
-
logger.debug("Completed reading intermediate steps")
|
|
71
|
-
|
|
72
|
-
loop.create_task(set_intermediate_done())
|
|
73
|
-
|
|
74
|
-
# Subscribe to the runner's "reactive_event_stream" (now a simple Observable)
|
|
75
|
-
_ = context.intermediate_step_manager.subscribe(on_next=on_next_cb,
|
|
76
|
-
on_error=on_error_cb,
|
|
77
|
-
on_complete=on_complete_cb)
|
|
78
|
-
|
|
79
|
-
# Wait until on_complete or on_error sets intermediate_done
|
|
80
|
-
return intermediate_done
|