hdsp-jupyter-extension 2.0.27__py3-none-any.whl → 2.0.29__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 (76) hide show
  1. agent_server/config/__init__.py +5 -0
  2. agent_server/config/server_config.py +213 -0
  3. agent_server/context_providers/__init__.py +4 -2
  4. agent_server/context_providers/actions.py +73 -7
  5. agent_server/context_providers/file.py +23 -23
  6. agent_server/core/__init__.py +2 -2
  7. agent_server/core/llm_service.py +2 -3
  8. agent_server/langchain/__init__.py +2 -2
  9. agent_server/langchain/agent.py +18 -251
  10. agent_server/langchain/agent_factory.py +26 -4
  11. agent_server/langchain/agent_prompts/planner_prompt.py +22 -31
  12. agent_server/langchain/custom_middleware.py +268 -43
  13. agent_server/langchain/llm_factory.py +102 -54
  14. agent_server/langchain/logging_utils.py +1 -1
  15. agent_server/langchain/middleware/__init__.py +5 -0
  16. agent_server/langchain/middleware/content_injection_middleware.py +110 -0
  17. agent_server/langchain/middleware/subagent_events.py +88 -9
  18. agent_server/langchain/middleware/subagent_middleware.py +501 -245
  19. agent_server/langchain/prompts.py +5 -22
  20. agent_server/langchain/state_schema.py +44 -0
  21. agent_server/langchain/tools/jupyter_tools.py +4 -5
  22. agent_server/langchain/tools/tool_registry.py +6 -0
  23. agent_server/main.py +4 -4
  24. agent_server/routers/agent.py +2 -2
  25. agent_server/routers/chat.py +334 -28
  26. agent_server/routers/config.py +197 -11
  27. agent_server/routers/config_schema.py +254 -0
  28. agent_server/routers/context.py +31 -8
  29. agent_server/routers/langchain_agent.py +348 -209
  30. hdsp_agent_core/managers/config_manager.py +60 -11
  31. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.data}/data/share/jupyter/labextensions/hdsp-agent/build_log.json +1 -1
  32. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.data}/data/share/jupyter/labextensions/hdsp-agent/package.json +2 -2
  33. hdsp_jupyter_extension-2.0.27.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.b5e4416b4e07ec087aad.js → hdsp_jupyter_extension-2.0.29.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.f2eca2f8fa682eb21f72.js +488 -25
  34. hdsp_jupyter_extension-2.0.29.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.f2eca2f8fa682eb21f72.js.map +1 -0
  35. jupyter_ext/labextension/static/lib_index_js.67505497667f9c0a763d.js → hdsp_jupyter_extension-2.0.29.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.cc0a7158a5e3de7f22f7.js +1327 -1054
  36. hdsp_jupyter_extension-2.0.29.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.cc0a7158a5e3de7f22f7.js.map +1 -0
  37. hdsp_jupyter_extension-2.0.27.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.4ab73bb5068405670214.js → hdsp_jupyter_extension-2.0.29.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.bfff374b5cc6a57e16d2.js +3 -3
  38. jupyter_ext/labextension/static/remoteEntry.4ab73bb5068405670214.js.map → hdsp_jupyter_extension-2.0.29.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.bfff374b5cc6a57e16d2.js.map +1 -1
  39. {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.29.dist-info}/METADATA +1 -1
  40. {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.29.dist-info}/RECORD +71 -67
  41. jupyter_ext/_version.py +1 -1
  42. jupyter_ext/handlers.py +41 -0
  43. jupyter_ext/labextension/build_log.json +1 -1
  44. jupyter_ext/labextension/package.json +2 -2
  45. jupyter_ext/labextension/static/{frontend_styles_index_js.b5e4416b4e07ec087aad.js → frontend_styles_index_js.f2eca2f8fa682eb21f72.js} +488 -25
  46. jupyter_ext/labextension/static/frontend_styles_index_js.f2eca2f8fa682eb21f72.js.map +1 -0
  47. hdsp_jupyter_extension-2.0.27.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.67505497667f9c0a763d.js → jupyter_ext/labextension/static/lib_index_js.cc0a7158a5e3de7f22f7.js +1327 -1054
  48. jupyter_ext/labextension/static/lib_index_js.cc0a7158a5e3de7f22f7.js.map +1 -0
  49. jupyter_ext/labextension/static/{remoteEntry.4ab73bb5068405670214.js → remoteEntry.bfff374b5cc6a57e16d2.js} +3 -3
  50. hdsp_jupyter_extension-2.0.27.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.4ab73bb5068405670214.js.map → jupyter_ext/labextension/static/remoteEntry.bfff374b5cc6a57e16d2.js.map +1 -1
  51. agent_server/langchain/middleware/description_injector.py +0 -150
  52. hdsp_jupyter_extension-2.0.27.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.b5e4416b4e07ec087aad.js.map +0 -1
  53. hdsp_jupyter_extension-2.0.27.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.67505497667f9c0a763d.js.map +0 -1
  54. jupyter_ext/labextension/static/frontend_styles_index_js.b5e4416b4e07ec087aad.js.map +0 -1
  55. jupyter_ext/labextension/static/lib_index_js.67505497667f9c0a763d.js.map +0 -1
  56. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.data}/data/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +0 -0
  57. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
  58. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.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
  59. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.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
  60. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.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
  61. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.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
  62. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.data}/data/share/jupyter/labextensions/hdsp-agent/static/style.js +0 -0
  63. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.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
  64. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.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
  65. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js +0 -0
  66. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.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
  67. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js +0 -0
  68. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.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
  69. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js +0 -0
  70. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.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
  71. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +0 -0
  72. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js.map +0 -0
  73. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js +0 -0
  74. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js.map +0 -0
  75. {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.29.dist-info}/WHEEL +0 -0
  76. {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.29.dist-info}/licenses/LICENSE +0 -0
