hdsp-jupyter-extension 2.0.27__py3-none-any.whl → 2.0.28__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 (70) hide show
  1. agent_server/context_providers/__init__.py +4 -2
  2. agent_server/context_providers/actions.py +73 -7
  3. agent_server/context_providers/file.py +23 -23
  4. agent_server/langchain/__init__.py +2 -2
  5. agent_server/langchain/agent.py +18 -251
  6. agent_server/langchain/agent_factory.py +26 -4
  7. agent_server/langchain/agent_prompts/planner_prompt.py +22 -31
  8. agent_server/langchain/custom_middleware.py +268 -43
  9. agent_server/langchain/llm_factory.py +102 -54
  10. agent_server/langchain/logging_utils.py +1 -1
  11. agent_server/langchain/middleware/__init__.py +5 -0
  12. agent_server/langchain/middleware/content_injection_middleware.py +110 -0
  13. agent_server/langchain/middleware/subagent_events.py +88 -9
  14. agent_server/langchain/middleware/subagent_middleware.py +501 -245
  15. agent_server/langchain/prompts.py +5 -22
  16. agent_server/langchain/state_schema.py +44 -0
  17. agent_server/langchain/tools/jupyter_tools.py +4 -5
  18. agent_server/langchain/tools/tool_registry.py +6 -0
  19. agent_server/routers/chat.py +305 -2
  20. agent_server/routers/config.py +193 -8
  21. agent_server/routers/config_schema.py +254 -0
  22. agent_server/routers/context.py +31 -8
  23. agent_server/routers/langchain_agent.py +276 -155
  24. hdsp_agent_core/managers/config_manager.py +100 -1
  25. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/build_log.json +1 -1
  26. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/package.json +2 -2
  27. 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.28.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.55727265b00191e68d9a.js +479 -15
  28. hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.55727265b00191e68d9a.js.map +1 -0
  29. jupyter_ext/labextension/static/lib_index_js.67505497667f9c0a763d.js → hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.df05d90f366bfd5fa023.js +1287 -190
  30. hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.df05d90f366bfd5fa023.js.map +1 -0
  31. hdsp_jupyter_extension-2.0.27.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.4ab73bb5068405670214.js → hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.08fce819ee32e9d25175.js +3 -3
  32. jupyter_ext/labextension/static/remoteEntry.4ab73bb5068405670214.js.map → hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.08fce819ee32e9d25175.js.map +1 -1
  33. {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/METADATA +1 -1
  34. {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/RECORD +65 -63
  35. jupyter_ext/_version.py +1 -1
  36. jupyter_ext/handlers.py +41 -0
  37. jupyter_ext/labextension/build_log.json +1 -1
  38. jupyter_ext/labextension/package.json +2 -2
  39. jupyter_ext/labextension/static/{frontend_styles_index_js.b5e4416b4e07ec087aad.js → frontend_styles_index_js.55727265b00191e68d9a.js} +479 -15
  40. jupyter_ext/labextension/static/frontend_styles_index_js.55727265b00191e68d9a.js.map +1 -0
  41. 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.df05d90f366bfd5fa023.js +1287 -190
  42. jupyter_ext/labextension/static/lib_index_js.df05d90f366bfd5fa023.js.map +1 -0
  43. jupyter_ext/labextension/static/{remoteEntry.4ab73bb5068405670214.js → remoteEntry.08fce819ee32e9d25175.js} +3 -3
  44. hdsp_jupyter_extension-2.0.27.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.4ab73bb5068405670214.js.map → jupyter_ext/labextension/static/remoteEntry.08fce819ee32e9d25175.js.map +1 -1
  45. agent_server/langchain/middleware/description_injector.py +0 -150
  46. hdsp_jupyter_extension-2.0.27.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.b5e4416b4e07ec087aad.js.map +0 -1
  47. hdsp_jupyter_extension-2.0.27.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.67505497667f9c0a763d.js.map +0 -1
  48. jupyter_ext/labextension/static/frontend_styles_index_js.b5e4416b4e07ec087aad.js.map +0 -1
  49. jupyter_ext/labextension/static/lib_index_js.67505497667f9c0a763d.js.map +0 -1
  50. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.data}/data/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +0 -0
  51. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
  52. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.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
  53. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.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
  54. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.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
  55. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.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
  56. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/style.js +0 -0
  57. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.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
  58. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.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
  59. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js +0 -0
  60. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.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
  61. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js +0 -0
  62. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.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
  63. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js +0 -0
  64. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.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
  65. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +0 -0
  66. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js.map +0 -0
  67. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js +0 -0
  68. {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js.map +0 -0
  69. {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/WHEEL +0 -0
  70. {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/licenses/LICENSE +0 -0
@@ -2,11 +2,11 @@
2
2
  Context Providers Module
3
3
 
4
4
  Provides context injection for LLM prompts (e.g., @file, @notebook, @selection).
5
- Also provides action commands (e.g., @reset).
5
+ Also provides action commands (e.g., /reset).
6
6
  Inspired by jupyter-ai's context provider architecture.
7
7
  """
8
8
 
9
- from .actions import ResetContextProvider
9
+ from .actions import CompactContextProvider, HelpContextProvider, ResetContextProvider
10
10
  from .base import BaseContextProvider, ContextCommand, find_commands
11
11
  from .file import FileContextProvider
12
12
  from .processor import ContextProcessor, process_context_commands
@@ -16,6 +16,8 @@ __all__ = [
16
16
  "BaseContextProvider",
17
17
  "FileContextProvider",
18
18
  "ResetContextProvider",
19
+ "CompactContextProvider",
20
+ "HelpContextProvider",
19
21
  "ContextProcessor",
20
22
  "find_commands",
21
23
  "process_context_commands",
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Action Context Providers
3
3
 
4
- These providers handle action commands like @reset that perform actions
4
+ These providers handle action commands like /reset that perform actions
5
5
  rather than providing context to the LLM.
6
6
  """
7
7
 
@@ -13,11 +13,11 @@ class ResetContextProvider(BaseContextProvider):
13
13
  Action provider for resetting the session.
14
14
 
15
15
  Usage:
16
- @reset - Clear conversation history and recreate agent
16
+ /reset - Clear conversation history and recreate agent
17
17
  """
18
18
 
19
- id = "@reset"
20
- description = "Clear session and reset agent"
19
+ id = "/reset"
20
+ description = "세션 초기화 에이전트 리셋"
21
21
  requires_arg = False
22
22
  only_start = False
23
23
 
@@ -25,15 +25,15 @@ class ResetContextProvider(BaseContextProvider):
25
25
  super().__init__(base_dir)
26
26
 
27
27
  def get_arg_options(self, arg_prefix: str) -> list[ListOptionsEntry]:
28
- """No arguments for @reset."""
28
+ """No arguments for /reset."""
29
29
  return []
30
30
 
31
31
  def make_context(self, command: ContextCommand) -> str:
32
- """@reset doesn't provide context - it's an action command."""
32
+ """/reset doesn't provide context - it's an action command."""
33
33
  return ""
34
34
 
35
35
  def replace_command(self, command: ContextCommand) -> str:
36
- """Replace @reset with empty string (it's an action, not context)."""
36
+ """Replace /reset with empty string (it's an action, not context)."""
37
37
  return ""
38
38
 
39
39
  def is_action_command(self) -> bool:
@@ -43,3 +43,69 @@ class ResetContextProvider(BaseContextProvider):
43
43
 
44
44
  # Add more action commands here as needed
45
45
  # Example: @clear, @help, @history, etc.
46
+
47
+
48
+ class CompactContextProvider(BaseContextProvider):
49
+ """
50
+ Action provider for compacting conversation history.
51
+
52
+ Usage:
53
+ /compact - Summarize older messages, keep recent 3 intact
54
+ """
55
+
56
+ id = "/compact"
57
+ description = "대화 기록 압축 및 요약"
58
+ requires_arg = False
59
+ only_start = False
60
+
61
+ def __init__(self, base_dir: str = "."):
62
+ super().__init__(base_dir)
63
+
64
+ def get_arg_options(self, arg_prefix: str) -> list[ListOptionsEntry]:
65
+ """No arguments for /compact."""
66
+ return []
67
+
68
+ def make_context(self, command: ContextCommand) -> str:
69
+ """/compact doesn't provide context - it's an action command."""
70
+ return ""
71
+
72
+ def replace_command(self, command: ContextCommand) -> str:
73
+ """Replace /compact with empty string (it's an action, not context)."""
74
+ return ""
75
+
76
+ def is_action_command(self) -> bool:
77
+ """Indicate this is an action command."""
78
+ return True
79
+
80
+
81
+ class HelpContextProvider(BaseContextProvider):
82
+ """
83
+ Action provider for displaying help information.
84
+
85
+ Usage:
86
+ /help - Show available commands and features guide
87
+ """
88
+
89
+ id = "/help"
90
+ description = "도움말 및 사용 가이드"
91
+ requires_arg = False
92
+ only_start = False
93
+
94
+ def __init__(self, base_dir: str = "."):
95
+ super().__init__(base_dir)
96
+
97
+ def get_arg_options(self, arg_prefix: str) -> list[ListOptionsEntry]:
98
+ """No arguments for /help."""
99
+ return []
100
+
101
+ def make_context(self, command: ContextCommand) -> str:
102
+ """/help doesn't provide context - it's an action command."""
103
+ return ""
104
+
105
+ def replace_command(self, command: ContextCommand) -> str:
106
+ """Replace /help with empty string (it's an action, not context)."""
107
+ return ""
108
+
109
+ def is_action_command(self) -> bool:
110
+ """Indicate this is an action command."""
111
+ return True
@@ -73,7 +73,7 @@ class FileContextProvider(BaseContextProvider):
73
73
  """
74
74
 
75
75
  id = "@file"
76
- description = "Include file contents in the prompt"
76
+ description = "파일 내용을 프롬프트에 포함"
77
77
  requires_arg = True
78
78
  only_start = False
79
79
 
@@ -152,7 +152,7 @@ class FileContextProvider(BaseContextProvider):
152
152
  ListOptionsEntry(
153
153
  id=self.command_id,
154
154
  label=f"{self.command_id}:{label_path}",
155
- description="Directory"
155
+ description="디렉토리"
156
156
  if is_dir
157
157
  else self._get_file_description(path),
158
158
  only_start=self.only_start,
@@ -168,26 +168,26 @@ class FileContextProvider(BaseContextProvider):
168
168
  basename = os.path.basename(filepath)
169
169
 
170
170
  descriptions = {
171
- ".py": "Python file",
172
- ".ipynb": "Jupyter notebook",
173
- ".md": "Markdown file",
174
- ".txt": "Text file",
175
- ".json": "JSON file",
176
- ".yaml": "YAML file",
177
- ".yml": "YAML file",
178
- ".toml": "TOML file",
179
- ".js": "JavaScript file",
180
- ".ts": "TypeScript file",
181
- ".tsx": "TypeScript React file",
182
- ".jsx": "JavaScript React file",
183
- ".html": "HTML file",
184
- ".css": "CSS file",
185
- ".sql": "SQL file",
186
- ".sh": "Shell script",
187
- ".csv": "CSV file",
188
- ".r": "R file",
189
- ".R": "R file",
190
- ".jl": "Julia file",
171
+ ".py": "Python 파일",
172
+ ".ipynb": "Jupyter 노트북",
173
+ ".md": "Markdown 파일",
174
+ ".txt": "텍스트 파일",
175
+ ".json": "JSON 파일",
176
+ ".yaml": "YAML 파일",
177
+ ".yml": "YAML 파일",
178
+ ".toml": "TOML 파일",
179
+ ".js": "JavaScript 파일",
180
+ ".ts": "TypeScript 파일",
181
+ ".tsx": "TypeScript React 파일",
182
+ ".jsx": "JavaScript React 파일",
183
+ ".html": "HTML 파일",
184
+ ".css": "CSS 파일",
185
+ ".sql": "SQL 파일",
186
+ ".sh": " 스크립트",
187
+ ".csv": "CSV 파일",
188
+ ".r": "R 파일",
189
+ ".R": "R 파일",
190
+ ".jl": "Julia 파일",
191
191
  }
192
192
 
193
193
  # Check special filenames
@@ -196,7 +196,7 @@ class FileContextProvider(BaseContextProvider):
196
196
  if basename == "Makefile":
197
197
  return "Makefile"
198
198
 
199
- return descriptions.get(ext, "File")
199
+ return descriptions.get(ext, "파일")
200
200
 
201
201
  def make_context(self, command: ContextCommand) -> str:
202
202
  """
@@ -12,7 +12,7 @@ Architecture:
12
12
  - state.py: Agent state management
13
13
  """
14
14
 
15
- from agent_server.langchain.agent import create_simple_chat_agent, create_agent_system
15
+ from agent_server.langchain.agent import create_agent_system
16
16
  from agent_server.langchain.state import AgentState
17
17
 
18
- __all__ = ["create_simple_chat_agent", "create_agent_system", "AgentState"]
18
+ __all__ = ["create_agent_system", "AgentState"]
@@ -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(