hdsp-jupyter-extension 2.0.11__py3-none-any.whl → 2.0.13__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.
Files changed (81) hide show
  1. agent_server/langchain/MULTI_AGENT_ARCHITECTURE.md +1114 -0
  2. agent_server/langchain/__init__.py +2 -2
  3. agent_server/langchain/agent.py +72 -33
  4. agent_server/langchain/agent_factory.py +400 -0
  5. agent_server/langchain/agent_prompts/__init__.py +25 -0
  6. agent_server/langchain/agent_prompts/athena_query_prompt.py +71 -0
  7. agent_server/langchain/agent_prompts/planner_prompt.py +85 -0
  8. agent_server/langchain/agent_prompts/python_developer_prompt.py +123 -0
  9. agent_server/langchain/agent_prompts/researcher_prompt.py +38 -0
  10. agent_server/langchain/custom_middleware.py +652 -195
  11. agent_server/langchain/hitl_config.py +34 -10
  12. agent_server/langchain/middleware/__init__.py +24 -0
  13. agent_server/langchain/middleware/code_history_middleware.py +412 -0
  14. agent_server/langchain/middleware/description_injector.py +150 -0
  15. agent_server/langchain/middleware/skill_middleware.py +298 -0
  16. agent_server/langchain/middleware/subagent_events.py +171 -0
  17. agent_server/langchain/middleware/subagent_middleware.py +329 -0
  18. agent_server/langchain/prompts.py +96 -101
  19. agent_server/langchain/skills/data_analysis.md +236 -0
  20. agent_server/langchain/skills/data_loading.md +158 -0
  21. agent_server/langchain/skills/inference.md +392 -0
  22. agent_server/langchain/skills/model_training.md +318 -0
  23. agent_server/langchain/skills/pyspark.md +352 -0
  24. agent_server/langchain/subagents/__init__.py +20 -0
  25. agent_server/langchain/subagents/base.py +173 -0
  26. agent_server/langchain/tools/__init__.py +3 -0
  27. agent_server/langchain/tools/jupyter_tools.py +58 -20
  28. agent_server/langchain/tools/lsp_tools.py +1 -1
  29. agent_server/langchain/tools/shared/__init__.py +26 -0
  30. agent_server/langchain/tools/shared/qdrant_search.py +175 -0
  31. agent_server/langchain/tools/tool_registry.py +219 -0
  32. agent_server/langchain/tools/workspace_tools.py +197 -0
  33. agent_server/routers/config.py +40 -1
  34. agent_server/routers/langchain_agent.py +818 -337
  35. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/build_log.json +1 -1
  36. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/package.json +7 -2
  37. hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2d9fb488c82498c45c2d.js → hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.037b3c8e5d6a92b63b16.js +1108 -179
  38. hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.037b3c8e5d6a92b63b16.js.map +1 -0
  39. jupyter_ext/labextension/static/lib_index_js.58c1e128ba0b76f41f04.js → hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.5449ba3c7e25177d2987.js +3916 -8128
  40. hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.5449ba3c7e25177d2987.js.map +1 -0
  41. hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.9da31d1134a53b0c4af5.js → hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.a8e0b064eb9b1c1ff463.js +17 -17
  42. hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.a8e0b064eb9b1c1ff463.js.map +1 -0
  43. {hdsp_jupyter_extension-2.0.11.dist-info → hdsp_jupyter_extension-2.0.13.dist-info}/METADATA +1 -1
  44. {hdsp_jupyter_extension-2.0.11.dist-info → hdsp_jupyter_extension-2.0.13.dist-info}/RECORD +75 -51
  45. jupyter_ext/_version.py +1 -1
  46. jupyter_ext/handlers.py +59 -8
  47. jupyter_ext/labextension/build_log.json +1 -1
  48. jupyter_ext/labextension/package.json +7 -2
  49. jupyter_ext/labextension/static/{frontend_styles_index_js.2d9fb488c82498c45c2d.js → frontend_styles_index_js.037b3c8e5d6a92b63b16.js} +1108 -179
  50. jupyter_ext/labextension/static/frontend_styles_index_js.037b3c8e5d6a92b63b16.js.map +1 -0
  51. hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.58c1e128ba0b76f41f04.js → jupyter_ext/labextension/static/lib_index_js.5449ba3c7e25177d2987.js +3916 -8128
  52. jupyter_ext/labextension/static/lib_index_js.5449ba3c7e25177d2987.js.map +1 -0
  53. jupyter_ext/labextension/static/{remoteEntry.9da31d1134a53b0c4af5.js → remoteEntry.a8e0b064eb9b1c1ff463.js} +17 -17
  54. jupyter_ext/labextension/static/remoteEntry.a8e0b064eb9b1c1ff463.js.map +1 -0
  55. hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2d9fb488c82498c45c2d.js.map +0 -1
  56. hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.58c1e128ba0b76f41f04.js.map +0 -1
  57. hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.9da31d1134a53b0c4af5.js.map +0 -1
  58. jupyter_ext/labextension/static/frontend_styles_index_js.2d9fb488c82498c45c2d.js.map +0 -1
  59. jupyter_ext/labextension/static/lib_index_js.58c1e128ba0b76f41f04.js.map +0 -1
  60. jupyter_ext/labextension/static/remoteEntry.9da31d1134a53b0c4af5.js.map +0 -1
  61. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +0 -0
  62. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
  63. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.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
  64. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.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
  65. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.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
  66. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.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
  67. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/style.js +0 -0
  68. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.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
  69. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.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
  70. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js +0 -0
  71. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.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
  72. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js +0 -0
  73. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.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
  74. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js +0 -0
  75. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.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
  76. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +0 -0
  77. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js.map +0 -0
  78. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js +0 -0
  79. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js.map +0 -0
  80. {hdsp_jupyter_extension-2.0.11.dist-info → hdsp_jupyter_extension-2.0.13.dist-info}/WHEEL +0 -0
  81. {hdsp_jupyter_extension-2.0.11.dist-info → hdsp_jupyter_extension-2.0.13.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,329 @@
1
+ """
2
+ SubAgentMiddleware
3
+
4
+ Middleware that enables subagent delegation via the `task` tool.
5
+ Based on Deep Agents library pattern (benchmarked, not installed).
6
+
7
+ Key features:
8
+ - Provides task(agent_name, description) tool for subagent invocation
9
+ - Context isolation: subagents run in clean context
10
+ - Synchronous execution: subagent returns result directly to caller
11
+ - Nested subagent support: python_developer can call athena_query
12
+ """
13
+
14
+ import logging
15
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional
16
+
17
+ from langchain_core.tools import tool
18
+ from pydantic import BaseModel, Field
19
+
20
+ if TYPE_CHECKING:
21
+ pass
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+ # Global registry for subagent factories (set by AgentFactory)
26
+ _subagent_factory = None
27
+ _current_llm_config = None
28
+
29
+
30
+ def set_subagent_factory(factory_func, llm_config: Dict[str, Any]):
31
+ """
32
+ Set the subagent factory function.
33
+ Called by AgentFactory during initialization.
34
+ """
35
+ global _subagent_factory, _current_llm_config
36
+ _subagent_factory = factory_func
37
+ _current_llm_config = llm_config
38
+ logger.info("SubAgentMiddleware factory initialized")
39
+
40
+
41
+ def get_subagent_factory():
42
+ """Get the current subagent factory function."""
43
+ return _subagent_factory, _current_llm_config
44
+
45
+
46
+ def create_task_tool(
47
+ caller_name: str,
48
+ allowed_subagents: Optional[List[str]] = None,
49
+ ):
50
+ """
51
+ Create a task tool for calling subagents.
52
+
53
+ The task tool executes subagents synchronously and returns their result.
54
+ Subagents like python_developer return generated code/analysis,
55
+ which the Main Agent can then execute if needed.
56
+
57
+ Args:
58
+ caller_name: Name of the agent creating this tool (for logging/validation)
59
+ allowed_subagents: Optional list of subagent names this agent can call.
60
+ If None, all subagents are allowed (for Main Agent).
61
+
62
+ Returns:
63
+ A tool that can be used to delegate tasks to subagents.
64
+ """
65
+ from agent_server.langchain.subagents.base import (
66
+ SUBAGENT_CONFIGS,
67
+ get_subagent_config,
68
+ )
69
+
70
+ # Build description based on allowed subagents
71
+ if allowed_subagents:
72
+ available = [
73
+ f"- {name}: {SUBAGENT_CONFIGS[name].description}"
74
+ for name in allowed_subagents
75
+ if name in SUBAGENT_CONFIGS
76
+ ]
77
+ else:
78
+ # Main Agent (planner) can call non-restricted subagents OR those explicitly allowing "planner"
79
+ available = [
80
+ f"- {config.name}: {config.description}"
81
+ for config in SUBAGENT_CONFIGS.values()
82
+ if not config.callable_by or caller_name in config.callable_by
83
+ ]
84
+
85
+ available_str = "\n".join(available)
86
+
87
+ # Create Pydantic schema for the task tool (required for Gemini compatibility)
88
+ class TaskInput(BaseModel):
89
+ """Input schema for task tool"""
90
+
91
+ agent_name: str = Field(
92
+ description=f"Name of the subagent to invoke. Available: {', '.join(allowed_subagents) if allowed_subagents else 'python_developer, researcher, athena_query'}"
93
+ )
94
+ description: str = Field(
95
+ description="Detailed task description for the subagent (Korean preferred)"
96
+ )
97
+ context: Optional[str] = Field(
98
+ default=None,
99
+ description="Additional context for the subagent: resource info (file sizes, memory), previous code, variable state, etc."
100
+ )
101
+
102
+ @tool(args_schema=TaskInput)
103
+ def task_tool(agent_name: str, description: str, context: Optional[str] = None) -> str:
104
+ """
105
+ Delegate a task to a specialized subagent.
106
+
107
+ The subagent will execute the task and return its result (code, analysis, etc.).
108
+ Code execution tools (jupyter_cell_tool, write_file_tool) are handled by Main Agent.
109
+
110
+ Args:
111
+ agent_name: Name of the subagent to invoke
112
+ description: Detailed task description for the subagent
113
+ context: Additional context (resource info, previous code, etc.)
114
+
115
+ Returns:
116
+ Result from the subagent execution (string summary or generated code)
117
+ """
118
+ # Validate subagent exists
119
+ if agent_name not in SUBAGENT_CONFIGS:
120
+ return f"Error: Unknown agent '{agent_name}'. Available agents:\n{available_str}"
121
+
122
+ # Validate caller is allowed to call this subagent
123
+ config = get_subagent_config(agent_name)
124
+ if allowed_subagents and agent_name not in allowed_subagents:
125
+ return f"Error: '{caller_name}' cannot call '{agent_name}'. Allowed: {allowed_subagents}"
126
+
127
+ if config.callable_by and caller_name not in config.callable_by:
128
+ return f"Error: '{agent_name}' can only be called by: {config.callable_by}"
129
+
130
+ logger.info(
131
+ f"[{caller_name}] Invoking subagent '{agent_name}': {description[:100]}..."
132
+ )
133
+
134
+ # Import subagent event emitters
135
+ from agent_server.langchain.middleware.subagent_events import (
136
+ emit_subagent_start,
137
+ emit_subagent_complete,
138
+ set_current_subagent,
139
+ clear_current_subagent,
140
+ )
141
+
142
+ # Emit subagent start event for UI
143
+ emit_subagent_start(agent_name, description)
144
+
145
+ # Get the factory and config
146
+ factory_func, llm_config = get_subagent_factory()
147
+ if factory_func is None:
148
+ return "Error: SubAgentMiddleware not initialized. Call set_subagent_factory first."
149
+
150
+ try:
151
+ # Set current subagent context for tool call tracking
152
+ set_current_subagent(agent_name)
153
+
154
+ # Create the subagent
155
+ subagent = factory_func(agent_name, llm_config)
156
+
157
+ # Execute subagent synchronously with clean context
158
+ # The subagent runs in isolation, receiving task description + optional context
159
+ import uuid
160
+
161
+ subagent_thread_id = f"subagent-{agent_name}-{uuid.uuid4().hex[:8]}"
162
+ subagent_config = {
163
+ "configurable": {
164
+ "thread_id": subagent_thread_id,
165
+ }
166
+ }
167
+
168
+ # Inject code history for python_developer
169
+ enhanced_context = context
170
+ if agent_name == "python_developer":
171
+ try:
172
+ from agent_server.langchain.middleware.code_history_middleware import (
173
+ get_context_with_history,
174
+ get_code_history_tracker,
175
+ )
176
+ tracker = get_code_history_tracker()
177
+ if tracker.get_entry_count() > 0:
178
+ enhanced_context = get_context_with_history(context)
179
+ logger.info(
180
+ f"[{caller_name}] Injected code history into context "
181
+ f"(entries={tracker.get_entry_count()}, "
182
+ f"context_len={len(enhanced_context) if enhanced_context else 0})"
183
+ )
184
+ except Exception as e:
185
+ logger.warning(f"Failed to inject code history: {e}")
186
+
187
+ # Build the message content with optional context
188
+ if enhanced_context:
189
+ message_content = f"""## Task
190
+ {description}
191
+
192
+ ## Context (provided by Main Agent)
193
+ {enhanced_context}"""
194
+ else:
195
+ message_content = description
196
+
197
+ logger.info(f"[{caller_name}] Subagent message length: {len(message_content)}")
198
+
199
+ # Execute the subagent
200
+ result = subagent.invoke(
201
+ {"messages": [{"role": "user", "content": message_content}]},
202
+ config=subagent_config,
203
+ )
204
+
205
+ # Extract the final message from the result
206
+ messages = result.get("messages", [])
207
+ if messages:
208
+ final_message = messages[-1]
209
+ if hasattr(final_message, "content"):
210
+ response = final_message.content
211
+ else:
212
+ response = str(final_message)
213
+ else:
214
+ response = "Subagent completed but returned no messages."
215
+
216
+ logger.info(
217
+ f"[{caller_name}] Subagent '{agent_name}' returned: {str(response)[:200]}..."
218
+ )
219
+
220
+ # Extract description from python_developer response for auto-injection
221
+ if agent_name == "python_developer":
222
+ try:
223
+ from agent_server.langchain.middleware.description_injector import (
224
+ process_task_tool_response,
225
+ )
226
+ process_task_tool_response(agent_name, str(response))
227
+ except Exception as e:
228
+ logger.warning(f"Failed to extract description: {e}")
229
+
230
+ # Emit subagent complete event for UI
231
+ emit_subagent_complete(agent_name, str(response)[:100])
232
+
233
+ return response
234
+
235
+ except Exception as e:
236
+ error_msg = f"Subagent '{agent_name}' failed: {str(e)}"
237
+ logger.error(error_msg, exc_info=True)
238
+ # Emit complete event even on error
239
+ emit_subagent_complete(agent_name, f"Error: {str(e)[:50]}")
240
+ return f"Error: {error_msg}"
241
+ finally:
242
+ # Always clear subagent context
243
+ clear_current_subagent()
244
+
245
+ # Update tool docstring with available agents
246
+ task_tool.__doc__ = f"""Delegate a task to a specialized subagent.
247
+
248
+ Available agents:
249
+ {available_str}
250
+
251
+ The subagent will analyze the task and return its result.
252
+ - python_developer: Returns generated Python code and analysis
253
+ - researcher: Returns search results and findings
254
+ - athena_query: Returns SQL query string
255
+
256
+ For code execution (running Python, writing files), use Main Agent's tools directly.
257
+
258
+ IMPORTANT: For python_developer, ALWAYS provide context with:
259
+ - Resource info (file sizes, memory) from check_resource_tool
260
+ - Previous code context if building on existing work
261
+ - Variable names and their current state
262
+
263
+ Args:
264
+ agent_name: Name of the subagent to invoke
265
+ description: Detailed task description for the subagent
266
+ context: Additional context (resource info, previous code, etc.)
267
+
268
+ Returns:
269
+ Result from the subagent execution
270
+ """
271
+
272
+ return task_tool
273
+
274
+
275
+ class SubAgentMiddleware:
276
+ """
277
+ Middleware that adds subagent delegation capability.
278
+
279
+ This middleware:
280
+ 1. Adds the `task` tool to the agent's toolset
281
+ 2. The task tool executes subagents synchronously
282
+ 3. Subagent results are returned directly to the caller
283
+
284
+ Usage:
285
+ middleware = SubAgentMiddleware(
286
+ caller_name="main_agent",
287
+ allowed_subagents=["python_developer", "researcher"],
288
+ )
289
+
290
+ agent = create_agent(
291
+ model=llm,
292
+ tools=tools,
293
+ middleware=[middleware, ...],
294
+ )
295
+ """
296
+
297
+ def __init__(
298
+ self,
299
+ caller_name: str,
300
+ allowed_subagents: Optional[List[str]] = None,
301
+ ):
302
+ """
303
+ Initialize SubAgentMiddleware.
304
+
305
+ Args:
306
+ caller_name: Name of the agent using this middleware
307
+ allowed_subagents: List of subagents this agent can call.
308
+ None means all non-restricted subagents.
309
+ """
310
+ self.caller_name = caller_name
311
+ self.allowed_subagents = allowed_subagents
312
+ self.task_tool = create_task_tool(caller_name, allowed_subagents)
313
+
314
+ logger.info(
315
+ f"SubAgentMiddleware initialized for '{caller_name}' "
316
+ f"with allowed_subagents={allowed_subagents}"
317
+ )
318
+
319
+ def get_tools(self) -> List[Any]:
320
+ """Get the tools provided by this middleware."""
321
+ return [self.task_tool]
322
+
323
+ def __call__(self, tools: List[Any]) -> List[Any]:
324
+ """
325
+ Add task tool to the agent's toolset.
326
+
327
+ This is called during agent creation to augment the tool list.
328
+ """
329
+ return tools + [self.task_tool]
@@ -1,67 +1,62 @@
1
1
  """
2
2
  Prompt templates for LangChain agent.
3
-
4
- Contains system prompts, JSON schema for fallback tool calling,
5
- and middleware-specific prompts.
6
3
  """
7
4
 
8
- DEFAULT_SYSTEM_PROMPT = """You are an expert Python data scientist and Jupyter notebook assistant. Respond in Korean only.
9
-
10
- # Core Rules
11
- 1. Be concise (≤4 lines unless detail requested)
12
- 2. ALWAYS call a tool in every response - never respond with text only
13
- 3. ALWAYS include a brief Korean explanation before tool calls
14
-
15
- # Task Workflow
16
-
17
- ## Simple Tasks (1-2 steps)
18
- Execute directly without todos.
19
-
20
- ## Complex Tasks (3+ steps)
21
- 1. Create todos with write_todos (all items in Korean)
22
- 2. ALWAYS include "작업 요약 및 다음단계 제시" as the LAST item
23
- 3. After each tool result: check todos → call next tool → repeat
24
- 4. **Final todo ("작업 요약 다음단계 제시")**:
25
- - FIRST: Output summary JSON in your content (REQUIRED!)
26
- - THEN: Call write_todos to mark all as completed
27
- - Both must be in the SAME response
28
-
29
- ### Summary JSON Format (MUST output before marking complete)
30
- ```json
31
- {"summary": "실행된 작업 요약", "next_items": [{"subject": "제목", "description": "설명"}]}
32
- ```
33
- Suggest 3-5 next items. **You CANNOT mark "작업 요약" as completed without outputting this JSON first.**
34
-
35
- # Mandatory Checks
36
-
37
- ## Resource Check (BEFORE data operations)
38
- Call `check_resource_tool` FIRST when:
39
- - Loading files (.csv, .parquet, .json, .xlsx, .pickle, .h5, .feather)
40
- - Using pandas/polars/dask for dataframes
41
- - Training ML models
42
-
43
- # Tool Usage
44
-
45
- ## File Search (execute_command_tool)
46
- ```bash
47
- find . -iname '*filename*.csv' 2>/dev/null # Find by name
48
- grep -rn 'pattern' --include='*.py' . # Search contents
49
- ```
50
-
51
- ## File Reading (read_file_tool)
52
- - Large files: `read_file_tool(path, limit=100)` first
53
- - Use `offset` for pagination
54
- - Small files (<500 lines): Read without limit
55
-
56
- ## Code Output
57
- - For plots/charts: Use English labels only
58
- - Use LSP tools for error checking and symbol lookup
59
- - Use multiedit_file_tool for multiple changes
60
-
61
- # Forbidden
62
- - Empty responses (no tool call AND no content)
63
- - Tool calls without Korean explanation
64
- - Stopping with pending/in_progress todos
5
+ DEFAULT_SYSTEM_PROMPT = """You are an expert Python data scientist and Jupyter notebook assistant.
6
+
7
+ # 핵심 규칙
8
+ 1. 한국어로 응답하세요
9
+ 2. 간결하게 (4줄 이하, 상세 요청 예외)
10
+ 3. 모든 응답에 도구를 호출하세요 (텍스트만 응답 금지)
11
+ 4. **[필수] 도구 호출 시 반드시 텍스트 설명을 함께 출력**
12
+ - 예: "파일 구조를 확인하겠습니다." + tool_call
13
+ - 설명 없이 tool_call만 하면 안 됨
14
+
15
+ # 작업 흐름
16
+ 1. 간단한 작업 (1-2단계): 바로 실행
17
+ 2. 복잡한 작업 (3단계+): write_todos로 계획 → 순차 실행
18
+
19
+ # write_todos 규칙 [필수]
20
+ - 한국어로 작성
21
+ - **🔴 기존 todo 절대 삭제 금지**: 전체 리스트를 항상 포함하고 status만 변경
22
+ - 잘못된 예: [{"content": "작업 요약", "status": "completed"}] 기존 todo 삭제됨!
23
+ - 올바른 예: [{"content": "기존 작업1", "status": "completed"}, {"content": "기존 작업2", "status": "completed"}, {"content": "작업 요약", "status": "completed"}]
24
+ - **일괄 업데이트**: 연속 완료된 todo는 번의 write_todos 호출로 처리
25
+ - in_progress는 **1개만** 유지
26
+ - **[필수] 마지막 todo는 반드시 "작업 요약 다음 단계 제시"로 생성**
27
+ - **🔴 [실행 순서 필수]**: "작업 요약 및 다음 단계 제시"는 **반드시 가장 마지막에 실행**
28
+ - 다른 모든 todo가 completed 상태가 후에만 이 todo를 in_progress로 변경
29
+ - 비슷한 이름의 다른 작업(보고서 검토, 결과 정리 등)과 혼동 금지
30
+ - **[중요] "작업 요약 다음 단계 제시"는 summary JSON 출력 후에만 completed 표시**
31
+
32
+ # 모든 작업 완료 후 [필수]
33
+ 마지막 todo "작업 요약 및 다음 단계 제시"를 completed로 변경한 후,
34
+ **텍스트 응답으로** 아래 JSON을 출력하세요 (todo content가 아님!):
35
+ {"summary": "완료된 작업 요약", "next_items": [{"subject": "제목", "description": "설명"}]}
36
+ - next_items 3개 이상 필수
37
+ - **summary JSON 없이 종료 금지**
38
+ - **주의**: JSON은 todo 항목이 아닌 일반 텍스트 응답으로 출력
39
+
40
+ # 도구 사용
41
+ - check_resource_tool: 대용량 파일/데이터프레임 작업 전 필수
42
+ - read_file_tool: 대용량 파일은 limit=100으로 먼저 확인
43
+ - jupyter_cell_tool: 차트 라벨은 영어로
44
+ - **파일 수정 후**: diagnostics_tool로 오류 확인 필수
45
+
46
+ # 사용자 입력 요청 [중요]
47
+ - **ask_user_tool**: 사용자 응답이 필요할 때 사용 (파일 업로드, 선택, 정보 요청)
48
+ - 파일 업로드: ask_user_tool(question="kaggle.json 파일을 업로드해 주세요", input_type="file")
49
+ - 선택 요청: ask_user_tool(question="모델 선택?", options=["Logistic", "RandomForest"])
50
+ - 정보 요청: ask_user_tool(question="API 키를 입력해 주세요", input_type="text")
51
+ - **markdown_tool**: 정보 출력용 (사용자 응답 불필요)
52
+ - ⚠️ 사용자 응답이 필요하면 markdown_tool 대신 반드시 ask_user_tool 사용!
53
+
54
+ # 금지 사항
55
+ - 응답 (도구 호출도 없고 내용도 없음)
56
+ - 설명 없이 도구만 호출
57
+ - pending/in_progress todo 남기고 종료
58
+ - "작업 요약 및 다음 단계 제시" todo 없이 todo 리스트 생성
59
+ - **🔴 다른 pending todo가 있는데 "작업 요약 및 다음 단계 제시"를 먼저 실행** (순서 위반)
65
60
  """
66
61
 
67
62
  JSON_TOOL_SCHEMA = """Respond with ONLY valid JSON:
@@ -70,61 +65,61 @@ JSON_TOOL_SCHEMA = """Respond with ONLY valid JSON:
70
65
  Tools:
71
66
  - jupyter_cell_tool: {"code": "<python>"}
72
67
  - markdown_tool: {"content": "<markdown>"}
73
- - write_todos: {"todos": [{"content": "한국어 내용", "status": "pending|in_progress|completed"}]}
68
+ - ask_user_tool: {"question": "<질문>", "options": ["선택1", "선택2"], "input_type": "text|file|selection"}
69
+ - write_todos: {"todos": [{"content": "내용", "status": "pending|in_progress|completed"}]}
74
70
  - read_file_tool: {"path": "<path>", "offset": 0, "limit": 500}
75
- - write_file_tool: {"path": "<path>", "content": "<content>", "overwrite": false}
76
- - search_notebook_cells_tool: {"pattern": "<regex>"}
71
+ - write_file_tool: {"path": "<path>", "content": "<content>"}
72
+ - multiedit_file_tool: {"path": "<path>", "edits": [{"old_string": "...", "new_string": "..."}]}
77
73
  - execute_command_tool: {"command": "<cmd>"}
78
74
  - check_resource_tool: {"files": ["<path>"], "dataframes": ["<var>"]}
79
75
 
80
76
  No markdown wrapping. JSON only."""
81
77
 
82
- TODO_LIST_SYSTEM_PROMPT = """
83
- # Todo Rules
84
-
85
- ## New Message = Fresh Start
86
- - Each user message is a NEW task
87
- - Ignore completion status from chat history
88
- - Execute ALL current todos from scratch
89
-
90
- ## Structure
91
- All todo items must be in Korean. Always end with:
92
- - 작업 요약 및 다음단계 제시 ← 필수 마지막 항목!
93
-
94
- ## Workflow
95
- 1. Find pending/in_progress todo
96
- 2. Execute it NOW in THIS response
97
- 3. Mark completed
98
- 4. Repeat until all done
99
-
100
- ## 🔴 Final Todo ("작업 요약 및 다음단계 제시") - CRITICAL
101
- When executing this todo, you MUST:
102
- 1. Output the summary JSON in your content FIRST:
103
- {"summary": "작업 내용 요약", "next_items": [{"subject": "...", "description": "..."}]}
104
- 2. THEN call write_todos to mark all as completed
105
- 3. If you don't output the JSON, the todo will NOT be marked as completed!
106
-
107
- ## Completion Check
108
- - ✅ Done: Executed in THIS response
109
- - ❌ Not done: Only visible in chat history
110
- - ❌ "작업 요약" cannot be completed without outputting summary JSON
111
-
112
- ## Forbidden
113
- - Marking complete without executing in THIS response
114
- - Marking "작업 요약" complete without outputting JSON summary
115
- - Todos without "작업 요약 및 다음단계 제시" as final item
78
+ # Merged into DEFAULT_SYSTEM_PROMPT
79
+ TODO_LIST_SYSTEM_PROMPT = ""
80
+
81
+ TODO_LIST_TOOL_DESCRIPTION = """Todo 리스트 관리 도구.
82
+
83
+ 사용 시점:
84
+ - 3단계 이상의 복잡한 작업
85
+ - 진행 상황 추적이 필요할 때
86
+
87
+ 규칙:
88
+ - in_progress는 1개만
89
+ - 완료 즉시 completed로 변경
90
+ - **[필수] 마지막 todo는 반드시 "작업 요약 및 다음 단계 제시"로 생성**
91
+ - **🔴 [실행 순서]**: todo는 반드시 리스트 순서대로 실행하고, "작업 요약 및 다음 단계 제시"는 맨 마지막에 실행
92
+ - "작업 요약 다음 단계 제시" todo 에서는 전체 작업 요약과 다음 단계를 제시하는 내용을 JSON 형태로 출력:
93
+ {"summary": "완료 요약", "next_items": [{"subject": "...", "description": "..."}]}
94
+ (next_items 3개 이상 필수)
116
95
  """
117
96
 
118
- TODO_LIST_TOOL_DESCRIPTION = """Update task list for tracking progress.
119
- This tool ONLY tracks status - does NOT execute tasks.
120
- After calling: immediately call next action tool (unless ALL completed)."""
97
+ # List of tools available to the agent
98
+ TOOL_LIST = [
99
+ "jupyter_cell_tool",
100
+ "markdown_tool",
101
+ "ask_user_tool",
102
+ "write_todos",
103
+ "read_file_tool",
104
+ "write_file_tool",
105
+ "multiedit_file_tool",
106
+ "search_notebook_cells_tool",
107
+ "execute_command_tool",
108
+ "check_resource_tool",
109
+ "list_workspace_tool",
110
+ "search_files_tool",
111
+ ]
121
112
 
122
- # Non-HITL tools that execute immediately without user approval
113
+ # Tools that don't require HITL (Human-in-the-Loop) approval
123
114
  NON_HITL_TOOLS = {
124
115
  "markdown_tool",
125
116
  "markdown",
126
117
  "read_file_tool",
127
118
  "read_file",
119
+ "list_files_tool",
120
+ "list_files",
121
+ "search_workspace_tool",
122
+ "search_workspace",
128
123
  "search_notebook_cells_tool",
129
124
  "search_notebook_cells",
130
125
  "write_todos",