hdsp-jupyter-extension 2.0.26__py3-none-any.whl → 2.0.27__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/agent_prompts/planner_prompt.py +5 -9
- agent_server/langchain/custom_middleware.py +10 -0
- agent_server/langchain/middleware/code_history_middleware.py +126 -37
- agent_server/langchain/middleware/subagent_middleware.py +24 -2
- agent_server/routers/langchain_agent.py +37 -1
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/build_log.json +1 -1
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/package.json +2 -2
- hdsp_jupyter_extension-2.0.26.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.0fe2dcbbd176ee0efceb.js → hdsp_jupyter_extension-2.0.27.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.4ab73bb5068405670214.js +2 -2
- jupyter_ext/labextension/static/remoteEntry.0fe2dcbbd176ee0efceb.js.map → hdsp_jupyter_extension-2.0.27.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.4ab73bb5068405670214.js.map +1 -1
- {hdsp_jupyter_extension-2.0.26.dist-info → hdsp_jupyter_extension-2.0.27.dist-info}/METADATA +1 -1
- {hdsp_jupyter_extension-2.0.26.dist-info → hdsp_jupyter_extension-2.0.27.dist-info}/RECORD +41 -41
- jupyter_ext/_version.py +1 -1
- jupyter_ext/labextension/build_log.json +1 -1
- jupyter_ext/labextension/package.json +2 -2
- jupyter_ext/labextension/static/{remoteEntry.0fe2dcbbd176ee0efceb.js → remoteEntry.4ab73bb5068405670214.js} +2 -2
- hdsp_jupyter_extension-2.0.26.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.0fe2dcbbd176ee0efceb.js.map → jupyter_ext/labextension/static/remoteEntry.4ab73bb5068405670214.js.map +1 -1
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.data}/data/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.b5e4416b4e07ec087aad.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.b5e4416b4e07ec087aad.js.map +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.67505497667f9c0a763d.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.67505497667f9c0a763d.js.map +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.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.26.data → hdsp_jupyter_extension-2.0.27.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.26.data → hdsp_jupyter_extension-2.0.27.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.26.data → hdsp_jupyter_extension-2.0.27.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.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/static/style.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.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.26.data → hdsp_jupyter_extension-2.0.27.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.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js.map +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js.map +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.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.26.data → hdsp_jupyter_extension-2.0.27.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.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.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.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.27.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js.map +0 -0
- {hdsp_jupyter_extension-2.0.26.dist-info → hdsp_jupyter_extension-2.0.27.dist-info}/WHEEL +0 -0
- {hdsp_jupyter_extension-2.0.26.dist-info → hdsp_jupyter_extension-2.0.27.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,9 +5,10 @@ Main Agent (Supervisor) System Prompt for Multi-Agent Mode
|
|
|
5
5
|
PLANNER_SYSTEM_PROMPT = """당신은 작업을 조율하는 Main Agent입니다. 한국어로 응답하세요.
|
|
6
6
|
|
|
7
7
|
# 핵심 원칙
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
1. 3단계 이상의 복잡한 작업을 요청받은 경우에만 write_todos 로 작업 목록 관리
|
|
9
|
+
2. **직접 코드, 쿼리 작성 금지** - 모든 코드/쿼리 생성은 task_tool로 서브에이전트에게 위임
|
|
10
10
|
3. 서브에이전트가 반환한 코드를 적절한 도구로 실행
|
|
11
|
+
4. 모든 응답 content는 2~3줄 내외로 핵심만 명확하게 전달
|
|
11
12
|
|
|
12
13
|
# 작업 흐름
|
|
13
14
|
|
|
@@ -69,13 +70,8 @@ PLANNER_SYSTEM_PROMPT = """당신은 작업을 조율하는 Main Agent입니다.
|
|
|
69
70
|
- **final_summary_tool 호출 없이 종료 금지**
|
|
70
71
|
|
|
71
72
|
# 도구 사용시 주의할 점
|
|
72
|
-
|
|
73
|
-
##
|
|
74
|
-
- task_tool: 서브에이전트에게 작업 위임
|
|
75
|
-
|
|
76
|
-
## 탐색 (⚠️ 파일 위치 모를 때: search_files_tool → list_workspace_tool → 재검색 → ask_user_tool 순서로!)
|
|
77
|
-
- list_workspace_tool: 파일/디렉토리 목록
|
|
78
|
-
- search_files_tool: 파일 내용 검색 (regex 지원, 예: "titanic|error|*.csv")
|
|
73
|
+
## 파일 위치 모를 때 탐색 순서: search_files_tool → list_workspace_tool → 재검색 → ask_user_tool 순서로!)
|
|
74
|
+
## list_workspace_tool로 전체 디렉토리 파일 목록 검색 금지! 최대한 pattern 으로 drill down 해서 검색할 것
|
|
79
75
|
|
|
80
76
|
# 금지 사항
|
|
81
77
|
- 직접 코드/SQL 작성 (반드시 task_tool 사용)
|
|
@@ -898,6 +898,16 @@ def create_limit_tool_calls_middleware(wrap_model_call):
|
|
|
898
898
|
removed_count,
|
|
899
899
|
)
|
|
900
900
|
|
|
901
|
+
# Clear content when tool_calls exist to avoid duplicate information
|
|
902
|
+
# Some models return both content and tool_calls, causing redundant
|
|
903
|
+
# "thinking" text in the conversation history
|
|
904
|
+
if msg.tool_calls and msg.content:
|
|
905
|
+
logger.info(
|
|
906
|
+
"Clearing AIMessage content (len=%d) because tool_calls exist",
|
|
907
|
+
len(msg.content),
|
|
908
|
+
)
|
|
909
|
+
msg.content = ""
|
|
910
|
+
|
|
901
911
|
return response
|
|
902
912
|
|
|
903
913
|
return limit_tool_calls_to_one
|
|
@@ -12,11 +12,12 @@ Features:
|
|
|
12
12
|
|
|
13
13
|
import logging
|
|
14
14
|
import threading
|
|
15
|
-
import tiktoken
|
|
16
15
|
from dataclasses import dataclass, field
|
|
17
16
|
from datetime import datetime
|
|
18
17
|
from typing import Any, Dict, List, Optional
|
|
19
18
|
|
|
19
|
+
import tiktoken
|
|
20
|
+
|
|
20
21
|
logger = logging.getLogger(__name__)
|
|
21
22
|
|
|
22
23
|
# Token limit for context (including system prompt)
|
|
@@ -31,7 +32,9 @@ PYTHON_DEV_SYSTEM_PROMPT_TOKENS = 2000
|
|
|
31
32
|
class CodeHistoryEntry:
|
|
32
33
|
"""Represents a single code execution or file operation."""
|
|
33
34
|
|
|
34
|
-
tool_name:
|
|
35
|
+
tool_name: (
|
|
36
|
+
str # jupyter_cell_tool, write_file_tool, edit_file_tool, multiedit_file_tool
|
|
37
|
+
)
|
|
35
38
|
timestamp: datetime = field(default_factory=datetime.now)
|
|
36
39
|
|
|
37
40
|
# For jupyter_cell_tool
|
|
@@ -50,7 +53,9 @@ class CodeHistoryEntry:
|
|
|
50
53
|
timestamp_str = self.timestamp.strftime("%H:%M:%S")
|
|
51
54
|
|
|
52
55
|
if self.tool_name == "jupyter_cell_tool":
|
|
53
|
-
output_preview =
|
|
56
|
+
output_preview = (
|
|
57
|
+
self._truncate(self.output, 500) if self.output else "(no output)"
|
|
58
|
+
)
|
|
54
59
|
return f"""## Cell ({timestamp_str})
|
|
55
60
|
```python
|
|
56
61
|
{self.code}
|
|
@@ -86,7 +91,7 @@ Changes: {edit_count} edits applied"""
|
|
|
86
91
|
if self.tool_name == "jupyter_cell_tool":
|
|
87
92
|
# Extract first meaningful line of code
|
|
88
93
|
if self.code:
|
|
89
|
-
first_line = self.code.strip().split(
|
|
94
|
+
first_line = self.code.strip().split("\n")[0][:60]
|
|
90
95
|
return f"- Cell: {first_line}..."
|
|
91
96
|
return "- Cell: (empty)"
|
|
92
97
|
|
|
@@ -156,7 +161,9 @@ class CodeHistoryTracker:
|
|
|
156
161
|
output=output,
|
|
157
162
|
)
|
|
158
163
|
self._history.append(entry)
|
|
159
|
-
logger.info(
|
|
164
|
+
logger.info(
|
|
165
|
+
f"CodeHistory: Added jupyter_cell (total: {len(self._history)})"
|
|
166
|
+
)
|
|
160
167
|
|
|
161
168
|
def add_write_file(self, file_path: str, content: str) -> None:
|
|
162
169
|
"""Track a write_file_tool execution."""
|
|
@@ -167,7 +174,9 @@ class CodeHistoryTracker:
|
|
|
167
174
|
content=content,
|
|
168
175
|
)
|
|
169
176
|
self._history.append(entry)
|
|
170
|
-
logger.info(
|
|
177
|
+
logger.info(
|
|
178
|
+
f"CodeHistory: Added write_file {file_path} (total: {len(self._history)})"
|
|
179
|
+
)
|
|
171
180
|
|
|
172
181
|
def add_edit_file(self, file_path: str, old_content: str, new_content: str) -> None:
|
|
173
182
|
"""Track an edit_file_tool execution."""
|
|
@@ -179,7 +188,9 @@ class CodeHistoryTracker:
|
|
|
179
188
|
new_content=new_content,
|
|
180
189
|
)
|
|
181
190
|
self._history.append(entry)
|
|
182
|
-
logger.info(
|
|
191
|
+
logger.info(
|
|
192
|
+
f"CodeHistory: Added edit_file {file_path} (total: {len(self._history)})"
|
|
193
|
+
)
|
|
183
194
|
|
|
184
195
|
def add_multiedit_file(self, file_path: str, edits: List[Dict[str, str]]) -> None:
|
|
185
196
|
"""Track a multiedit_file_tool execution."""
|
|
@@ -190,7 +201,9 @@ class CodeHistoryTracker:
|
|
|
190
201
|
edits=edits,
|
|
191
202
|
)
|
|
192
203
|
self._history.append(entry)
|
|
193
|
-
logger.info(
|
|
204
|
+
logger.info(
|
|
205
|
+
f"CodeHistory: Added multiedit_file {file_path} (total: {len(self._history)})"
|
|
206
|
+
)
|
|
194
207
|
|
|
195
208
|
def get_context_for_subagent(
|
|
196
209
|
self,
|
|
@@ -216,8 +229,12 @@ class CodeHistoryTracker:
|
|
|
216
229
|
return existing_context or ""
|
|
217
230
|
|
|
218
231
|
# Calculate available tokens for history
|
|
219
|
-
existing_tokens =
|
|
220
|
-
|
|
232
|
+
existing_tokens = (
|
|
233
|
+
self._count_tokens(existing_context) if existing_context else 0
|
|
234
|
+
)
|
|
235
|
+
available_tokens = (
|
|
236
|
+
max_tokens - system_prompt_tokens - existing_tokens - 500
|
|
237
|
+
) # 500 buffer
|
|
221
238
|
|
|
222
239
|
# Build full history string
|
|
223
240
|
full_history = self._build_full_history()
|
|
@@ -311,49 +328,110 @@ class CodeHistoryTracker:
|
|
|
311
328
|
return len(self._history)
|
|
312
329
|
|
|
313
330
|
|
|
314
|
-
# Global tracker
|
|
315
|
-
|
|
331
|
+
# Global tracker instances per threadId
|
|
332
|
+
_code_history_trackers: Dict[str, CodeHistoryTracker] = {}
|
|
333
|
+
_trackers_lock = threading.Lock()
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def get_code_history_tracker(thread_id: Optional[str] = None) -> CodeHistoryTracker:
|
|
337
|
+
"""
|
|
338
|
+
Get the CodeHistoryTracker instance for the given thread_id.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
thread_id: Thread ID for session isolation. If None, returns a temporary tracker.
|
|
316
342
|
|
|
343
|
+
Returns:
|
|
344
|
+
CodeHistoryTracker instance for the thread
|
|
345
|
+
"""
|
|
346
|
+
if thread_id is None:
|
|
347
|
+
logger.warning(
|
|
348
|
+
"get_code_history_tracker called without thread_id - using temporary tracker"
|
|
349
|
+
)
|
|
350
|
+
return CodeHistoryTracker()
|
|
317
351
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
return _code_history_tracker
|
|
352
|
+
with _trackers_lock:
|
|
353
|
+
if thread_id not in _code_history_trackers:
|
|
354
|
+
_code_history_trackers[thread_id] = CodeHistoryTracker()
|
|
355
|
+
logger.info(f"CodeHistory: Created new tracker for thread_id={thread_id}")
|
|
356
|
+
return _code_history_trackers[thread_id]
|
|
324
357
|
|
|
325
358
|
|
|
326
|
-
def track_jupyter_cell(code: str, output: str) -> None:
|
|
359
|
+
def track_jupyter_cell(code: str, output: str, thread_id: Optional[str] = None) -> None:
|
|
327
360
|
"""Convenience function to track jupyter_cell_tool execution."""
|
|
328
|
-
get_code_history_tracker().add_jupyter_cell(code, output)
|
|
361
|
+
get_code_history_tracker(thread_id).add_jupyter_cell(code, output)
|
|
329
362
|
|
|
330
363
|
|
|
331
|
-
def track_write_file(
|
|
364
|
+
def track_write_file(
|
|
365
|
+
file_path: str, content: str, thread_id: Optional[str] = None
|
|
366
|
+
) -> None:
|
|
332
367
|
"""Convenience function to track write_file_tool execution."""
|
|
333
|
-
get_code_history_tracker().add_write_file(file_path, content)
|
|
368
|
+
get_code_history_tracker(thread_id).add_write_file(file_path, content)
|
|
334
369
|
|
|
335
370
|
|
|
336
|
-
def track_edit_file(
|
|
371
|
+
def track_edit_file(
|
|
372
|
+
file_path: str,
|
|
373
|
+
old_content: str,
|
|
374
|
+
new_content: str,
|
|
375
|
+
thread_id: Optional[str] = None,
|
|
376
|
+
) -> None:
|
|
337
377
|
"""Convenience function to track edit_file_tool execution."""
|
|
338
|
-
get_code_history_tracker().add_edit_file(
|
|
378
|
+
get_code_history_tracker(thread_id).add_edit_file(
|
|
379
|
+
file_path, old_content, new_content
|
|
380
|
+
)
|
|
339
381
|
|
|
340
382
|
|
|
341
|
-
def track_multiedit_file(
|
|
383
|
+
def track_multiedit_file(
|
|
384
|
+
file_path: str,
|
|
385
|
+
edits: List[Dict[str, str]],
|
|
386
|
+
thread_id: Optional[str] = None,
|
|
387
|
+
) -> None:
|
|
342
388
|
"""Convenience function to track multiedit_file_tool execution."""
|
|
343
|
-
get_code_history_tracker().add_multiedit_file(file_path, edits)
|
|
389
|
+
get_code_history_tracker(thread_id).add_multiedit_file(file_path, edits)
|
|
344
390
|
|
|
345
391
|
|
|
346
|
-
def get_context_with_history(
|
|
392
|
+
def get_context_with_history(
|
|
393
|
+
existing_context: Optional[str] = None,
|
|
394
|
+
thread_id: Optional[str] = None,
|
|
395
|
+
) -> str:
|
|
347
396
|
"""Get context string with code history injected."""
|
|
348
|
-
return get_code_history_tracker().get_context_for_subagent(
|
|
397
|
+
return get_code_history_tracker(thread_id).get_context_for_subagent(
|
|
398
|
+
existing_context
|
|
399
|
+
)
|
|
400
|
+
|
|
349
401
|
|
|
402
|
+
def clear_code_history(thread_id: Optional[str] = None) -> None:
|
|
403
|
+
"""
|
|
404
|
+
Clear code history for a specific thread or all threads.
|
|
350
405
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
406
|
+
Args:
|
|
407
|
+
thread_id: Thread ID to clear. If None, clears all threads.
|
|
408
|
+
"""
|
|
409
|
+
if thread_id is None:
|
|
410
|
+
# Clear all trackers
|
|
411
|
+
with _trackers_lock:
|
|
412
|
+
for tid, tracker in _code_history_trackers.items():
|
|
413
|
+
tracker.clear()
|
|
414
|
+
logger.info(f"CodeHistory: Cleared history for thread_id={tid}")
|
|
415
|
+
_code_history_trackers.clear()
|
|
416
|
+
logger.info("CodeHistory: Cleared all thread trackers")
|
|
417
|
+
else:
|
|
418
|
+
# Clear specific thread
|
|
419
|
+
with _trackers_lock:
|
|
420
|
+
if thread_id in _code_history_trackers:
|
|
421
|
+
_code_history_trackers[thread_id].clear()
|
|
422
|
+
del _code_history_trackers[thread_id]
|
|
423
|
+
logger.info(
|
|
424
|
+
f"CodeHistory: Cleared and removed tracker for thread_id={thread_id}"
|
|
425
|
+
)
|
|
426
|
+
else:
|
|
427
|
+
logger.info(f"CodeHistory: No tracker found for thread_id={thread_id}")
|
|
354
428
|
|
|
355
429
|
|
|
356
|
-
def track_tool_execution(
|
|
430
|
+
def track_tool_execution(
|
|
431
|
+
tool_name: str,
|
|
432
|
+
args: Dict[str, Any],
|
|
433
|
+
thread_id: Optional[str] = None,
|
|
434
|
+
) -> None:
|
|
357
435
|
"""
|
|
358
436
|
Track a tool execution from HITL decision processing.
|
|
359
437
|
|
|
@@ -363,6 +441,7 @@ def track_tool_execution(tool_name: str, args: Dict[str, Any]) -> None:
|
|
|
363
441
|
Args:
|
|
364
442
|
tool_name: Name of the tool (jupyter_cell_tool, write_file_tool, etc.)
|
|
365
443
|
args: Tool arguments including execution_result
|
|
444
|
+
thread_id: Thread ID for session isolation
|
|
366
445
|
"""
|
|
367
446
|
if not args:
|
|
368
447
|
return
|
|
@@ -371,21 +450,26 @@ def track_tool_execution(tool_name: str, args: Dict[str, Any]) -> None:
|
|
|
371
450
|
if not execution_result:
|
|
372
451
|
return
|
|
373
452
|
|
|
374
|
-
tracker = get_code_history_tracker()
|
|
453
|
+
tracker = get_code_history_tracker(thread_id)
|
|
375
454
|
|
|
376
455
|
if tool_name == "jupyter_cell_tool":
|
|
377
456
|
code = args.get("code", "")
|
|
378
457
|
output = execution_result.get("output", "")
|
|
379
458
|
if code:
|
|
380
459
|
tracker.add_jupyter_cell(code, output)
|
|
381
|
-
logger.info(
|
|
460
|
+
logger.info(
|
|
461
|
+
f"CodeHistory: Tracked jupyter_cell execution "
|
|
462
|
+
f"(code len={len(code)}, thread_id={thread_id})"
|
|
463
|
+
)
|
|
382
464
|
|
|
383
465
|
elif tool_name == "write_file_tool":
|
|
384
466
|
file_path = args.get("path", "")
|
|
385
467
|
content = args.get("content", "")
|
|
386
468
|
if file_path:
|
|
387
469
|
tracker.add_write_file(file_path, content)
|
|
388
|
-
logger.info(
|
|
470
|
+
logger.info(
|
|
471
|
+
f"CodeHistory: Tracked write_file to {file_path} (thread_id={thread_id})"
|
|
472
|
+
)
|
|
389
473
|
|
|
390
474
|
elif tool_name == "edit_file_tool":
|
|
391
475
|
file_path = args.get("path", "")
|
|
@@ -393,7 +477,9 @@ def track_tool_execution(tool_name: str, args: Dict[str, Any]) -> None:
|
|
|
393
477
|
new_string = args.get("new_string", "")
|
|
394
478
|
if file_path:
|
|
395
479
|
tracker.add_edit_file(file_path, old_string, new_string)
|
|
396
|
-
logger.info(
|
|
480
|
+
logger.info(
|
|
481
|
+
f"CodeHistory: Tracked edit_file to {file_path} (thread_id={thread_id})"
|
|
482
|
+
)
|
|
397
483
|
|
|
398
484
|
elif tool_name == "multiedit_file_tool":
|
|
399
485
|
file_path = args.get("path", "")
|
|
@@ -409,4 +495,7 @@ def track_tool_execution(tool_name: str, args: Dict[str, Any]) -> None:
|
|
|
409
495
|
elif isinstance(edit, dict):
|
|
410
496
|
edits_as_dicts.append(edit)
|
|
411
497
|
tracker.add_multiedit_file(file_path, edits_as_dicts)
|
|
412
|
-
logger.info(
|
|
498
|
+
logger.info(
|
|
499
|
+
f"CodeHistory: Tracked multiedit_file to {file_path} "
|
|
500
|
+
f"({len(edits)} edits, thread_id={thread_id})"
|
|
501
|
+
)
|
|
@@ -12,6 +12,7 @@ Key features:
|
|
|
12
12
|
- Subagent caching: compiled agents are cached to avoid recompilation overhead
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
+
import contextvars
|
|
15
16
|
import hashlib
|
|
16
17
|
import json
|
|
17
18
|
import logging
|
|
@@ -25,6 +26,11 @@ if TYPE_CHECKING:
|
|
|
25
26
|
|
|
26
27
|
logger = logging.getLogger(__name__)
|
|
27
28
|
|
|
29
|
+
# Context variable to track the current main agent's thread_id
|
|
30
|
+
_current_thread_id: contextvars.ContextVar[Optional[str]] = contextvars.ContextVar(
|
|
31
|
+
"current_thread_id", default=None
|
|
32
|
+
)
|
|
33
|
+
|
|
28
34
|
# Global registry for subagent factories (set by AgentFactory)
|
|
29
35
|
_subagent_factory = None
|
|
30
36
|
_current_llm_config = None
|
|
@@ -92,6 +98,17 @@ def clear_subagent_cache():
|
|
|
92
98
|
logger.info(f"Subagent cache cleared ({count} entries removed)")
|
|
93
99
|
|
|
94
100
|
|
|
101
|
+
def set_current_thread_id(thread_id: str) -> None:
|
|
102
|
+
"""Set the current main agent's thread_id for code history tracking."""
|
|
103
|
+
_current_thread_id.set(thread_id)
|
|
104
|
+
logger.debug(f"Set current thread_id: {thread_id}")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_current_thread_id() -> Optional[str]:
|
|
108
|
+
"""Get the current main agent's thread_id."""
|
|
109
|
+
return _current_thread_id.get()
|
|
110
|
+
|
|
111
|
+
|
|
95
112
|
def create_task_tool(
|
|
96
113
|
caller_name: str,
|
|
97
114
|
allowed_subagents: Optional[List[str]] = None,
|
|
@@ -232,13 +249,18 @@ def create_task_tool(
|
|
|
232
249
|
get_context_with_history,
|
|
233
250
|
)
|
|
234
251
|
|
|
235
|
-
|
|
252
|
+
# Get main agent's thread_id for session-scoped history
|
|
253
|
+
main_thread_id = get_current_thread_id()
|
|
254
|
+
tracker = get_code_history_tracker(main_thread_id)
|
|
236
255
|
if tracker.get_entry_count() > 0:
|
|
237
|
-
enhanced_context = get_context_with_history(
|
|
256
|
+
enhanced_context = get_context_with_history(
|
|
257
|
+
context, main_thread_id
|
|
258
|
+
)
|
|
238
259
|
t3 = time.time()
|
|
239
260
|
logger.info(
|
|
240
261
|
f"[TIMING] code history injection took {t3-t2:.2f}s "
|
|
241
262
|
f"(entries={tracker.get_entry_count()}, "
|
|
263
|
+
f"thread_id={main_thread_id}, "
|
|
242
264
|
f"context_len={len(enhanced_context) if enhanced_context else 0})"
|
|
243
265
|
)
|
|
244
266
|
except Exception as e:
|
|
@@ -695,6 +695,19 @@ async def stream_agent(request: AgentRequest):
|
|
|
695
695
|
thread_id,
|
|
696
696
|
)
|
|
697
697
|
|
|
698
|
+
# Handle @reset command
|
|
699
|
+
if "@reset" in request.request:
|
|
700
|
+
logger.info(f"@reset command detected for thread: {thread_id}")
|
|
701
|
+
from agent_server.langchain.middleware.code_history_middleware import (
|
|
702
|
+
clear_code_history,
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
# Clear code history and checkpointer for this thread
|
|
706
|
+
clear_code_history(thread_id)
|
|
707
|
+
if thread_id in _simple_agent_checkpointers:
|
|
708
|
+
del _simple_agent_checkpointers[thread_id]
|
|
709
|
+
logger.info(f"Session reset complete for thread: {thread_id}")
|
|
710
|
+
|
|
698
711
|
async def event_generator():
|
|
699
712
|
try:
|
|
700
713
|
# Use simple agent with HITL
|
|
@@ -739,6 +752,15 @@ async def stream_agent(request: AgentRequest):
|
|
|
739
752
|
len(_simple_agent_checkpointers),
|
|
740
753
|
)
|
|
741
754
|
|
|
755
|
+
# Clear code history for new threads
|
|
756
|
+
if not is_existing_thread:
|
|
757
|
+
from agent_server.langchain.middleware.code_history_middleware import (
|
|
758
|
+
clear_code_history,
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
clear_code_history(thread_id)
|
|
762
|
+
logger.info(f"Code history cleared for new thread: {thread_id}")
|
|
763
|
+
|
|
742
764
|
resolved_workspace_root = _resolve_workspace_root(request.workspaceRoot)
|
|
743
765
|
|
|
744
766
|
# Get agent mode (single or multi)
|
|
@@ -838,6 +860,13 @@ async def stream_agent(request: AgentRequest):
|
|
|
838
860
|
# Prepare config with thread_id
|
|
839
861
|
config = {"configurable": {"thread_id": thread_id}}
|
|
840
862
|
|
|
863
|
+
# Set current thread_id for code history tracking
|
|
864
|
+
from agent_server.langchain.middleware.subagent_middleware import (
|
|
865
|
+
set_current_thread_id,
|
|
866
|
+
)
|
|
867
|
+
|
|
868
|
+
set_current_thread_id(thread_id)
|
|
869
|
+
|
|
841
870
|
# Check existing state and ALWAYS reset todos for new request
|
|
842
871
|
# Each new user request starts a fresh todo list
|
|
843
872
|
should_reset_todos = False
|
|
@@ -2116,6 +2145,13 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2116
2145
|
# Prepare config with thread_id
|
|
2117
2146
|
config = {"configurable": {"thread_id": request.threadId}}
|
|
2118
2147
|
|
|
2148
|
+
# Set current thread_id for code history tracking
|
|
2149
|
+
from agent_server.langchain.middleware.subagent_middleware import (
|
|
2150
|
+
set_current_thread_id,
|
|
2151
|
+
)
|
|
2152
|
+
|
|
2153
|
+
set_current_thread_id(request.threadId)
|
|
2154
|
+
|
|
2119
2155
|
pending_actions = _simple_agent_pending_actions.get(request.threadId, [])
|
|
2120
2156
|
|
|
2121
2157
|
# Convert decisions to LangChain format
|
|
@@ -2152,7 +2188,7 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2152
2188
|
"edit_file_tool",
|
|
2153
2189
|
"multiedit_file_tool",
|
|
2154
2190
|
):
|
|
2155
|
-
track_tool_execution(tool_name, args)
|
|
2191
|
+
track_tool_execution(tool_name, args, request.threadId)
|
|
2156
2192
|
langgraph_decisions.append(
|
|
2157
2193
|
{
|
|
2158
2194
|
"type": "edit",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hdsp-agent",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.27",
|
|
4
4
|
"description": "HDSP Agent JupyterLab Extension - Thin client for Agent Server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
}
|
|
133
133
|
},
|
|
134
134
|
"_build": {
|
|
135
|
-
"load": "static/remoteEntry.
|
|
135
|
+
"load": "static/remoteEntry.4ab73bb5068405670214.js",
|
|
136
136
|
"extension": "./extension",
|
|
137
137
|
"style": "./style"
|
|
138
138
|
}
|
|
@@ -281,7 +281,7 @@ __webpack_require__.d(exports, {
|
|
|
281
281
|
/******/ register("@emotion/react", "11.14.0", () => (Promise.all([__webpack_require__.e("vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js"), __webpack_require__.e("vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195"), __webpack_require__.e("vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js"), __webpack_require__.e("webpack_sharing_consume_default_react"), __webpack_require__.e("node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80")]).then(() => (() => (__webpack_require__(/*! ./node_modules/@emotion/react/dist/emotion-react.browser.development.esm.js */ "./node_modules/@emotion/react/dist/emotion-react.browser.development.esm.js"))))));
|
|
282
282
|
/******/ register("@emotion/styled", "11.14.1", () => (Promise.all([__webpack_require__.e("vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195"), __webpack_require__.e("vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js"), __webpack_require__.e("webpack_sharing_consume_default_react"), __webpack_require__.e("webpack_sharing_consume_default_emotion_react_emotion_react")]).then(() => (() => (__webpack_require__(/*! ./node_modules/@emotion/styled/dist/emotion-styled.browser.development.esm.js */ "./node_modules/@emotion/styled/dist/emotion-styled.browser.development.esm.js"))))));
|
|
283
283
|
/******/ register("@mui/material", "5.18.0", () => (Promise.all([__webpack_require__.e("vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js"), __webpack_require__.e("vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195"), __webpack_require__.e("vendors-node_modules_mui_material_index_js"), __webpack_require__.e("vendors-node_modules_mui_material_utils_createSvgIcon_js"), __webpack_require__.e("webpack_sharing_consume_default_react"), __webpack_require__.e("webpack_sharing_consume_default_emotion_react_emotion_react-webpack_sharing_consume_default_e-5f41bf")]).then(() => (() => (__webpack_require__(/*! ./node_modules/@mui/material/index.js */ "./node_modules/@mui/material/index.js"))))));
|
|
284
|
-
/******/ register("hdsp-agent", "2.0.
|
|
284
|
+
/******/ register("hdsp-agent", "2.0.27", () => (Promise.all([__webpack_require__.e("vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js"), __webpack_require__.e("vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195"), __webpack_require__.e("vendors-node_modules_mui_material_utils_createSvgIcon_js"), __webpack_require__.e("webpack_sharing_consume_default_react"), __webpack_require__.e("webpack_sharing_consume_default_emotion_react_emotion_react-webpack_sharing_consume_default_e-5f41bf"), __webpack_require__.e("lib_index_js")]).then(() => (() => (__webpack_require__(/*! ./lib/index.js */ "./lib/index.js"))))));
|
|
285
285
|
/******/ }
|
|
286
286
|
/******/ break;
|
|
287
287
|
/******/ }
|
|
@@ -622,4 +622,4 @@ __webpack_require__.d(exports, {
|
|
|
622
622
|
/******/
|
|
623
623
|
/******/ })()
|
|
624
624
|
;
|
|
625
|
-
//# sourceMappingURL=remoteEntry.
|
|
625
|
+
//# sourceMappingURL=remoteEntry.4ab73bb5068405670214.js.map
|