fred-runtime 3.3.5__tar.gz → 3.3.6__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.
Files changed (154) hide show
  1. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/PKG-INFO +1 -1
  2. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/deep/deep_runtime.py +46 -8
  3. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime.egg-info/PKG-INFO +1 -1
  4. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime.egg-info/SOURCES.txt +1 -0
  5. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/pyproject.toml +1 -1
  6. fred_runtime-3.3.6/tests/test_deep_agent_middleware.py +182 -0
  7. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/README.md +0 -0
  8. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/__init__.py +0 -0
  9. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/__main__.py +0 -0
  10. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/app/__init__.py +0 -0
  11. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/app/_catalogs.py +0 -0
  12. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/app/agent_app.py +0 -0
  13. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/app/config.py +0 -0
  14. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/app/config_loader.py +0 -0
  15. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/app/container.py +0 -0
  16. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/app/context.py +0 -0
  17. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/app/dependencies.py +0 -0
  18. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/app/mcp_config.py +0 -0
  19. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/app/observability_factory.py +0 -0
  20. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/app/openai_compat_router.py +0 -0
  21. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/capabilities/__init__.py +0 -0
  22. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/capabilities/assembly.py +0 -0
  23. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/capabilities/assets.py +0 -0
  24. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/capabilities/demo.py +0 -0
  25. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/capabilities/demo_migrations/env.py +0 -0
  26. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/capabilities/demo_migrations/versions/d1e2m0a0b0c0_create_demo_echo_notes.py +0 -0
  27. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/capabilities/document_access/__init__.py +0 -0
  28. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/capabilities/document_access/capability.py +0 -0
  29. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/capabilities/errors.py +0 -0
  30. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/capabilities/mcp.py +0 -0
  31. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/capabilities/openapi_dump.py +0 -0
  32. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/capabilities/registry.py +0 -0
  33. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/cli/__init__.py +0 -0
  34. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/cli/completion.py +0 -0
  35. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/cli/entrypoint.py +0 -0
  36. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/cli/history_display.py +0 -0
  37. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/cli/kpi_display.py +0 -0
  38. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/cli/pod_client.py +0 -0
  39. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/cli/repl.py +0 -0
  40. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/cli/repl_helpers.py +0 -0
  41. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/cli/url_helpers.py +0 -0
  42. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/client.py +0 -0
  43. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/__init__.py +0 -0
  44. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/context_aware_tool.py +0 -0
  45. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/kf_base_client.py +0 -0
  46. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/kf_fast_text_client.py +0 -0
  47. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/kf_http_client.py +0 -0
  48. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/kf_markdown_media_client.py +0 -0
  49. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/kf_vectorsearch_client.py +0 -0
  50. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/kf_workspace_client.py +0 -0
  51. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/mcp_interceptors.py +0 -0
  52. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/mcp_runtime.py +0 -0
  53. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/mcp_toolkit.py +0 -0
  54. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/mcp_utils.py +0 -0
  55. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/structures.py +0 -0
  56. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/token_expiry.py +0 -0
  57. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/common/tool_node_utils.py +0 -0
  58. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/deep/__init__.py +0 -0
  59. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/eval/__init__.py +0 -0
  60. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/eval/collector.py +0 -0
  61. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/graph/__init__.py +0 -0
  62. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/graph/graph_runtime.py +0 -0
  63. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/integrations/__init__.py +0 -0
  64. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/integrations/inprocess_toolkit_registry.py +0 -0
  65. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/integrations/kf_vector_search/__init__.py +0 -0
  66. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/integrations/kf_vector_search/toolkit.py +0 -0
  67. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/integrations/v2_runtime/__init__.py +0 -0
  68. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/integrations/v2_runtime/adapters.py +0 -0
  69. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/migrations.py +0 -0
  70. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/model_routing/__init__.py +0 -0
  71. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/model_routing/catalog.py +0 -0
  72. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/model_routing/contracts.py +0 -0
  73. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/model_routing/provider.py +0 -0
  74. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/model_routing/resolver.py +0 -0
  75. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/__init__.py +0 -0
  76. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/middleware/__init__.py +0 -0
  77. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/middleware/checkpoint_hygiene.py +0 -0
  78. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/middleware/dynamic_prompt.py +0 -0
  79. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/middleware/frame.py +0 -0
  80. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/middleware/hitl.py +0 -0
  81. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/middleware/model_routing.py +0 -0
  82. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/middleware/shared.py +0 -0
  83. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/middleware/tool_observability.py +0 -0
  84. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/middleware/tracing_kpi.py +0 -0
  85. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/react_langchain_adapter.py +0 -0
  86. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/react_message_codec.py +0 -0
  87. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/react_model_adapter.py +0 -0
  88. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/react_prompting.py +0 -0
  89. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/react_runtime.py +0 -0
  90. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/react_stream_adapter.py +0 -0
  91. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/react_tool_binding.py +0 -0
  92. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/react_tool_loop.py +0 -0
  93. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/react_tool_rendering.py +0 -0
  94. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/react_tool_resolution.py +0 -0
  95. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/react_tool_utils.py +0 -0
  96. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/react/react_tracing.py +0 -0
  97. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/runtime_context.py +0 -0
  98. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/runtime_support/__init__.py +0 -0
  99. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/runtime_support/checkpoints.py +0 -0
  100. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/runtime_support/model_metadata.py +0 -0
  101. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/runtime_support/request_context_helpers.py +0 -0
  102. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/runtime_support/sql_checkpointer.py +0 -0
  103. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/runtime_support/user_token_refresher.py +0 -0
  104. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/support/__init__.py +0 -0
  105. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/support/filesystem_context.py +0 -0
  106. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/support/thinking.py +0 -0
  107. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime/support/tool_loop.py +0 -0
  108. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime.egg-info/dependency_links.txt +0 -0
  109. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime.egg-info/entry_points.txt +0 -0
  110. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime.egg-info/requires.txt +0 -0
  111. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/fred_runtime.egg-info/top_level.txt +0 -0
  112. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/setup.cfg +0 -0
  113. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_agent_app.py +0 -0
  114. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_capability_agent_1973.py +0 -0
  115. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_capability_chat_controls_1976.py +0 -0
  116. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_capability_chat_parts_1977.py +0 -0
  117. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_capability_document_access_1906.py +0 -0
  118. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_capability_endpoints_1974.py +0 -0
  119. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_capability_registry_1973.py +0 -0
  120. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_capability_routes_tables_1979.py +0 -0
  121. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_capability_selection_1974.py +0 -0
  122. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_client.py +0 -0
  123. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_config_loader.py +0 -0
  124. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_context.py +0 -0
  125. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_context_aware_tool.py +0 -0
  126. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_conversational_memory.py +0 -0
  127. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_eval_collector.py +0 -0
  128. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_eval_trace.py +0 -0
  129. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_fred_workspace_fs.py +0 -0
  130. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_graph_runtime_invoke_agent.py +0 -0
  131. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_graph_runtime_observability.py +0 -0
  132. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_history.py +0 -0
  133. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_kf_vector_search_toolkit.py +0 -0
  134. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_kf_workspace_client.py +0 -0
  135. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_knowledge_search_tool_invoker_sources.py +0 -0
  136. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_kpi_display.py +0 -0
  137. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_mcp_config.py +0 -0
  138. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_model_routing.py +0 -0
  139. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_openai_compat_router.py +0 -0
  140. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_pod_client.py +0 -0
  141. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_react_loop_regressions_1972.py +0 -0
  142. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_react_middleware_frame.py +0 -0
  143. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_react_prompting.py +0 -0
  144. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_react_thinking.py +0 -0
  145. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_repl_helpers.py +0 -0
  146. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_runtime_context_prompt_injection.py +0 -0
  147. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_smoke.py +0 -0
  148. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_spike_capability_state_1971.py +0 -0
  149. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_sql_checkpointer_owner.py +0 -0
  150. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_token_expiry.py +0 -0
  151. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_tool_observability_middleware.py +0 -0
  152. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_tracing_kpi_content_confinement.py +0 -0
  153. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/tests/test_url_helpers.py +0 -0
  154. {fred_runtime-3.3.5 → fred_runtime-3.3.6}/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.3.5
