deepanalysts 0.2.3__tar.gz → 0.2.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.
Files changed (44) hide show
  1. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/PKG-INFO +7 -7
  2. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/README.md +2 -2
  3. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/backends/sandbox.py +4 -4
  4. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/clients/basement.py +3 -3
  5. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/middleware/memory.py +4 -4
  6. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/middleware/tool_errors.py +15 -2
  7. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts.egg-info/PKG-INFO +7 -7
  8. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts.egg-info/SOURCES.txt +1 -0
  9. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/pyproject.toml +12 -5
  10. deepanalysts-0.2.5/tests/test_tool_errors.py +106 -0
  11. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/__init__.py +0 -0
  12. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/backends/__init__.py +0 -0
  13. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/backends/basement.py +0 -0
  14. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/backends/composite.py +0 -0
  15. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/backends/filesystem.py +0 -0
  16. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/backends/protocol.py +0 -0
  17. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/backends/state.py +0 -0
  18. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/backends/store.py +0 -0
  19. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/backends/supabase_storage.py +0 -0
  20. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/backends/utils.py +0 -0
  21. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/clients/__init__.py +0 -0
  22. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/middleware/__init__.py +0 -0
  23. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/middleware/_utils.py +0 -0
  24. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/middleware/filesystem.py +0 -0
  25. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/middleware/patch_tool_calls.py +0 -0
  26. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/middleware/skills.py +0 -0
  27. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/middleware/subagents.py +0 -0
  28. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/middleware/summarization.py +0 -0
  29. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/utils/__init__.py +0 -0
  30. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts/utils/retry.py +0 -0
  31. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts.egg-info/dependency_links.txt +0 -0
  32. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts.egg-info/requires.txt +0 -0
  33. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/deepanalysts.egg-info/top_level.txt +0 -0
  34. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/setup.cfg +0 -0
  35. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/tests/test_basement.py +0 -0
  36. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/tests/test_composite_backend.py +0 -0
  37. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/tests/test_filesystem_middleware.py +0 -0
  38. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/tests/test_prompt_sections.py +0 -0
  39. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/tests/test_sandbox_backend.py +0 -0
  40. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/tests/test_skills_middleware.py +0 -0
  41. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/tests/test_store_backend.py +0 -0
  42. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/tests/test_summarization_middleware.py +0 -0
  43. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/tests/test_supabase_storage_backend.py +0 -0
  44. {deepanalysts-0.2.3 → deepanalysts-0.2.5}/tests/test_utils.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepanalysts
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: LangChain/LangGraph middleware for building AI agents with memory, skills, and filesystem support
5
5
  Author-email: Ganchuluun Narantsatsralt <tsatsralt@swifttech.cloud>
6
6
  License: MIT
7
- Project-URL: Homepage, https://github.com/SKE-Labs/embient-cli
8
- Project-URL: Documentation, https://github.com/SKE-Labs/embient-cli/tree/main/libs/deepanalysts
9
- Project-URL: Repository, https://github.com/SKE-Labs/embient-cli.git
10
- Project-URL: Issues, https://github.com/SKE-Labs/embient-cli/issues
7
+ Project-URL: Homepage, https://github.com/SKE-Labs/deepalpha-cli
8
+ Project-URL: Documentation, https://github.com/SKE-Labs/deepalpha-cli/tree/main/libs/deepanalysts
9
+ Project-URL: Repository, https://github.com/SKE-Labs/deepalpha-cli.git
10
+ Project-URL: Issues, https://github.com/SKE-Labs/deepalpha-cli/issues
11
11
  Keywords: langchain,langgraph,agents,middleware,ai,trading
12
12
  Classifier: Development Status :: 3 - Alpha
13
13
  Classifier: Intended Audience :: Developers
@@ -57,7 +57,7 @@ Deep Analysts provides a complete middleware stack for LangChain agents:
57
57
  - **Backends**: Store (LangGraph BaseStore), Sandbox (subprocess execution), Composite (path-based routing)
