hdsp-jupyter-extension 2.0.1__py3-none-any.whl → 2.0.2__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.
- agent_server/langchain/__init__.py +18 -0
- agent_server/langchain/agent.py +694 -0
- agent_server/langchain/executors/__init__.py +15 -0
- agent_server/langchain/executors/jupyter_executor.py +429 -0
- agent_server/langchain/executors/notebook_searcher.py +477 -0
- agent_server/langchain/middleware/__init__.py +36 -0
- agent_server/langchain/middleware/code_search_middleware.py +278 -0
- agent_server/langchain/middleware/error_handling_middleware.py +338 -0
- agent_server/langchain/middleware/jupyter_execution_middleware.py +301 -0
- agent_server/langchain/middleware/rag_middleware.py +227 -0
- agent_server/langchain/middleware/validation_middleware.py +240 -0
- agent_server/langchain/state.py +159 -0
- agent_server/langchain/tools/__init__.py +39 -0
- agent_server/langchain/tools/file_tools.py +279 -0
- agent_server/langchain/tools/jupyter_tools.py +143 -0
- agent_server/langchain/tools/search_tools.py +309 -0
- agent_server/main.py +13 -0
- agent_server/routers/health.py +14 -0
- agent_server/routers/langchain_agent.py +1368 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/share/jupyter/labextensions/hdsp-agent/build_log.json +1 -1
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/share/jupyter/labextensions/hdsp-agent/package.json +2 -2
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2607ff74c74acfa83158.js → hdsp_jupyter_extension-2.0.2.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.634cf0ae0f3592d0882f.js +408 -4
- hdsp_jupyter_extension-2.0.2.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.634cf0ae0f3592d0882f.js.map +1 -0
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.622c1a5918b3aafb2315.js → hdsp_jupyter_extension-2.0.2.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.1366019c413f1d68467f.js +753 -65
- hdsp_jupyter_extension-2.0.2.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.1366019c413f1d68467f.js.map +1 -0
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.729f933de01ad5620730.js → hdsp_jupyter_extension-2.0.2.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.3379c4b222c042de2b01.js +8 -8
- hdsp_jupyter_extension-2.0.2.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.3379c4b222c042de2b01.js.map +1 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js → hdsp_jupyter_extension-2.0.2.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js +2 -209
- hdsp_jupyter_extension-2.0.2.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js.map +1 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js → hdsp_jupyter_extension-2.0.2.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js +209 -2
- hdsp_jupyter_extension-2.0.2.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js.map +1 -0
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js → hdsp_jupyter_extension-2.0.2.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js +212 -3
- hdsp_jupyter_extension-2.0.2.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js.map +1 -0
- {hdsp_jupyter_extension-2.0.1.dist-info → hdsp_jupyter_extension-2.0.2.dist-info}/METADATA +1 -1
- {hdsp_jupyter_extension-2.0.1.dist-info → hdsp_jupyter_extension-2.0.2.dist-info}/RECORD +66 -49
- jupyter_ext/_version.py +1 -1
- jupyter_ext/handlers.py +126 -1
- jupyter_ext/labextension/build_log.json +1 -1
- jupyter_ext/labextension/package.json +2 -2
- jupyter_ext/labextension/static/{frontend_styles_index_js.2607ff74c74acfa83158.js → frontend_styles_index_js.634cf0ae0f3592d0882f.js} +408 -4
- jupyter_ext/labextension/static/frontend_styles_index_js.634cf0ae0f3592d0882f.js.map +1 -0
- jupyter_ext/labextension/static/{lib_index_js.622c1a5918b3aafb2315.js → lib_index_js.1366019c413f1d68467f.js} +753 -65
- jupyter_ext/labextension/static/lib_index_js.1366019c413f1d68467f.js.map +1 -0
- jupyter_ext/labextension/static/{remoteEntry.729f933de01ad5620730.js → remoteEntry.3379c4b222c042de2b01.js} +8 -8
- jupyter_ext/labextension/static/remoteEntry.3379c4b222c042de2b01.js.map +1 -0
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js → jupyter_ext/labextension/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js +2 -209
- jupyter_ext/labextension/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js.map +1 -0
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js → jupyter_ext/labextension/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js +209 -2
- jupyter_ext/labextension/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js.map +1 -0
- jupyter_ext/labextension/static/{vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js → vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js} +212 -3
- jupyter_ext/labextension/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js.map +1 -0
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2607ff74c74acfa83158.js.map +0 -1
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.622c1a5918b3aafb2315.js.map +0 -1
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.729f933de01ad5620730.js.map +0 -1
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js.map +0 -1
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js.map +0 -1
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js.map +0 -1
- jupyter_ext/labextension/static/frontend_styles_index_js.2607ff74c74acfa83158.js.map +0 -1
- jupyter_ext/labextension/static/lib_index_js.622c1a5918b3aafb2315.js.map +0 -1
- jupyter_ext/labextension/static/remoteEntry.729f933de01ad5620730.js.map +0 -1
- jupyter_ext/labextension/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js.map +0 -1
- jupyter_ext/labextension/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js.map +0 -1
- jupyter_ext/labextension/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js.map +0 -1
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80.c095373419d05e6f141a.js +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80.c095373419d05e6f141a.js.map +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b81.61e75fb98ecff46cf836.js +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b81.61e75fb98ecff46cf836.js.map +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/share/jupyter/labextensions/hdsp-agent/static/style.js +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195.e2553aab0c3963b83dd7.js +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195.e2553aab0c3963b83dd7.js.map +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js.map +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.2.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js.map +0 -0
- {hdsp_jupyter_extension-2.0.1.dist-info → hdsp_jupyter_extension-2.0.2.dist-info}/WHEEL +0 -0
- {hdsp_jupyter_extension-2.0.1.dist-info → hdsp_jupyter_extension-2.0.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Search Tools for LangChain Agent
|
|
3
|
+
|
|
4
|
+
Provides tools for searching code in workspace and notebooks:
|
|
5
|
+
- search_workspace: Search files in the workspace
|
|
6
|
+
- search_notebook_cells: Search cells in Jupyter notebooks
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import re
|
|
12
|
+
from typing import Any, Dict, List, Optional
|
|
13
|
+
|
|
14
|
+
from langchain_core.tools import tool
|
|
15
|
+
from pydantic import BaseModel, Field
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SearchWorkspaceInput(BaseModel):
|
|
19
|
+
"""Input schema for search_workspace tool"""
|
|
20
|
+
pattern: str = Field(description="Search pattern (regex or text)")
|
|
21
|
+
file_types: List[str] = Field(
|
|
22
|
+
default=["*.py", "*.ipynb"],
|
|
23
|
+
description="File patterns to search (e.g., ['*.py', '*.ipynb'])"
|
|
24
|
+
)
|
|
25
|
+
path: str = Field(default=".", description="Directory to search in")
|
|
26
|
+
max_results: int = Field(default=50, description="Maximum number of results")
|
|
27
|
+
case_sensitive: bool = Field(default=False, description="Case-sensitive search")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SearchNotebookCellsInput(BaseModel):
|
|
31
|
+
"""Input schema for search_notebook_cells tool"""
|
|
32
|
+
pattern: str = Field(description="Search pattern (regex or text)")
|
|
33
|
+
notebook_path: Optional[str] = Field(
|
|
34
|
+
default=None,
|
|
35
|
+
description="Specific notebook to search (None = all notebooks)"
|
|
36
|
+
)
|
|
37
|
+
cell_type: Optional[str] = Field(
|
|
38
|
+
default=None,
|
|
39
|
+
description="Cell type filter: 'code', 'markdown', or None for all"
|
|
40
|
+
)
|
|
41
|
+
max_results: int = Field(default=30, description="Maximum number of results")
|
|
42
|
+
case_sensitive: bool = Field(default=False, description="Case-sensitive search")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _search_in_file(
|
|
46
|
+
file_path: str,
|
|
47
|
+
pattern: str,
|
|
48
|
+
case_sensitive: bool = False
|
|
49
|
+
) -> List[Dict[str, Any]]:
|
|
50
|
+
"""Search for pattern in a regular file"""
|
|
51
|
+
results = []
|
|
52
|
+
flags = 0 if case_sensitive else re.IGNORECASE
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
compiled = re.compile(pattern, flags)
|
|
56
|
+
except re.error:
|
|
57
|
+
# If pattern is not valid regex, use literal search
|
|
58
|
+
compiled = re.compile(re.escape(pattern), flags)
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
62
|
+
for line_num, line in enumerate(f, 1):
|
|
63
|
+
if compiled.search(line):
|
|
64
|
+
results.append({
|
|
65
|
+
"file_path": file_path,
|
|
66
|
+
"line_number": line_num,
|
|
67
|
+
"content": line.strip()[:200],
|
|
68
|
+
"match_type": "line",
|
|
69
|
+
})
|
|
70
|
+
except Exception:
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
return results
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _search_in_notebook(
|
|
77
|
+
notebook_path: str,
|
|
78
|
+
pattern: str,
|
|
79
|
+
cell_type: Optional[str] = None,
|
|
80
|
+
case_sensitive: bool = False
|
|
81
|
+
) -> List[Dict[str, Any]]:
|
|
82
|
+
"""Search for pattern in a Jupyter notebook"""
|
|
83
|
+
results = []
|
|
84
|
+
flags = 0 if case_sensitive else re.IGNORECASE
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
compiled = re.compile(pattern, flags)
|
|
88
|
+
except re.error:
|
|
89
|
+
compiled = re.compile(re.escape(pattern), flags)
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
with open(notebook_path, "r", encoding="utf-8") as f:
|
|
93
|
+
notebook = json.load(f)
|
|
94
|
+
|
|
95
|
+
cells = notebook.get("cells", [])
|
|
96
|
+
for idx, cell in enumerate(cells):
|
|
97
|
+
current_type = cell.get("cell_type", "code")
|
|
98
|
+
|
|
99
|
+
# Filter by cell type if specified
|
|
100
|
+
if cell_type and current_type != cell_type:
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
source = cell.get("source", [])
|
|
104
|
+
if isinstance(source, list):
|
|
105
|
+
source = "".join(source)
|
|
106
|
+
|
|
107
|
+
if compiled.search(source):
|
|
108
|
+
# Find matching lines
|
|
109
|
+
matching_lines = []
|
|
110
|
+
for line_num, line in enumerate(source.split("\n"), 1):
|
|
111
|
+
if compiled.search(line):
|
|
112
|
+
matching_lines.append({
|
|
113
|
+
"line": line_num,
|
|
114
|
+
"content": line.strip()[:150]
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
results.append({
|
|
118
|
+
"file_path": notebook_path,
|
|
119
|
+
"cell_index": idx,
|
|
120
|
+
"cell_type": current_type,
|
|
121
|
+
"content": source[:300] + "..." if len(source) > 300 else source,
|
|
122
|
+
"matching_lines": matching_lines[:5],
|
|
123
|
+
"match_type": "cell",
|
|
124
|
+
})
|
|
125
|
+
except Exception:
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
return results
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@tool(args_schema=SearchWorkspaceInput)
|
|
132
|
+
def search_workspace_tool(
|
|
133
|
+
pattern: str,
|
|
134
|
+
file_types: List[str] = None,
|
|
135
|
+
path: str = ".",
|
|
136
|
+
max_results: int = 50,
|
|
137
|
+
case_sensitive: bool = False,
|
|
138
|
+
workspace_root: str = "."
|
|
139
|
+
) -> Dict[str, Any]:
|
|
140
|
+
"""
|
|
141
|
+
Search for a pattern across files in the workspace.
|
|
142
|
+
|
|
143
|
+
Searches both regular files and Jupyter notebooks.
|
|
144
|
+
For notebooks, searches within cell contents.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
pattern: Search pattern (regex or text)
|
|
148
|
+
file_types: File patterns to search (default: ['*.py', '*.ipynb'])
|
|
149
|
+
path: Directory to search in
|
|
150
|
+
max_results: Maximum number of results to return
|
|
151
|
+
case_sensitive: Whether search is case-sensitive
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Dict with search results
|
|
155
|
+
"""
|
|
156
|
+
import fnmatch
|
|
157
|
+
|
|
158
|
+
if file_types is None:
|
|
159
|
+
file_types = ["*.py", "*.ipynb"]
|
|
160
|
+
|
|
161
|
+
results = []
|
|
162
|
+
files_searched = 0
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
search_path = os.path.normpath(os.path.join(workspace_root, path))
|
|
166
|
+
|
|
167
|
+
for root, _, filenames in os.walk(search_path):
|
|
168
|
+
for filename in filenames:
|
|
169
|
+
# Check if file matches any pattern
|
|
170
|
+
if not any(fnmatch.fnmatch(filename, ft) for ft in file_types):
|
|
171
|
+
continue
|
|
172
|
+
|
|
173
|
+
file_path = os.path.join(root, filename)
|
|
174
|
+
rel_path = os.path.relpath(file_path, workspace_root)
|
|
175
|
+
files_searched += 1
|
|
176
|
+
|
|
177
|
+
if filename.endswith(".ipynb"):
|
|
178
|
+
# Search in notebook
|
|
179
|
+
matches = _search_in_notebook(
|
|
180
|
+
file_path, pattern, None, case_sensitive
|
|
181
|
+
)
|
|
182
|
+
for m in matches:
|
|
183
|
+
m["file_path"] = rel_path
|
|
184
|
+
results.extend(matches)
|
|
185
|
+
else:
|
|
186
|
+
# Search in regular file
|
|
187
|
+
matches = _search_in_file(file_path, pattern, case_sensitive)
|
|
188
|
+
for m in matches:
|
|
189
|
+
m["file_path"] = rel_path
|
|
190
|
+
results.extend(matches)
|
|
191
|
+
|
|
192
|
+
if len(results) >= max_results:
|
|
193
|
+
break
|
|
194
|
+
|
|
195
|
+
if len(results) >= max_results:
|
|
196
|
+
break
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
"tool": "search_workspace",
|
|
200
|
+
"success": True,
|
|
201
|
+
"pattern": pattern,
|
|
202
|
+
"path": path,
|
|
203
|
+
"files_searched": files_searched,
|
|
204
|
+
"total_results": len(results),
|
|
205
|
+
"results": results[:max_results],
|
|
206
|
+
"truncated": len(results) > max_results,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
except Exception as e:
|
|
210
|
+
return {
|
|
211
|
+
"tool": "search_workspace",
|
|
212
|
+
"success": False,
|
|
213
|
+
"error": f"Search failed: {str(e)}",
|
|
214
|
+
"pattern": pattern,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@tool(args_schema=SearchNotebookCellsInput)
|
|
219
|
+
def search_notebook_cells_tool(
|
|
220
|
+
pattern: str,
|
|
221
|
+
notebook_path: Optional[str] = None,
|
|
222
|
+
cell_type: Optional[str] = None,
|
|
223
|
+
max_results: int = 30,
|
|
224
|
+
case_sensitive: bool = False,
|
|
225
|
+
workspace_root: str = "."
|
|
226
|
+
) -> Dict[str, Any]:
|
|
227
|
+
"""
|
|
228
|
+
Search for a pattern in Jupyter notebook cells.
|
|
229
|
+
|
|
230
|
+
Can search a specific notebook or all notebooks in workspace.
|
|
231
|
+
Optionally filter by cell type (code/markdown).
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
pattern: Search pattern (regex or text)
|
|
235
|
+
notebook_path: Specific notebook to search (None = all)
|
|
236
|
+
cell_type: Filter by cell type ('code', 'markdown', or None)
|
|
237
|
+
max_results: Maximum number of results
|
|
238
|
+
case_sensitive: Whether search is case-sensitive
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
Dict with matching cells
|
|
242
|
+
"""
|
|
243
|
+
results = []
|
|
244
|
+
notebooks_searched = 0
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
if notebook_path:
|
|
248
|
+
# Search specific notebook
|
|
249
|
+
full_path = os.path.normpath(
|
|
250
|
+
os.path.join(workspace_root, notebook_path)
|
|
251
|
+
)
|
|
252
|
+
if os.path.exists(full_path) and full_path.endswith(".ipynb"):
|
|
253
|
+
matches = _search_in_notebook(
|
|
254
|
+
full_path, pattern, cell_type, case_sensitive
|
|
255
|
+
)
|
|
256
|
+
for m in matches:
|
|
257
|
+
m["file_path"] = notebook_path
|
|
258
|
+
results.extend(matches)
|
|
259
|
+
notebooks_searched = 1
|
|
260
|
+
else:
|
|
261
|
+
# Search all notebooks
|
|
262
|
+
for root, _, filenames in os.walk(workspace_root):
|
|
263
|
+
for filename in filenames:
|
|
264
|
+
if not filename.endswith(".ipynb"):
|
|
265
|
+
continue
|
|
266
|
+
|
|
267
|
+
file_path = os.path.join(root, filename)
|
|
268
|
+
rel_path = os.path.relpath(file_path, workspace_root)
|
|
269
|
+
notebooks_searched += 1
|
|
270
|
+
|
|
271
|
+
matches = _search_in_notebook(
|
|
272
|
+
file_path, pattern, cell_type, case_sensitive
|
|
273
|
+
)
|
|
274
|
+
for m in matches:
|
|
275
|
+
m["file_path"] = rel_path
|
|
276
|
+
results.extend(matches)
|
|
277
|
+
|
|
278
|
+
if len(results) >= max_results:
|
|
279
|
+
break
|
|
280
|
+
|
|
281
|
+
if len(results) >= max_results:
|
|
282
|
+
break
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
"tool": "search_notebook_cells",
|
|
286
|
+
"success": True,
|
|
287
|
+
"pattern": pattern,
|
|
288
|
+
"notebook_path": notebook_path,
|
|
289
|
+
"cell_type": cell_type,
|
|
290
|
+
"notebooks_searched": notebooks_searched,
|
|
291
|
+
"total_results": len(results),
|
|
292
|
+
"results": results[:max_results],
|
|
293
|
+
"truncated": len(results) > max_results,
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
except Exception as e:
|
|
297
|
+
return {
|
|
298
|
+
"tool": "search_notebook_cells",
|
|
299
|
+
"success": False,
|
|
300
|
+
"error": f"Search failed: {str(e)}",
|
|
301
|
+
"pattern": pattern,
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
# Export all tools
|
|
306
|
+
SEARCH_TOOLS = [
|
|
307
|
+
search_workspace_tool,
|
|
308
|
+
search_notebook_cells_tool,
|
|
309
|
+
]
|
agent_server/main.py
CHANGED
|
@@ -16,6 +16,14 @@ from fastapi.middleware.cors import CORSMiddleware
|
|
|
16
16
|
|
|
17
17
|
from agent_server.routers import agent, chat, config, file_resolver, health, rag
|
|
18
18
|
|
|
19
|
+
# Optional LangChain router (requires langchain dependencies)
|
|
20
|
+
try:
|
|
21
|
+
from agent_server.routers import langchain_agent
|
|
22
|
+
LANGCHAIN_AVAILABLE = True
|
|
23
|
+
except ImportError:
|
|
24
|
+
LANGCHAIN_AVAILABLE = False
|
|
25
|
+
langchain_agent = None
|
|
26
|
+
|
|
19
27
|
# Configure logging
|
|
20
28
|
logging.basicConfig(
|
|
21
29
|
level=logging.INFO,
|
|
@@ -143,6 +151,11 @@ app.include_router(chat.router, prefix="/chat", tags=["Chat"])
|
|
|
143
151
|
app.include_router(rag.router, prefix="/rag", tags=["RAG"])
|
|
144
152
|
app.include_router(file_resolver.router, prefix="/file", tags=["File Resolution"])
|
|
145
153
|
|
|
154
|
+
# Register LangChain agent router if available
|
|
155
|
+
if LANGCHAIN_AVAILABLE:
|
|
156
|
+
app.include_router(langchain_agent.router, prefix="/agent", tags=["LangChain Agent"])
|
|
157
|
+
logger.info("LangChain agent router registered")
|
|
158
|
+
|
|
146
159
|
|
|
147
160
|
def run():
|
|
148
161
|
"""Entry point for `hdsp-agent-server` CLI command"""
|
agent_server/routers/health.py
CHANGED
|
@@ -32,6 +32,20 @@ async def health_check() -> HealthResponse:
|
|
|
32
32
|
)
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
@router.get("/status", response_model=HealthResponse)
|
|
36
|
+
async def status_check() -> HealthResponse:
|
|
37
|
+
"""
|
|
38
|
+
Status check endpoint (alias for health check).
|
|
39
|
+
|
|
40
|
+
Returns server status, timestamp, and version.
|
|
41
|
+
"""
|
|
42
|
+
return HealthResponse(
|
|
43
|
+
status="healthy",
|
|
44
|
+
timestamp=datetime.utcnow().isoformat(),
|
|
45
|
+
version="1.0.0",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
35
49
|
@router.get("/")
|
|
36
50
|
async def root():
|
|
37
51
|
"""Root endpoint with server information"""
|