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.
- agent_server/config/__init__.py +5 -0
- agent_server/config/server_config.py +213 -0
- agent_server/context_providers/__init__.py +4 -2
- agent_server/context_providers/actions.py +73 -7
- agent_server/context_providers/file.py +23 -23
- agent_server/core/__init__.py +2 -2
- agent_server/core/llm_service.py +2 -3
- agent_server/langchain/__init__.py +2 -2
- agent_server/langchain/agent.py +18 -251
- agent_server/langchain/agent_factory.py +26 -4
- agent_server/langchain/agent_prompts/planner_prompt.py +22 -31
- agent_server/langchain/custom_middleware.py +268 -43
- agent_server/langchain/llm_factory.py +102 -54
- agent_server/langchain/logging_utils.py +1 -1
- agent_server/langchain/middleware/__init__.py +5 -0
- agent_server/langchain/middleware/content_injection_middleware.py +110 -0
- agent_server/langchain/middleware/subagent_events.py +88 -9
- agent_server/langchain/middleware/subagent_middleware.py +501 -245
- agent_server/langchain/prompts.py +5 -22
- agent_server/langchain/state_schema.py +44 -0
- agent_server/langchain/tools/jupyter_tools.py +4 -5
- agent_server/langchain/tools/tool_registry.py +6 -0
- agent_server/main.py +4 -4
- agent_server/routers/agent.py +2 -2
- agent_server/routers/chat.py +334 -28
- agent_server/routers/config.py +197 -11
- agent_server/routers/config_schema.py +254 -0
- agent_server/routers/context.py +31 -8
- agent_server/routers/langchain_agent.py +348 -209
- hdsp_agent_core/managers/config_manager.py +60 -11
- {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
- {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.data}/data/share/jupyter/labextensions/hdsp-agent/package.json +2 -2
- 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
- hdsp_jupyter_extension-2.0.29.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.f2eca2f8fa682eb21f72.js.map +1 -0
- 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
- hdsp_jupyter_extension-2.0.29.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.cc0a7158a5e3de7f22f7.js.map +1 -0
- 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
- 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
- {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.29.dist-info}/METADATA +1 -1
- {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.29.dist-info}/RECORD +71 -67
- jupyter_ext/_version.py +1 -1
- jupyter_ext/handlers.py +41 -0
- jupyter_ext/labextension/build_log.json +1 -1
- jupyter_ext/labextension/package.json +2 -2
- jupyter_ext/labextension/static/{frontend_styles_index_js.b5e4416b4e07ec087aad.js → frontend_styles_index_js.f2eca2f8fa682eb21f72.js} +488 -25
- jupyter_ext/labextension/static/frontend_styles_index_js.f2eca2f8fa682eb21f72.js.map +1 -0
- 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
- jupyter_ext/labextension/static/lib_index_js.cc0a7158a5e3de7f22f7.js.map +1 -0
- jupyter_ext/labextension/static/{remoteEntry.4ab73bb5068405670214.js → remoteEntry.bfff374b5cc6a57e16d2.js} +3 -3
- 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
- agent_server/langchain/middleware/description_injector.py +0 -150
- hdsp_jupyter_extension-2.0.27.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.b5e4416b4e07ec087aad.js.map +0 -1
- hdsp_jupyter_extension-2.0.27.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.67505497667f9c0a763d.js.map +0 -1
- jupyter_ext/labextension/static/frontend_styles_index_js.b5e4416b4e07ec087aad.js.map +0 -1
- jupyter_ext/labextension/static/lib_index_js.67505497667f9c0a763d.js.map +0 -1
- {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
- {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.29.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.29.dist-info}/WHEEL +0 -0
- {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.29.dist-info}/licenses/LICENSE +0 -0
agent_server/langchain/agent.py
CHANGED
|
@@ -1,282 +1,49 @@
|
|
|
1
1
|
"""
|
|
2
2
|
LangChain Agent
|
|
3
3
|
|
|
4
|
-
Main agent creation module for
|
|
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
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
10
9
|
|
|
11
|
-
from agent_server.langchain.
|
|
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
|
|
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
|
|
25
|
+
Create a multi-agent system (Planner + Subagents).
|
|
71
26
|
|
|
72
|
-
This is
|
|
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
|
|
39
|
+
Configured multi-agent system
|
|
85
40
|
"""
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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 =
|
|
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",
|
|
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.
|
|
9
|
-
2.
|
|
10
|
-
3.
|
|
11
|
-
4.
|
|
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
|
-
|
|
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(
|
|
33
|
-
| python_developer | **파일 생성/수정** | **write_file_tool
|
|
34
|
-
| athena_query | SQL 표시 | markdown_tool | markdown_tool(
|
|
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
|
-
-
|
|
39
|
-
-
|
|
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"}
|
|
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
|
-
- **
|
|
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 결과를
|
|
69
|
+
- **task_tool 결과를 처리하지 않고 바로 완료** (python_developer → jupyter_cell_tool, athena_query → markdown_tool 필수)
|
|
70
|
+
- jupyter_cell_tool에 code 인자를 직접 전달 (자동 주입되므로 불필요)
|
|
80
71
|
- 빈 응답
|
|
81
72
|
"""
|
|
82
73
|
|