hdsp-jupyter-extension 2.0.26__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 (71) 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 -35
  8. agent_server/langchain/custom_middleware.py +278 -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/code_history_middleware.py +126 -37
  13. agent_server/langchain/middleware/content_injection_middleware.py +110 -0
  14. agent_server/langchain/middleware/subagent_events.py +88 -9
  15. agent_server/langchain/middleware/subagent_middleware.py +518 -240
  16. agent_server/langchain/prompts.py +5 -22
  17. agent_server/langchain/state_schema.py +44 -0
  18. agent_server/langchain/tools/jupyter_tools.py +4 -5
  19. agent_server/langchain/tools/tool_registry.py +6 -0
  20. agent_server/routers/chat.py +305 -2
  21. agent_server/routers/config.py +193 -8
  22. agent_server/routers/config_schema.py +254 -0
  23. agent_server/routers/context.py +31 -8
  24. agent_server/routers/langchain_agent.py +310 -153
  25. hdsp_agent_core/managers/config_manager.py +100 -1
  26. {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/build_log.json +1 -1
  27. {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/package.json +2 -2
  28. hdsp_jupyter_extension-2.0.26.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
  29. hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.55727265b00191e68d9a.js.map +1 -0
  30. 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
  31. hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.df05d90f366bfd5fa023.js.map +1 -0
  32. hdsp_jupyter_extension-2.0.26.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.0fe2dcbbd176ee0efceb.js → hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.08fce819ee32e9d25175.js +3 -3
  33. jupyter_ext/labextension/static/remoteEntry.0fe2dcbbd176ee0efceb.js.map → hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.08fce819ee32e9d25175.js.map +1 -1
  34. {hdsp_jupyter_extension-2.0.26.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/METADATA +1 -1
  35. {hdsp_jupyter_extension-2.0.26.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/RECORD +66 -64
  36. jupyter_ext/_version.py +1 -1
  37. jupyter_ext/handlers.py +41 -0
  38. jupyter_ext/labextension/build_log.json +1 -1
  39. jupyter_ext/labextension/package.json +2 -2
  40. jupyter_ext/labextension/static/{frontend_styles_index_js.b5e4416b4e07ec087aad.js → frontend_styles_index_js.55727265b00191e68d9a.js} +479 -15
  41. jupyter_ext/labextension/static/frontend_styles_index_js.55727265b00191e68d9a.js.map +1 -0
  42. hdsp_jupyter_extension-2.0.26.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.67505497667f9c0a763d.js → jupyter_ext/labextension/static/lib_index_js.df05d90f366bfd5fa023.js +1287 -190
  43. jupyter_ext/labextension/static/lib_index_js.df05d90f366bfd5fa023.js.map +1 -0
  44. jupyter_ext/labextension/static/{remoteEntry.0fe2dcbbd176ee0efceb.js → remoteEntry.08fce819ee32e9d25175.js} +3 -3
  45. hdsp_jupyter_extension-2.0.26.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.0fe2dcbbd176ee0efceb.js.map → jupyter_ext/labextension/static/remoteEntry.08fce819ee32e9d25175.js.map +1 -1
  46. agent_server/langchain/middleware/description_injector.py +0 -150
  47. hdsp_jupyter_extension-2.0.26.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.b5e4416b4e07ec087aad.js.map +0 -1
  48. hdsp_jupyter_extension-2.0.26.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.67505497667f9c0a763d.js.map +0 -1
  49. jupyter_ext/labextension/static/frontend_styles_index_js.b5e4416b4e07ec087aad.js.map +0 -1
  50. jupyter_ext/labextension/static/lib_index_js.67505497667f9c0a763d.js.map +0 -1
  51. {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +0 -0
  52. {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
  53. {hdsp_jupyter_extension-2.0.26.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
  54. {hdsp_jupyter_extension-2.0.26.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
  55. {hdsp_jupyter_extension-2.0.26.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
  56. {hdsp_jupyter_extension-2.0.26.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
  57. {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/style.js +0 -0
  58. {hdsp_jupyter_extension-2.0.26.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
  59. {hdsp_jupyter_extension-2.0.26.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
  60. {hdsp_jupyter_extension-2.0.26.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
  61. {hdsp_jupyter_extension-2.0.26.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
  62. {hdsp_jupyter_extension-2.0.26.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
  63. {hdsp_jupyter_extension-2.0.26.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
  64. {hdsp_jupyter_extension-2.0.26.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
  65. {hdsp_jupyter_extension-2.0.26.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
  66. {hdsp_jupyter_extension-2.0.26.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
  67. {hdsp_jupyter_extension-2.0.26.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
  68. {hdsp_jupyter_extension-2.0.26.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
  69. {hdsp_jupyter_extension-2.0.26.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
  70. {hdsp_jupyter_extension-2.0.26.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/WHEEL +0 -0
  71. {hdsp_jupyter_extension-2.0.26.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/licenses/LICENSE +0 -0
@@ -1,19 +1,42 @@
1
1
  """
2
2
  Config Router - Configuration management endpoints
3
+
4
+ Provides:
5
+ - GET/POST /config - Legacy full config (backward compatible)
6
+ - GET/POST /config/admin - Admin-only configuration
7
+ - GET/POST /config/user - User-only configuration
8
+ - GET /config/is-admin - Check if running in admin mode
9
+ - GET /config/prompts - Default system prompts
3
10
  """
4
11
 
5
- from typing import Any, Dict, List, Optional
12
+ from typing import Any, Dict, Optional
6
13
 
7
14
  from fastapi import APIRouter, HTTPException
8
15
  from hdsp_agent_core.managers.config_manager import ConfigManager
9
16
  from pydantic import BaseModel
10
17
 
18
+ from agent_server.langchain.agent_prompts.athena_query_prompt import (
19
+ ATHENA_QUERY_SYSTEM_PROMPT,
20
+ )
21
+
11
22
  # Import prompts from centralized location
12
- from agent_server.langchain.prompts import DEFAULT_SYSTEM_PROMPT
13
23
  from agent_server.langchain.agent_prompts.planner_prompt import PLANNER_SYSTEM_PROMPT
14
- from agent_server.langchain.agent_prompts.python_developer_prompt import PYTHON_DEVELOPER_SYSTEM_PROMPT
15
- from agent_server.langchain.agent_prompts.researcher_prompt import RESEARCHER_SYSTEM_PROMPT
16
- from agent_server.langchain.agent_prompts.athena_query_prompt import ATHENA_QUERY_SYSTEM_PROMPT
24
+ from agent_server.langchain.agent_prompts.python_developer_prompt import (
25
+ PYTHON_DEVELOPER_SYSTEM_PROMPT,
26
+ )
27
+ from agent_server.langchain.agent_prompts.researcher_prompt import (
28
+ RESEARCHER_SYSTEM_PROMPT,
29
+ )
30
+
31
+ # Import new schema models
32
+ from agent_server.routers.config_schema import (
33
+ AdminConfigResponse,
34
+ AdminConfigUpdateRequest,
35
+ IsAdminResponse,
36
+ UserConfigResponse,
37
+ UserConfigUpdateRequest,
38
+ is_admin_mode,
39
+ )
17
40
 
18
41
  router = APIRouter()
19
42
 
@@ -107,6 +130,170 @@ def _mask_api_keys(config: Dict[str, Any]) -> Dict[str, Any]:
107
130
  return masked
108
131
 
109
132
 
133
+ # =============================================================================
134
+ # Admin Mode Detection API
135
+ # =============================================================================
136
+
137
+
138
+ @router.get("/is-admin", response_model=IsAdminResponse)
139
+ async def check_admin_mode() -> IsAdminResponse:
140
+ """
141
+ Check if the current environment is in admin mode.
142
+
143
+ Admin mode is determined by environment variables:
144
+ - Neither SAGEMAKER_SPACE_NAME nor HDSP_PRJ_ID is set (development env)
145
+ - OR SAGEMAKER_SPACE_NAME contains 'pl2wadmprj'
146
+ - OR HDSP_PRJ_ID equals 'pl2wadmprj'
147
+ """
148
+ is_admin, reason = is_admin_mode()
149
+ return IsAdminResponse(isAdmin=is_admin, reason=reason)
150
+
151
+
152
+ # =============================================================================
153
+ # Admin Config API
154
+ # =============================================================================
155
+
156
+
157
+ @router.get("/admin", response_model=AdminConfigResponse)
158
+ async def get_admin_config() -> AdminConfigResponse:
159
+ """
160
+ Get admin configuration (LLM settings, prompts, server URLs).
161
+
162
+ API keys are masked for security.
163
+ """
164
+ try:
165
+ config_manager = ConfigManager.get_instance()
166
+ config = config_manager.get_admin_config()
167
+
168
+ # Mask API keys
169
+ masked_config = _mask_api_keys(config)
170
+
171
+ # Also mask embedding API key if present
172
+ if "embedding" in masked_config and isinstance(
173
+ masked_config["embedding"], dict
174
+ ):
175
+ embedding_config = masked_config["embedding"].copy()
176
+ if "apiKey" in embedding_config and embedding_config["apiKey"]:
177
+ key = embedding_config["apiKey"]
178
+ if len(key) > 8:
179
+ embedding_config["apiKey"] = f"{key[:4]}...{key[-4:]}"
180
+ else:
181
+ embedding_config["apiKey"] = "***"
182
+ masked_config["embedding"] = embedding_config
183
+
184
+ return AdminConfigResponse(
185
+ provider=masked_config.get("provider", "gemini"),
186
+ gemini=masked_config.get("gemini", {}),
187
+ openai=masked_config.get("openai", {}),
188
+ vllm=masked_config.get("vllm", {}),
189
+ summarization=masked_config.get("summarization", {}),
190
+ embedding=masked_config.get("embedding", {}),
191
+ rag=masked_config.get("rag", {}),
192
+ agentServerUrl=masked_config.get("agentServerUrl", "http://localhost:8000"),
193
+ agentServerTimeout=masked_config.get("agentServerTimeout", 120.0),
194
+ prompts=masked_config.get("prompts", {}),
195
+ useResponsesApi=masked_config.get("useResponsesApi", False),
196
+ idleTimeout=masked_config.get("idleTimeout", 300),
197
+ )
198
+ except Exception as e:
199
+ raise HTTPException(status_code=500, detail=str(e))
200
+
201
+
202
+ @router.post("/admin")
203
+ async def update_admin_config(request: AdminConfigUpdateRequest) -> Dict[str, Any]:
204
+ """
205
+ Update admin configuration.
206
+
207
+ Only updates provided fields.
208
+ """
209
+ try:
210
+ config_manager = ConfigManager.get_instance()
211
+
212
+ # Build update dict from request
213
+ updates = {}
214
+ if request.provider is not None:
215
+ updates["provider"] = request.provider
216
+ if request.gemini is not None:
217
+ updates["gemini"] = request.gemini
218
+ if request.openai is not None:
219
+ updates["openai"] = request.openai
220
+ if request.vllm is not None:
221
+ updates["vllm"] = request.vllm
222
+ if request.summarization is not None:
223
+ updates["summarization"] = request.summarization
224
+ if request.embedding is not None:
225
+ updates["embedding"] = request.embedding
226
+ if request.rag is not None:
227
+ updates["rag"] = request.rag
228
+ if request.agentServerUrl is not None:
229
+ updates["agentServerUrl"] = request.agentServerUrl
230
+ if request.agentServerTimeout is not None:
231
+ updates["agentServerTimeout"] = request.agentServerTimeout
232
+ if request.prompts is not None:
233
+ updates["prompts"] = request.prompts
234
+ if request.useResponsesApi is not None:
235
+ updates["useResponsesApi"] = request.useResponsesApi
236
+ if request.idleTimeout is not None:
237
+ updates["idleTimeout"] = request.idleTimeout
238
+
239
+ if updates:
240
+ config_manager.update_admin_config(updates)
241
+
242
+ return {"status": "success", "message": "Admin configuration updated"}
243
+ except Exception as e:
244
+ raise HTTPException(status_code=500, detail=str(e))
245
+
246
+
247
+ # =============================================================================
248
+ # User Config API
249
+ # =============================================================================
250
+
251
+
252
+ @router.get("/user", response_model=UserConfigResponse)
253
+ async def get_user_config() -> UserConfigResponse:
254
+ """
255
+ Get user configuration (preferences).
256
+ """
257
+ try:
258
+ config_manager = ConfigManager.get_instance()
259
+ config = config_manager.get_user_config()
260
+
261
+ return UserConfigResponse(
262
+ workspaceRoot=config.get("workspaceRoot", ""),
263
+ temperature=config.get("temperature", 0.7),
264
+ autoApprove=config.get("autoApprove", False),
265
+ )
266
+ except Exception as e:
267
+ raise HTTPException(status_code=500, detail=str(e))
268
+
269
+
270
+ @router.post("/user")
271
+ async def update_user_config(request: UserConfigUpdateRequest) -> Dict[str, Any]:
272
+ """
273
+ Update user configuration.
274
+
275
+ Only updates provided fields.
276
+ """
277
+ try:
278
+ config_manager = ConfigManager.get_instance()
279
+
280
+ # Build update dict from request
281
+ updates = {}
282
+ if request.workspaceRoot is not None:
283
+ updates["workspaceRoot"] = request.workspaceRoot
284
+ if request.temperature is not None:
285
+ updates["temperature"] = request.temperature
286
+ if request.autoApprove is not None:
287
+ updates["autoApprove"] = request.autoApprove
288
+
289
+ if updates:
290
+ config_manager.update_user_config(updates)
291
+
292
+ return {"status": "success", "message": "User configuration updated"}
293
+ except Exception as e:
294
+ raise HTTPException(status_code=500, detail=str(e))
295
+
296
+
110
297
  # =============================================================================
111
298
  # Default Prompts API
112
299
  # =============================================================================
@@ -115,7 +302,6 @@ def _mask_api_keys(config: Dict[str, Any]) -> Dict[str, Any]:
115
302
  class DefaultPromptsResponse(BaseModel):
116
303
  """Default prompts response model"""
117
304
 
118
- single: str # Single-agent mode system prompt
119
305
  planner: str # Multi-agent: Planner/Coordinator prompt
120
306
  python_developer: str # Multi-agent: Python Developer prompt
121
307
  researcher: str # Multi-agent: Researcher prompt
@@ -125,13 +311,12 @@ class DefaultPromptsResponse(BaseModel):
125
311
  @router.get("/prompts", response_model=DefaultPromptsResponse)
126
312
  async def get_default_prompts() -> DefaultPromptsResponse:
127
313
  """
128
- Get default system prompts for all agent modes.
314
+ Get default system prompts for multi-agent mode.
129
315
 
130
316
  Returns centralized prompts that frontend can use as defaults.
131
317
  This ensures prompts are managed in one place (backend).
132
318
  """
133
319
  return DefaultPromptsResponse(
134
- single=DEFAULT_SYSTEM_PROMPT,
135
320
  planner=PLANNER_SYSTEM_PROMPT,
136
321
  python_developer=PYTHON_DEVELOPER_SYSTEM_PROMPT,
137
322
  researcher=RESEARCHER_SYSTEM_PROMPT,
@@ -0,0 +1,254 @@
1
+ """
2
+ Config Schema - Pydantic models for Admin/User configuration
3
+
4
+ Admin Config: LLM settings, prompts, server URLs (managed by administrators)
5
+ User Config: Workspace settings, temperature, auto-approve (user preferences)
6
+ """
7
+
8
+ import os
9
+ from enum import Enum
10
+ from typing import Any, Dict, Optional
11
+
12
+ from pydantic import BaseModel, Field
13
+
14
+ # =============================================================================
15
+ # Enums
16
+ # =============================================================================
17
+
18
+
19
+ class LLMProvider(str, Enum):
20
+ """Supported LLM providers"""
21
+
22
+ GEMINI = "gemini"
23
+ OPENAI = "openai"
24
+ VLLM = "vllm"
25
+
26
+
27
+ # =============================================================================
28
+ # Admin Config Models
29
+ # =============================================================================
30
+
31
+
32
+ class GeminiConfig(BaseModel):
33
+ """Gemini provider configuration"""
34
+
35
+ apiKey: str = ""
36
+ model: str = "gemini-2.5-pro"
37
+
38
+
39
+ class OpenAIConfig(BaseModel):
40
+ """OpenAI provider configuration"""
41
+
42
+ apiKey: str = ""
43
+ model: str = "gpt-4o"
44
+
45
+
46
+ class VLLMConfig(BaseModel):
47
+ """vLLM/OpenRouter provider configuration"""
48
+
49
+ endpoint: str = "http://localhost:8000"
50
+ apiKey: str = ""
51
+ model: str = "meta-llama/Llama-2-7b-chat-hf"
52
+
53
+
54
+ class SummarizationConfig(BaseModel):
55
+ """Summarization LLM configuration (optional separate LLM)"""
56
+
57
+ enabled: bool = False
58
+ provider: LLMProvider = LLMProvider.GEMINI
59
+ model: Optional[str] = None # If None, use provider's default
60
+
61
+
62
+ class EmbeddingConfig(BaseModel):
63
+ """Embedding model configuration for RAG"""
64
+
65
+ provider: str = "openai"
66
+ model: str = "text-embedding-3-small"
67
+ apiKey: Optional[str] = None # If None, use main provider's key
68
+
69
+
70
+ class RAGConfig(BaseModel):
71
+ """RAG/Qdrant configuration"""
72
+
73
+ qdrantUrl: str = "http://localhost:6333"
74
+ collectionName: str = "hdsp_docs"
75
+
76
+
77
+ class AgentPromptsConfig(BaseModel):
78
+ """Custom system prompts for agents"""
79
+
80
+ planner: Optional[str] = None
81
+ python_developer: Optional[str] = None
82
+ researcher: Optional[str] = None
83
+ athena_query: Optional[str] = None
84
+
85
+
86
+ class AdminConfig(BaseModel):
87
+ """
88
+ Administrator-managed configuration.
89
+
90
+ Includes:
91
+ - LLM provider settings (API keys, models)
92
+ - Summarization LLM settings
93
+ - Embedding configuration
94
+ - RAG/Qdrant settings
95
+ - Agent server URL
96
+ - Agent prompts
97
+ - Idle timeout
98
+ - Use Responses API toggle
99
+ """
100
+
101
+ # LLM Provider Settings
102
+ provider: LLMProvider = LLMProvider.GEMINI
103
+ gemini: GeminiConfig = Field(default_factory=GeminiConfig)
104
+ openai: OpenAIConfig = Field(default_factory=OpenAIConfig)
105
+ vllm: VLLMConfig = Field(default_factory=VLLMConfig)
106
+
107
+ # Summarization LLM (optional separate LLM for context compression)
108
+ summarization: SummarizationConfig = Field(default_factory=SummarizationConfig)
109
+
110
+ # Embedding Configuration
111
+ embedding: EmbeddingConfig = Field(default_factory=EmbeddingConfig)
112
+
113
+ # RAG Configuration
114
+ rag: RAGConfig = Field(default_factory=RAGConfig)
115
+
116
+ # Agent Server Configuration
117
+ agentServerUrl: str = "http://localhost:8000"
118
+ agentServerTimeout: float = 120.0
119
+
120
+ # Agent Prompts (custom overrides)
121
+ prompts: AgentPromptsConfig = Field(default_factory=AgentPromptsConfig)
122
+
123
+ # Feature Toggles
124
+ useResponsesApi: bool = False
125
+ idleTimeout: int = 300 # seconds
126
+
127
+
128
+ # =============================================================================
129
+ # User Config Models
130
+ # =============================================================================
131
+
132
+
133
+ class UserConfig(BaseModel):
134
+ """
135
+ User-managed configuration (preferences).
136
+
137
+ Includes:
138
+ - Workspace root path
139
+ - Temperature setting
140
+ - Auto-approve toggle
141
+ """
142
+
143
+ workspaceRoot: str = ""
144
+ temperature: float = Field(default=0.7, ge=0.0, le=2.0)
145
+ autoApprove: bool = False
146
+
147
+
148
+ # =============================================================================
149
+ # Combined Config
150
+ # =============================================================================
151
+
152
+
153
+ class FullConfig(BaseModel):
154
+ """Full configuration combining admin and user settings"""
155
+
156
+ admin: AdminConfig = Field(default_factory=AdminConfig)
157
+ user: UserConfig = Field(default_factory=UserConfig)
158
+
159
+
160
+ # =============================================================================
161
+ # Admin Mode Detection
162
+ # =============================================================================
163
+
164
+
165
+ def is_admin_mode() -> tuple[bool, str | None]:
166
+ """
167
+ Detect if running in admin mode based on environment variables.
168
+
169
+ Admin mode conditions:
170
+ 1. NEITHER SAGEMAKER_SPACE_NAME NOR HDSP_PRJ_ID is set (development environment)
171
+ 2. OR SAGEMAKER_SPACE_NAME contains 'pl2wadmprj' as substring
172
+ 3. OR HDSP_PRJ_ID equals 'pl2wadmprj'
173
+
174
+ Returns:
175
+ tuple[bool, str | None]: (is_admin, reason)
176
+ """
177
+ sagemaker_space = os.environ.get("SAGEMAKER_SPACE_NAME")
178
+ hdsp_prj_id = os.environ.get("HDSP_PRJ_ID")
179
+
180
+ # Development environment: neither env var is set
181
+ if sagemaker_space is None and hdsp_prj_id is None:
182
+ return True, "development_environment"
183
+
184
+ # Check SAGEMAKER_SPACE_NAME contains admin project ID
185
+ if sagemaker_space and "pl2wadmprj" in sagemaker_space:
186
+ return True, "sagemaker_admin_space"
187
+
188
+ # Check HDSP_PRJ_ID equals admin project ID
189
+ if hdsp_prj_id == "pl2wadmprj":
190
+ return True, "hdsp_admin_project"
191
+
192
+ return False, None
193
+
194
+
195
+ # =============================================================================
196
+ # API Request/Response Models
197
+ # =============================================================================
198
+
199
+
200
+ class AdminConfigResponse(BaseModel):
201
+ """Admin config response (with masked API keys)"""
202
+
203
+ provider: str
204
+ gemini: Dict[str, Any]
205
+ openai: Dict[str, Any]
206
+ vllm: Dict[str, Any]
207
+ summarization: Dict[str, Any]
208
+ embedding: Dict[str, Any]
209
+ rag: Dict[str, Any]
210
+ agentServerUrl: str
211
+ agentServerTimeout: float
212
+ prompts: Dict[str, Any]
213
+ useResponsesApi: bool
214
+ idleTimeout: int
215
+
216
+
217
+ class AdminConfigUpdateRequest(BaseModel):
218
+ """Admin config update request"""
219
+
220
+ provider: Optional[str] = None
221
+ gemini: Optional[Dict[str, Any]] = None
222
+ openai: Optional[Dict[str, Any]] = None
223
+ vllm: Optional[Dict[str, Any]] = None
224
+ summarization: Optional[Dict[str, Any]] = None
225
+ embedding: Optional[Dict[str, Any]] = None
226
+ rag: Optional[Dict[str, Any]] = None
227
+ agentServerUrl: Optional[str] = None
228
+ agentServerTimeout: Optional[float] = None
229
+ prompts: Optional[Dict[str, Any]] = None
230
+ useResponsesApi: Optional[bool] = None
231
+ idleTimeout: Optional[int] = None
232
+
233
+
234
+ class UserConfigResponse(BaseModel):
235
+ """User config response"""
236
+
237
+ workspaceRoot: str
238
+ temperature: float
239
+ autoApprove: bool
240
+
241
+
242
+ class UserConfigUpdateRequest(BaseModel):
243
+ """User config update request"""
244
+
245
+ workspaceRoot: Optional[str] = None
246
+ temperature: Optional[float] = None
247
+ autoApprove: Optional[bool] = None
248
+
249
+
250
+ class IsAdminResponse(BaseModel):
251
+ """Response for admin mode check"""
252
+
253
+ isAdmin: bool
254
+ reason: Optional[str] = None
@@ -12,7 +12,12 @@ from typing import Optional
12
12
  from fastapi import APIRouter, Query
13
13
  from pydantic import BaseModel
14
14
 
15
- from agent_server.context_providers import FileContextProvider, ResetContextProvider
15
+ from agent_server.context_providers import (
16
+ CompactContextProvider,
17
+ FileContextProvider,
18
+ HelpContextProvider,
19
+ ResetContextProvider,
20
+ )
16
21
  from agent_server.context_providers.base import ListOptionsResponse
17
22
 
18
23
  router = APIRouter()
@@ -24,7 +29,9 @@ def _get_all_providers(base_dir: str):
24
29
  """Get all available context/action providers."""
25
30
  return {
26
31
  "@file": FileContextProvider(base_dir=base_dir),
27
- "@reset": ResetContextProvider(base_dir=base_dir),
32
+ "/reset": ResetContextProvider(base_dir=base_dir),
33
+ "/compact": CompactContextProvider(base_dir=base_dir),
34
+ "/help": HelpContextProvider(base_dir=base_dir),
28
35
  }
29
36
 
30
37
 
@@ -93,7 +100,7 @@ async def get_autocomplete_options(
93
100
  options = provider.get_arg_options(arg_prefix)
94
101
  return ListOptionsResponse(options=options)
95
102
  else:
96
- # For commands without args (like @reset), return the command itself
103
+ # For commands without args (like /reset), return the command itself
97
104
  return ListOptionsResponse(options=[provider.get_provider_option()])
98
105
 
99
106
  # No matching provider
@@ -145,7 +152,7 @@ async def list_context_providers():
145
152
  providers = [
146
153
  {
147
154
  "id": "@file",
148
- "description": "Include file contents in the prompt",
155
+ "description": "파일 내용을 프롬프트에 포함",
149
156
  "requires_arg": True,
150
157
  "is_action": False,
151
158
  "usage": "@file:path/to/file.py",
@@ -156,12 +163,28 @@ async def list_context_providers():
156
163
  ],
157
164
  },
158
165
  {
159
- "id": "@reset",
160
- "description": "Clear session and reset agent",
166
+ "id": "/reset",
167
+ "description": "세션 초기화 에이전트 리셋",
161
168
  "requires_arg": False,
162
169
  "is_action": True,
163
- "usage": "@reset",
164
- "examples": ["@reset"],
170
+ "usage": "/reset",
171
+ "examples": ["/reset"],
172
+ },
173
+ {
174
+ "id": "/compact",
175
+ "description": "대화 기록 압축 및 요약",
176
+ "requires_arg": False,
177
+ "is_action": True,
178
+ "usage": "/compact",
179
+ "examples": ["/compact"],
180
+ },
181
+ {
182
+ "id": "/help",
183
+ "description": "도움말 및 사용 가이드",
184
+ "requires_arg": False,
185
+ "is_action": True,
186
+ "usage": "/help",
187
+ "examples": ["/help"],
165
188
  },
166
189
  ]
167
190