58
58
  - **API Integration**: Basement client for syncing skills/memories to cloud
59
59
 
60
- Uses only langchain, langgraph, and standard libraries (no external embient dependencies).
60
+ Uses only langchain, langgraph, and standard libraries (no external deepalpha dependencies).
61
61
 
62
62
  ## Usage
63
63
 
@@ -196,7 +196,7 @@ middleware_stack = [
196
196
  from deepanalysts.clients import BasementClient
197
197
 
198
198
  client = BasementClient(
199
- base_url="https://basement.embient.ai",
199
+ base_url="https://basement.deepalpha.mn",
200
200
  token="jwt-token",
201
201
  )
202
202
 
@@ -22,7 +22,7 @@ Deep Analysts provides a complete middleware stack for LangChain agents:
22
22
  - **Backends**: Store (LangGraph BaseStore), Sandbox (subprocess execution), Composite (path-based routing)
23
23
  - **API Integration**: Basement client for syncing skills/memories to cloud
24
24
 
25
- Uses only langchain, langgraph, and standard libraries (no external embient dependencies).
25
+ Uses only langchain, langgraph, and standard libraries (no external deepalpha dependencies).
26
26
 
27
27
  ## Usage
28
28
 
@@ -161,7 +161,7 @@ middleware_stack = [
161
161
  from deepanalysts.clients import BasementClient
162
162
 
163
163
  client = BasementClient(
164
- base_url="https://basement.embient.ai",
164
+ base_url="https://basement.deepalpha.mn",
165
165
  token="jwt-token",
166
166
  )
167
167
 
@@ -88,9 +88,9 @@ os.makedirs(parent_dir, exist_ok=True)
88
88
 
89
89
  with open(file_path, 'w') as f:
90
90
  f.write(content)
91
- " <<'__EMBIENT_EOF__'
91
+ " <<'__DEEPALPHA_EOF__'
92
92
  {payload_b64}
93
- __EMBIENT_EOF__"""
93
+ __DEEPALPHA_EOF__"""
94
94
 
95
95
  # Use heredoc to pass edit parameters via stdin to avoid ARG_MAX limits.
96
96
  # Stdin format: base64-encoded JSON with {{"path": str, "old": str, "new": str}}.
@@ -146,9 +146,9 @@ with open(file_path, 'w') as f:
146
146
  f.write(result)
147
147
 
148
148
  print(count)
149
- " <<'__EMBIENT_EOF__'
149
+ " <<'__DEEPALPHA_EOF__'
150
150
  {payload_b64}
151
- __EMBIENT_EOF__"""
151
+ __DEEPALPHA_EOF__"""
152
152
 
153
153
  _READ_COMMAND_TEMPLATE = """python3 -c "
154
154
  import os
@@ -1,7 +1,7 @@
1
1
  """Basement API client for skills and memories.
2
2
 
3
3
  This provides a generic client interface that can be configured for either
4
- cloud (park) or local (embient-cli) usage.
4
+ cloud (park) or local (deepalpha-cli) usage.
5
5
  """
6
6
 
7
7
  from __future__ import annotations
@@ -15,7 +15,7 @@ import httpx
15
15
  logger = logging.getLogger(__name__)
16
16
 
17
17
  # Default API endpoint (can be overridden via BASEMENT_API env var)
18
- DEFAULT_BASEMENT_API = os.environ.get("BASEMENT_API", "https://basement.embient.ai")
18
+ DEFAULT_BASEMENT_API = os.environ.get("BASEMENT_API", "https://basement.deepalpha.mn")
19
19
 
20
20
 
21
21
  @runtime_checkable
@@ -52,7 +52,7 @@ class BasementClient:
52
52
  """Initialize the client.
53
53
 
54
54
  Args:
55
- base_url: API base URL (defaults to basement.embient.ai)
55
+ base_url: API base URL (defaults to basement.deepalpha.mn)
56
56
  token: Static JWT token to use
57
57
  token_provider: Callable that returns JWT token dynamically
58
58
  timeout: Request timeout in seconds
@@ -18,8 +18,8 @@ from deepanalysts.backends import CompositeBackend
18
18
  middleware = MemoryMiddleware(
19
19
  backend=backend,
20
20
  sources=[
21
- "~/.embient/AGENTS.md",
22
- "./.embient/AGENTS.md",
21
+ "~/.deepalpha/AGENTS.md",
22
+ "./.deepalpha/AGENTS.md",
23
23
  ],
24
24
  )
25
25
 
@@ -142,8 +142,8 @@ class MemoryMiddleware(AgentMiddleware):
142
142
  backend: Backend instance or factory function that takes runtime
143
143
  and returns a backend. Use a factory for StateBackend.
144
144
  Optional if using loader mode.
145
- sources: List of memory file paths to load (e.g., ["~/.embient/AGENTS.md",
146
- "./.embient/AGENTS.md"]). Display names are automatically derived
145
+ sources: List of memory file paths to load (e.g., ["~/.deepalpha/AGENTS.md",
146
+ "./.deepalpha/AGENTS.md"]). Display names are automatically derived
147
147
  from the paths. Sources are loaded in order. Optional if using loader.
148
148
  loader: Optional loader for API-based memory loading.
149
149
  When provided, takes precedence over backend/sources.
@@ -57,7 +57,13 @@ class ToolErrorHandlingMiddleware(AgentMiddleware):
57
57
  self._failure_counts.clear()
58
58
 
59
59
  def _on_failure(self, tool_name: str, error: str, tool_call_id: str) -> ToolMessage:
60
- """Track failure and return an appropriate (possibly escalated) error message."""
60
+ """Track failure and return an appropriate (possibly escalated) error message.
61
+
62
+ The returned ToolMessage carries `status="error"` and `name=tool_name`
63
+ so downstream middleware (and any caller scanning history) can
64
+ distinguish failed invocations from successful ones without parsing
65
+ content prefixes.
66
+ """
61
67
  count = self._failure_counts.get(tool_name, 0) + 1
62
68
  self._failure_counts[tool_name] = count
63
69
 
@@ -70,7 +76,12 @@ class ToolErrorHandlingMiddleware(AgentMiddleware):
70
76
  else:
71
77
  content = f"Tool error: {error}"
72
78
 
73
- return ToolMessage(content=content, tool_call_id=tool_call_id)
79
+ return ToolMessage(
80
+ content=content,
81
+ tool_call_id=tool_call_id,
82
+ name=tool_name,
83
+ status="error",
84
+ )
74
85
 
75
86
  def _blocked_message(self, tool_name: str, tool_call_id: str) -> ToolMessage:
76
87
  """Return a message for tools that have already been blocked."""
@@ -83,6 +94,8 @@ class ToolErrorHandlingMiddleware(AgentMiddleware):
83
94
  f"again. Inform the user and proceed with available information."
84
95
  ),
85
96
  tool_call_id=tool_call_id,
97
+ name=tool_name,
98
+ status="error",
86
99
  )
