deepagents-graph-memory 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- deepagents_graph_memory/__init__.py +20 -0
- deepagents_graph_memory/backend.py +838 -0
- deepagents_graph_memory/errors.py +21 -0
- deepagents_graph_memory/ladybug_store.py +847 -0
- deepagents_graph_memory/paths.py +253 -0
- deepagents_graph_memory/py.typed +1 -0
- deepagents_graph_memory/recall.py +936 -0
- deepagents_graph_memory/renderers.py +199 -0
- deepagents_graph_memory/stores.py +378 -0
- deepagents_graph_memory/tools.py +193 -0
- deepagents_graph_memory/vgs.py +210 -0
- deepagents_graph_memory-0.1.0.dist-info/METADATA +770 -0
- deepagents_graph_memory-0.1.0.dist-info/RECORD +16 -0
- deepagents_graph_memory-0.1.0.dist-info/WHEEL +5 -0
- deepagents_graph_memory-0.1.0.dist-info/licenses/LICENSE +21 -0
- deepagents_graph_memory-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# - Gives the agent callable tools to find context and record what happened during its work.
|
|
2
|
+
# - Can also offer tools for adding individual graph items and connections.
|
|
3
|
+
# - Tests: test_tools.py checks which tools are available and that writes work;
|
|
4
|
+
# test_trace.py and test_recall.py check the history and lookup tools.
|
|
5
|
+
|
|
6
|
+
"""Safe graph memory tools."""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
from collections.abc import Sequence
|
|
12
|
+
from typing import Any, Literal
|
|
13
|
+
|
|
14
|
+
from langchain.tools import ToolRuntime
|
|
15
|
+
from langchain_core.tools import tool
|
|
16
|
+
|
|
17
|
+
from deepagents_graph_memory.backend import GraphMemoryBackend
|
|
18
|
+
from deepagents_graph_memory.errors import GraphMemoryError, GraphMemoryValidationError
|
|
19
|
+
from deepagents_graph_memory.paths import validate_subject
|
|
20
|
+
from deepagents_graph_memory.recall import RecallMode
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def graph_memory_tools(graph_backend: GraphMemoryBackend, *, include_low_level_writes: bool = False, bound_subject: str | None = None) -> list[Any]:
|
|
24
|
+
"""Create safe graph memory recall and write tools.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
graph_backend: Graph memory backend to mutate.
|
|
28
|
+
include_low_level_writes: When true, expose generic node, edge, and
|
|
29
|
+
graph-document write tools. Keep this false for unconstrained agent
|
|
30
|
+
use; prefer domain-specific tools or `record_graph_trace`.
|
|
31
|
+
bound_subject: Optional subject all trace writes from these tools must use.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
LangChain tool objects for controlled graph writes.
|
|
35
|
+
"""
|
|
36
|
+
if bound_subject is not None:
|
|
37
|
+
bound_subject = validate_subject(bound_subject)
|
|
38
|
+
|
|
39
|
+
@tool
|
|
40
|
+
def recall_graph_memory(
|
|
41
|
+
query: str,
|
|
42
|
+
anchors: list[str] | None = None,
|
|
43
|
+
mode: RecallMode = "auto",
|
|
44
|
+
token_budget: int = 2000,
|
|
45
|
+
max_depth: int = 3,
|
|
46
|
+
max_nodes: int = 50,
|
|
47
|
+
max_edges: int = 100,
|
|
48
|
+
) -> str:
|
|
49
|
+
"""Recall relevant graph memory: entities, relationships, prior reasoning traces, and connected context.
|
|
50
|
+
|
|
51
|
+
When reporting state changes from retrieved traces, distinguish what actually changed from what the agent did in response.
|
|
52
|
+
A switch in tool or action is not the same as the underlying condition changing.
|
|
53
|
+
If the same outcome recurs across multiple steps, the state changed once when it first appeared and stayed unchanged afterward.
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
return graph_backend.recall_graph_memory(
|
|
57
|
+
query,
|
|
58
|
+
anchors=anchors,
|
|
59
|
+
mode=mode,
|
|
60
|
+
token_budget=token_budget,
|
|
61
|
+
max_depth=max_depth,
|
|
62
|
+
max_nodes=max_nodes,
|
|
63
|
+
max_edges=max_edges,
|
|
64
|
+
)
|
|
65
|
+
except GraphMemoryError as exc:
|
|
66
|
+
return f"Error: {exc}"
|
|
67
|
+
|
|
68
|
+
@tool
|
|
69
|
+
def add_graph_node(label: str, node_id: str, properties: dict[str, Any] | None = None) -> str:
|
|
70
|
+
"""Add or update a graph node.
|
|
71
|
+
|
|
72
|
+
Use this for entity memory such as any named thing in the workflow's domain.
|
|
73
|
+
"""
|
|
74
|
+
try:
|
|
75
|
+
graph_backend.add_graph_node(label, node_id, properties, source="graph_memory_tool")
|
|
76
|
+
except GraphMemoryError as exc:
|
|
77
|
+
return f"Error: {exc}"
|
|
78
|
+
return f"Added graph node {label}/{node_id}."
|
|
79
|
+
|
|
80
|
+
@tool
|
|
81
|
+
def add_graph_edge(
|
|
82
|
+
source_label: str,
|
|
83
|
+
source_id: str,
|
|
84
|
+
relationship: str,
|
|
85
|
+
target_label: str,
|
|
86
|
+
target_id: str,
|
|
87
|
+
properties: dict[str, Any] | None = None,
|
|
88
|
+
) -> str:
|
|
89
|
+
"""Add or update a directed graph relationship.
|
|
90
|
+
|
|
91
|
+
Use this for relationship memory such as any directed connection between two entities.
|
|
92
|
+
"""
|
|
93
|
+
try:
|
|
94
|
+
graph_backend.add_graph_edge(
|
|
95
|
+
source_label,
|
|
96
|
+
source_id,
|
|
97
|
+
relationship,
|
|
98
|
+
target_label,
|
|
99
|
+
target_id,
|
|
100
|
+
properties,
|
|
101
|
+
source="graph_memory_tool",
|
|
102
|
+
)
|
|
103
|
+
except GraphMemoryError as exc:
|
|
104
|
+
return f"Error: {exc}"
|
|
105
|
+
return f"Added graph edge {source_label}/{source_id} -[{relationship}]-> {target_label}/{target_id}."
|
|
106
|
+
|
|
107
|
+
@tool
|
|
108
|
+
def add_graph_documents(documents: list[Any]) -> str:
|
|
109
|
+
"""Add LangChain graph documents to graph memory."""
|
|
110
|
+
try:
|
|
111
|
+
graph_backend.add_graph_documents(cast_documents(documents))
|
|
112
|
+
except GraphMemoryError as exc:
|
|
113
|
+
return f"Error: {exc}"
|
|
114
|
+
return f"Added {len(documents)} graph document(s)."
|
|
115
|
+
|
|
116
|
+
@tool
|
|
117
|
+
def record_graph_trace(
|
|
118
|
+
situation: str,
|
|
119
|
+
rationale: str,
|
|
120
|
+
action: str,
|
|
121
|
+
outcome: str,
|
|
122
|
+
artifacts: list[str] | None = None,
|
|
123
|
+
evidence: list[str] | None = None,
|
|
124
|
+
evidence_refs: list[dict[str, str]] | None = None,
|
|
125
|
+
run_id: str | None = None,
|
|
126
|
+
agent_id: str | None = None,
|
|
127
|
+
subagent_id: str | None = None,
|
|
128
|
+
task_id: str | None = None,
|
|
129
|
+
subject: str | None = None,
|
|
130
|
+
observed_at: str | None = None,
|
|
131
|
+
supersedes: list[str] | None = None,
|
|
132
|
+
resolves: list[str] | None = None,
|
|
133
|
+
depends_on: list[str] | None = None,
|
|
134
|
+
finding_type: Literal["state", "interpretation"] = "interpretation",
|
|
135
|
+
operation_id: str | None = None,
|
|
136
|
+
runtime: ToolRuntime = None, # This installed ToolNode injects ToolRuntime, but not ToolRuntime | None.
|
|
137
|
+
) -> str:
|
|
138
|
+
"""Record a Situation/Rationale/Action/Outcome trace for long-running agent work."""
|
|
139
|
+
try:
|
|
140
|
+
if bound_subject is not None:
|
|
141
|
+
if subject is not None and validate_subject(subject) != bound_subject:
|
|
142
|
+
raise GraphMemoryValidationError("subject differs from the bound subject.")
|
|
143
|
+
subject = bound_subject
|
|
144
|
+
if operation_id is None and runtime is not None and runtime.tool_call_id:
|
|
145
|
+
thread_id = runtime.config.get("configurable", {}).get("thread_id")
|
|
146
|
+
operation_id = json.dumps(["tool-call", thread_id, runtime.tool_call_id], separators=(",", ":"))
|
|
147
|
+
trace_id = graph_backend.record_graph_trace(
|
|
148
|
+
situation=situation,
|
|
149
|
+
rationale=rationale,
|
|
150
|
+
action=action,
|
|
151
|
+
outcome=outcome,
|
|
152
|
+
artifacts=artifacts,
|
|
153
|
+
evidence=evidence,
|
|
154
|
+
evidence_refs=evidence_refs,
|
|
155
|
+
run_id=run_id,
|
|
156
|
+
agent_id=agent_id,
|
|
157
|
+
subagent_id=subagent_id,
|
|
158
|
+
task_id=task_id,
|
|
159
|
+
subject=subject,
|
|
160
|
+
observed_at=observed_at,
|
|
161
|
+
supersedes=supersedes,
|
|
162
|
+
resolves=resolves,
|
|
163
|
+
depends_on=depends_on,
|
|
164
|
+
finding_type=finding_type,
|
|
165
|
+
operation_id=operation_id,
|
|
166
|
+
source="graph_trace_tool",
|
|
167
|
+
)
|
|
168
|
+
except GraphMemoryError as exc:
|
|
169
|
+
return f"Error: {exc}"
|
|
170
|
+
return f"Recorded graph trace {trace_id}."
|
|
171
|
+
|
|
172
|
+
tools = [recall_graph_memory, record_graph_trace]
|
|
173
|
+
if include_low_level_writes:
|
|
174
|
+
tools.extend([add_graph_node, add_graph_edge, add_graph_documents])
|
|
175
|
+
return tools
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def cast_documents(documents: Sequence[Any]) -> Sequence[Any]:
|
|
179
|
+
"""Validate that a graph document payload is list-like.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
documents: Graph documents.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
The original graph document sequence.
|
|
186
|
+
|
|
187
|
+
Raises:
|
|
188
|
+
GraphMemoryError: If the payload is not list-like.
|
|
189
|
+
"""
|
|
190
|
+
if not isinstance(documents, list):
|
|
191
|
+
msg = "documents must be a list."
|
|
192
|
+
raise GraphMemoryError(msg)
|
|
193
|
+
return documents
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# - Sets up Deep Agents to work through graph tools and explains how to use that context.
|
|
2
|
+
# - Adds graph guidance while preserving application-supplied instructions.
|
|
3
|
+
# - Tests: test_vgs.py and test_combined_context.py check prompt and tool behavior.
|
|
4
|
+
|
|
5
|
+
"""Graph context guidance for Deep Agents."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import Awaitable, Callable
|
|
10
|
+
from typing import Any, cast
|
|
11
|
+
|
|
12
|
+
from deepagents import HarnessProfile, register_harness_profile
|
|
13
|
+
from deepagents.middleware import filesystem
|
|
14
|
+
from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse
|
|
15
|
+
from langchain_core.messages import AIMessage, SystemMessage
|
|
16
|
+
|
|
17
|
+
VFS_TOOL_NAMES = frozenset({"ls", "read_file", "write_file", "edit_file", "delete", "glob", "grep"})
|
|
18
|
+
"""Deep Agents default virtual-filesystem tool names."""
|
|
19
|
+
|
|
20
|
+
_GRAPH_CONTEXT_INTRO = """## Virtual Graph System (VGS)
|
|
21
|
+
|
|
22
|
+
Graph context is enabled. The graph is the source of truth for recorded structured workflow context:
|
|
23
|
+
situations, rationales, actions, outcomes, artifacts, failures, evidence, decisions,
|
|
24
|
+
dependencies, and provenance. Its contents are recorded claims and evidence, not verified truth.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
_COMMON_GRAPH_GUIDANCE = """## Reading Graph Context
|
|
28
|
+
|
|
29
|
+
- Use `recall_graph_memory` as the primary read path for graph context.
|
|
30
|
+
- Ask targeted questions. Include anchors such as graph source paths, file paths, run ids,
|
|
31
|
+
task ids, artifact ids, or subagent ids when you have them.
|
|
32
|
+
- Start with the default `auto` mode and modest budgets. Use `local` for one known entity
|
|
33
|
+
and `deep` only when the task clearly needs multi-hop context.
|
|
34
|
+
- Stop querying when recall returns enough connected context to act, returns no seeds,
|
|
35
|
+
repeats the same facts, reports that the next hop was not relevant, or reaches a node,
|
|
36
|
+
edge, depth, or token budget.
|
|
37
|
+
- Do not chase the graph to completeness. Use recalled facts to guide the next action,
|
|
38
|
+
then verify current external state with the available primary tools when the answer
|
|
39
|
+
depends on live files, tests, services, or command output.
|
|
40
|
+
- Compare findings linked to the same subject before a decision depends on them. Check
|
|
41
|
+
revisions, inputs, environments, and observation times to distinguish state changes
|
|
42
|
+
from competing explanations. If material disagreement remains, pause that dependent
|
|
43
|
+
decision and verify the disputed point with a targeted independent check.
|
|
44
|
+
- Truncated related findings are not evidence that the visible claims agree. Fetch or
|
|
45
|
+
narrow context before treating them as a resolved answer.
|
|
46
|
+
- A conclusion marked `needs recheck` relied on a changed premise. Inspect the
|
|
47
|
+
premise and its update or resolution, then verify current external state before
|
|
48
|
+
relying on that conclusion. `Dependency status unknown` also requires a narrower
|
|
49
|
+
recall or direct source check; it does not mean the conclusion is current.
|
|
50
|
+
|
|
51
|
+
## Writing Graph Context
|
|
52
|
+
|
|
53
|
+
- Use `record_graph_trace` for durable workflow events, especially meaningful observations,
|
|
54
|
+
decisions, actions, failures, outcomes, artifacts, and evidence.
|
|
55
|
+
- Do not write every thought. Prefer facts that will help resume work, avoid repeated
|
|
56
|
+
failed attempts, explain a decision, or connect evidence to an outcome.
|
|
57
|
+
- Record failures and dead ends with their outcomes so future work can avoid repeating them.
|
|
58
|
+
- When citing captured output, pass `evidence_refs` with a source ID assigned at collection,
|
|
59
|
+
a locator, and optional revision, observed_at, or your summary. Reuse the ID for the
|
|
60
|
+
same captured source across workers; give a separate execution a new ID even if its
|
|
61
|
+
output text matches. These are citations, not votes or proof of independent checks.
|
|
62
|
+
Plain `evidence` strings remain useful but do not identify a shared source.
|
|
63
|
+
- For related findings, supply a stable, narrow `subject` within the project namespace.
|
|
64
|
+
Reuse the exact subject key supplied by the parent or application for the same
|
|
65
|
+
question. When a write tool has a bound subject, omit `subject` in the call. Do
|
|
66
|
+
not invent a new key for another report of that question. Keep changing revisions
|
|
67
|
+
in trace context or evidence, and use separate subjects for environments whose
|
|
68
|
+
states should not be compared as one question.
|
|
69
|
+
Supply `observed_at` only from known evidence or tool output; do not guess from the
|
|
70
|
+
recording clock. Use `finding_type="state"` for mutable observed state and
|
|
71
|
+
`"interpretation"` for explanations. Use `supersedes` only for an evidenced newer
|
|
72
|
+
mutable state. Arrival order and elapsed time alone never supersede a finding;
|
|
73
|
+
preserve parallel contenders until evidence resolves them.
|
|
74
|
+
- After checking competing claims, record an evidenced resolution with `resolves`
|
|
75
|
+
pointing to at least two same-subject traces. Explain the review in the rationale
|
|
76
|
+
and evidence. This records a judgment; it does not make the graph verify truth.
|
|
77
|
+
- When a decision relies on recorded findings, pass their Trace IDs in `depends_on`.
|
|
78
|
+
Rechecking creates a new trace that cites the findings actually used; keep the
|
|
79
|
+
original decision as history.
|
|
80
|
+
- Do not store ordinary user preferences, profile facts, or unrelated notes in the graph.
|
|
81
|
+
- Generated `/graph/...` markdown paths are read-only views over graph data, not storage locations to edit.
|
|
82
|
+
- If low-level graph write tools are exposed, use them only with clear labels, relationship
|
|
83
|
+
names, scope/provenance metadata, and schema discipline. Do not create arbitrary node or
|
|
84
|
+
edge types just because a fact could be represented.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
VGS_SYSTEM_PROMPT_SUFFIX = (
|
|
88
|
+
_GRAPH_CONTEXT_INTRO + "\nIn VGS mode, do not assume the default Deep Agents filesystem tools are available.\n\n" + _COMMON_GRAPH_GUIDANCE
|
|
89
|
+
)
|
|
90
|
+
"""Default VGS system prompt."""
|
|
91
|
+
|
|
92
|
+
GRAPH_CONTEXT_SYSTEM_PROMPT_SUFFIX = (
|
|
93
|
+
_GRAPH_CONTEXT_INTRO
|
|
94
|
+
+ """
|
|
95
|
+
The virtual filesystem holds raw artifacts, logs, large tool results, normal memory,
|
|
96
|
+
skills, and preferences. Keep selective linked findings and decisions in the graph;
|
|
97
|
+
do not copy every file, read, or edit into it. Graph persistence does not preserve
|
|
98
|
+
the source files cited by a trace.
|
|
99
|
+
|
|
100
|
+
Choose the first tool for the task: open a current file for direct file work; recall
|
|
101
|
+
history or dependencies for context questions. Read the actual source and current
|
|
102
|
+
revision when needed. Save captured evidence before citing its source ID, locator,
|
|
103
|
+
revision, or observation time. If a source is missing, inaccessible, or changed,
|
|
104
|
+
the recorded finding remains historical and unverified against the current source.
|
|
105
|
+
|
|
106
|
+
File writes and graph writes are separate. If trace recording fails, retry with the
|
|
107
|
+
same operation identity without repeating a successful external action.
|
|
108
|
+
|
|
109
|
+
"""
|
|
110
|
+
+ _COMMON_GRAPH_GUIDANCE
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class _VGSSystemPromptMiddleware(AgentMiddleware[Any, Any, Any]):
|
|
115
|
+
"""Add graph guidance while preserving application-supplied instructions."""
|
|
116
|
+
|
|
117
|
+
def __init__(self, system_prompt: str, *, strip_filesystem_guidance: bool = True) -> None:
|
|
118
|
+
self.system_prompt = system_prompt
|
|
119
|
+
self.strip_filesystem_guidance = strip_filesystem_guidance
|
|
120
|
+
|
|
121
|
+
def wrap_model_call(
|
|
122
|
+
self,
|
|
123
|
+
request: ModelRequest[Any],
|
|
124
|
+
handler: Callable[[ModelRequest[Any]], ModelResponse[Any]],
|
|
125
|
+
) -> ModelResponse[Any] | AIMessage:
|
|
126
|
+
"""Apply VGS prompt guidance before the model call."""
|
|
127
|
+
return handler(
|
|
128
|
+
request.override(system_message=_apply_vgs_system_text(request.system_message, self.system_prompt, self.strip_filesystem_guidance)),
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
async def awrap_model_call(
|
|
132
|
+
self,
|
|
133
|
+
request: ModelRequest[Any],
|
|
134
|
+
handler: Callable[[ModelRequest[Any]], Awaitable[ModelResponse[Any]]],
|
|
135
|
+
) -> ModelResponse[Any] | AIMessage:
|
|
136
|
+
"""Async variant of `wrap_model_call`."""
|
|
137
|
+
return await handler(
|
|
138
|
+
request.override(system_message=_apply_vgs_system_text(request.system_message, self.system_prompt, self.strip_filesystem_guidance)),
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def graph_context_middleware() -> AgentMiddleware[Any, Any, Any]:
|
|
143
|
+
"""Add agent-local guidance for using graph context with Deep Agents files."""
|
|
144
|
+
return _VGSSystemPromptMiddleware(GRAPH_CONTEXT_SYSTEM_PROMPT_SUFFIX, strip_filesystem_guidance=False)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def vgs_harness_profile(*, system_prompt_suffix: str | None = VGS_SYSTEM_PROMPT_SUFFIX) -> HarnessProfile:
|
|
148
|
+
"""Create a Deep Agents harness profile for VGS mode.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
system_prompt_suffix: Optional VGS prompt text appended through middleware.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Harness profile that excludes Deep Agents filesystem tools.
|
|
155
|
+
"""
|
|
156
|
+
extra_middleware = () if system_prompt_suffix is None else (_VGSSystemPromptMiddleware(system_prompt_suffix),)
|
|
157
|
+
return HarnessProfile(excluded_tools=VFS_TOOL_NAMES, extra_middleware=extra_middleware)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def register_vgs_harness_profile(model: str, *, system_prompt_suffix: str | None = VGS_SYSTEM_PROMPT_SUFFIX) -> None:
|
|
161
|
+
"""Register VGS mode for a Deep Agents model key.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
model: Model key passed to `create_deep_agent`.
|
|
165
|
+
system_prompt_suffix: Optional VGS prompt text.
|
|
166
|
+
"""
|
|
167
|
+
register_harness_profile(model, vgs_harness_profile(system_prompt_suffix=system_prompt_suffix))
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _apply_vgs_system_text(system_message: SystemMessage | None, text: str, strip_filesystem_guidance: bool) -> SystemMessage:
|
|
171
|
+
if system_message is None:
|
|
172
|
+
content_blocks = []
|
|
173
|
+
elif isinstance(system_message.content, str):
|
|
174
|
+
content_blocks = [{"type": "text", "text": system_message.content}]
|
|
175
|
+
else:
|
|
176
|
+
content_blocks = list(system_message.content)
|
|
177
|
+
if strip_filesystem_guidance:
|
|
178
|
+
content_blocks = _remove_legacy_filesystem_guidance(content_blocks)
|
|
179
|
+
if content_blocks:
|
|
180
|
+
text = f"\n\n{text}"
|
|
181
|
+
content_blocks.append({"type": "text", "text": text})
|
|
182
|
+
content = cast("list[str | dict[str, str]]", content_blocks)
|
|
183
|
+
return system_message.model_copy(update={"content": content}) if system_message else SystemMessage(content=content)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _remove_legacy_filesystem_guidance(content_blocks: list[Any]) -> list[Any]:
|
|
187
|
+
# Deep Agents 0.7 puts this guidance in tool descriptions, not system prompts.
|
|
188
|
+
if not getattr(filesystem, "FILESYSTEM_SYSTEM_PROMPT", None):
|
|
189
|
+
return content_blocks
|
|
190
|
+
execution_prompt = getattr(filesystem, "EXECUTION_SYSTEM_PROMPT", "")
|
|
191
|
+
result = []
|
|
192
|
+
for block in content_blocks:
|
|
193
|
+
text = block.get("text") if isinstance(block, dict) and block.get("type") == "text" else None
|
|
194
|
+
if not isinstance(text, str):
|
|
195
|
+
result.append(block)
|
|
196
|
+
continue
|
|
197
|
+
stripped = text.strip()
|
|
198
|
+
_, separator, execution = stripped.partition("## Execute Tool `execute`")
|
|
199
|
+
execution = separator + execution
|
|
200
|
+
if not (
|
|
201
|
+
stripped.startswith("## Following Conventions")
|
|
202
|
+
and "## Filesystem Tools `ls`, `read_file`, `write_file`, `edit_file`, `glob`, `grep`" in stripped
|
|
203
|
+
and "## Large Tool Results" in stripped
|
|
204
|
+
and "Offloaded tool results are stored under " in stripped
|
|
205
|
+
and (not execution or execution == execution_prompt)
|
|
206
|
+
):
|
|
207
|
+
result.append(block)
|
|
208
|
+
elif execution:
|
|
209
|
+
result.append({**block, "text": text[: len(text) - len(text.lstrip())] + execution})
|
|
210
|
+
return result
|