fred-runtime 2.0.11__tar.gz → 3.1.0__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.11 → fred_runtime-3.1.0}/PKG-INFO +3 -3
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/app/__init__.py +0 -2
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/app/agent_app.py +31 -15
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/app/config.py +9 -37
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/app/context.py +15 -26
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/app/observability_factory.py +5 -14
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/context_aware_tool.py +50 -16
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/kf_workspace_client.py +81 -177
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/graph/graph_runtime.py +43 -108
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/integrations/v2_runtime/adapters.py +107 -139
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/react/react_langchain_adapter.py +4 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/react/react_message_codec.py +8 -11
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/react/react_runtime.py +63 -10
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/react/react_stream_adapter.py +73 -13
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/react/react_tool_resolution.py +18 -58
- fred_runtime-3.1.0/fred_runtime/support/thinking.py +211 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/support/tool_loop.py +9 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime.egg-info/PKG-INFO +3 -3
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime.egg-info/SOURCES.txt +3 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime.egg-info/requires.txt +2 -2
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/pyproject.toml +3 -3
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_agent_app.py +7 -3
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_context_aware_tool.py +44 -1
- fred_runtime-3.1.0/tests/test_fred_workspace_fs.py +179 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_history.py +7 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_kf_workspace_client.py +79 -18
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_openai_compat_router.py +7 -0
- fred_runtime-3.1.0/tests/test_react_thinking.py +472 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/README.md +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/__init__.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/app/_catalogs.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/app/config_loader.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/app/container.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/app/dependencies.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/app/mcp_config.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/app/openai_compat_router.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/cli/__init__.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/cli/completion.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/cli/entrypoint.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/cli/history_display.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/cli/kpi_display.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/cli/pod_client.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/cli/repl.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/cli/repl_helpers.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/cli/url_helpers.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/client.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/__init__.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/kf_base_client.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/kf_fast_text_client.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/kf_http_client.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/kf_logs_client.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/kf_markdown_media_client.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/kf_vectorsearch_client.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/mcp_interceptors.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/mcp_runtime.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/mcp_toolkit.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/mcp_utils.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/structures.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/token_expiry.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/common/tool_node_utils.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/deep/__init__.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/deep/deep_runtime.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/eval/__init__.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/eval/collector.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/graph/__init__.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/integrations/__init__.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/integrations/v2_runtime/__init__.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/model_routing/__init__.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/model_routing/catalog.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/model_routing/contracts.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/model_routing/provider.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/model_routing/resolver.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/react/__init__.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/react/react_model_adapter.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/react/react_prompting.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/react/react_tool_binding.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/react/react_tool_loop.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/react/react_tool_rendering.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/react/react_tool_utils.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/react/react_tracing.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/runtime_context.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/runtime_support/__init__.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/runtime_support/checkpoints.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/runtime_support/model_metadata.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/runtime_support/request_context_helpers.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/runtime_support/sql_checkpointer.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/runtime_support/user_token_refresher.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/support/__init__.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/support/filesystem_context.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime/support/tool_approval.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime.egg-info/dependency_links.txt +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime.egg-info/entry_points.txt +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/fred_runtime.egg-info/top_level.txt +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/setup.cfg +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_client.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_config_loader.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_context.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_conversational_memory.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_eval_collector.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_eval_trace.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_graph_runtime_invoke_agent.py +1 -1
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_graph_runtime_observability.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_kpi_display.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_mcp_config.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_model_routing.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_pod_client.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_repl_helpers.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_smoke.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_token_expiry.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/tests/test_url_helpers.py +0 -0
- {fred_runtime-2.0.11 → fred_runtime-3.1.0}/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:
|
|
3
|
+
Version: 3.1.0
|
|
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,8 @@ 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>=
|
|
16
|
-
Requires-Dist: fred-sdk>=
|
|
15
|
+
Requires-Dist: fred-core>=3.1.0
|
|
16
|
+
Requires-Dist: fred-sdk>=3.1.0
|
|
17
17
|
Requires-Dist: alembic>=1.18.4
|
|
18
18
|
Requires-Dist: deepagents>=0.4.11
|
|
19
19
|
Requires-Dist: httpx>=0.28.1
|
|
@@ -22,7 +22,6 @@ from .agent_app import create_agent_app
|
|
|
22
22
|
from .config import (
|
|
23
23
|
AgentPodConfig,
|
|
24
24
|
LangfuseObservabilityConfig,
|
|
25
|
-
MetricsBackend,
|
|
26
25
|
PodAIConfig,
|
|
27
26
|
PodAppConfig,
|
|
28
27
|
PodObservabilityConfig,
|
|
@@ -40,7 +39,6 @@ from .config_loader import (
|
|
|
40
39
|
__all__ = [
|
|
41
40
|
"AgentPodConfig",
|
|
42
41
|
"LangfuseObservabilityConfig",
|
|
43
|
-
"MetricsBackend",
|
|
44
42
|
"PodAIConfig",
|
|
45
43
|
"PodAppConfig",
|
|
46
44
|
"PodObservabilityConfig",
|
|
@@ -57,6 +57,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
|
|
57
57
|
from fastapi.responses import StreamingResponse
|
|
58
58
|
from fred_core.common.config_loader import get_config
|
|
59
59
|
from fred_core.history.history_schema import ChatMessage
|
|
60
|
+
from fred_core.kpi import KPIMiddleware
|
|
60
61
|
from fred_core.kpi.kpi_writer_structures import KPIActor
|
|
61
62
|
from fred_core.logs.log_setup import log_setup
|
|
62
63
|
from fred_core.logs.memory_log_store import RamLogStore
|
|
@@ -110,10 +111,9 @@ from fred_runtime.runtime_support.checkpoints import load_checkpoint
|
|
|
110
111
|
from ..common.structures import AgentSettingsLike
|
|
111
112
|
from ..integrations.v2_runtime.adapters import (
|
|
112
113
|
CompositeToolInvoker,
|
|
113
|
-
FredArtifactPublisher,
|
|
114
114
|
FredKnowledgeSearchToolInvoker,
|
|
115
115
|
FredMcpToolProvider,
|
|
116
|
-
|
|
116
|
+
FredWorkspaceFs,
|
|
117
117
|
KPIWriterMetricsAdapter,
|
|
118
118
|
build_default_tracer,
|
|
119
119
|
)
|
|
@@ -127,7 +127,11 @@ from ..runtime_support import refresh_user_access_token_from_keycloak
|
|
|
127
127
|
from .config import AgentPodConfig
|
|
128
128
|
from .container import build_pod_container
|
|
129
129
|
from .context import AuditEventRecord, KpiTurnRecord, PodApplicationContext
|
|
130
|
-
from .dependencies import
|
|
130
|
+
from .dependencies import (
|
|
131
|
+
attach_pod_container,
|
|
132
|
+
get_pod_container,
|
|
133
|
+
get_pod_container_from_app,
|
|
134
|
+
)
|
|
131
135
|
from .observability_factory import bootstrap_observability
|
|
132
136
|
|
|
133
137
|
logger = logging.getLogger(__name__)
|
|
@@ -656,11 +660,7 @@ def _build_runtime_services(
|
|
|
656
660
|
binding=binding,
|
|
657
661
|
settings=settings,
|
|
658
662
|
)
|
|
659
|
-
|
|
660
|
-
binding=binding,
|
|
661
|
-
settings=settings,
|
|
662
|
-
)
|
|
663
|
-
resource_reader = FredResourceReader(
|
|
663
|
+
workspace_fs = FredWorkspaceFs(
|
|
664
664
|
binding=binding,
|
|
665
665
|
settings=settings,
|
|
666
666
|
)
|
|
@@ -672,8 +672,7 @@ def _build_runtime_services(
|
|
|
672
672
|
settings=settings,
|
|
673
673
|
ports=AuthoredToolRuntimePorts(
|
|
674
674
|
chat_model_factory=runtime_config.chat_model_factory,
|
|
675
|
-
|
|
676
|
-
resource_reader=resource_reader,
|
|
675
|
+
workspace_fs=workspace_fs,
|
|
677
676
|
fallback_tool_invoker=base_tool_invoker,
|
|
678
677
|
media_fetcher=_build_media_fetcher(binding=binding, settings=settings),
|
|
679
678
|
),
|
|
@@ -703,8 +702,7 @@ def _build_runtime_services(
|
|
|
703
702
|
chat_model_factory=runtime_config.chat_model_factory,
|
|
704
703
|
tool_invoker=tool_invoker,
|
|
705
704
|
tool_provider=tool_provider,
|
|
706
|
-
|
|
707
|
-
resource_reader=resource_reader,
|
|
705
|
+
workspace_fs=workspace_fs,
|
|
708
706
|
checkpointer=runtime_config.checkpointer,
|
|
709
707
|
agent_invoker=agent_invoker,
|
|
710
708
|
)
|
|
@@ -850,6 +848,7 @@ class _McpCatalogResponse(BaseModel):
|
|
|
850
848
|
class _ResolvedAgentInstance(BaseModel):
|
|
851
849
|
agent_instance_id: str
|
|
852
850
|
template_agent_id: str
|
|
851
|
+
display_name: str = ""
|
|
853
852
|
owner_scope: str
|
|
854
853
|
owner_user_id: str | None = None
|
|
855
854
|
owner_team_id: str | None = None
|
|
@@ -862,6 +861,7 @@ class _ResolvedExecutionTarget:
|
|
|
862
861
|
definition: ReActAgentDefinition | GraphAgentDefinition
|
|
863
862
|
effective_agent_id: str
|
|
864
863
|
team_id: str | None = None
|
|
864
|
+
agent_instance_name: str | None = None
|
|
865
865
|
|
|
866
866
|
|
|
867
867
|
def _apply_runtime_tuning(
|
|
@@ -1050,6 +1050,7 @@ async def _resolve_agent_instance(
|
|
|
1050
1050
|
),
|
|
1051
1051
|
effective_agent_id=resolution.agent_instance_id,
|
|
1052
1052
|
team_id=resolution.owner_team_id,
|
|
1053
|
+
agent_instance_name=resolution.display_name or None,
|
|
1053
1054
|
)
|
|
1054
1055
|
|
|
1055
1056
|
|
|
@@ -1593,6 +1594,7 @@ def _emit_turn_completed(
|
|
|
1593
1594
|
user_id: str,
|
|
1594
1595
|
team_id: str | None,
|
|
1595
1596
|
agent_instance_id: str | None,
|
|
1597
|
+
agent_instance_name: str | None,
|
|
1596
1598
|
template_agent_id: str | None,
|
|
1597
1599
|
payloads: list[dict[str, Any]],
|
|
1598
1600
|
turn_start: float,
|
|
@@ -1626,6 +1628,8 @@ def _emit_turn_completed(
|
|
|
1626
1628
|
prom_dims: dict[str, str | None] = {
|
|
1627
1629
|
"team_id": team_id,
|
|
1628
1630
|
"template_agent_id": template_agent_id,
|
|
1631
|
+
"agent_instance_id": agent_instance_id,
|
|
1632
|
+
"agent_instance_name": agent_instance_name,
|
|
1629
1633
|
"runtime_id": runtime_id,
|
|
1630
1634
|
"model_name": outcome.model_name,
|
|
1631
1635
|
"finish_reason": outcome.finish_reason,
|
|
@@ -1642,7 +1646,7 @@ def _emit_turn_completed(
|
|
|
1642
1646
|
"input_tokens": outcome.input_tokens,
|
|
1643
1647
|
"output_tokens": outcome.output_tokens,
|
|
1644
1648
|
},
|
|
1645
|
-
actor=KPIActor(type="
|
|
1649
|
+
actor=KPIActor(type="human", user_id=user_id),
|
|
1646
1650
|
)
|
|
1647
1651
|
|
|
1648
1652
|
if outcome.is_error:
|
|
@@ -1651,7 +1655,7 @@ def _emit_turn_completed(
|
|
|
1651
1655
|
type="counter",
|
|
1652
1656
|
value=1,
|
|
1653
1657
|
dims=prom_dims,
|
|
1654
|
-
actor=KPIActor(type="
|
|
1658
|
+
actor=KPIActor(type="human", user_id=user_id),
|
|
1655
1659
|
)
|
|
1656
1660
|
|
|
1657
1661
|
# Append to container ring buffer (high-cardinality fields safe here).
|
|
@@ -1682,6 +1686,7 @@ async def _stream(
|
|
|
1682
1686
|
access_token: str | None = None,
|
|
1683
1687
|
*,
|
|
1684
1688
|
team_id: str | None = None,
|
|
1689
|
+
agent_instance_name: str | None = None,
|
|
1685
1690
|
registry: Mapping[str, ReActAgentDefinition | GraphAgentDefinition] | None = None,
|
|
1686
1691
|
security_enabled: bool = False,
|
|
1687
1692
|
container: PodApplicationContext,
|
|
@@ -1734,6 +1739,7 @@ async def _stream(
|
|
|
1734
1739
|
user_id=user_id,
|
|
1735
1740
|
team_id=resolved_team_id,
|
|
1736
1741
|
agent_instance_id=request.agent_instance_id,
|
|
1742
|
+
agent_instance_name=agent_instance_name,
|
|
1737
1743
|
template_agent_id=definition.agent_id,
|
|
1738
1744
|
payloads=collected,
|
|
1739
1745
|
turn_start=turn_start,
|
|
@@ -2627,6 +2633,7 @@ def _build_agent_router(
|
|
|
2627
2633
|
user_id=user_id_str,
|
|
2628
2634
|
team_id=target.team_id,
|
|
2629
2635
|
agent_instance_id=request.agent_instance_id,
|
|
2636
|
+
agent_instance_name=target.agent_instance_name,
|
|
2630
2637
|
template_agent_id=target.definition.agent_id,
|
|
2631
2638
|
payloads=payloads,
|
|
2632
2639
|
turn_start=turn_start,
|
|
@@ -2730,6 +2737,7 @@ def _build_agent_router(
|
|
|
2730
2737
|
user_id=user_id_str,
|
|
2731
2738
|
team_id=target.team_id,
|
|
2732
2739
|
agent_instance_id=request.agent_instance_id,
|
|
2740
|
+
agent_instance_name=target.agent_instance_name,
|
|
2733
2741
|
template_agent_id=target.definition.agent_id,
|
|
2734
2742
|
payloads=payloads,
|
|
2735
2743
|
turn_start=turn_start,
|
|
@@ -2840,6 +2848,7 @@ def _build_agent_router(
|
|
|
2840
2848
|
internal_req,
|
|
2841
2849
|
access_token=access_token,
|
|
2842
2850
|
team_id=target.team_id,
|
|
2851
|
+
agent_instance_name=target.agent_instance_name,
|
|
2843
2852
|
registry=registry,
|
|
2844
2853
|
security_enabled=security_enabled,
|
|
2845
2854
|
container=container,
|
|
@@ -2966,7 +2975,7 @@ def create_agent_app(
|
|
|
2966
2975
|
"enabled" if security_enabled else "disabled",
|
|
2967
2976
|
"sql" if container.get_checkpointer() is not None else "none",
|
|
2968
2977
|
"sql" if container.get_history_store() is not None else "none",
|
|
2969
|
-
config.observability.
|
|
2978
|
+
"prometheus" if config.observability.kpi.prometheus.enabled else "logging",
|
|
2970
2979
|
list(registry.keys()),
|
|
2971
2980
|
)
|
|
2972
2981
|
yield
|
|
@@ -2992,6 +3001,13 @@ def create_agent_app(
|
|
|
2992
3001
|
)
|
|
2993
3002
|
logger.debug("[fred-runtime] CORS allow_origins=%s", authorized_origins)
|
|
2994
3003
|
|
|
3004
|
+
# KPI middleware — writer is lazily resolved from app.state because the
|
|
3005
|
+
# container (and its KPI writer) is only initialised during lifespan startup.
|
|
3006
|
+
app.add_middleware(
|
|
3007
|
+
KPIMiddleware,
|
|
3008
|
+
kpi=lambda: get_pod_container_from_app(app).get_kpi_writer(),
|
|
3009
|
+
)
|
|
3010
|
+
|
|
2995
3011
|
api_router = APIRouter(prefix=base_url)
|
|
2996
3012
|
api_router.include_router(
|
|
2997
3013
|
_build_agent_router(registry, security_enabled=security_enabled)
|
|
@@ -70,6 +70,7 @@ if TYPE_CHECKING:
|
|
|
70
70
|
from fred_runtime.runtime_context import McpConfigurationLike
|
|
71
71
|
|
|
72
72
|
from fred_core.common import (
|
|
73
|
+
KpiObservabilityConfig,
|
|
73
74
|
OpenSearchStoreConfig,
|
|
74
75
|
PostgresStoreConfig,
|
|
75
76
|
TemporalSchedulerConfig,
|
|
@@ -120,26 +121,6 @@ class PodAppConfig(BaseModel):
|
|
|
120
121
|
),
|
|
121
122
|
)
|
|
122
123
|
gcu_version: str | None = None
|
|
123
|
-
metrics_address: str = "127.0.0.1"
|
|
124
|
-
metrics_port: int = 9000
|
|
125
|
-
kpi_process_metrics_interval_sec: int = Field(
|
|
126
|
-
default=0,
|
|
127
|
-
description=(
|
|
128
|
-
"Emit process and SQL pool KPIs every N seconds. Set 0 to disable "
|
|
129
|
-
"the background emitters."
|
|
130
|
-
),
|
|
131
|
-
)
|
|
132
|
-
kpi_log_summary_interval_sec: float = Field(
|
|
133
|
-
default=0.0,
|
|
134
|
-
description=(
|
|
135
|
-
"Emit periodic KPI summary logs every N seconds for local benches. "
|
|
136
|
-
"Set 0 to disable."
|
|
137
|
-
),
|
|
138
|
-
)
|
|
139
|
-
kpi_log_summary_top_n: int = Field(
|
|
140
|
-
default=0,
|
|
141
|
-
description="Top-N KPI summary rows to log. 0 means all / disabled.",
|
|
142
|
-
)
|
|
143
124
|
openai_compat: bool = True
|
|
144
125
|
"""
|
|
145
126
|
Enable the OpenAI-compatible /v1/chat/completions and /v1/models endpoints.
|
|
@@ -209,21 +190,6 @@ class TracerBackend(str, Enum):
|
|
|
209
190
|
langfuse = "langfuse"
|
|
210
191
|
|
|
211
192
|
|
|
212
|
-
class MetricsBackend(str, Enum):
|
|
213
|
-
"""
|
|
214
|
-
Metrics emission backend for the agent pod.
|
|
215
|
-
|
|
216
|
-
- null — no metrics, all timer events are dropped
|
|
217
|
-
- logging — each timer is emitted as a structured log entry (default)
|
|
218
|
-
- prometheus — KPI/process metrics are exported in Prometheus format on the
|
|
219
|
-
dedicated metrics port configured under `app`
|
|
220
|
-
"""
|
|
221
|
-
|
|
222
|
-
null = "null"
|
|
223
|
-
logging = "logging"
|
|
224
|
-
prometheus = "prometheus"
|
|
225
|
-
|
|
226
|
-
|
|
227
193
|
class LangfuseObservabilityConfig(BaseModel):
|
|
228
194
|
"""
|
|
229
195
|
Langfuse connection settings.
|
|
@@ -245,14 +211,20 @@ class PodObservabilityConfig(BaseModel):
|
|
|
245
211
|
Example:
|
|
246
212
|
observability:
|
|
247
213
|
tracer: logging # null | logging | langfuse
|
|
248
|
-
|
|
214
|
+
kpi:
|
|
215
|
+
log:
|
|
216
|
+
enabled: true
|
|
217
|
+
prometheus:
|
|
218
|
+
enabled: false
|
|
219
|
+
opensearch:
|
|
220
|
+
enabled: false
|
|
249
221
|
langfuse:
|
|
250
222
|
host: "http://localhost:3001"
|
|
251
223
|
# LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY in .env
|
|
252
224
|
"""
|
|
253
225
|
|
|
254
226
|
tracer: TracerBackend = TracerBackend.logging
|
|
255
|
-
|
|
227
|
+
kpi: KpiObservabilityConfig = Field(default_factory=KpiObservabilityConfig)
|
|
256
228
|
langfuse: LangfuseObservabilityConfig = Field(
|
|
257
229
|
default_factory=LangfuseObservabilityConfig
|
|
258
230
|
)
|
|
@@ -24,16 +24,14 @@ from datetime import datetime, timezone
|
|
|
24
24
|
from typing import TYPE_CHECKING, TypedDict
|
|
25
25
|
|
|
26
26
|
from fred_core.kpi.base_kpi_writer import BaseKPIWriter
|
|
27
|
+
from fred_core.kpi.kpi_factory import build_kpi_writer
|
|
27
28
|
from fred_core.kpi.kpi_process import emit_process_kpis, emit_sql_pool_kpis
|
|
28
|
-
from fred_core.kpi.kpi_writer import KPIDefaults, KPIWriter
|
|
29
|
-
from fred_core.kpi.log_kpi_store import KpiLogStore
|
|
30
29
|
from fred_core.kpi.noop_kpi_writer import NoOpKPIWriter
|
|
31
|
-
from fred_core.kpi.prometheus_kpi_store import PrometheusKPIStore
|
|
32
30
|
from fred_sdk.contracts.runtime import HistoryStorePort
|
|
33
31
|
from prometheus_client import start_http_server
|
|
34
32
|
from sqlalchemy.ext.asyncio import AsyncEngine
|
|
35
33
|
|
|
36
|
-
from fred_runtime.app.config import AgentPodConfig
|
|
34
|
+
from fred_runtime.app.config import AgentPodConfig
|
|
37
35
|
|
|
38
36
|
if TYPE_CHECKING:
|
|
39
37
|
pass
|
|
@@ -117,18 +115,11 @@ class PodApplicationContext:
|
|
|
117
115
|
def initialize_kpi_writer(self) -> None:
|
|
118
116
|
"""Build the KPI writer from pod observability config."""
|
|
119
117
|
config = self.configuration
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if backend == MetricsBackend.prometheus:
|
|
126
|
-
store = PrometheusKPIStore(delegate=store) # type: ignore[arg-type]
|
|
127
|
-
self._kpi_writer = KPIWriter(
|
|
128
|
-
store=store,
|
|
129
|
-
defaults=KPIDefaults(static_dims={"service": "fred-runtime"}),
|
|
130
|
-
summary_interval_s=config.app.kpi_log_summary_interval_sec,
|
|
131
|
-
summary_top_n=config.app.kpi_log_summary_top_n,
|
|
118
|
+
self._kpi_writer = build_kpi_writer(
|
|
119
|
+
kpi_config=config.observability.kpi,
|
|
120
|
+
opensearch_config=config.storage.opensearch,
|
|
121
|
+
service_name="fred-runtime",
|
|
122
|
+
log_level=config.app.log_level,
|
|
132
123
|
)
|
|
133
124
|
|
|
134
125
|
async def initialize_sql(self) -> None:
|
|
@@ -160,25 +151,23 @@ class PodApplicationContext:
|
|
|
160
151
|
|
|
161
152
|
def start_metrics_exporter(self) -> None:
|
|
162
153
|
"""Start the Prometheus scrape endpoint when configured."""
|
|
163
|
-
|
|
164
|
-
if
|
|
154
|
+
prom_cfg = self.configuration.observability.kpi.prometheus
|
|
155
|
+
if not prom_cfg.enabled:
|
|
165
156
|
return
|
|
166
|
-
result = start_http_server(
|
|
167
|
-
config.app.metrics_port,
|
|
168
|
-
addr=config.app.metrics_address,
|
|
169
|
-
)
|
|
157
|
+
result = start_http_server(prom_cfg.port, addr=prom_cfg.address)
|
|
170
158
|
self._metrics_exporter = result if isinstance(result, tuple) else None
|
|
171
159
|
logger.info(
|
|
172
160
|
"[fred-runtime] Prometheus metrics exporter ready at %s:%s",
|
|
173
|
-
|
|
174
|
-
|
|
161
|
+
prom_cfg.address,
|
|
162
|
+
prom_cfg.port,
|
|
175
163
|
)
|
|
176
164
|
|
|
177
165
|
async def start_kpi_tasks(self) -> None:
|
|
178
166
|
"""Start background KPI flush tasks (process + SQL pool health)."""
|
|
179
167
|
kpi_writer = self.get_kpi_writer()
|
|
180
|
-
|
|
181
|
-
|
|
168
|
+
interval_s = float(
|
|
169
|
+
self.configuration.observability.kpi.process_metrics_interval_sec
|
|
170
|
+
)
|
|
182
171
|
if interval_s <= 0 or isinstance(kpi_writer, NoOpKPIWriter):
|
|
183
172
|
return
|
|
184
173
|
tasks: list[asyncio.Task[None]] = [
|
|
@@ -51,7 +51,7 @@ from fred_core.portable import (
|
|
|
51
51
|
set_tracer,
|
|
52
52
|
)
|
|
53
53
|
|
|
54
|
-
from .config import
|
|
54
|
+
from .config import PodObservabilityConfig, TracerBackend
|
|
55
55
|
|
|
56
56
|
logger = logging.getLogger(__name__)
|
|
57
57
|
|
|
@@ -83,9 +83,9 @@ def bootstrap_observability(
|
|
|
83
83
|
set_tracer(tracer)
|
|
84
84
|
set_metrics_provider(metrics)
|
|
85
85
|
logger.info(
|
|
86
|
-
"[fred-runtime] observability ready — tracer=%s
|
|
86
|
+
"[fred-runtime] observability ready — tracer=%s prometheus=%s",
|
|
87
87
|
config.tracer.value,
|
|
88
|
-
config.
|
|
88
|
+
config.kpi.prometheus.enabled,
|
|
89
89
|
)
|
|
90
90
|
|
|
91
91
|
|
|
@@ -160,15 +160,10 @@ def _build_metrics(
|
|
|
160
160
|
*,
|
|
161
161
|
kpi_writer: BaseKPIWriter | None = None,
|
|
162
162
|
) -> MetricsProvider:
|
|
163
|
-
if config.
|
|
164
|
-
return MetricsProvider()
|
|
165
|
-
if config.metrics == MetricsBackend.logging:
|
|
166
|
-
return LoggingMetricsProvider()
|
|
167
|
-
if config.metrics == MetricsBackend.prometheus:
|
|
163
|
+
if config.kpi.prometheus.enabled:
|
|
168
164
|
if kpi_writer is None:
|
|
169
165
|
logger.warning(
|
|
170
|
-
"[fred-runtime]
|
|
171
|
-
" — falling back to logging"
|
|
166
|
+
"[fred-runtime] prometheus enabled without a KPI writer — falling back to logging"
|
|
172
167
|
)
|
|
173
168
|
return LoggingMetricsProvider()
|
|
174
169
|
from fred_runtime.integrations.v2_runtime.adapters import (
|
|
@@ -176,8 +171,4 @@ def _build_metrics(
|
|
|
176
171
|
)
|
|
177
172
|
|
|
178
173
|
return cast(MetricsProvider, KPIWriterMetricsAdapter(kpi_writer))
|
|
179
|
-
logger.warning(
|
|
180
|
-
"[fred-runtime] Unknown metrics backend '%s' — falling back to logging",
|
|
181
|
-
config.metrics,
|
|
182
|
-
)
|
|
183
174
|
return LoggingMetricsProvider()
|
|
@@ -163,23 +163,57 @@ class ContextAwareTool(BaseTool):
|
|
|
163
163
|
|
|
164
164
|
tool_properties = self._get_tool_properties()
|
|
165
165
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
166
|
+
# Snapshot what the agent (or LLM) explicitly requested, so every decision
|
|
167
|
+
# below can be logged as "requested -> applied" with no ambiguity. The
|
|
168
|
+
# picker selection (RuntimeContext) is a default scope an agent may NARROW;
|
|
169
|
+
# an explicit per-call scope is respected, never silently overwritten.
|
|
170
|
+
caller_document_uids = kwargs.get("document_uids")
|
|
171
|
+
caller_library_ids = kwargs.get("document_library_tags_ids")
|
|
172
|
+
picker_document_uids = get_document_uids(context)
|
|
173
|
+
picker_library_ids = get_document_library_tags_ids(context)
|
|
174
|
+
|
|
175
|
+
# Document scope is the most specific selector. If the agent passed it,
|
|
176
|
+
# keep it verbatim; otherwise fill from the picker selection.
|
|
177
|
+
if "document_uids" in tool_properties:
|
|
178
|
+
if caller_document_uids:
|
|
179
|
+
logger.info(
|
|
180
|
+
"ContextAwareTool(%s) document_uids: agent-scoped=%s (picker=%s NOT applied)",
|
|
181
|
+
self.name,
|
|
182
|
+
caller_document_uids,
|
|
183
|
+
picker_document_uids,
|
|
184
|
+
)
|
|
185
|
+
elif picker_document_uids:
|
|
186
|
+
kwargs["document_uids"] = picker_document_uids
|
|
187
|
+
logger.info(
|
|
188
|
+
"ContextAwareTool(%s) document_uids: applied picker selection=%s",
|
|
189
|
+
self.name,
|
|
190
|
+
picker_document_uids,
|
|
191
|
+
)
|
|
174
192
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
193
|
+
# Library scope is broader than document scope. Inject the picker libraries
|
|
194
|
+
# only when the agent did not scope by library AND did not scope by document
|
|
195
|
+
# (a per-call document scope must not be widened back to whole libraries).
|
|
196
|
+
if "document_library_tags_ids" in tool_properties:
|
|
197
|
+
if caller_library_ids:
|
|
198
|
+
logger.info(
|
|
199
|
+
"ContextAwareTool(%s) library: agent-scoped=%s (picker=%s NOT applied)",
|
|
200
|
+
self.name,
|
|
201
|
+
caller_library_ids,
|
|
202
|
+
picker_library_ids,
|
|
203
|
+
)
|
|
204
|
+
elif caller_document_uids:
|
|
205
|
+
logger.info(
|
|
206
|
+
"ContextAwareTool(%s) library: NOT injected — agent scoped by document_uids=%s",
|
|
207
|
+
self.name,
|
|
208
|
+
caller_document_uids,
|
|
209
|
+
)
|
|
210
|
+
elif picker_library_ids:
|
|
211
|
+
kwargs["document_library_tags_ids"] = picker_library_ids
|
|
212
|
+
logger.info(
|
|
213
|
+
"ContextAwareTool(%s) library: applied picker selection=%s",
|
|
214
|
+
self.name,
|
|
215
|
+
picker_library_ids,
|
|
216
|
+
)
|
|
183
217
|
|
|
184
218
|
session_id = context.session_id
|
|
185
219
|
if (
|