87
100
 
88
101
  def wrap_tool_call(
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepanalysts
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: LangChain/LangGraph middleware for building AI agents with memory, skills, and filesystem support
5
5
  Author-email: Ganchuluun Narantsatsralt <tsatsralt@swifttech.cloud>
6
6
  License: MIT
7
- Project-URL: Homepage, https://github.com/SKE-Labs/embient-cli
8
- Project-URL: Documentation, https://github.com/SKE-Labs/embient-cli/tree/main/libs/deepanalysts
9
- Project-URL: Repository, https://github.com/SKE-Labs/embient-cli.git
10
- Project-URL: Issues, https://github.com/SKE-Labs/embient-cli/issues
7
+ Project-URL: Homepage, https://github.com/SKE-Labs/deepalpha-cli
8
+ Project-URL: Documentation, https://github.com/SKE-Labs/deepalpha-cli/tree/main/libs/deepanalysts
9
+ Project-URL: Repository, https://github.com/SKE-Labs/deepalpha-cli.git
10
+ Project-URL: Issues, https://github.com/SKE-Labs/deepalpha-cli/issues
11
11
  Keywords: langchain,langgraph,agents,middleware,ai,trading
12
12
  Classifier: Development Status :: 3 - Alpha
13
13
  Classifier: Intended Audience :: Developers
@@ -57,7 +57,7 @@ Deep Analysts provides a complete middleware stack for LangChain agents:
57
57
  - **Backends**: Store (LangGraph BaseStore), Sandbox (subprocess execution), Composite (path-based routing)
58
58
  - **API Integration**: Basement client for syncing skills/memories to cloud
59
59
 
60
- Uses only langchain, langgraph, and standard libraries (no external embient dependencies).
60
+ Uses only langchain, langgraph, and standard libraries (no external deepalpha dependencies).
61
61
 
62
62
  ## Usage
63
63
 
@@ -196,7 +196,7 @@ middleware_stack = [
196
196
  from deepanalysts.clients import BasementClient
197
197
 
198
198
  client = BasementClient(
199
- base_url="https://basement.embient.ai",
199
+ base_url="https://basement.deepalpha.mn",
200
200
  token="jwt-token",
201
201
  )
202
202
 
@@ -38,4 +38,5 @@ tests/test_skills_middleware.py
38
38
  tests/test_store_backend.py
39
39
  tests/test_summarization_middleware.py
40
40
  tests/test_supabase_storage_backend.py
41
+ tests/test_tool_errors.py
41
42
  tests/test_utils.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "deepanalysts"
3
- version = "0.2.3"
3
+ version = "0.2.5"
4
4
  description = "LangChain/LangGraph middleware for building AI agents with memory, skills, and filesystem support"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -26,10 +26,10 @@ dependencies = [
26
26
  ]
27
27
 
28
28
  [project.urls]
29
- Homepage = "https://github.com/SKE-Labs/embient-cli"
30
- Documentation = "https://github.com/SKE-Labs/embient-cli/tree/main/libs/deepanalysts"
31
- Repository = "https://github.com/SKE-Labs/embient-cli.git"
32
- Issues = "https://github.com/SKE-Labs/embient-cli/issues"
29
+ Homepage = "https://github.com/SKE-Labs/deepalpha-cli"
30
+ Documentation = "https://github.com/SKE-Labs/deepalpha-cli/tree/main/libs/deepanalysts"
31
+ Repository = "https://github.com/SKE-Labs/deepalpha-cli.git"
32
+ Issues = "https://github.com/SKE-Labs/deepalpha-cli/issues"
33
33
 
34
34
  [project.optional-dependencies]
35
35
  postgres = [
@@ -52,3 +52,10 @@ include = ["deepanalysts*"]
52
52
 
53
53
  [tool.setuptools.package-data]
54
54
  deepanalysts = ["py.typed"]
55
+
56
+ [dependency-groups]
57
+ dev = [
58
+ "anyio>=4.12.1",
59
+ "pytest>=9.0.2",
60
+ "pytest-anyio>=0.0.0",
61
+ ]
@@ -0,0 +1,106 @@
1
+ """Tests for ToolErrorHandlingMiddleware error-message contract.
2
+
3
+ The middleware must mark failure ToolMessages with `status="error"` and
4
+ `name=tool_name` so downstream middleware can distinguish failed tool
5
+ invocations from successful ones without parsing content prefixes.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import pytest
11
+ from langchain.agents.middleware.types import ToolCallRequest
12
+ from langchain_core.messages import ToolMessage
13
+ from langchain_core.tools import ToolException
14
+
15
+ from deepanalysts.middleware import ToolErrorHandlingMiddleware
16
+
17
+ pytest_plugins = ("anyio",)
18
+
19
+
20
+ def _request(tool_name: str, call_id: str = "c1") -> ToolCallRequest:
21
+ return ToolCallRequest(
22
+ tool_call={"name": tool_name, "args": {}, "id": call_id},
23
+ tool=None,
24
+ state={"messages": []},
25
+ runtime=None,
26
+ )
27
+
28
+
29
+ async def _raise_tool_exc(req: ToolCallRequest) -> ToolMessage:
30
+ raise ToolException("boom")
31
+
32
+
33
+ async def _raise_unexpected(req: ToolCallRequest) -> ToolMessage:
34
+ raise RuntimeError("unexpected")
35
+
36
+
37
+ async def _success(req: ToolCallRequest) -> ToolMessage:
38
+ return ToolMessage(
39
+ content="ok",
40
+ name=req.tool_call["name"],
41
+ tool_call_id=req.tool_call["id"],
42
+ )
43
+
44
+
45
+ class TestErrorMessageStatus:
46
+ @pytest.mark.anyio
47
+ async def test_tool_exception_marks_status_error(self):
48
+ mw = ToolErrorHandlingMiddleware()
49
+ result = await mw.awrap_tool_call(_request("my_tool"), _raise_tool_exc)
50
+ assert isinstance(result, ToolMessage)
51
+ assert result.status == "error"
52
+ assert result.name == "my_tool"
53
+ assert result.content.startswith("Tool error:")
54
+
55
+ @pytest.mark.anyio
56
+ async def test_unexpected_exception_marks_status_error(self):
57
+ mw = ToolErrorHandlingMiddleware()
58
+ result = await mw.awrap_tool_call(_request("my_tool"), _raise_unexpected)
59
+ assert result.status == "error"
60
+ assert result.name == "my_tool"
61
+
62
+ @pytest.mark.anyio
63
+ async def test_circuit_breaker_stop_marks_status_error(self):
64
+ """After max_retries failures, the next failure escalates to STOP:
65
+ — that message must also be tagged status=error."""
66
+ mw = ToolErrorHandlingMiddleware(max_retries=2)
67
+ # Two failures to trip the breaker on the next failure-handling path.
68
+ await mw.awrap_tool_call(_request("my_tool", "c1"), _raise_tool_exc)
69
+ result = await mw.awrap_tool_call(_request("my_tool", "c2"), _raise_tool_exc)
70
+ assert result.content.startswith("STOP:")
71
+ assert result.status == "error"
72
+ assert result.name == "my_tool"
73
+
74
+ @pytest.mark.anyio
75
+ async def test_blocked_message_marks_status_error(self):
76
+ """Once the circuit is open, subsequent calls short-circuit to a
77
+ BLOCKED message — that message must also be status=error."""
78
+ mw = ToolErrorHandlingMiddleware(max_retries=1)
79
+ # Trip the breaker.
80
+ await mw.awrap_tool_call(_request("my_tool", "c1"), _raise_tool_exc)
81
+ # This call is short-circuited via _blocked_message.
82
+ result = await mw.awrap_tool_call(_request("my_tool", "c2"), _success)
83
+ assert result.content.startswith("BLOCKED:")
84
+ assert result.status == "error"
85
+ assert result.name == "my_tool"
86
+
87
+ @pytest.mark.anyio
88
+ async def test_success_passes_through_unchanged(self):
89
+ mw = ToolErrorHandlingMiddleware()
90
+ result = await mw.awrap_tool_call(_request("my_tool"), _success)
91
+ # Successful results are returned as-is — status defaults to success.
92
+ assert result.status == "success"
93
+ assert result.content == "ok"
94
+
95
+
96
+ class TestSyncPath:
97
+ def test_sync_tool_exception_marks_status_error(self):
98
+ mw = ToolErrorHandlingMiddleware()
99
+
100
+ def raise_exc(req: ToolCallRequest) -> ToolMessage:
101
+ raise ToolException("boom")
102
+
103
+ result = mw.wrap_tool_call(_request("my_tool"), raise_exc)
104
+ assert result.status == "error"
105
+ assert result.name == "my_tool"
106
+ assert result.content.startswith("Tool error:")
File without changes