@@ -1,282 +1,49 @@
1
1
  """
2
2
  LangChain Agent
3
3
 
4
- Main agent creation module for tool-driven chat execution.
5
- Supports both single-agent and multi-agent modes.
4
+ Main agent creation module for multi-agent system.
6
5
  """
7
6
 
8
7
  import logging
9
- from typing import Any, Dict, Optional, Literal
8
+ from typing import Any, Dict, Optional
10
9
 
11
- from agent_server.langchain.custom_middleware import (
12
- create_handle_empty_response_middleware,
13
- create_inject_continuation_middleware,
14
- create_limit_tool_calls_middleware,
15
- create_normalize_tool_args_middleware,
16
- create_patch_tool_calls_middleware,
17
- )
18
- from agent_server.langchain.hitl_config import get_hitl_interrupt_config
19
- from agent_server.langchain.llm_factory import create_llm, create_summarization_llm
20
- from agent_server.langchain.prompts import (
21
- DEFAULT_SYSTEM_PROMPT,
22
- TODO_LIST_SYSTEM_PROMPT,
23
- TODO_LIST_TOOL_DESCRIPTION,
24
- )
25
- from agent_server.langchain.tools import (
26
- ask_user_tool,
27
- check_resource_tool,
28
- diagnostics_tool,
29
- edit_file_tool,
30
- execute_command_tool,
31
- jupyter_cell_tool,
32
- markdown_tool,
33
- multiedit_file_tool,
34
- read_file_tool,
35
- references_tool,
36
- search_notebook_cells_tool,
37
- write_file_tool,
38
- )
10
+ from agent_server.langchain.agent_factory import create_multi_agent_system
39
11
 
40
12
  logger = logging.getLogger(__name__)
41
13
 
42
14
 