3
+ Version: 3.3.6
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
@@ -29,14 +29,25 @@ from __future__ import annotations
29
29
  from collections.abc import Sequence
30
30
  from typing import cast
31
31
 
32
+ from fred_core.kpi import BaseKPIWriter
32
33
  from fred_sdk.contracts.context import BoundRuntimeContext
33
- from fred_sdk.contracts.runtime import Executor
34
+ from fred_sdk.contracts.runtime import Executor, TracerPort
34
35
  from langchain.agents.middleware import AgentMiddleware
35
36
  from langchain.agents.middleware.tool_call_limit import ToolCallLimitMiddleware
36
37
  from langchain_core.language_models.chat_models import BaseChatModel
37
38
  from langchain_core.tools import BaseTool
38
39
  from langgraph.types import Checkpointer
39
40
 
41
+ from fred_runtime.react.middleware.tool_observability import (
42
+ ToolObservabilityMiddleware,
43
+ )
44
+ from fred_runtime.react.middleware.tracing_kpi import TracingKpiMiddleware
45
+ from fred_runtime.react.react_model_adapter import (
46
+ REACT_MODEL_OPERATION_ROUTING,
47
+ )
48
+ from fred_runtime.react.react_model_adapter import (
49
+ infer_react_model_operation_from_messages as _infer_react_model_operation_from_messages,
50
+ )
40
51
  from fred_runtime.react.react_prompting import (
41
52
  compose_system_prompt as _compose_system_prompt,
42
53
  )
