fred-runtime 3.3.3__tar.gz → 3.3.5__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-3.3.3 → fred_runtime-3.3.5}/PKG-INFO +5 -5
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/app/agent_app.py +6 -5
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/capabilities/document_access/capability.py +47 -5
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/cli/pod_client.py +2 -1
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/integrations/kf_vector_search/toolkit.py +6 -3
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/integrations/v2_runtime/adapters.py +9 -2
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/react_runtime.py +36 -1
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/runtime_support/sql_checkpointer.py +17 -2
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime.egg-info/PKG-INFO +5 -5
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime.egg-info/SOURCES.txt +1 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime.egg-info/requires.txt +4 -4
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/pyproject.toml +5 -5
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_agent_app.py +80 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_capability_document_access_1906.py +94 -3
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_kf_vector_search_toolkit.py +41 -0
- fred_runtime-3.3.5/tests/test_knowledge_search_tool_invoker_sources.py +104 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_react_thinking.py +38 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_sql_checkpointer_owner.py +18 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/README.md +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/__main__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/app/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/app/_catalogs.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/app/config.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/app/config_loader.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/app/container.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/app/context.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/app/dependencies.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/app/mcp_config.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/app/observability_factory.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/app/openai_compat_router.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/capabilities/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/capabilities/assembly.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/capabilities/assets.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/capabilities/demo.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/capabilities/demo_migrations/env.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/capabilities/demo_migrations/versions/d1e2m0a0b0c0_create_demo_echo_notes.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/capabilities/document_access/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/capabilities/errors.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/capabilities/mcp.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/capabilities/openapi_dump.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/capabilities/registry.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/cli/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/cli/completion.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/cli/entrypoint.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/cli/history_display.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/cli/kpi_display.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/cli/repl.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/cli/repl_helpers.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/cli/url_helpers.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/client.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/context_aware_tool.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/kf_base_client.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/kf_fast_text_client.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/kf_http_client.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/kf_markdown_media_client.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/kf_vectorsearch_client.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/kf_workspace_client.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/mcp_interceptors.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/mcp_runtime.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/mcp_toolkit.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/mcp_utils.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/structures.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/token_expiry.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/common/tool_node_utils.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/deep/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/deep/deep_runtime.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/eval/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/eval/collector.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/graph/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/graph/graph_runtime.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/integrations/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/integrations/inprocess_toolkit_registry.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/integrations/kf_vector_search/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/integrations/v2_runtime/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/migrations.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/model_routing/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/model_routing/catalog.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/model_routing/contracts.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/model_routing/provider.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/model_routing/resolver.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/middleware/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/middleware/checkpoint_hygiene.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/middleware/dynamic_prompt.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/middleware/frame.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/middleware/hitl.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/middleware/model_routing.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/middleware/shared.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/middleware/tool_observability.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/middleware/tracing_kpi.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/react_langchain_adapter.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/react_message_codec.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/react_model_adapter.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/react_prompting.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/react_stream_adapter.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/react_tool_binding.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/react_tool_loop.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/react_tool_rendering.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/react_tool_resolution.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/react_tool_utils.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/react/react_tracing.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/runtime_context.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/runtime_support/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/runtime_support/checkpoints.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/runtime_support/model_metadata.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/runtime_support/request_context_helpers.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/runtime_support/user_token_refresher.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/support/__init__.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/support/filesystem_context.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/support/thinking.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/support/tool_loop.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime.egg-info/dependency_links.txt +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime.egg-info/entry_points.txt +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime.egg-info/top_level.txt +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/setup.cfg +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_capability_agent_1973.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_capability_chat_controls_1976.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_capability_chat_parts_1977.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_capability_endpoints_1974.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_capability_registry_1973.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_capability_routes_tables_1979.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_capability_selection_1974.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_client.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_config_loader.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_context.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_context_aware_tool.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_conversational_memory.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_eval_collector.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_eval_trace.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_fred_workspace_fs.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_graph_runtime_invoke_agent.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_graph_runtime_observability.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_history.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_kf_workspace_client.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_kpi_display.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_mcp_config.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_model_routing.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_openai_compat_router.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_pod_client.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_react_loop_regressions_1972.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_react_middleware_frame.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_react_prompting.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_repl_helpers.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_runtime_context_prompt_injection.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_smoke.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_spike_capability_state_1971.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_token_expiry.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_tool_observability_middleware.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_tracing_kpi_content_confinement.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/tests/test_url_helpers.py +0 -0
- {fred_runtime-3.3.3 → fred_runtime-3.3.5}/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.
|
|
3
|
+
Version: 3.3.5
|
|
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
|
|
@@ -13,14 +13,14 @@ Classifier: Operating System :: OS Independent
|
|
|
13
13
|
Requires-Python: <3.13,>=3.12
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
15
|
Requires-Dist: fred-core>=3.4.1
|
|
16
|
-
Requires-Dist: fred-sdk>=3.3.
|
|
16
|
+
Requires-Dist: fred-sdk>=3.3.3
|
|
17
17
|
Requires-Dist: alembic>=1.18.4
|
|
18
|
-
Requires-Dist: deepagents>=0.
|
|
18
|
+
Requires-Dist: deepagents>=0.6.12
|
|
19
19
|
Requires-Dist: httpx>=0.28.1
|
|
20
20
|
Requires-Dist: langchain-mcp-adapters>=0.2.1
|
|
21
|
-
Requires-Dist: langchain>=
|
|
21
|
+
Requires-Dist: langchain>=1.3.14
|
|
22
22
|
Requires-Dist: langchain-core>=0.3.0
|
|
23
|
-
Requires-Dist: langgraph>=1.
|
|
23
|
+
Requires-Dist: langgraph>=1.2.9
|
|
24
24
|
Requires-Dist: langfuse>=3.0.0
|
|
25
25
|
Requires-Dist: requests<3,>=2.32
|
|
26
26
|
Requires-Dist: PyYAML>=6.0
|
|
@@ -3419,13 +3419,12 @@ def _build_agent_router(
|
|
|
3419
3419
|
|
|
3420
3420
|
@router.delete(
|
|
3421
3421
|
"/checkpoints/{session_id}",
|
|
3422
|
-
status_code=status.
|
|
3423
|
-
response_model=None,
|
|
3422
|
+
status_code=status.HTTP_200_OK,
|
|
3424
3423
|
)
|
|
3425
3424
|
async def delete_checkpoint_thread(
|
|
3426
3425
|
session_id: str,
|
|
3427
3426
|
caller: KeycloakUser | None = Depends(_authenticated_user),
|
|
3428
|
-
) ->
|
|
3427
|
+
) -> dict[str, int]:
|
|
3429
3428
|
"""
|
|
3430
3429
|
Purge all checkpoint data for one session.
|
|
3431
3430
|
|
|
@@ -3438,7 +3437,8 @@ def _build_agent_router(
|
|
|
3438
3437
|
History store rows are NOT deleted — use DELETE /sessions/{session_id}
|
|
3439
3438
|
to remove those separately.
|
|
3440
3439
|
|
|
3441
|
-
Returns
|
|
3440
|
+
Returns {"deleted": n} (n = checkpoint rows removed) on success,
|
|
3441
|
+
403 when not owned, 503 when no checkpointer.
|
|
3442
3442
|
"""
|
|
3443
3443
|
caller_uid = caller.uid if caller is not None else None
|
|
3444
3444
|
history_store = _get_history_store_for_owned_access(caller)
|
|
@@ -3453,7 +3453,8 @@ def _build_agent_router(
|
|
|
3453
3453
|
detail="Access denied.",
|
|
3454
3454
|
)
|
|
3455
3455
|
cp = _get_checkpointer()
|
|
3456
|
-
await cp.adelete_thread(session_id)
|
|
3456
|
+
deleted = await cp.adelete_thread(session_id)
|
|
3457
|
+
return {"deleted": deleted}
|
|
3457
3458
|
|
|
3458
3459
|
@router.post(
|
|
3459
3460
|
"/execute",
|
{fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/capabilities/document_access/capability.py
RENAMED
|
@@ -62,6 +62,10 @@ from __future__ import annotations
|
|
|
62
62
|
import json
|
|
63
63
|
from collections.abc import Sequence
|
|
64
64
|
|
|
65
|
+
from fred_core.store.vector_search import (
|
|
66
|
+
DEFAULT_MIN_SOURCE_SCORE_RATIO,
|
|
67
|
+
select_citable_sources,
|
|
68
|
+
)
|
|
65
69
|
from fred_sdk.contracts.capability import (
|
|
66
70
|
AgentCapability,
|
|
67
71
|
CapabilityContext,
|
|
@@ -78,7 +82,7 @@ from fred_sdk.contracts.models import FieldSpec
|
|
|
78
82
|
from fred_sdk.contracts.runtime import DocumentSearchResult
|
|
79
83
|
from langchain.agents.middleware import AgentMiddleware
|
|
80
84
|
from langchain_core.tools import BaseTool, tool
|
|
81
|
-
from pydantic import BaseModel
|
|
85
|
+
from pydantic import BaseModel, Field
|
|
82
86
|
|
|
83
87
|
# The tool-result `tool_ref` this capability stamps on its artifact — distinct
|
|
84
88
|
# from the builtin `knowledge.search` ref so the two paths stay traceable apart.
|
|
@@ -131,6 +135,9 @@ class DocumentAccessConfig(BaseModel):
|
|
|
131
135
|
list means "no capability-side narrowing at this level" (the session binding
|
|
132
136
|
still bounds it). `default_top_k` and `search_policy` set retrieval
|
|
133
137
|
defaults; `show_document_scope_control` toggles the computed chat control.
|
|
138
|
+
`min_source_score_ratio` bounds what's citable as a "source" in the chat
|
|
139
|
+
UI (RAG-DATASET-DISCOVERY-RFC.md §7) — it never narrows what the model
|
|
140
|
+
itself sees, only what a human is shown as evidence.
|
|
134
141
|
"""
|
|
135
142
|
|
|
136
143
|
library_tag_ids: list[str] = []
|
|
@@ -138,6 +145,16 @@ class DocumentAccessConfig(BaseModel):
|
|
|
138
145
|
default_top_k: int = 8
|
|
139
146
|
search_policy: str | None = None
|
|
140
147
|
show_document_scope_control: bool = True
|
|
148
|
+
min_source_score_ratio: float = Field(
|
|
149
|
+
default=DEFAULT_MIN_SOURCE_SCORE_RATIO,
|
|
150
|
+
ge=0.0,
|
|
151
|
+
le=1.0,
|
|
152
|
+
description=(
|
|
153
|
+
"A hit must score at least this fraction of the best hit in the "
|
|
154
|
+
"same search call to be citable as a source. Does not affect what "
|
|
155
|
+
"the model itself can read — only the human-facing Sources panel."
|
|
156
|
+
),
|
|
157
|
+
)
|
|
141
158
|
|
|
142
159
|
|
|
143
160
|
class DocumentAccessTurnOptions(BaseModel):
|
|
@@ -189,6 +206,7 @@ class _DocumentAccessMiddleware(AgentMiddleware):
|
|
|
189
206
|
)
|
|
190
207
|
default_top_k = config.default_top_k if config.default_top_k > 0 else 8
|
|
191
208
|
search_policy = config.search_policy
|
|
209
|
+
min_source_score_ratio = config.min_source_score_ratio
|
|
192
210
|
|
|
193
211
|
@tool(
|
|
194
212
|
"search_documents_using_vectorization",
|
|
@@ -206,6 +224,10 @@ class _DocumentAccessMiddleware(AgentMiddleware):
|
|
|
206
224
|
conversational exchanges (greetings, thanks, clarifying what was just
|
|
207
225
|
said).
|
|
208
226
|
|
|
227
|
+
Covers prose/text documents. If a hit describes a structured/tabular
|
|
228
|
+
dataset (a "dataset pointer"), do not answer from it directly — pivot
|
|
229
|
+
to the tabular/SQL tool it names instead.
|
|
230
|
+
|
|
209
231
|
Returns ranked hits with title and content. Only use information
|
|
210
232
|
actually present in the returned hits; never invent facts beyond
|
|
211
233
|
them.
|
|
@@ -241,13 +263,19 @@ class _DocumentAccessMiddleware(AgentMiddleware):
|
|
|
241
263
|
for hit in hits
|
|
242
264
|
],
|
|
243
265
|
}
|
|
244
|
-
# `blocks` feed the LLM the hit
|
|
245
|
-
#
|
|
246
|
-
# sources
|
|
266
|
+
# `blocks` feed the LLM the full hit set (the model needs to see a
|
|
267
|
+
# dataset pointer to know to pivot to the tabular tool, and every
|
|
268
|
+
# hit to reason with) — `sources` (the chat Sources panel) is
|
|
269
|
+
# narrowed separately: never a pointer chunk (no real content to
|
|
270
|
+
# cite), and never a hit that's noise relative to the best match
|
|
271
|
+
# in this call (found live citing near-zero-relevance paragraphs
|
|
272
|
+
# from an unrelated document, RAG-DATASET-DISCOVERY-RFC.md §7).
|
|
247
273
|
artifact = ToolInvocationResult(
|
|
248
274
|
tool_ref=DOCUMENT_ACCESS_TOOL_REF,
|
|
249
275
|
blocks=(ToolContentBlock(kind=ToolContentKind.JSON, data=content),),
|
|
250
|
-
sources=
|
|
276
|
+
sources=select_citable_sources(
|
|
277
|
+
hits, min_score_ratio=min_source_score_ratio
|
|
278
|
+
),
|
|
251
279
|
)
|
|
252
280
|
return json.dumps(content), artifact
|
|
253
281
|
|
|
@@ -316,6 +344,20 @@ class DocumentAccessCapability(
|
|
|
316
344
|
),
|
|
317
345
|
default=True,
|
|
318
346
|
),
|
|
347
|
+
FieldSpec(
|
|
348
|
+
key="min_source_score_ratio",
|
|
349
|
+
type="number",
|
|
350
|
+
title="Minimum source relevance ratio",
|
|
351
|
+
description=(
|
|
352
|
+
"A hit must score at least this fraction of the best hit "
|
|
353
|
+
"in the same search to be shown as a cited source. Only "
|
|
354
|
+
"affects the human-facing Sources panel, never what the "
|
|
355
|
+
"model itself can read."
|
|
356
|
+
),
|
|
357
|
+
default=DEFAULT_MIN_SOURCE_SCORE_RATIO,
|
|
358
|
+
min=0.0,
|
|
359
|
+
max=1.0,
|
|
360
|
+
),
|
|
319
361
|
],
|
|
320
362
|
# No new chat part / side panel / router / owned table — the pilot's
|
|
321
363
|
# smallest real surface (RFC §10). team_scope=default_on: baseline
|
|
@@ -265,10 +265,11 @@ class AgentPodClient:
|
|
|
265
265
|
response.raise_for_status()
|
|
266
266
|
return int(response.json().get("deleted", 0))
|
|
267
267
|
|
|
268
|
-
def delete_checkpoint(self, session_id: str) ->
|
|
268
|
+
def delete_checkpoint(self, session_id: str) -> int:
|
|
269
269
|
url = f"{self.base_url}/agents/checkpoints/{session_id}"
|
|
270
270
|
response = self.http_client.delete(url, headers=self._auth_headers())
|
|
271
271
|
response.raise_for_status()
|
|
272
|
+
return int(response.json().get("deleted", 0))
|
|
272
273
|
|
|
273
274
|
def get_checkpoint_stats(self) -> dict[str, Any]:
|
|
274
275
|
url = f"{self.base_url}/agents/checkpoints/_stats"
|
{fred_runtime-3.3.3 → fred_runtime-3.3.5}/fred_runtime/integrations/kf_vector_search/toolkit.py
RENAMED
|
@@ -49,7 +49,7 @@ from typing import Sequence
|
|
|
49
49
|
|
|
50
50
|
from fred_core.common import OwnerFilter
|
|
51
51
|
from fred_core.common.team_id import is_personal_team_id
|
|
52
|
-
from fred_core.store.vector_search import VectorSearchHit
|
|
52
|
+
from fred_core.store.vector_search import VectorSearchHit, select_citable_sources
|
|
53
53
|
from fred_sdk.contracts.context import (
|
|
54
54
|
ToolContentBlock,
|
|
55
55
|
ToolContentKind,
|
|
@@ -168,7 +168,10 @@ class KfVectorSearchToolkit:
|
|
|
168
168
|
# Return a ToolInvocationResult directly: the runtime-provider resolver
|
|
169
169
|
# invokes this tool with a plain args dict, so a content_and_artifact
|
|
170
170
|
# tuple would lose the artifact (and its sources). `blocks` feed the LLM
|
|
171
|
-
# the hit
|
|
171
|
+
# the full hit set; `sources` (the Sources panel) is narrowed separately —
|
|
172
|
+
# never a dataset pointer chunk, never a hit that's noise relative to the
|
|
173
|
+
# best match in this call (RAG-DATASET-DISCOVERY-RFC.md §7). Mirrors
|
|
174
|
+
# `_invoke_knowledge_search`'s use of the same shared default ratio.
|
|
172
175
|
return ToolInvocationResult(
|
|
173
176
|
tool_ref=KF_VECTOR_SEARCH_PROVIDER,
|
|
174
177
|
blocks=(
|
|
@@ -180,7 +183,7 @@ class KfVectorSearchToolkit:
|
|
|
180
183
|
},
|
|
181
184
|
),
|
|
182
185
|
),
|
|
183
|
-
sources=
|
|
186
|
+
sources=select_citable_sources(hits),
|
|
184
187
|
)
|
|
185
188
|
|
|
186
189
|
return [search_documents_using_vectorization]
|
|
@@ -48,7 +48,7 @@ from fred_core.kpi.base_kpi_writer import BaseKPIWriter
|
|
|
48
48
|
from fred_core.kpi.kpi_writer_structures import KPIActor
|
|
49
49
|
from fred_core.portable import LoggingTracer, MetricsProvider, Tracer, get_tracer
|
|
50
50
|
from fred_core.security.oidc import get_keycloak_client_id, get_keycloak_url
|
|
51
|
-
from fred_core.store.vector_search import VectorSearchHit
|
|
51
|
+
from fred_core.store.vector_search import VectorSearchHit, select_citable_sources
|
|
52
52
|
from fred_sdk.contracts.context import (
|
|
53
53
|
BoundRuntimeContext,
|
|
54
54
|
FsEntry,
|
|
@@ -514,6 +514,13 @@ class FredKnowledgeSearchToolInvoker(ToolInvokerPort):
|
|
|
514
514
|
k: v for k, v in hit.model_dump(mode="json").items() if k in _LLM_FIELDS
|
|
515
515
|
}
|
|
516
516
|
|
|
517
|
+
# `sources` is narrowed separately from the model-visible content: never
|
|
518
|
+
# a dataset pointer chunk (no real content to cite), and never a hit
|
|
519
|
+
# that's noise relative to the best match in this call
|
|
520
|
+
# (RAG-DATASET-DISCOVERY-RFC.md §7). This builtin tool predates
|
|
521
|
+
# capability config fields, so unlike document_access's
|
|
522
|
+
# `min_source_score_ratio`, the ratio here is the shared default, not
|
|
523
|
+
# yet independently tunable per agent instance.
|
|
517
524
|
return ToolInvocationResult(
|
|
518
525
|
tool_ref=request.tool_ref,
|
|
519
526
|
blocks=(
|
|
@@ -525,7 +532,7 @@ class FredKnowledgeSearchToolInvoker(ToolInvokerPort):
|
|
|
525
532
|
},
|
|
526
533
|
),
|
|
527
534
|
),
|
|
528
|
-
sources=
|
|
535
|
+
sources=select_citable_sources(hits),
|
|
529
536
|
)
|
|
530
537
|
|
|
531
538
|
async def _invoke_traces_summarize_conversation(
|
|
@@ -33,6 +33,7 @@ open.
|
|
|
33
33
|
from __future__ import annotations
|
|
34
34
|
|
|
35
35
|
import logging
|
|
36
|
+
import time
|
|
36
37
|
import uuid
|
|
37
38
|
from collections.abc import AsyncIterator, Sequence
|
|
38
39
|
from contextlib import nullcontext
|
|
@@ -219,6 +220,11 @@ def _tool_thought_title(tool_name: str) -> str:
|
|
|
219
220
|
return f"Calling {readable}"
|
|
220
221
|
|
|
221
222
|
|
|
223
|
+
def _elapsed_ms_since(started_at: float) -> int:
|
|
224
|
+
"""Return the whole-millisecond duration elapsed since a monotonic timestamp."""
|
|
225
|
+
return int((time.monotonic() - started_at) * 1000)
|
|
226
|
+
|
|
227
|
+
|
|
222
228
|
class _TransportBackedReActExecutor(Executor[ReActInput, ReActOutput]):
|
|
223
229
|
"""
|
|
224
230
|
Executes one ReAct run against the compiled LangChain/LangGraph agent.
|
|
@@ -341,11 +347,15 @@ class _TransportBackedReActExecutor(Executor[ReActInput, ReActOutput]):
|
|
|
341
347
|
# Maps tool call_id → thought_id so THOUGHT_END can close the block
|
|
342
348
|
# opened by THOUGHT_START before the corresponding TOOL_CALL event.
|
|
343
349
|
active_thought_ids: dict[str, str] = {}
|
|
350
|
+
# Maps tool call_id → monotonic start time so THOUGHT_END can report a
|
|
351
|
+
# real `duration_ms` instead of leaving the field permanently unset.
|
|
352
|
+
active_thought_started_at: dict[str, float] = {}
|
|
344
353
|
# Open model-native reasoning block (RUNTIME-05 Layer 2b). When a provider
|
|
345
354
|
# streams reasoning chunks (Mistral ThinkChunk, Claude thinking), they are
|
|
346
355
|
# promoted to a single THOUGHT block with source="model_native" that must be
|
|
347
356
|
# closed before the first answer delta and before the run ends.
|
|
348
357
|
model_native_thought_id: str | None = None
|
|
358
|
+
model_native_thought_started_at: float | None = None
|
|
349
359
|
phase_timer_ctx.__enter__()
|
|
350
360
|
try:
|
|
351
361
|
async for raw_event in self._compiled_agent.astream(
|
|
@@ -372,6 +382,7 @@ class _TransportBackedReActExecutor(Executor[ReActInput, ReActOutput]):
|
|
|
372
382
|
for fragment in decoded.thought_fragments:
|
|
373
383
|
if model_native_thought_id is None:
|
|
374
384
|
model_native_thought_id = uuid.uuid4().hex
|
|
385
|
+
model_native_thought_started_at = time.monotonic()
|
|
375
386
|
yield ThoughtStartEvent(
|
|
376
387
|
sequence=sequence,
|
|
377
388
|
thought_id=model_native_thought_id,
|
|
@@ -393,9 +404,15 @@ class _TransportBackedReActExecutor(Executor[ReActInput, ReActOutput]):
|
|
|
393
404
|
yield ThoughtEndEvent(
|
|
394
405
|
sequence=sequence,
|
|
395
406
|
thought_id=model_native_thought_id,
|
|
407
|
+
duration_ms=_elapsed_ms_since(
|
|
408
|
+
model_native_thought_started_at
|
|
409
|
+
)
|
|
410
|
+
if model_native_thought_started_at is not None
|
|
411
|
+
else None,
|
|
396
412
|
)
|
|
397
413
|
sequence += 1
|
|
398
414
|
model_native_thought_id = None
|
|
415
|
+
model_native_thought_started_at = None
|
|
399
416
|
if not suppress_assistant_deltas:
|
|
400
417
|
yield AssistantDeltaRuntimeEvent(
|
|
401
418
|
sequence=sequence,
|
|
@@ -448,11 +465,17 @@ class _TransportBackedReActExecutor(Executor[ReActInput, ReActOutput]):
|
|
|
448
465
|
)
|
|
449
466
|
sequence += 1
|
|
450
467
|
thought_id = active_thought_ids.pop(message.tool_call_id, None)
|
|
468
|
+
thought_started_at = active_thought_started_at.pop(
|
|
469
|
+
message.tool_call_id, None
|
|
470
|
+
)
|
|
451
471
|
if thought_id:
|
|
452
472
|
yield ThoughtEndEvent(
|
|
453
473
|
sequence=sequence,
|
|
454
474
|
thought_id=thought_id,
|
|
455
475
|
conclusion="Error" if is_error else "Done",
|
|
476
|
+
duration_ms=_elapsed_ms_since(thought_started_at)
|
|
477
|
+
if thought_started_at is not None
|
|
478
|
+
else None,
|
|
456
479
|
)
|
|
457
480
|
sequence += 1
|
|
458
481
|
continue
|
|
@@ -463,6 +486,7 @@ class _TransportBackedReActExecutor(Executor[ReActInput, ReActOutput]):
|
|
|
463
486
|
call_id = str(tool_call.get("id") or "")
|
|
464
487
|
thought_id = uuid.uuid4().hex
|
|
465
488
|
active_thought_ids[call_id] = thought_id
|
|
489
|
+
active_thought_started_at[call_id] = time.monotonic()
|
|
466
490
|
yield ThoughtStartEvent(
|
|
467
491
|
sequence=sequence,
|
|
468
492
|
thought_id=thought_id,
|
|
@@ -502,9 +526,13 @@ class _TransportBackedReActExecutor(Executor[ReActInput, ReActOutput]):
|
|
|
502
526
|
yield ThoughtEndEvent(
|
|
503
527
|
sequence=sequence,
|
|
504
528
|
thought_id=model_native_thought_id,
|
|
529
|
+
duration_ms=_elapsed_ms_since(model_native_thought_started_at)
|
|
530
|
+
if model_native_thought_started_at is not None
|
|
531
|
+
else None,
|
|
505
532
|
)
|
|
506
533
|
sequence += 1
|
|
507
534
|
model_native_thought_id = None
|
|
535
|
+
model_native_thought_started_at = None
|
|
508
536
|
|
|
509
537
|
if last_tool_error is not None or last_assistant_message is not None:
|
|
510
538
|
final_content = (
|
|
@@ -532,14 +560,18 @@ class _TransportBackedReActExecutor(Executor[ReActInput, ReActOutput]):
|
|
|
532
560
|
is_error=True,
|
|
533
561
|
)
|
|
534
562
|
sequence += 1
|
|
563
|
+
thought_started_at = active_thought_started_at.get(call_id)
|
|
535
564
|
yield ThoughtEndEvent(
|
|
536
565
|
sequence=sequence,
|
|
537
566
|
thought_id=thought_id,
|
|
538
567
|
conclusion="Error",
|
|
539
|
-
duration_ms=
|
|
568
|
+
duration_ms=_elapsed_ms_since(thought_started_at)
|
|
569
|
+
if thought_started_at is not None
|
|
570
|
+
else None,
|
|
540
571
|
)
|
|
541
572
|
sequence += 1
|
|
542
573
|
active_thought_ids.clear()
|
|
574
|
+
active_thought_started_at.clear()
|
|
543
575
|
# Close an open model-native reasoning block so its accordion does not
|
|
544
576
|
# spin forever after a mid-stream failure.
|
|
545
577
|
if model_native_thought_id is not None:
|
|
@@ -547,6 +579,9 @@ class _TransportBackedReActExecutor(Executor[ReActInput, ReActOutput]):
|
|
|
547
579
|
sequence=sequence,
|
|
548
580
|
thought_id=model_native_thought_id,
|
|
549
581
|
conclusion="Error",
|
|
582
|
+
duration_ms=_elapsed_ms_since(model_native_thought_started_at)
|
|
583
|
+
if model_native_thought_started_at is not None
|
|
584
|
+
else None,
|
|
550
585
|
)
|
|
551
586
|
sequence += 1
|
|
552
587
|
raise
|
|
@@ -589,7 +589,21 @@ class FredSqlCheckpointer(BaseCheckpointSaver[str]):
|
|
|
589
589
|
def delete_thread(self, thread_id: str) -> None: # type: ignore[override]
|
|
590
590
|
raise _sync_checkpointer_error("delete_thread")
|
|
591
591
|
|
|
592
|
-
async def adelete_thread(self, thread_id: str) ->
|
|
592
|
+
async def adelete_thread(self, thread_id: str) -> int: # type: ignore[override]
|
|
593
|
+
"""
|
|
594
|
+
Delete all checkpoint rows for one thread and return the checkpoint count.
|
|
595
|
+
|
|
596
|
+
Why this exists:
|
|
597
|
+
- Callers erasing a conversation (CTRLP-12) need an honest deleted-row
|
|
598
|
+
count for their receipt, matching the sibling history-store delete
|
|
599
|
+
which already returns one. Before this, callers had no way to know
|
|
600
|
+
how much was actually purged.
|
|
601
|
+
- The returned count is the number of rows removed from
|
|
602
|
+
`checkpoints_table` (one row per turn) — the same unit the
|
|
603
|
+
`/agents/checkpoints/{session_id}` listing endpoint reports elsewhere.
|
|
604
|
+
`writes`/`blobs`/`thread_owner` rows are still purged but are
|
|
605
|
+
implementation detail, not a separately meaningful count.
|
|
606
|
+
"""
|
|
593
607
|
async with self.phase("v2_checkpoint_delete_thread"):
|
|
594
608
|
await self._ensure_tables()
|
|
595
609
|
async with self.store.begin() as conn:
|
|
@@ -598,7 +612,7 @@ class FredSqlCheckpointer(BaseCheckpointSaver[str]):
|
|
|
598
612
|
self.writes_table.c.thread_id == thread_id
|
|
599
613
|
)
|
|
600
614
|
)
|
|
601
|
-
await conn.execute(
|
|
615
|
+
result = await conn.execute(
|
|
602
616
|
delete(self.checkpoints_table).where(
|
|
603
617
|
self.checkpoints_table.c.thread_id == thread_id
|
|
604
618
|
)
|
|
@@ -616,6 +630,7 @@ class FredSqlCheckpointer(BaseCheckpointSaver[str]):
|
|
|
616
630
|
self.thread_owner_table.c.thread_id == thread_id
|
|
617
631
|
)
|
|
618
632
|
)
|
|
633
|
+
return result.rowcount or 0
|
|
619
634
|
|
|
620
635
|
# ------------------------- owner / age index (CTRLP-12 A4) -------------------------
|
|
621
636
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fred-runtime
|
|
3
|
-
Version: 3.3.
|
|
3
|
+
Version: 3.3.5
|
|
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
|
|
@@ -13,14 +13,14 @@ Classifier: Operating System :: OS Independent
|
|
|
13
13
|
Requires-Python: <3.13,>=3.12
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
15
|
Requires-Dist: fred-core>=3.4.1
|
|
16
|
-
Requires-Dist: fred-sdk>=3.3.
|
|
16
|
+
Requires-Dist: fred-sdk>=3.3.3
|
|
17
17
|
Requires-Dist: alembic>=1.18.4
|
|
18
|
-
Requires-Dist: deepagents>=0.
|
|
18
|
+
Requires-Dist: deepagents>=0.6.12
|
|
19
19
|
Requires-Dist: httpx>=0.28.1
|
|
20
20
|
Requires-Dist: langchain-mcp-adapters>=0.2.1
|
|
21
|
-
Requires-Dist: langchain>=
|
|
21
|
+
Requires-Dist: langchain>=1.3.14
|
|
22
22
|
Requires-Dist: langchain-core>=0.3.0
|
|
23
|
-
Requires-Dist: langgraph>=1.
|
|
23
|
+
Requires-Dist: langgraph>=1.2.9
|
|
24
24
|
Requires-Dist: langfuse>=3.0.0
|
|
25
25
|
Requires-Dist: requests<3,>=2.32
|
|
26
26
|
Requires-Dist: PyYAML>=6.0
|
|
@@ -129,6 +129,7 @@ tests/test_graph_runtime_observability.py
|
|
|
129
129
|
tests/test_history.py
|
|
130
130
|
tests/test_kf_vector_search_toolkit.py
|
|
131
131
|
tests/test_kf_workspace_client.py
|
|
132
|
+
tests/test_knowledge_search_tool_invoker_sources.py
|
|
132
133
|
tests/test_kpi_display.py
|
|
133
134
|
tests/test_mcp_config.py
|
|
134
135
|
tests/test_model_routing.py
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
fred-core>=3.4.1
|
|
2
|
-
fred-sdk>=3.3.
|
|
2
|
+
fred-sdk>=3.3.3
|
|
3
3
|
alembic>=1.18.4
|
|
4
|
-
deepagents>=0.
|
|
4
|
+
deepagents>=0.6.12
|
|
5
5
|
httpx>=0.28.1
|
|
6
6
|
langchain-mcp-adapters>=0.2.1
|
|
7
|
-
langchain>=
|
|
7
|
+
langchain>=1.3.14
|
|
8
8
|
langchain-core>=0.3.0
|
|
9
|
-
langgraph>=1.
|
|
9
|
+
langgraph>=1.2.9
|
|
10
10
|
langfuse>=3.0.0
|
|
11
11
|
requests<3,>=2.32
|
|
12
12
|
PyYAML>=6.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fred-runtime"
|
|
3
|
-
version = "3.3.
|
|
3
|
+
version = "3.3.5"
|
|
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"
|
|
@@ -8,14 +8,14 @@ license = { text = "Apache-2.0" }
|
|
|
8
8
|
authors = [{ name = "Thales", email = "noreply@thalesgroup.com" }]
|
|
9
9
|
dependencies = [
|
|
10
10
|
"fred-core>=3.4.1",
|
|
11
|
-
"fred-sdk>=3.3.
|
|
11
|
+
"fred-sdk>=3.3.3",
|
|
12
12
|
"alembic>=1.18.4",
|
|
13
|
-
"deepagents>=0.
|
|
13
|
+
"deepagents>=0.6.12",
|
|
14
14
|
"httpx>=0.28.1",
|
|
15
15
|
"langchain-mcp-adapters>=0.2.1",
|
|
16
|
-
"langchain>=
|
|
16
|
+
"langchain>=1.3.14",
|
|
17
17
|
"langchain-core>=0.3.0",
|
|
18
|
-
"langgraph>=1.
|
|
18
|
+
"langgraph>=1.2.9",
|
|
19
19
|
"langfuse>=3.0.0",
|
|
20
20
|
"requests>=2.32,<3",
|
|
21
21
|
"PyYAML>=6.0",
|
|
@@ -366,6 +366,86 @@ def test_create_agent_app_executes_local_authored_tools_and_honors_base_url(
|
|
|
366
366
|
assert payloads[-1]["content"] == "Echo complete."
|
|
367
367
|
|
|
368
368
|
|
|
369
|
+
def test_delete_checkpoint_thread_returns_deleted_count(monkeypatch, tmp_path) -> None:
|
|
370
|
+
"""
|
|
371
|
+
`DELETE /agents/checkpoints/{session_id}` must report how many checkpoint
|
|
372
|
+
rows it actually purged, mirroring the sibling `DELETE /agents/sessions/{id}`
|
|
373
|
+
(history) endpoint's `{"deleted": n}` body.
|
|
374
|
+
|
|
375
|
+
Why this exists:
|
|
376
|
+
- before this fix the endpoint returned a bare 204 with no body, so
|
|
377
|
+
`ConversationErasureService` (control-plane, CTRLP-12) could not report a
|
|
378
|
+
real `deleted_count` for the `runtime_checkpoint` store in its erase
|
|
379
|
+
receipt — every erasure looked identical whether it purged one
|
|
380
|
+
checkpoint or a hundred.
|
|
381
|
+
|
|
382
|
+
How to use it:
|
|
383
|
+
- run via the default offline `make test` suite in `fred-runtime`
|
|
384
|
+
"""
|
|
385
|
+
|
|
386
|
+
model = ToolFriendlyFakeChatModel(
|
|
387
|
+
responses=[
|
|
388
|
+
AIMessage(
|
|
389
|
+
content="",
|
|
390
|
+
tool_calls=[
|
|
391
|
+
{
|
|
392
|
+
"id": "call-echo-1",
|
|
393
|
+
"name": "demo_echo",
|
|
394
|
+
"args": {"text": "hello"},
|
|
395
|
+
}
|
|
396
|
+
],
|
|
397
|
+
),
|
|
398
|
+
AIMessage(content="Echo complete."),
|
|
399
|
+
]
|
|
400
|
+
)
|
|
401
|
+
monkeypatch.setattr(
|
|
402
|
+
agent_app_module,
|
|
403
|
+
"_build_chat_model_factory",
|
|
404
|
+
lambda config: StaticChatModelFactory(model),
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
definition = _EchoAgent()
|
|
408
|
+
registry: dict[str, ReActAgentDefinition] = {definition.agent_id: definition}
|
|
409
|
+
app = create_agent_app(registry=registry, config=_build_test_config(tmp_path))
|
|
410
|
+
|
|
411
|
+
with TestClient(app) as client:
|
|
412
|
+
execute_response = client.post(
|
|
413
|
+
"/pod/v1/agents/execute",
|
|
414
|
+
json={
|
|
415
|
+
"agent_id": "rags.sample.echo",
|
|
416
|
+
"input": "hello",
|
|
417
|
+
"session_id": "session-checkpoint-delete",
|
|
418
|
+
"runtime_context": {"user_id": "alice"},
|
|
419
|
+
},
|
|
420
|
+
)
|
|
421
|
+
assert execute_response.status_code == 200
|
|
422
|
+
|
|
423
|
+
threads_before = client.get(
|
|
424
|
+
"/pod/v1/agents/checkpoints/session-checkpoint-delete"
|
|
425
|
+
)
|
|
426
|
+
assert threads_before.status_code == 200
|
|
427
|
+
assert len(threads_before.json()["checkpoints"]) > 0
|
|
428
|
+
|
|
429
|
+
delete_response = client.delete(
|
|
430
|
+
"/pod/v1/agents/checkpoints/session-checkpoint-delete"
|
|
431
|
+
)
|
|
432
|
+
assert delete_response.status_code == 200
|
|
433
|
+
deleted = delete_response.json()["deleted"]
|
|
434
|
+
assert deleted > 0
|
|
435
|
+
|
|
436
|
+
threads_after = client.get(
|
|
437
|
+
"/pod/v1/agents/checkpoints/session-checkpoint-delete"
|
|
438
|
+
)
|
|
439
|
+
assert threads_after.json()["checkpoints"] == []
|
|
440
|
+
|
|
441
|
+
# Idempotent: a retry against an already-purged thread deletes nothing.
|
|
442
|
+
retry_response = client.delete(
|
|
443
|
+
"/pod/v1/agents/checkpoints/session-checkpoint-delete"
|
|
444
|
+
)
|
|
445
|
+
assert retry_response.status_code == 200
|
|
446
|
+
assert retry_response.json() == {"deleted": 0}
|
|
447
|
+
|
|
448
|
+
|
|
369
449
|
class _ContextPromptAgent(ReActAgent):
|
|
370
450
|
"""Tiny agent that surfaces the bound context_prompt_text through a tool."""
|
|
371
451
|
|