43
- def _get_all_tools():
44
- """Get all available tools for the agent."""
45
- return [
46
- jupyter_cell_tool,
47
- markdown_tool,
48
- ask_user_tool, # HITL - waits for user response
49
- read_file_tool,
50
- write_file_tool,
51
- edit_file_tool,
52
- multiedit_file_tool,
53
- search_notebook_cells_tool,
54
- execute_command_tool,
55
- check_resource_tool,
56
- diagnostics_tool,
57
- references_tool,
58
- ]
59
-
60
-
61
- def create_simple_chat_agent(
15
+ def create_agent_system(
62
16
  llm_config: Dict[str, Any],
63
17
  workspace_root: str = ".",
64
18
  enable_hitl: bool = True,
65
19
  enable_todo_list: bool = True,
66
20
  checkpointer: Optional[object] = None,
67
21
  system_prompt_override: Optional[str] = None,
22
+ agent_prompts: Optional[Dict[str, str]] = None,
68
23
  ):
69
24
  """
70
- Create a simple chat agent using LangChain's create_agent with Human-in-the-Loop.
25
+ Create a multi-agent system (Planner + Subagents).
71
26
 
72
- This is a simplified version for chat mode that uses LangChain's built-in
73
- HumanInTheLoopMiddleware and TodoListMiddleware.
27
+ This is the main entry point for creating agents.
74
28
 
75
29
  Args:
76
30
  llm_config: LLM configuration
77
- workspace_root: Root directory
31
+ workspace_root: Root directory for file operations
78
32
  enable_hitl: Enable Human-in-the-Loop for code execution
79
33
  enable_todo_list: Enable TodoListMiddleware for task planning
80
34
  checkpointer: Optional checkpointer for state persistence
81
35
  system_prompt_override: Optional custom system prompt
36
+ agent_prompts: Optional dict of per-agent prompts
82
37
 
83
38
  Returns:
84
- Configured agent with HITL and TodoList middleware
39
+ Configured multi-agent system
85
40
  """
86
- try:
87
- from langchain.agents import create_agent
88
- from langchain.agents.middleware import (
89
- AgentMiddleware,
90
- HumanInTheLoopMiddleware,
91
- ModelCallLimitMiddleware,
92
- SummarizationMiddleware,
93
- TodoListMiddleware,
94
- ToolCallLimitMiddleware,
95
- wrap_model_call,
96
- )
97
- from langchain_core.messages import ToolMessage as LCToolMessage
98
- from langgraph.checkpoint.memory import InMemorySaver
99
- from langgraph.types import Overwrite
100
- except ImportError as e:
101
- logger.error(f"Failed to import LangChain agent components: {e}")
102
- raise ImportError(
103
- "LangChain agent components not available. "
104
- "Install with: pip install langchain langgraph"
105
- ) from e
106
-
107
- # Create LLM
108
- llm = create_llm(llm_config)
109
-
110
- # Get tools
111
- tools = _get_all_tools()
112
-
113
- # Configure middleware
114
- middleware = []
115
- # Add empty response handler middleware
116
- handle_empty_response = create_handle_empty_response_middleware(wrap_model_call)
117
- middleware.append(handle_empty_response)
118
-
119
- # Add tool call limiter middleware
120
- limit_tool_calls = create_limit_tool_calls_middleware(wrap_model_call)
121
- middleware.append(limit_tool_calls)
122
-
123
- # Add tool args normalization middleware (convert list args to strings based on schema)
124
- normalize_tool_args = create_normalize_tool_args_middleware(
125
- wrap_model_call, tools=tools
126
- )
127
- middleware.append(normalize_tool_args)
128
-
129
- # Add continuation prompt middleware
130
- inject_continuation = create_inject_continuation_middleware(wrap_model_call)
131
- middleware.append(inject_continuation)
132
-
133
- # Add patch tool calls middleware
134
- patch_tool_calls = create_patch_tool_calls_middleware(
135
- AgentMiddleware, LCToolMessage, Overwrite
136
- )
137
- middleware.append(patch_tool_calls)
138
-
139
- # Add TodoListMiddleware for task planning
140
- # NOTE: system_prompt removed to avoid multi-part content array
141
- # that causes Gemini MALFORMED_FUNCTION_CALL error
142
- if enable_todo_list:
143
- todo_middleware = TodoListMiddleware(
144
- tool_description=TODO_LIST_TOOL_DESCRIPTION,
145
- )
146
- middleware.append(todo_middleware)
147
-
148
- if enable_hitl:
149
- # Add Human-in-the-Loop middleware for code execution
150
- hitl_middleware = HumanInTheLoopMiddleware(
151
- interrupt_on=get_hitl_interrupt_config(),
152
- description_prefix="Tool execution pending approval",
153
- )
154
- middleware.append(hitl_middleware)
155
-
156
- # Add loop prevention middleware
157
- # ModelCallLimitMiddleware: Prevent infinite LLM calls
158
- model_limit_middleware = ModelCallLimitMiddleware(
159
- run_limit=30, # Max 30 LLM calls per user message
160
- exit_behavior="end", # Gracefully end when limit reached
161
- )
162
- middleware.append(model_limit_middleware)
163
- logger.info("Added ModelCallLimitMiddleware with run_limit=30")
164
-
165
- # ToolCallLimitMiddleware: Prevent specific tools from being called too many times
166
- # run_limit resets automatically per user message
167
- write_todos_limit = ToolCallLimitMiddleware(
168
- tool_name="write_todos",
169
- run_limit=20, # Max 20 write_todos calls per user message
170
- exit_behavior="continue",
171
- )
172
- middleware.append(write_todos_limit)
173
- logger.info("Added ToolCallLimitMiddleware for write_todos (20/msg)")
174
-
175
- # Add SummarizationMiddleware to maintain context across cycles
176
- summary_llm = create_summarization_llm(llm_config)
177
- if summary_llm:
178
- try:
179
- summarization_middleware = SummarizationMiddleware(
180
- model=summary_llm,
181
- trigger=("tokens", 8000), # Trigger when exceeding 8000 tokens
182
- keep=("messages", 10), # Keep last 10 messages intact
183
- )
184
- middleware.append(summarization_middleware)
185
- logger.info(
186
- "Added SummarizationMiddleware with model=%s, trigger=8000 tokens, keep=10 msgs",
187
- getattr(summary_llm, "model", str(summary_llm)),
188
- )
189
- except Exception as e:
190
- logger.warning("Failed to add SummarizationMiddleware: %s", e)
191
-
192
- # System prompt for the agent (override applies only to LangChain agent)
193
- if system_prompt_override and system_prompt_override.strip():
194
- system_prompt = system_prompt_override.strip()
195
- logger.info("SimpleChatAgent using custom system prompt override")
196
- else:
197
- system_prompt = DEFAULT_SYSTEM_PROMPT
198
-
199
- # Add vLLM/gpt-oss specific prompt (minimal - main rules are in DEFAULT_SYSTEM_PROMPT)
200
- provider = llm_config.get("provider", "")
201
- if provider == "vllm":
202
- vllm_prompt = """
203
- ## 🔴 추가 강조
204
- - 반드시 한국어로 응답
205
- - 빈 응답 절대 금지 - 항상 도구 호출 필수
206
- """
207
- system_prompt = system_prompt + "\n" + vllm_prompt
208
- logger.info("Added vLLM-specific prompt")
209
-
210
- logger.info("SimpleChatAgent system_prompt: %s", system_prompt)
211
-
212
- # Create agent with checkpointer (required for HITL)
213
- agent = create_agent(
214
- model=llm,
215
- tools=tools,
216
- middleware=middleware,
217
- checkpointer=checkpointer or InMemorySaver(), # Required for interrupt/resume
218
- system_prompt=system_prompt, # Tell the agent to use tools
41
+ logger.info("Creating multi-agent system (Planner + Subagents)")
42
+ return create_multi_agent_system(
43
+ llm_config=llm_config,
44
+ checkpointer=checkpointer,
45
+ enable_hitl=enable_hitl,
46
+ enable_todo_list=enable_todo_list,
47
+ system_prompt_override=system_prompt_override,
48
+ agent_prompts=agent_prompts,
219
49
  )
220
-
221
- return agent
222
-
223
-
224
- # =============================================================================
225
- # Multi-Agent System Support
226
- # =============================================================================
227
-
228
-
229
- def create_agent_system(
230
- llm_config: Dict[str, Any],
231
- workspace_root: str = ".",
232
- enable_hitl: bool = True,
233
- enable_todo_list: bool = True,
234
- checkpointer: Optional[object] = None,
235
- system_prompt_override: Optional[str] = None,
236
- agent_mode: Literal["single", "multi"] = "single",
237
- agent_prompts: Optional[Dict[str, str]] = None,
238
- ):
239
- """
240
- Create an agent system based on the specified mode.
241
-
242
- This is the main entry point for creating agents. It supports both:
243
- - "single": Traditional single-agent with all tools (backward compatible)
244
- - "multi": Multi-agent system with Planner supervising subagents
245
-
246
- Args:
247
- llm_config: LLM configuration
248
- workspace_root: Root directory for file operations
249
- enable_hitl: Enable Human-in-the-Loop for code execution
250
- enable_todo_list: Enable TodoListMiddleware for task planning
251
- checkpointer: Optional checkpointer for state persistence
252
- system_prompt_override: Optional custom system prompt
253
- agent_mode: "single" for traditional mode, "multi" for multi-agent
254
- agent_prompts: Optional dict of per-agent prompts (for multi-agent mode)
255
-
256
- Returns:
257
- Configured agent (single or Planner for multi-agent)
258
- """
259
- if agent_mode == "multi":
260
- # Use multi-agent system with Planner + Subagents
261
- from agent_server.langchain.agent_factory import create_multi_agent_system
262
-
263
- logger.info("Creating multi-agent system (Planner + Subagents)")
264
- return create_multi_agent_system(
265
- llm_config=llm_config,
266
- checkpointer=checkpointer,
267
- enable_hitl=enable_hitl,
268
- enable_todo_list=enable_todo_list,
269
- system_prompt_override=system_prompt_override,
270
- agent_prompts=agent_prompts,
271
- )
272
- else:
273
- # Use traditional single-agent mode
274
- logger.info("Creating single-agent system (all tools in one agent)")
275
- return create_simple_chat_agent(
276
- llm_config=llm_config,
277
- workspace_root=workspace_root,
278
- enable_hitl=enable_hitl,
279
- enable_todo_list=enable_todo_list,
280
- checkpointer=checkpointer,
281
- system_prompt_override=system_prompt_override,
282
- )
@@ -29,11 +29,16 @@ from agent_server.langchain.custom_middleware import (
29
29
  )
30
30
  from agent_server.langchain.hitl_config import get_hitl_interrupt_config
31
31
  from agent_server.langchain.llm_factory import create_llm, create_summarization_llm
32
+ from agent_server.langchain.middleware.content_injection_middleware import (
33
+ ContentInjectionMiddleware,
34
+ )
32
35
  from agent_server.langchain.middleware.skill_middleware import get_skill_middleware
33
36
  from agent_server.langchain.middleware.subagent_middleware import (
37
+ _create_main_task_tool,
34
38
  create_task_tool,
35
39
  set_subagent_factory,
36
40
  )
41
+ from agent_server.langchain.state_schema import HDSPAgentState
37
42
  from agent_server.langchain.subagents.base import (
38
43
  get_available_subagents_for_planner,
39
44
  get_subagent_config,
@@ -146,6 +151,7 @@ def create_subagent(
146
151
  middleware=middleware,
147
152
  checkpointer=checkpointer or InMemorySaver(), # Needed for HITL
148
153
  system_prompt=system_prompt,
154
+ state_schema=HDSPAgentState,
149
155
  )
150
156
 
151
157
  logger.info(
@@ -229,11 +235,11 @@ def create_main_agent(
229
235
  # Using "planner" for backward compatibility with tool_registry
230
236
  tools = get_tools_for_agent("planner")
231
237
 
232
- # Create task tool for calling subagents
238
+ # Create task tool for calling subagents (Main Agent version with Command/State)
233
239
  available_subagents = [
234
240
  config.name for config in get_available_subagents_for_planner()
235
241
  ]
236
- task_tool = create_task_tool(
242
+ task_tool = _create_main_task_tool(
237
243
  caller_name="planner",
238
244
  allowed_subagents=None, # Main Agent (Planner) can call all non-restricted subagents
239
245
  )
@@ -242,6 +248,13 @@ def create_main_agent(
242
248
  # Configure middleware
243
249
  middleware = []
244
250
 
251
+ # Add ContentInjectionMiddleware FIRST (before HITL)
252
+ # Injects generated code/SQL from state into tool args
253
+ # so HITL sees the full content for user approval
254
+ content_injection = ContentInjectionMiddleware()
255
+ middleware.append(content_injection)
256
+ logger.info("Added ContentInjectionMiddleware (before HITL)")
257
+
245
258
  # Add empty response handler middleware (critical for Gemini compatibility)
246
259
  handle_empty_response = create_handle_empty_response_middleware(wrap_model_call)
247
260
  middleware.append(handle_empty_response)
@@ -268,6 +281,14 @@ def create_main_agent(
268
281
  )
269
282
  middleware.append(patch_tool_calls)
270
283
 
284
+ # Add TodoActiveMiddleware BEFORE TodoListMiddleware
285
+ # TodoActiveMiddleware wraps around TodoListMiddleware to intercept
286
+ # write_todos/final_summary_tool results and set todo_active state flag
287
+ from agent_server.langchain.custom_middleware import TodoActiveMiddleware
288
+
289
+ todo_active_middleware = TodoActiveMiddleware()
290
+ middleware.append(todo_active_middleware)
291
+
271
292
  # Add TodoListMiddleware for task planning
272
293
  # NOTE: system_prompt removed to avoid multi-part content array
273
294
  if enable_todo_list:
@@ -307,8 +328,8 @@ def create_main_agent(
307
328
  try:
308
329
  summarization = SummarizationMiddleware(
309
330
  model=summary_llm,
310
- trigger=("tokens", 10000), # Higher trigger for multi-agent
311
- keep=("messages", 15),
331
+ trigger=("tokens", 30000), # Trigger when tokens exceed this threshold
332
+ keep=("messages", 15), # Keep last 15 messages after compaction
312
333
  )
313
334
  middleware.append(summarization)
314
335
  logger.info("Added SummarizationMiddleware to Main Agent")
@@ -345,6 +366,7 @@ def create_main_agent(
345
366
  middleware=middleware,
346
367
  checkpointer=checkpointer or InMemorySaver(),
347
368
  system_prompt=system_prompt,
369
+ state_schema=HDSPAgentState,
348
370
  )
349
371
 
350
372
  logger.info(
@@ -5,15 +5,16 @@ Main Agent (Supervisor) System Prompt for Multi-Agent Mode
5
5
  PLANNER_SYSTEM_PROMPT = """당신은 작업을 조율하는 Main Agent입니다. 한국어로 응답하세요.
6
6
 
7
7
  # 핵심 원칙
8
- 1. 3단계 이상의 복잡한 작업을 요청받은 경우에만 write_todos 작업 목록 관리
9
- 2. **직접 코드, 쿼리 작성 금지** - 모든 코드/쿼리 생성은 task_tool로 서브에이전트에게 위임
10
- 3. 서브에이전트가 반환한 코드를 적절한 도구로 실행
11
- 4. 모든 응답 content는 2~3줄 내외로 핵심만 명확하게 전달
8
+ 1. **간단한 작업 (1-2단계)**: write_todos 사용 금지 바로 실행하고 종료
9
+ 2. **복잡한 작업 (3단계+)**: write_todos로 계획 순차 실행 완료 final_summary_tool 호출
10
+ 3. **직접 코드, 쿼리 작성 금지** - 모든 코드/쿼리 생성은 task_tool로 서브에이전트에게 위임
11
+ 4. 서브에이전트가 반환한 코드를 적절한 도구로 실행
12
12
 
13
13
  # 작업 흐름
14
14
 
15
15
  ## Step 1: 계획 수립
16
- 3단계 이상의 복잡한 작업을 요청받은 경우에만 write_todos 작업 목록 생성 (마지막 항목은 반드시 "작업 요약 다음 단계 제시")
16
+ - **간단한 작업 (1-2단계)**: write_todos 없이 바로 실행. 완료 추가 도구 호출 없이 종료.
17
+ - **복잡한 작업 (3단계+)**: write_todos로 작업 목록 생성 (실제 작업만 포함, 요약은 시스템이 자동 처리)
17
18
 
18
19
  ## Step 2: 코드/쿼리 생성 요청
19
20
  필요한 경우, task_tool을 호출하여 서브에이전트에게 위임:
@@ -25,18 +26,20 @@ PLANNER_SYSTEM_PROMPT = """당신은 작업을 조율하는 Main Agent입니다.
25
26
  | researcher | 정보 검색 | task_tool(agent_name="researcher", description="관련 문서 검색") |
26
27
 
27
28
  ## Step 3: 결과 실행/적용 (필수!)
28
- **task_tool 호출 했다면, 호출 후 반드시 결과를 처리해야 함:**
29
+ **task_tool 호출 후 반드시 결과를 처리해야 함. 코드/SQL은 자동 주입됩니다:**
29
30
 
30
- | 서브에이전트 | 작업 유형 | 처리 방법 | 예시 |
31
- |-------------|----------|----------|------|
32
- | python_developer | 코드 실행 (데이터 분석, 시각화) | jupyter_cell_tool | jupyter_cell_tool(code=반환된_코드) |
33
- | python_developer | **파일 생성/수정** | **write_file_tool 또는 multiedit_file_tool** | write_file_tool(path="script.js", content=반환된_코드) |
34
- | athena_query | SQL 표시 | markdown_tool | markdown_tool(content="```sql\n반환된_쿼리\n```") |
31
+ | 서브에이전트 | 작업 유형 | 처리 방법 | 호출 방법 |
32
+ |-------------|----------|----------|----------|
33
+ | python_developer | 코드 실행 (데이터 분석, 시각화) | jupyter_cell_tool | jupyter_cell_tool() ← 코드 자동 주입, code 파라미터 불필요 |
34
+ | python_developer | **파일 생성/수정** | **write_file_tool** | write_file_tool(path="파일경로") content 자동 주입 |
35
+ | athena_query | SQL 표시 | markdown_tool | markdown_tool() ← SQL 자동 주입, content 파라미터 불필요 |
35
36
  | researcher | 텍스트 요약 | 직접 응답 | - |
36
37
 
37
- **🔴 중요: 코드 저장 도구 선택**
38
- - **파일 생성/수정 요청** `write_file_tool` 또는 `multiedit_file_tool` 사용
39
- - **코드 실행 요청** (데이터 분석, 차트 등) `jupyter_cell_tool` 사용
38
+ **🔴 중요: 코드/SQL 자동 주입**
39
+ - task_tool이 생성한 코드/SQL은 **State를 통해 자동 주입**됩니다
40
+ - **코드를 직접 복사하거나 인자로 전달할 필요 없음** 도구만 호출하면 됨
41
+ - **파일 생성/수정 요청** → `write_file_tool(path=...)` 사용 (content 자동 주입)
42
+ - **코드 실행 요청** (데이터 분석, 차트 등) → `jupyter_cell_tool()` 사용 (code 자동 주입)
40
43
  - **❌ markdown_tool은 코드 저장용이 아님!** (표시 전용)
41
44
 
42
45
  **중요**: task_tool 결과를 받은 후 바로 write_todos로 완료 처리하지 말고, 반드시 위 도구로 결과를 먼저 적용!
@@ -49,34 +52,22 @@ PLANNER_SYSTEM_PROMPT = """당신은 작업을 조율하는 Main Agent입니다.
49
52
  - **🔴 기존 todo 절대 삭제 금지**: 전체 리스트를 항상 포함하고 status만 변경
50
53
  - **🔴 상태 전환 순서 필수**: pending → in_progress → completed (건너뛰기 금지!)
51
54
  - **🔴 초기 생성 규칙**: 첫 write_todos 호출 시 첫 번째 todo만 in_progress, 나머지는 모두 pending
52
- - 올바른 초기 예: [{"content": "작업1", "status": "in_progress"}, {"content": "작업2", "status": "pending"}, {"content": "작업 요약 및 다음 단계 제시", "status": "pending"}]
55
+ - 올바른 초기 예: [{"content": "작업1", "status": "in_progress"}, {"content": "작업2", "status": "pending"}]
53
56
  - 잘못된 초기 예: [{"content": "작업1", "status": "completed"}, ...] ← 실제 작업 없이 completed 금지!
54
57
  - **🔴 completed 전환 조건**: 실제 도구(task_tool, jupyter_cell_tool 등)로 작업 수행 후에만 completed로 변경
55
58
  - in_progress 상태는 **동시에 1개만** 허용 (completed, pending todo는 삭제하지 않고 모두 유지)
56
59
  - content에 도구(tool)명 언급 금지
57
- - **[필수] 마지막 todo 반드시 "작업 요약 다음 단계 제시"**
58
-
59
- # "작업 요약 및 다음 단계 제시" todo 완료 시 [필수]
60
- 1. "작업 요약 및 다음 단계 제시"를 **in_progress**로 변경 (write_todos 호출)
61
- 2. **반드시 final_summary_tool 호출**:
62
- final_summary_tool(
63
- summary="완료된 작업 요약",
64
- next_items=[{"subject": "제목", "description": "설명"}, ...]
65
- )
66
-
67
- 3. final_summary_tool 호출 후 "작업 요약 및 다음 단계 제시"를 **completed**로 변경
68
-
69
- - next_items 3개 이상 필수
70
- - **final_summary_tool 호출 없이 종료 금지**
60
+ - **"작업 요약" todo 추가 금지**: 실제 작업만 todo로 생성 (요약은 시스템이 자동 처리)
71
61
 
72
62
  # 도구 사용시 주의할 점
73
63
  ## 파일 위치 모를 때 탐색 순서: search_files_tool → list_workspace_tool → 재검색 → ask_user_tool 순서로!)
74
- ## list_workspace_tool로 전체 디렉토리 파일 목록 검색 금지! 최대한 pattern 으로 drill down 해서 검색할 것
64
+ ## list_workspace_tool로 전체 디렉토리 파일 목록 검색 금지! 최대한 pattern 으로 drill down 해서 검색할 것
75
65
 
76
66
  # 금지 사항
77
67
  - 직접 코드/SQL 작성 (반드시 task_tool 사용)
78
68
  - task_tool 없이 jupyter_cell_tool 호출
79
- - **task_tool 결과를 표시하지 않고 바로 완료 처리** (athena_query → markdown_tool 필수)
69
+ - **task_tool 결과를 처리하지 않고 바로 완료** (python_developer → jupyter_cell_tool, athena_query → markdown_tool 필수)
70
+ - jupyter_cell_tool에 code 인자를 직접 전달 (자동 주입되므로 불필요)
80
71
  - 빈 응답
81
72
  """
82
73