@@ -53,6 +64,7 @@ from fred_runtime.react.react_runtime import (
53
64
  )
54
65
  from fred_runtime.react.react_tool_binding import ReActToolBinder
55
66
  from fred_runtime.react.react_tool_resolution import ReActRuntimeToolResolver
67
+ from fred_runtime.runtime_context import get_runtime_context
56
68
 
57
69
  _FILESYSTEM_TOOL_NAMES: tuple[str, ...] = (
58
70
  "ls",
@@ -135,7 +147,10 @@ class DeepAgentRuntime(ReActRuntime):
135
147
  system_prompt=system_prompt,
136
148
  checkpointer=cast(Checkpointer, self.services.checkpointer),
137
149
  middleware=_build_deepagent_runtime_middleware(
138
- filesystem_tools_enabled=filesystem_tools_enabled
150
+ filesystem_tools_enabled=filesystem_tools_enabled,
151
+ tracer=self.services.tracer,
152
+ kpi=get_runtime_context().get_kpi_writer(),
153
+ binding=binding,
139
154
  ),
140
155
  )
141
156
  return _TransportBackedReActExecutor(
@@ -229,12 +244,26 @@ def _filesystem_prompt_suffix(*, filesystem_tools_enabled: bool) -> str:
229
244
 
230
245
 
231
246
  def _build_deepagent_runtime_middleware(
232
- *, filesystem_tools_enabled: bool
247
+ *,
248
+ filesystem_tools_enabled: bool,
249
+ tracer: TracerPort | None,
250
+ kpi: BaseKPIWriter | None,
251
+ binding: BoundRuntimeContext,
233
252
  ) -> list[AgentMiddleware]:
234
253
  """
235
- Build Deep runtime middleware for filesystem-tool policy.
254
+ Build Deep runtime middleware: platform observability first, then the
255
+ filesystem-tool policy guard.
236
256
 
237
257
  Why this exists:
258
+ - Deep used to bypass `build_react_platform_middleware_frame()` entirely
259
+ (it overrides `build_executor`, so it never went through
260
+ `_create_compiled_react_agent`), which meant a Deep turn emitted no
261
+ `[LLM][CALL]`/`[LLM][RESPONSE]` logs, no `llm.call_latency_ms` KPI, and
262
+ no `agent.tool.invocation.*` audit events — the same
263
+ `TracingKpiMiddleware`/`ToolObservabilityMiddleware` pair ReAct always
264
+ gets. `create_deep_agent` accepts a plain `middleware=` list, so the
265
+ fix is to hand it the same two middleware instances, same order, no
266
+ new machinery.
238
267
  - when the standard filesystem MCP tools are absent, Deep should block
239
268
  accidental filesystem calls explicitly
240
269
  - when those tools are present, Deep should not add special blocking and
@@ -243,15 +272,24 @@ def _build_deepagent_runtime_middleware(
243
272
  How to use it:
244
273
  - call once while creating the compiled deep agent
245
274
  - pass whether standard filesystem tools are available in the resolved tool
246
- list
275
+ list, plus the same tracer/kpi/binding used to build the tool bindings
247
276
 
248
277
  Example:
249
- - `middleware = _build_deepagent_runtime_middleware(filesystem_tools_enabled=True)`
278
+ - `middleware = _build_deepagent_runtime_middleware(filesystem_tools_enabled=True, tracer=tracer, kpi=kpi, binding=binding)`
250
279
  """
280
+ middleware: list[AgentMiddleware] = [
281
+ TracingKpiMiddleware(
282
+ tracer=tracer,
283
+ kpi=kpi,
284
+ binding=binding,
285
+ infer_operation_from_messages=_infer_react_model_operation_from_messages,
286
+ default_operation=REACT_MODEL_OPERATION_ROUTING,
287
+ ),
288
+ ToolObservabilityMiddleware(kpi=kpi, binding=binding),
289
+ ]
251
290
  if filesystem_tools_enabled:
252
- return []
291
+ return middleware
253
292
 
254
- middleware: list[AgentMiddleware] = []
255
293
  for tool_name in _FILESYSTEM_TOOL_NAMES:
256
294
  middleware.append(
257
295
  cast(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fred-runtime
3
- Version: 3.3.5
3
+ Version: 3.3.6
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
@@ -121,6 +121,7 @@ tests/test_config_loader.py
121
121
  tests/test_context.py
122
122
  tests/test_context_aware_tool.py
123
123
  tests/test_conversational_memory.py
124
+ tests/test_deep_agent_middleware.py
124
125
  tests/test_eval_collector.py
125
126
  tests/test_eval_trace.py
126
127
  tests/test_fred_workspace_fs.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fred-runtime"
3
- version = "3.3.5"
3
+ version = "3.3.6"
4
4
  description = "Runtime adapters and infrastructure wiring for Fred v2 agents."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12,<3.13"
@@ -0,0 +1,182 @@
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
+ """
16
+ Tests proving DeepAgentRuntime gets the same observability guarantees as
17
+ ReActRuntime.
18
+
19
+ Deep overrides `build_executor` and never goes through
20
+ `build_react_platform_middleware_frame()`, so it used to silently skip
21
+ `TracingKpiMiddleware`/`ToolObservabilityMiddleware` — no `[LLM][CALL]` logs,
22
+ no `llm.call_latency_ms`/`agent.tool_latency_ms` KPI, no
23
+ `agent.tool.invocation.*` audit events for any Deep turn. These tests cover
24
+ both the middleware-list builder in isolation (mirrors
25
+ `test_react_middleware_frame.py::test_frame_order_is_fixed`) and the real
26
+ `build_executor` wiring (mirrors
27
+ `test_runtime_context_prompt_injection.py`'s stubbed-compile pattern).
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ from types import SimpleNamespace
33
+ from typing import Any, cast
34
+
35
+ import fred_runtime.deep.deep_runtime as deep_mod
36
+ import pytest
37
+ from fred_runtime.react.middleware.tool_observability import (
38
+ ToolObservabilityMiddleware,
39
+ )
40
+ from fred_runtime.react.middleware.tracing_kpi import TracingKpiMiddleware
41
+ from fred_runtime.runtime_context import RuntimeConfig, set_runtime_context
42
+ from fred_runtime.runtime_context import RuntimeContext as ProcessRuntimeContext
43
+ from fred_sdk.contracts.context import (
44
+ BoundRuntimeContext,
45
+ PortableContext,
46
+ PortableEnvironment,
47
+ RuntimeContext,
48
+ )
49
+ from fred_sdk.contracts.models import ReActAgentDefinition
50
+ from fred_sdk.contracts.runtime import RuntimeServices
51
+ from langchain.agents.middleware import ToolCallLimitMiddleware
52
+ from langchain_core.language_models.chat_models import BaseChatModel
53
+
54
+
55
+ def _binding() -> BoundRuntimeContext:
56
+ return BoundRuntimeContext(
57
+ runtime_context=RuntimeContext(),
58
+ portable_context=PortableContext(
59
+ request_id="request-1",
60
+ correlation_id="correlation-1",
61
+ actor="user-1",
62
+ tenant="team-1",
63
+ environment=PortableEnvironment.DEV,
64
+ ),
65
+ )
66
+
67
+
68
+ # ---------------------------------------------------------------------------
69
+ # _build_deepagent_runtime_middleware — list composition
70
+ # ---------------------------------------------------------------------------
71
+
72
+
73
+ def test_middleware_leads_with_observability_when_filesystem_enabled() -> None:
74
+ middleware = deep_mod._build_deepagent_runtime_middleware(
75
+ filesystem_tools_enabled=True,
76
+ tracer=None,
77
+ kpi=None,
78
+ binding=_binding(),
79
+ )
80
+ assert [type(m) for m in middleware] == [
81
+ TracingKpiMiddleware,
82
+ ToolObservabilityMiddleware,
83
+ ]
84
+
85
+
86
+ def test_middleware_keeps_observability_first_then_filesystem_guards() -> None:
87
+ middleware = deep_mod._build_deepagent_runtime_middleware(
88
+ filesystem_tools_enabled=False,
89
+ tracer=None,
90
+ kpi=None,
91
+ binding=_binding(),
92
+ )
93
+ assert type(middleware[0]) is TracingKpiMiddleware
94
+ assert type(middleware[1]) is ToolObservabilityMiddleware
95
+ assert all(type(m) is ToolCallLimitMiddleware for m in middleware[2:])
96
+ # One guard per disabled filesystem tool name (ls/read_file/write_file/
97
+ # edit_file/glob/grep/execute).
98
+ assert len(middleware) == 2 + 7
99
+
100
+
101
+ # ---------------------------------------------------------------------------
102
+ # build_executor — the real wiring, stubbed compile step
103
+ # ---------------------------------------------------------------------------
104
+
105
+
106
+ class _FakePolicy:
107
+ def __init__(self) -> None:
108
+ self.system_prompt_template = "BASE-TEMPLATE"
109
+ self.guardrails: list[object] = []
110
+ self.tool_approval = SimpleNamespace(enabled=False)
111
+ self.tool_selection = SimpleNamespace(
112
+ max_tool_calls_per_turn=None, allow_parallel_calls=False
113
+ )
114
+
115
+
116
+ class _FakeDefinition:
117
+ agent_id = "agent-1"
118
+ declared_tool_refs: tuple[object, ...] = ()
119
+ tuning_values: dict[str, str] = {}
120
+
121
+ def policy(self) -> _FakePolicy:
122
+ return _FakePolicy()
123
+
124
+
125
+ class _FakeResolver:
126
+ def __init__(self, **_: object) -> None:
127
+ pass
128
+
129
+ def resolve_tools(self) -> list[object]:
130
+ return []
131
+
132
+
133
+ class _FakeBinder:
134
+ def __init__(self, **_: object) -> None:
135
+ pass
136
+
137
+ def build_tools(self) -> list[object]:
138
+ return []
139
+
140
+
141
+ class _FakeExecutor:
142
+ def __init__(self, **kwargs: object) -> None:
143
+ self.kwargs = kwargs
144
+
145
+
146
+ def _fake_definition() -> ReActAgentDefinition:
147
+ return cast(ReActAgentDefinition, _FakeDefinition())
148
+
149
+
150
+ @pytest.mark.asyncio
151
+ async def test_deep_build_executor_wires_observability_middleware(
152
+ monkeypatch: pytest.MonkeyPatch,
153
+ ) -> None:
154
+ captured: dict[str, Any] = {}
155
+
156
+ def _fake_compile(**kwargs: object) -> object:
157
+ captured["middleware"] = list(cast(list, kwargs["middleware"]))
158
+ return object()
159
+
160
+ # build_executor reads the KPI writer off the process-wide runtime
161
+ # context (mirrors `test_kf_workspace_client.py`'s `_make_client`
162
+ # helper) — a NoOpKPIWriter is enough, no real config needed.
163
+ set_runtime_context(
164
+ ProcessRuntimeContext(RuntimeConfig(knowledge_flow_url="http://test"))
165
+ )
166
+ monkeypatch.setattr(deep_mod, "ReActRuntimeToolResolver", _FakeResolver)
167
+ monkeypatch.setattr(deep_mod, "ReActToolBinder", _FakeBinder)
168
+ monkeypatch.setattr(deep_mod, "_TransportBackedReActExecutor", _FakeExecutor)
169
+ monkeypatch.setattr(deep_mod, "_create_compiled_deep_agent", _fake_compile)
170
+
171
+ runtime = deep_mod.DeepAgentRuntime(
172
+ definition=_fake_definition(), services=RuntimeServices()
173
+ )
174
+ runtime._model = cast(BaseChatModel, SimpleNamespace())
175
+
176
+ await runtime.build_executor(_binding())
177
+
178
+ # The fake tool pipeline resolves no tools, so the filesystem guard
179
+ # clause also fires — this test only cares that observability leads.
180
+ wired = captured["middleware"]
181
+ assert type(wired[0]) is TracingKpiMiddleware
182
+ assert type(wired[1]) is ToolObservabilityMiddleware
File without changes
File without changes