fred-runtime 2.0.8__tar.gz → 2.0.11__tar.gz
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.
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/PKG-INFO +5 -2
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/app/agent_app.py +15 -0
- fred_runtime-2.0.11/fred_runtime/app/container.py +27 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/app/context.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/app/dependencies.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/app/mcp_config.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/cli/__init__.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/cli/completion.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/cli/entrypoint.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/cli/history_display.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/cli/kpi_display.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/cli/pod_client.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/cli/repl.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/cli/repl_helpers.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/cli/url_helpers.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/client.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/context_aware_tool.py +21 -5
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/token_expiry.py +14 -0
- fred_runtime-2.0.11/fred_runtime/deep/__init__.py +15 -0
- fred_runtime-2.0.11/fred_runtime/graph/__init__.py +15 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/graph/graph_runtime.py +155 -9
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/integrations/v2_runtime/adapters.py +19 -1
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/model_routing/__init__.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/model_routing/catalog.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/model_routing/provider.py +14 -0
- fred_runtime-2.0.11/fred_runtime/react/__init__.py +15 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/react/react_message_codec.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/react/react_model_adapter.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/react/react_tool_rendering.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/react/react_tool_utils.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/runtime_support/checkpoints.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/runtime_support/sql_checkpointer.py +14 -0
- fred_runtime-2.0.11/fred_runtime/support/__init__.py +15 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/support/filesystem_context.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/support/tool_approval.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/support/tool_loop.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime.egg-info/PKG-INFO +5 -2
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime.egg-info/SOURCES.txt +2 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime.egg-info/requires.txt +4 -1
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/pyproject.toml +10 -2
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_agent_app.py +107 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_client.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_config_loader.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_context.py +14 -0
- fred_runtime-2.0.11/tests/test_context_aware_tool.py +78 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_conversational_memory.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_eval_collector.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_eval_trace.py +14 -0
- fred_runtime-2.0.11/tests/test_graph_runtime_invoke_agent.py +154 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_graph_runtime_observability.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_history.py +53 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_kpi_display.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_mcp_config.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_model_routing.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_openai_compat_router.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_pod_client.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_repl_helpers.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_smoke.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_token_expiry.py +14 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_url_helpers.py +14 -0
- fred_runtime-2.0.8/fred_runtime/app/container.py +0 -13
- fred_runtime-2.0.8/fred_runtime/deep/__init__.py +0 -1
- fred_runtime-2.0.8/fred_runtime/graph/__init__.py +0 -1
- fred_runtime-2.0.8/fred_runtime/react/__init__.py +0 -1
- fred_runtime-2.0.8/fred_runtime/support/__init__.py +0 -1
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/README.md +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/__init__.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/app/__init__.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/app/_catalogs.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/app/config.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/app/config_loader.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/app/observability_factory.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/app/openai_compat_router.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/__init__.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/kf_base_client.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/kf_fast_text_client.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/kf_http_client.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/kf_logs_client.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/kf_markdown_media_client.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/kf_vectorsearch_client.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/kf_workspace_client.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/mcp_interceptors.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/mcp_runtime.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/mcp_toolkit.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/mcp_utils.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/structures.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/common/tool_node_utils.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/deep/deep_runtime.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/eval/__init__.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/eval/collector.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/integrations/__init__.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/integrations/v2_runtime/__init__.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/model_routing/contracts.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/model_routing/resolver.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/react/react_langchain_adapter.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/react/react_prompting.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/react/react_runtime.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/react/react_stream_adapter.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/react/react_tool_binding.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/react/react_tool_loop.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/react/react_tool_resolution.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/react/react_tracing.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/runtime_context.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/runtime_support/__init__.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/runtime_support/model_metadata.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/runtime_support/request_context_helpers.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime/runtime_support/user_token_refresher.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime.egg-info/dependency_links.txt +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime.egg-info/entry_points.txt +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/fred_runtime.egg-info/top_level.txt +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/setup.cfg +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_kf_workspace_client.py +0 -0
- {fred_runtime-2.0.8 → fred_runtime-2.0.11}/tests/test_user_token_refresher.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fred-runtime
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.11
|
|
4
4
|
Summary: Runtime adapters and infrastructure wiring for Fred v2 agents.
|
|
5
5
|
Author-email: Thales <noreply@thalesgroup.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -12,8 +12,9 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
Requires-Python: <3.13,>=3.12
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
|
-
Requires-Dist: fred-core>=
|
|
15
|
+
Requires-Dist: fred-core>=2.0.5
|
|
16
16
|
Requires-Dist: fred-sdk>=0.1.11
|
|
17
|
+
Requires-Dist: alembic>=1.18.4
|
|
17
18
|
Requires-Dist: deepagents>=0.4.11
|
|
18
19
|
Requires-Dist: httpx>=0.28.1
|
|
19
20
|
Requires-Dist: langchain-mcp-adapters>=0.2.1
|
|
@@ -30,7 +31,9 @@ Requires-Dist: bandit>=1.8.6; extra == "dev"
|
|
|
30
31
|
Requires-Dist: basedpyright==1.31.0; extra == "dev"
|
|
31
32
|
Requires-Dist: detect-secrets>=1.5.0; extra == "dev"
|
|
32
33
|
Requires-Dist: pytest>=8.4.2; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-asyncio>=1.2.0; extra == "dev"
|
|
33
35
|
Requires-Dist: pytest-cov>=6.2.1; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-socket>=0.7.0; extra == "dev"
|
|
34
37
|
Requires-Dist: ruff>=0.12.5; extra == "dev"
|
|
35
38
|
|
|
36
39
|
# fred-runtime
|
|
@@ -563,6 +563,21 @@ class LocalRegistryAgentInvoker(AgentInvokerPort):
|
|
|
563
563
|
|
|
564
564
|
context_dict = request.context.model_dump(mode="json")
|
|
565
565
|
context_dict.setdefault("execution_action", ExecutionGrantAction.EXECUTE.value)
|
|
566
|
+
# RFC AGENT-INVOKE: apply the caller's per-call scope onto the callee's
|
|
567
|
+
# retrieval context. These keys are read back when the callee's
|
|
568
|
+
# RuntimeContext is built, so they narrow its document/library/search world.
|
|
569
|
+
# Scope narrows only; the callee still runs under the delegated identity.
|
|
570
|
+
if request.scope is not None:
|
|
571
|
+
if request.scope.document_uids is not None:
|
|
572
|
+
context_dict["selected_document_uids"] = list(
|
|
573
|
+
request.scope.document_uids
|
|
574
|
+
)
|
|
575
|
+
if request.scope.library_ids is not None:
|
|
576
|
+
context_dict["selected_document_libraries_ids"] = list(
|
|
577
|
+
request.scope.library_ids
|
|
578
|
+
)
|
|
579
|
+
if request.scope.search_policy is not None:
|
|
580
|
+
context_dict["search_policy"] = request.scope.search_policy
|
|
566
581
|
execute_request = _AgentExecuteRequest.model_construct(
|
|
567
582
|
agent_id=request.agent_id,
|
|
568
583
|
agent_instance_id=None,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Pod container factory — single composition-root entry point."""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from fred_runtime.app.config import AgentPodConfig
|
|
20
|
+
from fred_runtime.app.context import PodApplicationContext
|
|
21
|
+
|
|
22
|
+
PodContainer = PodApplicationContext
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def build_pod_container(configuration: AgentPodConfig) -> PodContainer:
|
|
26
|
+
"""Create a fresh PodApplicationContext with no side effects."""
|
|
27
|
+
return PodApplicationContext(configuration)
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
"""PodApplicationContext — single composition root for a fred-runtime agent pod."""
|
|
2
16
|
|
|
3
17
|
from __future__ import annotations
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
"""FastAPI dependency helpers for pod container injection."""
|
|
2
16
|
|
|
3
17
|
from __future__ import annotations
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
"""Typed representation of the mcp_catalog.yaml loaded by an agent pod."""
|
|
2
16
|
|
|
3
17
|
from __future__ import annotations
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
from .completion import completion_candidates
|
|
2
16
|
from .entrypoint import build_parser, main
|
|
3
17
|
from .history_display import (
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
from __future__ import annotations
|
|
2
16
|
|
|
3
17
|
from collections.abc import Sequence
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
from __future__ import annotations
|
|
2
16
|
|
|
3
17
|
import argparse
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
from __future__ import annotations
|
|
2
16
|
|
|
3
17
|
import json
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
from __future__ import annotations
|
|
2
16
|
|
|
3
17
|
import re
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
from __future__ import annotations
|
|
2
16
|
|
|
3
17
|
import json
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
from __future__ import annotations
|
|
2
16
|
|
|
3
17
|
import getpass
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
from __future__ import annotations
|
|
2
16
|
|
|
3
17
|
import uuid
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
from __future__ import annotations
|
|
2
16
|
|
|
3
17
|
import os
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
"""
|
|
2
16
|
Compatibility shim — all implementation has moved to fred_runtime.cli.*
|
|
3
17
|
|
|
@@ -6,6 +6,7 @@ from typing import Any, Callable, Optional
|
|
|
6
6
|
|
|
7
7
|
import httpx # ← we log/inspect HTTP errors coming from MCP adapters
|
|
8
8
|
from fred_core.common import OwnerFilter
|
|
9
|
+
from fred_core.common.team_id import is_personal_team_id
|
|
9
10
|
from fred_core.kpi.kpi_writer_structures import KPIActor
|
|
10
11
|
from fred_sdk.support.mcp_utils import normalize_mcp_content
|
|
11
12
|
from langchain_core.tools import BaseTool
|
|
@@ -20,6 +21,7 @@ from fred_runtime.runtime_context import get_runtime_context
|
|
|
20
21
|
from fred_runtime.runtime_support import (
|
|
21
22
|
RuntimeContextProvider,
|
|
22
23
|
get_document_library_tags_ids,
|
|
24
|
+
get_document_uids,
|
|
23
25
|
get_vector_search_scopes,
|
|
24
26
|
)
|
|
25
27
|
|
|
@@ -170,6 +172,15 @@ class ContextAwareTool(BaseTool):
|
|
|
170
172
|
library_ids,
|
|
171
173
|
)
|
|
172
174
|
|
|
175
|
+
document_uids = get_document_uids(context)
|
|
176
|
+
if document_uids and "document_uids" in tool_properties:
|
|
177
|
+
kwargs["document_uids"] = document_uids
|
|
178
|
+
logger.info(
|
|
179
|
+
"ContextAwareTool(%s) injecting document filter: %s",
|
|
180
|
+
self.name,
|
|
181
|
+
document_uids,
|
|
182
|
+
)
|
|
183
|
+
|
|
173
184
|
session_id = context.session_id
|
|
174
185
|
if (
|
|
175
186
|
session_id
|
|
@@ -183,19 +194,24 @@ class ContextAwareTool(BaseTool):
|
|
|
183
194
|
session_id,
|
|
184
195
|
)
|
|
185
196
|
|
|
186
|
-
# Force team_id depending on agent settings
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
# Force team_id depending on agent settings.
|
|
198
|
+
# Personal-space IDs ("personal-<uuid>") are not real ReBAC teams;
|
|
199
|
+
# don't forward them to tools that look up team membership.
|
|
200
|
+
effective_team_id = (
|
|
201
|
+
settings.team_id if not is_personal_team_id(settings.team_id) else None
|
|
202
|
+
)
|
|
203
|
+
if "team_id" in tool_properties and effective_team_id:
|
|
204
|
+
kwargs["team_id"] = effective_team_id
|
|
189
205
|
logger.info(
|
|
190
206
|
"ContextAwareTool(%s) injecting team_id: %s",
|
|
191
207
|
self.name,
|
|
192
|
-
|
|
208
|
+
effective_team_id,
|
|
193
209
|
)
|
|
194
210
|
|
|
195
211
|
# Force owner_filter depending on agent settings
|
|
196
212
|
if "owner_filter" in tool_properties:
|
|
197
213
|
owner_filter = (
|
|
198
|
-
OwnerFilter.TEAM if
|
|
214
|
+
OwnerFilter.TEAM if effective_team_id else OwnerFilter.PERSONAL
|
|
199
215
|
)
|
|
200
216
|
kwargs["owner_filter"] = owner_filter.value
|
|
201
217
|
logger.info(
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
"""Shared helpers to detect expired access tokens in HTTP responses/errors."""
|
|
2
16
|
|
|
3
17
|
from __future__ import annotations
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# Copyright Thales 2026
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
1
15
|
"""
|
|
2
16
|
Executable runtime for v2 graph agents.
|
|
3
17
|
|
|
@@ -22,7 +36,7 @@ import uuid
|
|
|
22
36
|
from collections.abc import AsyncIterator, Awaitable, Callable, Mapping
|
|
23
37
|
from contextlib import asynccontextmanager, nullcontext
|
|
24
38
|
from dataclasses import dataclass, field
|
|
25
|
-
from typing import Protocol, cast
|
|
39
|
+
from typing import Any, Protocol, cast
|
|
26
40
|
|
|
27
41
|
from fred_core.portable import MetricsProvider
|
|
28
42
|
from fred_sdk.contracts.context import (
|
|
@@ -33,6 +47,7 @@ from fred_sdk.contracts.context import (
|
|
|
33
47
|
BoundRuntimeContext,
|
|
34
48
|
ConversationTurn,
|
|
35
49
|
FetchedResource,
|
|
50
|
+
InvocationScope,
|
|
36
51
|
PublishedArtifact,
|
|
37
52
|
ResourceFetchRequest,
|
|
38
53
|
ResourceScope,
|
|
@@ -76,7 +91,7 @@ from langchain_core.messages import BaseMessage
|
|
|
76
91
|
from langchain_core.runnables import RunnableConfig
|
|
77
92
|
from langchain_core.tools import BaseTool
|
|
78
93
|
from langgraph.checkpoint.base import Checkpoint, CheckpointMetadata, empty_checkpoint
|
|
79
|
-
from pydantic import BaseModel
|
|
94
|
+
from pydantic import BaseModel, ValidationError
|
|
80
95
|
|
|
81
96
|
from fred_runtime.runtime_support.checkpoints import (
|
|
82
97
|
AsyncCheckpointReader,
|
|
@@ -86,6 +101,84 @@ from fred_runtime.runtime_support.model_metadata import runtime_metadata_from_me
|
|
|
86
101
|
|
|
87
102
|
logger = logging.getLogger(__name__)
|
|
88
103
|
|
|
104
|
+
# RFC AGENT-INVOKE: typed agent invocation — how many times invoke_agent re-asks a
|
|
105
|
+
# callee for a valid JSON object before giving up and returning structured=None.
|
|
106
|
+
_STRUCTURED_OUTPUT_MAX_ATTEMPTS = 2
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _structured_output_instruction(schema: dict[str, Any]) -> str:
|
|
110
|
+
"""Instruction appended to the callee message asking for schema-conformant JSON."""
|
|
111
|
+
return (
|
|
112
|
+
"\n\nIMPORTANT: Respond with ONLY a single valid JSON object that conforms to "
|
|
113
|
+
"this JSON Schema. No prose, no explanation, no markdown code fences:\n"
|
|
114
|
+
+ json.dumps(schema, ensure_ascii=False)
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _try_json_object(text: str) -> dict[str, Any] | None:
|
|
119
|
+
"""Parse ``text`` as JSON, returning it only if it is a JSON object."""
|
|
120
|
+
try:
|
|
121
|
+
parsed = json.loads(text)
|
|
122
|
+
except (ValueError, TypeError):
|
|
123
|
+
return None
|
|
124
|
+
return parsed if isinstance(parsed, dict) else None
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _extract_json_object(text: str) -> dict[str, Any] | None:
|
|
128
|
+
"""Best-effort extraction of a single JSON object from an agent's free text.
|
|
129
|
+
|
|
130
|
+
Tries, in order: the whole string, a fenced ``` block, then the first balanced
|
|
131
|
+
``{...}`` span. Returns ``None`` if nothing parses to an object.
|
|
132
|
+
"""
|
|
133
|
+
if not text:
|
|
134
|
+
return None
|
|
135
|
+
candidate = text.strip()
|
|
136
|
+
obj = _try_json_object(candidate)
|
|
137
|
+
if obj is not None:
|
|
138
|
+
return obj
|
|
139
|
+
|
|
140
|
+
fence = candidate.find("```")
|
|
141
|
+
if fence != -1:
|
|
142
|
+
rest = candidate[fence + 3 :]
|
|
143
|
+
if rest[:4].lower() == "json":
|
|
144
|
+
rest = rest[4:]
|
|
145
|
+
end = rest.find("```")
|
|
146
|
+
if end != -1:
|
|
147
|
+
obj = _try_json_object(rest[:end].strip())
|
|
148
|
+
if obj is not None:
|
|
149
|
+
return obj
|
|
150
|
+
|
|
151
|
+
start = candidate.find("{")
|
|
152
|
+
while start != -1:
|
|
153
|
+
depth = 0
|
|
154
|
+
for index in range(start, len(candidate)):
|
|
155
|
+
char = candidate[index]
|
|
156
|
+
if char == "{":
|
|
157
|
+
depth += 1
|
|
158
|
+
elif char == "}":
|
|
159
|
+
depth -= 1
|
|
160
|
+
if depth == 0:
|
|
161
|
+
obj = _try_json_object(candidate[start : index + 1])
|
|
162
|
+
if obj is not None:
|
|
163
|
+
return obj
|
|
164
|
+
break
|
|
165
|
+
start = candidate.find("{", start + 1)
|
|
166
|
+
return None
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _coerce_structured_payload(
|
|
170
|
+
content: str, output_schema: type[BaseModel]
|
|
171
|
+
) -> dict[str, Any] | None:
|
|
172
|
+
"""Extract + validate a callee's text into ``output_schema``; ``None`` on failure."""
|
|
173
|
+
parsed = _extract_json_object(content)
|
|
174
|
+
if parsed is None:
|
|
175
|
+
return None
|
|
176
|
+
try:
|
|
177
|
+
return output_schema.model_validate(parsed).model_dump(mode="json")
|
|
178
|
+
except ValidationError:
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
|
|
89
182
|
GraphNodeHandler = Callable[
|
|
90
183
|
[BaseModel, GraphNodeContext], GraphNodeResult | Awaitable[GraphNodeResult]
|
|
91
184
|
]
|
|
@@ -669,7 +762,16 @@ class _GraphNodeExecutionContext:
|
|
|
669
762
|
message: str,
|
|
670
763
|
*,
|
|
671
764
|
prior_turns: tuple[ConversationTurn, ...] = (),
|
|
765
|
+
output_schema: type[BaseModel] | None = None,
|
|
766
|
+
scope: InvocationScope | None = None,
|
|
672
767
|
) -> AgentInvocationResult:
|
|
768
|
+
"""Invoke another registered agent for one turn (RFC AGENT-INVOKE).
|
|
769
|
+
|
|
770
|
+
When ``output_schema`` is given the callee is asked for a JSON object of that
|
|
771
|
+
shape; the validated payload is attached to ``result.structured`` (with a
|
|
772
|
+
bounded retry, ``None`` if it could not be coerced). When ``scope`` is given,
|
|
773
|
+
the callee's retrieval world is narrowed for this call only.
|
|
774
|
+
"""
|
|
673
775
|
agent_invoker = self.services.agent_invoker
|
|
674
776
|
if agent_invoker is None:
|
|
675
777
|
raise RuntimeError(
|
|
@@ -680,6 +782,13 @@ class _GraphNodeExecutionContext:
|
|
|
680
782
|
|
|
681
783
|
self.emit_status("invoke_agent", detail=agent_id)
|
|
682
784
|
|
|
785
|
+
schema_dict = (
|
|
786
|
+
output_schema.model_json_schema() if output_schema is not None else None
|
|
787
|
+
)
|
|
788
|
+
max_attempts = (
|
|
789
|
+
_STRUCTURED_OUTPUT_MAX_ATTEMPTS if output_schema is not None else 1
|
|
790
|
+
)
|
|
791
|
+
|
|
683
792
|
span = _start_runtime_span(
|
|
684
793
|
services=self.services,
|
|
685
794
|
binding=self.binding,
|
|
@@ -690,6 +799,7 @@ class _GraphNodeExecutionContext:
|
|
|
690
799
|
"target_agent_id": agent_id,
|
|
691
800
|
},
|
|
692
801
|
)
|
|
802
|
+
result: AgentInvocationResult | None = None
|
|
693
803
|
try:
|
|
694
804
|
with _graph_phase_timer(
|
|
695
805
|
metrics=self.services.metrics,
|
|
@@ -702,14 +812,49 @@ class _GraphNodeExecutionContext:
|
|
|
702
812
|
"target_agent_id": agent_id,
|
|
703
813
|
},
|
|
704
814
|
) as kpi_dims:
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
815
|
+
for attempt in range(max_attempts):
|
|
816
|
+
effective_message = message
|
|
817
|
+
if schema_dict is not None:
|
|
818
|
+
effective_message = message + _structured_output_instruction(
|
|
819
|
+
schema_dict
|
|
820
|
+
)
|
|
821
|
+
if attempt > 0:
|
|
822
|
+
effective_message = (
|
|
823
|
+
"Your previous response was not a valid JSON object. "
|
|
824
|
+
+ effective_message
|
|
825
|
+
)
|
|
826
|
+
result = await agent_invoker.invoke(
|
|
827
|
+
AgentInvocationRequest(
|
|
828
|
+
agent_id=agent_id,
|
|
829
|
+
message=effective_message,
|
|
830
|
+
context=self.binding.portable_context,
|
|
831
|
+
prior_turns=prior_turns,
|
|
832
|
+
scope=scope,
|
|
833
|
+
output_schema=schema_dict,
|
|
834
|
+
)
|
|
835
|
+
)
|
|
836
|
+
if output_schema is None or result.is_error:
|
|
837
|
+
break
|
|
838
|
+
structured = _coerce_structured_payload(
|
|
839
|
+
result.content, output_schema
|
|
840
|
+
)
|
|
841
|
+
if structured is not None:
|
|
842
|
+
result = result.model_copy(update={"structured": structured})
|
|
843
|
+
break
|
|
844
|
+
|
|
845
|
+
assert result is not None
|
|
846
|
+
if (
|
|
847
|
+
output_schema is not None
|
|
848
|
+
and not result.is_error
|
|
849
|
+
and result.structured is None
|
|
850
|
+
):
|
|
851
|
+
logger.warning(
|
|
852
|
+
"invoke_agent(%s): could not coerce output to %s after %d "
|
|
853
|
+
"attempt(s); returning structured=None",
|
|
854
|
+
agent_id,
|
|
855
|
+
output_schema.__name__,
|
|
856
|
+
max_attempts,
|
|
711
857
|
)
|
|
712
|
-
)
|
|
713
858
|
if result.is_error:
|
|
714
859
|
kpi_dims["status"] = "error"
|
|
715
860
|
if span is not None:
|
|
@@ -723,6 +868,7 @@ class _GraphNodeExecutionContext:
|
|
|
723
868
|
finally:
|
|
724
869
|
if span is not None:
|
|
725
870
|
span.end()
|
|
871
|
+
assert result is not None
|
|
726
872
|
return result
|
|
727
873
|
|
|
728
874
|
async def publish_text(
|