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.
- 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/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/routers/chat.py +305 -2
- agent_server/routers/config.py +193 -8
- agent_server/routers/config_schema.py +254 -0
- agent_server/routers/context.py +31 -8
- agent_server/routers/langchain_agent.py +276 -155
- hdsp_agent_core/managers/config_manager.py +100 -1
- {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
- {hdsp_jupyter_extension-2.0.27.data → hdsp_jupyter_extension-2.0.28.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.28.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.55727265b00191e68d9a.js +479 -15
- hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.55727265b00191e68d9a.js.map +1 -0
- 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
- hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.df05d90f366bfd5fa023.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.28.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.08fce819ee32e9d25175.js +3 -3
- 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
- {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/METADATA +1 -1
- {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/RECORD +65 -63
- 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.55727265b00191e68d9a.js} +479 -15
- jupyter_ext/labextension/static/frontend_styles_index_js.55727265b00191e68d9a.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.df05d90f366bfd5fa023.js +1287 -190
- jupyter_ext/labextension/static/lib_index_js.df05d90f366bfd5fa023.js.map +1 -0
- jupyter_ext/labextension/static/{remoteEntry.4ab73bb5068405670214.js → remoteEntry.08fce819ee32e9d25175.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.08fce819ee32e9d25175.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.28.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.28.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {hdsp_jupyter_extension-2.0.27.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/WHEEL +0 -0
- {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.,
|
|
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
|
|
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
|
-
|
|
16
|
+
/reset - Clear conversation history and recreate agent
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
|
-
id = "
|
|
20
|
-
description = "
|
|
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
|
|
28
|
+
"""No arguments for /reset."""
|
|
29
29
|
return []
|
|
30
30
|
|
|
31
31
|
def make_context(self, command: ContextCommand) -> str:
|
|
32
|
-
"""
|
|
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
|
|
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 = "
|
|
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="
|
|
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
|
|
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
|
|
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, "
|
|
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
|
|
15
|
+
from agent_server.langchain.agent import create_agent_system
|
|
16
16
|
from agent_server.langchain.state import AgentState
|
|
17
17
|
|
|
18
|
-
__all__ = ["
|
|
18
|
+
__all__ = ["create_agent_system", "AgentState"]
|
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(
|