hdsp-jupyter-extension 2.0.28__py3-none-any.whl → 2.0.30__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 (54) hide show
  1. agent_server/config/__init__.py +5 -0
  2. agent_server/config/server_config.py +213 -0
  3. agent_server/core/__init__.py +2 -2
  4. agent_server/core/llm_service.py +2 -3
  5. agent_server/main.py +4 -4
  6. agent_server/routers/agent.py +2 -2
  7. agent_server/routers/chat.py +31 -28
  8. agent_server/routers/config.py +8 -7
  9. agent_server/routers/langchain_agent.py +97 -79
  10. hdsp_agent_core/managers/config_manager.py +37 -87
  11. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.data}/data/share/jupyter/labextensions/hdsp-agent/build_log.json +1 -1
  12. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.data}/data/share/jupyter/labextensions/hdsp-agent/package.json +2 -2
  13. hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.55727265b00191e68d9a.js → hdsp_jupyter_extension-2.0.30.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.f2eca2f8fa682eb21f72.js +11 -12
  14. hdsp_jupyter_extension-2.0.30.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.f2eca2f8fa682eb21f72.js.map +1 -0
  15. jupyter_ext/labextension/static/lib_index_js.df05d90f366bfd5fa023.js → hdsp_jupyter_extension-2.0.30.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.cc0a7158a5e3de7f22f7.js +125 -949
  16. hdsp_jupyter_extension-2.0.30.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.cc0a7158a5e3de7f22f7.js.map +1 -0
  17. hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.08fce819ee32e9d25175.js → hdsp_jupyter_extension-2.0.30.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.f6cc904a0e9d0d4a9792.js +3 -3
  18. jupyter_ext/labextension/static/remoteEntry.08fce819ee32e9d25175.js.map → hdsp_jupyter_extension-2.0.30.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.f6cc904a0e9d0d4a9792.js.map +1 -1
  19. {hdsp_jupyter_extension-2.0.28.dist-info → hdsp_jupyter_extension-2.0.30.dist-info}/METADATA +1 -1
  20. {hdsp_jupyter_extension-2.0.28.dist-info → hdsp_jupyter_extension-2.0.30.dist-info}/RECORD +50 -48
  21. jupyter_ext/_version.py +1 -1
  22. jupyter_ext/labextension/build_log.json +1 -1
  23. jupyter_ext/labextension/package.json +2 -2
  24. jupyter_ext/labextension/static/{frontend_styles_index_js.55727265b00191e68d9a.js → frontend_styles_index_js.f2eca2f8fa682eb21f72.js} +11 -12
  25. jupyter_ext/labextension/static/frontend_styles_index_js.f2eca2f8fa682eb21f72.js.map +1 -0
  26. hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.df05d90f366bfd5fa023.js → jupyter_ext/labextension/static/lib_index_js.cc0a7158a5e3de7f22f7.js +125 -949
  27. jupyter_ext/labextension/static/lib_index_js.cc0a7158a5e3de7f22f7.js.map +1 -0
  28. jupyter_ext/labextension/static/{remoteEntry.08fce819ee32e9d25175.js → remoteEntry.f6cc904a0e9d0d4a9792.js} +3 -3
  29. hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.08fce819ee32e9d25175.js.map → jupyter_ext/labextension/static/remoteEntry.f6cc904a0e9d0d4a9792.js.map +1 -1
  30. hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.55727265b00191e68d9a.js.map +0 -1
  31. hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.df05d90f366bfd5fa023.js.map +0 -1
  32. jupyter_ext/labextension/static/frontend_styles_index_js.55727265b00191e68d9a.js.map +0 -1
  33. jupyter_ext/labextension/static/lib_index_js.df05d90f366bfd5fa023.js.map +0 -1
  34. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.data}/data/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +0 -0
  35. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
  36. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.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
  37. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.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
  38. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.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
  39. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.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
  40. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.data}/data/share/jupyter/labextensions/hdsp-agent/static/style.js +0 -0
  41. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.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
  42. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.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
  43. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js +0 -0
  44. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.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
  45. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js +0 -0
  46. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.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
  47. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js +0 -0
  48. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.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
  49. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +0 -0
  50. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js.map +0 -0
  51. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js +0 -0
  52. {hdsp_jupyter_extension-2.0.28.data → hdsp_jupyter_extension-2.0.30.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js.map +0 -0
  53. {hdsp_jupyter_extension-2.0.28.dist-info → hdsp_jupyter_extension-2.0.30.dist-info}/WHEEL +0 -0
  54. {hdsp_jupyter_extension-2.0.28.dist-info → hdsp_jupyter_extension-2.0.30.dist-info}/licenses/LICENSE +0 -0
@@ -30,10 +30,11 @@ __webpack_require__.r(__webpack_exports__);
30
30
  * - LLM Provider settings (API keys, models, endpoints)
31
31
  * - Summarization LLM settings
32
32
  * - Embedding configuration
33
- * - RAG/Qdrant settings
34
33
  * - User preferences (also editable by admin)
35
34
  * - Agent prompts
36
35
  * - Server settings
36
+ *
37
+ * Note: RAG/Qdrant URL is configured in Agent Server only (not exposed in UI)
37
38
  */
38
39
 
39
40
 
@@ -55,7 +56,6 @@ const AdminSettingsPanel = ({ onClose, onSave, apiService }) => {
55
56
  llm: true,
56
57
  summarization: false,
57
58
  embedding: false,
58
- rag: false,
59
59
  user: true,
60
60
  server: false,
61
61
  prompts: false
@@ -91,10 +91,6 @@ const AdminSettingsPanel = ({ onClose, onSave, apiService }) => {
91
91
  const [embeddingApiKey, setEmbeddingApiKey] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
92
92
  const [embeddingEndpoint, setEmbeddingEndpoint] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
93
93
  // ═══════════════════════════════════════════════════════════════════════════
94
- // RAG Settings
95
- // ═══════════════════════════════════════════════════════════════════════════
96
- const [qdrantUrl, setQdrantUrl] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('http://localhost:6333');
97
- // ═══════════════════════════════════════════════════════════════════════════
98
94
  // User Settings (also editable by admin)
99
95
  // ═══════════════════════════════════════════════════════════════════════════
100
96
  const [workspaceRoot, setWorkspaceRoot] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
@@ -105,7 +101,7 @@ const AdminSettingsPanel = ({ onClose, onSave, apiService }) => {
105
101
  // ═══════════════════════════════════════════════════════════════════════════
106
102
  const [agentServerUrl, setAgentServerUrl] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('http://localhost:8000');
107
103
  const [agentServerTimeout, setAgentServerTimeout] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(120);
108
- const [idleTimeout, setIdleTimeout] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(300);
104
+ const [idleTimeout, setIdleTimeout] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(3600); // Default: 1 hour
109
105
  // ═══════════════════════════════════════════════════════════════════════════
110
106
  // Agent Prompts
111
107
  // ═══════════════════════════════════════════════════════════════════════════
@@ -171,14 +167,10 @@ const AdminSettingsPanel = ({ onClose, onSave, apiService }) => {
171
167
  setEmbeddingApiKey(adminConfig.embedding.apiKey || '');
172
168
  setEmbeddingEndpoint(adminConfig.embedding.endpoint || '');
173
169
  }
174
- // RAG
175
- if (adminConfig.rag) {
176
- setQdrantUrl(adminConfig.rag.qdrantUrl || 'http://localhost:6333');
177
- }
178
170
  // Server
179
171
  setAgentServerUrl(adminConfig.agentServerUrl || 'http://localhost:8000');
180
172
  setAgentServerTimeout(adminConfig.agentServerTimeout || 120);
181
- setIdleTimeout(adminConfig.idleTimeout || 300);
173
+ setIdleTimeout(adminConfig.idleTimeout || 3600); // Default: 1 hour
182
174
  // Prompts
183
175
  if (adminConfig.prompts) {
184
176
  setAgentPrompts(adminConfig.prompts);
@@ -236,10 +228,6 @@ const AdminSettingsPanel = ({ onClose, onSave, apiService }) => {
236
228
  apiKey: embeddingApiKey || undefined,
237
229
  ...(embeddingProvider === 'vllm' ? { endpoint: embeddingEndpoint } : {})
238
230
  },
239
- rag: {
240
- qdrantUrl,
241
- collectionName: '' // Will be set by agent
242
- },
243
231
  agentServerUrl,
244
232
  agentServerTimeout,
245
233
  prompts: agentPrompts,
@@ -252,29 +240,13 @@ const AdminSettingsPanel = ({ onClose, onSave, apiService }) => {
252
240
  temperature,
253
241
  autoApprove
254
242
  });
255
- // Also save to localStorage for Chat mode compatibility
256
- const llmConfigForLocalStorage = {
257
- provider,
258
- gemini: {
259
- apiKey: geminiApiKeys[0] || '',
260
- apiKeys: geminiApiKeys.filter(k => k && k.trim()),
261
- model: geminiModel
262
- },
263
- openai: {
264
- apiKey: openaiApiKey,
265
- model: openaiModel
266
- },
267
- vllm: {
268
- endpoint: vllmEndpoint,
269
- apiKey: vllmApiKey,
270
- model: vllmModel
271
- },
243
+ // Save only user preferences to localStorage
244
+ const userPrefs = {
272
245
  workspaceRoot: workspaceRoot.trim(),
273
- autoApprove,
274
- agentPrompts
246
+ autoApprove
275
247
  };
276
- (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.saveLLMConfig)(llmConfigForLocalStorage);
277
- console.log('[AdminSettingsPanel] Config saved to localStorage for Chat mode');
248
+ (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.saveLLMConfig)(userPrefs);
249
+ console.log('[AdminSettingsPanel] User preferences saved to localStorage');
278
250
  onSave(adminConfig);
279
251
  onClose();
280
252
  }
@@ -478,12 +450,6 @@ const AdminSettingsPanel = ({ onClose, onSave, apiService }) => {
478
450
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
479
451
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "\uBAA8\uB378"),
480
452
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "text", className: "jp-admin-input", value: embeddingModel, onChange: (e) => setEmbeddingModel(e.target.value), placeholder: "text-embedding-3-small" }))))),
481
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section" },
482
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(SectionHeader, { title: "RAG / Qdrant", sectionKey: "rag", subtitle: "\uBCA1\uD130 \uB370\uC774\uD130\uBCA0\uC774\uC2A4" }),
483
- expandedSections.rag && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section-content" },
484
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
485
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "Qdrant URL"),
486
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "text", className: "jp-admin-input", value: qdrantUrl, onChange: (e) => setQdrantUrl(e.target.value), placeholder: "http://localhost:6333" }))))),
487
453
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section" },
488
454
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(SectionHeader, { title: "\uC11C\uBC84 \uC124\uC815", sectionKey: "server", subtitle: "Agent \uC11C\uBC84, \uD0C0\uC784\uC544\uC6C3" }),
489
455
  expandedSections.server && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section-content" },
@@ -1297,22 +1263,11 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
1297
1263
  return;
1298
1264
  }
1299
1265
  setLlmConfig(config);
1300
- // Log loaded model configuration
1301
- console.log('=== HDSP Agent Model Configuration (localStorage) ===');
1302
- console.log('Provider:', config.provider);
1303
- if (config.provider === 'gemini') {
1304
- console.log('Gemini Model:', config.gemini?.model || 'gemini-2.5-flash (default)');
1305
- console.log('Gemini API Key:', config.gemini?.apiKey ? '✓ Configured' : '✗ Not configured');
1306
- }
1307
- else if (config.provider === 'vllm') {
1308
- console.log('vLLM Model:', config.vllm?.model || 'default');
1309
- console.log('vLLM Endpoint:', config.vllm?.endpoint || 'http://localhost:8000/v1');
1310
- console.log('vLLM API Key:', config.vllm?.apiKey ? '✓ Configured' : '✗ Not configured');
1311
- }
1312
- else if (config.provider === 'openai') {
1313
- console.log('OpenAI Model:', config.openai?.model || 'gpt-4');
1314
- console.log('OpenAI API Key:', config.openai?.apiKey ? '✓ Configured' : '✗ Not configured');
1315
- }
1266
+ // Log loaded user preferences (LLM settings now come from server AdminConfig)
1267
+ console.log('=== HDSP Agent User Preferences (localStorage) ===');
1268
+ console.log('Workspace Root:', config.workspaceRoot || '(default)');
1269
+ console.log('Auto Approve:', config.autoApprove ? '✓ Enabled' : '✗ Disabled');
1270
+ console.log('Note: LLM settings are managed by Agent Server AdminConfig');
1316
1271
  console.log('====================================================');
1317
1272
  };
1318
1273
  // ═══════════════════════════════════════════════════════════════════════════
@@ -1504,14 +1459,14 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
1504
1459
  setMessages(prev => [...prev, errorMessage]);
1505
1460
  };
1506
1461
  const handleSaveConfig = (config) => {
1507
- console.log('[AgentPanel] Saving config to localStorage');
1508
- console.log('Provider:', config.provider);
1509
- console.log('API Key configured:', (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.hasValidApiKey)(config) ? '✓ Yes' : '✗ No');
1462
+ console.log('[AgentPanel] Saving user preferences to localStorage');
1463
+ console.log('Workspace Root:', config.workspaceRoot || '(default)');
1464
+ console.log('Auto Approve:', config.autoApprove ? '✓ Enabled' : '✗ Disabled');
1510
1465
  // Save to localStorage using ApiKeyManager
1511
1466
  (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.saveLLMConfig)(config);
1512
1467
  // Update state
1513
1468
  setLlmConfig(config);
1514
- console.log('[AgentPanel] Config saved successfully');
1469
+ console.log('[AgentPanel] User preferences saved successfully');
1515
1470
  };
1516
1471
  // File selection handlers (simplified - old agent mode removed)
1517
1472
  const handleFileSelect = async (index) => {
@@ -1726,22 +1681,8 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
1726
1681
  currentConfig = (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.getLLMConfig)() || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.getDefaultLLMConfig)();
1727
1682
  setLlmConfig(currentConfig);
1728
1683
  }
1729
- // Check API key using ApiKeyManager
1730
- const hasApiKey = (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.hasValidApiKey)(currentConfig);
1684
+ // Get user preferences (API keys are now managed by Agent Server)
1731
1685
  const autoApproveEnabled = getAutoApproveEnabled(currentConfig);
1732
- if (!hasApiKey) {
1733
- // Show error message and open settings
1734
- const providerName = currentConfig?.provider || 'LLM';
1735
- const errorMessage = {
1736
- id: makeMessageId(),
1737
- role: 'assistant',
1738
- content: `API Key가 설정되지 않았습니다.\n\n${providerName === 'gemini' ? 'Gemini' : providerName === 'openai' ? 'OpenAI' : 'vLLM'} API Key를 먼저 설정해주세요.\n\n설정 버튼을 클릭하여 API Key를 입력하세요.`,
1739
- timestamp: Date.now()
1740
- };
1741
- setMessages(prev => [...prev, errorMessage]);
1742
- setShowSettings(true);
1743
- return;
1744
- }
1745
1686
  const userMessage = {
1746
1687
  id: makeMessageId(),
1747
1688
  role: 'user',
@@ -3863,7 +3804,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
3863
3804
  setTimeout(() => {
3864
3805
  const textarea = document.querySelector('.jp-agent-input');
3865
3806
  if (textarea) {
3866
- textarea.placeholder = '메시지를 입력하세요...';
3807
+ textarea.placeholder = '메세지를 입력하세요.';
3867
3808
  }
3868
3809
  }, 100);
3869
3810
  // Resume with user response passed as feedback
@@ -4201,7 +4142,13 @@ SyntaxError: '(' was never closed
4201
4142
  };
4202
4143
  // Execute /help action - Display help guide (no LLM call)
4203
4144
  const executeHelpAction = () => {
4204
- const helpContent = `## HDSP Agent 사용 가이드
4145
+ // Create logo with 60px height for help content (use base64 data URL)
4146
+ const svgBase64 = btoa(unescape(encodeURIComponent(_logoSvg__WEBPACK_IMPORTED_MODULE_14__.headerLogoSvg)));
4147
+ const helpContent = `<div style="text-align: center; margin-bottom: 16px;"><img src="data:image/svg+xml;base64,${svgBase64}" style="max-width: 50%; height: auto;" alt="HALO Logo" /></div>
4148
+
4149
+ ## HALO 사용 가이드
4150
+
4151
+ **HALO**는 **H**DSP **A**ssistant with **L**LM **O**perator의 약자로, LLM 기반의 지능형 데이터 분석 어시스턴트입니다. JupyterLab 환경에서 AI와 대화하며 데이터 분석, 코드 작성, 문제 해결을 도와받을 수 있습니다.
4205
4152
 
4206
4153
  ### 사용 가능한 명령어
4207
4154
 
@@ -4713,7 +4660,7 @@ Jupyter 노트북의 각 코드 셀 옆에 있는 아이콘 버튼을 활용하
4713
4660
  ? '다른 방향 제시'
4714
4661
  : (inputMode === 'agent'
4715
4662
  ? '노트북 작업을 입력하세요... (예: @file:path로 파일 참조)'
4716
- : '메시지를 입력하세요... (@file:path로 파일 참조)'), disabled: isLoading || isAgentRunning }),
4663
+ : '메세지를 입력하세요.'), disabled: isLoading || isAgentRunning }),
4717
4664
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-button-container" }, inputMode === 'chat' && isStreaming ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-stop-button", onClick: stopCurrentTask, title: "\uC751\uB2F5 \uC911\uB2E8 (Esc)" },
4718
4665
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "14", height: "14" },
4719
4666
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("rect", { x: "6", y: "6", width: "12", height: "12", rx: "1" })),
@@ -5405,450 +5352,65 @@ __webpack_require__.r(__webpack_exports__);
5405
5352
  /* harmony export */ });
5406
5353
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react");
5407
5354
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
5408
- /* harmony import */ var _mui_icons_material_CheckCircle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @mui/icons-material/CheckCircle */ "./node_modules/@mui/icons-material/esm/CheckCircle.js");
5409
- /* harmony import */ var _mui_icons_material_Error__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @mui/icons-material/Error */ "./node_modules/@mui/icons-material/esm/Error.js");
5410
- /* harmony import */ var _mui_icons_material_HourglassEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @mui/icons-material/HourglassEmpty */ "./node_modules/@mui/icons-material/esm/HourglassEmpty.js");
5411
- /* harmony import */ var _mui_icons_material_Code__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @mui/icons-material/Code */ "./node_modules/@mui/icons-material/esm/Code.js");
5412
- /* harmony import */ var _mui_icons_material_Search__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @mui/icons-material/Search */ "./node_modules/@mui/icons-material/esm/Search.js");
5413
- /* harmony import */ var _mui_icons_material_Analytics__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @mui/icons-material/Analytics */ "./node_modules/@mui/icons-material/esm/Analytics.js");
5414
- /* harmony import */ var _mui_icons_material_GpsFixed__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! @mui/icons-material/GpsFixed */ "./node_modules/@mui/icons-material/esm/GpsFixed.js");
5415
- /* harmony import */ var _mui_icons_material_ExpandMore__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @mui/icons-material/ExpandMore */ "./node_modules/@mui/icons-material/esm/ExpandMore.js");
5416
- /* harmony import */ var _mui_icons_material_ChevronRight__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! @mui/icons-material/ChevronRight */ "./node_modules/@mui/icons-material/esm/ChevronRight.js");
5417
- /* harmony import */ var _services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../services/ApiKeyManager */ "./lib/services/ApiKeyManager.js");
5355
+ /* harmony import */ var _services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../services/ApiKeyManager */ "./lib/services/ApiKeyManager.js");
5418
5356
  /**
5419
5357
  * Settings Panel Component
5420
- * Allows users to configure LLM provider settings
5358
+ * User preference settings panel
5421
5359
  *
5422
- * API keys are stored in browser localStorage and sent with each request.
5423
- * The Agent Server does not store API keys - it receives them with each request.
5360
+ * NOTE: LLM settings (provider, API keys, models, prompts) are now managed
5361
+ * by the Agent Server's AdminConfig. This panel only handles user preferences:
5362
+ * - workspaceRoot: User's workspace directory
5363
+ * - autoApprove: Skip human approval for code execution
5424
5364
  */
5425
5365
 
5426
5366
 
5427
-
5428
-
5429
-
5430
-
5431
-
5432
-
5433
-
5434
-
5435
-
5436
5367
  const SettingsPanel = ({ onClose, onSave, currentConfig }) => {
5437
5368
  // Initialize from localStorage or props
5438
- const initConfig = currentConfig || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.getLLMConfig)() || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.getDefaultLLMConfig)();
5439
- const [provider, setProvider] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.provider || 'gemini');
5440
- const [isTesting, setIsTesting] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
5441
- const [testResults, setTestResults] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({});
5442
- // Track active key index (first valid key by default)
5443
- const [activeKeyIndex, setActiveKeyIndex] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(0);
5444
- // Gemini settings - support multiple keys (max 10)
5445
- const [geminiApiKeys, setGeminiApiKeys] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.gemini?.apiKeys?.length
5446
- ? initConfig.gemini.apiKeys
5447
- : initConfig.gemini?.apiKey
5448
- ? [initConfig.gemini.apiKey]
5449
- : ['']);
5450
- // Legacy single key for backward compatibility
5451
- const geminiApiKey = geminiApiKeys[0] || '';
5452
- // Validate gemini model - only allow valid options
5453
- const validateGeminiModel = (model) => {
5454
- const validModels = ['gemini-2.5-pro', 'gemini-2.5-flash'];
5455
- if (model && validModels.includes(model)) {
5456
- return model;
5457
- }
5458
- return 'gemini-2.5-flash';
5459
- };
5460
- const [geminiModel, setGeminiModel] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(validateGeminiModel(initConfig.gemini?.model));
5461
- // vLLM settings
5462
- const [vllmEndpoint, setVllmEndpoint] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.vllm?.endpoint || 'http://localhost:8000/v1');
5463
- const [vllmApiKey, setVllmApiKey] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.vllm?.apiKey || '');
5464
- const [vllmModel, setVllmModel] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.vllm?.model || 'meta-llama/Llama-2-7b-chat-hf');
5465
- const [vllmUseResponsesApi, setVllmUseResponsesApi] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(Boolean(initConfig.vllm?.useResponsesApi));
5466
- const [vllmTemperature, setVllmTemperature] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.vllm?.temperature ?? 0.0);
5467
- // OpenAI settings
5468
- const [openaiApiKey, setOpenaiApiKey] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.openai?.apiKey || '');
5469
- const [openaiModel, setOpenaiModel] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.openai?.model || 'gpt-4');
5470
- const [systemPrompt, setSystemPrompt] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.systemPrompt || '');
5369
+ const initConfig = currentConfig || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_1__.getLLMConfig)() || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_1__.getDefaultLLMConfig)();
5370
+ // User preferences (the only settings stored in localStorage)
5471
5371
  const [workspaceRoot, setWorkspaceRoot] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.workspaceRoot || '');
5472
5372
  const [autoApprove, setAutoApprove] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(Boolean(initConfig.autoApprove));
5473
- const [idleTimeoutMinutes, setIdleTimeoutMinutes] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.idleTimeoutMinutes ?? 60);
5474
- // Multi-Agent settings
5475
- const [agentPrompts, setAgentPrompts] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.agentPrompts || {});
5476
- const [expandedAgents, setExpandedAgents] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({});
5477
- // Summarization LLM settings
5478
- const [summarizationEnabled, setSummarizationEnabled] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(Boolean(initConfig.summarization?.enabled));
5479
- const [summarizationProvider, setSummarizationProvider] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.summarization?.provider || 'gemini');
5480
- const [summarizationModel, setSummarizationModel] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.summarization?.model || '');
5481
- // Default prompts fetched from API
5482
- const [defaultPrompts, setDefaultPrompts] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)((0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.getCachedPrompts)());
5483
- const [isLoadingPrompts, setIsLoadingPrompts] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(!(0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.getCachedPrompts)());
5484
- // Fetch default prompts from API on mount
5485
- (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
5486
- if (!defaultPrompts) {
5487
- setIsLoadingPrompts(true);
5488
- (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.fetchDefaultPrompts)().then(prompts => {
5489
- setDefaultPrompts(prompts);
5490
- setIsLoadingPrompts(false);
5491
- });
5492
- }
5493
- }, []);
5494
5373
  // Update state when currentConfig changes
5495
5374
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
5496
5375
  if (currentConfig) {
5497
- setProvider(currentConfig.provider || 'gemini');
5498
- // Handle multiple keys
5499
- if (currentConfig.gemini?.apiKeys?.length) {
5500
- setGeminiApiKeys(currentConfig.gemini.apiKeys);
5501
- }
5502
- else if (currentConfig.gemini?.apiKey) {
5503
- setGeminiApiKeys([currentConfig.gemini.apiKey]);
5504
- }
5505
- else {
5506
- setGeminiApiKeys(['']);
5507
- }
5508
- setGeminiModel(validateGeminiModel(currentConfig.gemini?.model));
5509
- setVllmEndpoint(currentConfig.vllm?.endpoint || 'http://localhost:8000/v1');
5510
- setVllmApiKey(currentConfig.vllm?.apiKey || '');
5511
- setVllmModel(currentConfig.vllm?.model || 'meta-llama/Llama-2-7b-chat-hf');
5512
- setVllmUseResponsesApi(Boolean(currentConfig.vllm?.useResponsesApi));
5513
- setVllmTemperature(currentConfig.vllm?.temperature ?? 0.0);
5514
- setOpenaiApiKey(currentConfig.openai?.apiKey || '');
5515
- setOpenaiModel(currentConfig.openai?.model || 'gpt-4');
5516
- setSystemPrompt(currentConfig.systemPrompt || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.getDefaultLLMConfig)().systemPrompt || '');
5517
5376
  setWorkspaceRoot(currentConfig.workspaceRoot || '');
5518
5377
  setAutoApprove(Boolean(currentConfig.autoApprove));
5519
- setIdleTimeoutMinutes(currentConfig.idleTimeoutMinutes ?? 60);
5520
- // Use currentConfig prompts, or cached defaults, or empty object
5521
- const cachedDefaults = (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.getCachedPrompts)();
5522
- setAgentPrompts(currentConfig.agentPrompts || (cachedDefaults ? {
5523
- planner: cachedDefaults.planner,
5524
- python_developer: cachedDefaults.python_developer,
5525
- researcher: cachedDefaults.researcher,
5526
- athena_query: cachedDefaults.athena_query,
5527
- } : {}));
5528
- // Summarization settings
5529
- setSummarizationEnabled(Boolean(currentConfig.summarization?.enabled));
5530
- setSummarizationProvider(currentConfig.summarization?.provider || 'gemini');
5531
- setSummarizationModel(currentConfig.summarization?.model || '');
5532
5378
  }
5533
5379
  }, [currentConfig]);
5534
- // Helper: Build LLM config from state
5380
+ // Build config from state
5535
5381
  const buildLLMConfig = () => ({
5536
- provider,
5537
- gemini: {
5538
- apiKey: geminiApiKeys[0] || '',
5539
- apiKeys: geminiApiKeys.filter(k => k && k.trim()),
5540
- model: geminiModel
5541
- },
5542
- vllm: {
5543
- endpoint: vllmEndpoint,
5544
- apiKey: vllmApiKey,
5545
- model: vllmModel,
5546
- useResponsesApi: vllmUseResponsesApi,
5547
- temperature: vllmTemperature
5548
- },
5549
- openai: {
5550
- apiKey: openaiApiKey,
5551
- model: openaiModel
5552
- },
5553
5382
  workspaceRoot: workspaceRoot.trim() ? workspaceRoot.trim() : undefined,
5554
- systemPrompt: systemPrompt && systemPrompt.trim() ? systemPrompt : undefined,
5555
- agentPrompts,
5556
- summarization: summarizationEnabled ? {
5557
- enabled: true,
5558
- provider: summarizationProvider,
5559
- model: summarizationModel.trim() || undefined,
5560
- } : undefined,
5561
- autoApprove,
5562
- idleTimeoutMinutes
5383
+ autoApprove
5563
5384
  });
5564
- // Handlers for multiple API keys
5565
- const handleAddKey = () => {
5566
- if (geminiApiKeys.length < 10) {
5567
- setGeminiApiKeys([...geminiApiKeys, '']);
5568
- }
5569
- };
5570
- const handleRemoveKey = (index) => {
5571
- if (geminiApiKeys.length > 1) {
5572
- const newKeys = geminiApiKeys.filter((_, i) => i !== index);
5573
- setGeminiApiKeys(newKeys);
5574
- }
5575
- };
5576
- const handleKeyChange = (index, value) => {
5577
- const newKeys = [...geminiApiKeys];
5578
- newKeys[index] = value;
5579
- setGeminiApiKeys(newKeys);
5580
- };
5581
- const validKeyCount = geminiApiKeys.filter(k => k && k.trim()).length;
5582
- const handleTest = async () => {
5583
- setIsTesting(true);
5584
- setTestResults({});
5585
- if (provider === 'gemini') {
5586
- // Test each Gemini key individually
5587
- const validKeys = geminiApiKeys.map((k, i) => ({ key: k, index: i }))
5588
- .filter(({ key }) => key && key.trim());
5589
- for (const { key, index } of validKeys) {
5590
- setTestResults(prev => ({ ...prev, [index]: 'testing' }));
5591
- const testConfig = {
5592
- provider: 'gemini',
5593
- gemini: { apiKey: key, apiKeys: [key], model: geminiModel }
5594
- };
5595
- try {
5596
- const result = await (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.testApiKey)(testConfig);
5597
- setTestResults(prev => ({ ...prev, [index]: result.success ? 'success' : 'error' }));
5598
- }
5599
- catch {
5600
- setTestResults(prev => ({ ...prev, [index]: 'error' }));
5601
- }
5602
- }
5603
- }
5604
- else {
5605
- // For other providers, just test the single config
5606
- try {
5607
- const config = buildLLMConfig();
5608
- const result = await (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.testApiKey)(config);
5609
- setTestResults({ 0: result.success ? 'success' : 'error' });
5610
- }
5611
- catch {
5612
- setTestResults({ 0: 'error' });
5613
- }
5614
- }
5615
- setIsTesting(false);
5616
- };
5617
- // Handle clicking a key row to set it as active
5618
- const handleSetActiveKey = (index) => {
5619
- if (geminiApiKeys[index] && geminiApiKeys[index].trim()) {
5620
- setActiveKeyIndex(index);
5621
- }
5622
- };
5623
5385
  const handleSave = () => {
5624
5386
  const config = buildLLMConfig();
5625
5387
  // Save to localStorage
5626
- (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.saveLLMConfig)(config);
5388
+ (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_1__.saveLLMConfig)(config);
5627
5389
  // Notify parent component
5628
5390
  onSave(config);
5629
5391
  onClose();
5630
5392
  };
5631
- // Get test status icon for a key
5632
- const getTestStatusIcon = (index) => {
5633
- const status = testResults[index];
5634
- if (status === 'testing')
5635
- return react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_HourglassEmpty__WEBPACK_IMPORTED_MODULE_3__["default"], { sx: { fontSize: 16, color: 'info.main' } });
5636
- if (status === 'success')
5637
- return react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_CheckCircle__WEBPACK_IMPORTED_MODULE_1__["default"], { sx: { fontSize: 16, color: 'success.main' } });
5638
- if (status === 'error')
5639
- return react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_Error__WEBPACK_IMPORTED_MODULE_2__["default"], { sx: { fontSize: 16, color: 'error.main' } });
5640
- return null;
5641
- };
5642
5393
  return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-overlay" },
5643
5394
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-dialog" },
5644
5395
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-header" },
5645
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", null, "LLM \uC124\uC815"),
5396
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", null, "\uC0AC\uC6A9\uC790 \uC124\uC815"),
5646
5397
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-settings-close", onClick: onClose, title: "\uB2EB\uAE30" }, "\u00D7")),
5647
5398
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-content" },
5648
5399
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-notice" },
5649
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "API \uD0A4\uB294 \uBE0C\uB77C\uC6B0\uC800\uC5D0 \uC548\uC804\uD558\uAC8C \uC800\uC7A5\uB418\uBA70, \uC694\uCCAD \uC2DC\uC5D0\uB9CC \uC11C\uBC84\uB85C \uC804\uC1A1\uB429\uB2C8\uB2E4.")),
5650
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5651
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" }, "\uD504\uB85C\uBC14\uC774\uB354"),
5652
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("select", { className: "jp-agent-settings-select", value: provider, onChange: (e) => setProvider(e.target.value) },
5653
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gemini" }, "Google Gemini"),
5654
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "vllm" }, "vLLM"),
5655
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "openai" }, "OpenAI"))),
5400
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "LLM \uC124\uC815 (\uD504\uB85C\uBC14\uC774\uB354, API \uD0A4, \uBAA8\uB378, \uD504\uB86C\uD504\uD2B8)\uC740 \uAD00\uB9AC\uC790 \uC124\uC815\uC5D0\uC11C \uAD00\uB9AC\uB429\uB2C8\uB2E4.")),
5656
5401
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5657
5402
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label", htmlFor: "jp-agent-workspace-root" },
5658
5403
  "\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8",
5659
5404
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { fontWeight: 'normal', marginLeft: '8px', color: '#666' } }, "\uBE44\uC6B0\uBA74 \uD604\uC7AC \uB178\uD2B8\uBD81 \uD3F4\uB354 \uAE30\uC900, \uC808\uB300/\uC0C1\uB300 \uACBD\uB85C \uAC00\uB2A5")),
5660
5405
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { id: "jp-agent-workspace-root", type: "text", className: "jp-agent-settings-input", value: workspaceRoot, onChange: (e) => setWorkspaceRoot(e.target.value), placeholder: "\uC608: /Users/you/project", "data-testid": "workspace-root-input" })),
5661
- provider === 'gemini' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-provider" },
5662
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h3", null, "Gemini \uC124\uC815"),
5663
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5664
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" },
5665
- "API \uD0A4 (",
5666
- validKeyCount,
5667
- "/10)",
5668
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { fontWeight: 'normal', marginLeft: '8px', color: '#666' } }, "Rate limit \uC2DC \uC790\uB3D9 \uB85C\uD14C\uC774\uC158")),
5669
- geminiApiKeys.map((key, index) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { key: index, className: "jp-agent-settings-key-row", style: {
5670
- display: 'flex',
5671
- gap: '8px',
5672
- marginBottom: '8px',
5673
- alignItems: 'center',
5674
- padding: '4px',
5675
- borderRadius: '4px',
5676
- background: activeKeyIndex === index && key && key.trim() ? 'rgba(66, 133, 244, 0.1)' : 'transparent',
5677
- border: activeKeyIndex === index && key && key.trim() ? '1px solid rgba(66, 133, 244, 0.3)' : '1px solid transparent'
5678
- } },
5679
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "radio", name: "activeKey", checked: activeKeyIndex === index && key && key.trim() !== '', onChange: () => handleSetActiveKey(index), disabled: !key || !key.trim(), title: "\uD65C\uC131 \uD0A4\uB85C \uC124\uC815", style: { cursor: key && key.trim() ? 'pointer' : 'default' } }),
5680
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { style: {
5681
- minWidth: '20px',
5682
- color: '#666',
5683
- fontSize: '12px'
5684
- } },
5685
- index + 1,
5686
- "."),
5687
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "password", className: "jp-agent-settings-input", style: { flex: 1 }, value: key, onChange: (e) => handleKeyChange(index, e.target.value), placeholder: "AIza..." }),
5688
- getTestStatusIcon(index) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { style: { fontSize: '14px', minWidth: '20px' } }, getTestStatusIcon(index))),
5689
- geminiApiKeys.length > 1 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-agent-settings-button-icon", onClick: () => handleRemoveKey(index), title: "\uD0A4 \uC0AD\uC81C", style: {
5690
- padding: '4px 8px',
5691
- background: '#ff4444',
5692
- color: 'white',
5693
- border: 'none',
5694
- borderRadius: '4px',
5695
- cursor: 'pointer'
5696
- } }, "\u00D7"))))),
5697
- geminiApiKeys.length < 10 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-agent-settings-button jp-agent-settings-button-secondary", onClick: handleAddKey, style: { marginTop: '8px' } }, "+ \uD0A4 \uCD94\uAC00"))),
5698
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5699
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" }, "\uBAA8\uB378"),
5700
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("select", { className: "jp-agent-settings-select", value: geminiModel, onChange: (e) => setGeminiModel(e.target.value) },
5701
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gemini-2.5-flash" }, "Gemini 2.5 Flash"),
5702
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gemini-2.5-pro" }, "Gemini 2.5 Pro"))))),
5703
- provider === 'vllm' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-provider" },
5704
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h3", null, "vLLM / OpenAI Compatible \uC124\uC815"),
5705
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5706
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" }, "API Base URL (\uC804\uCCB4 \uACBD\uB85C \uC785\uB825)"),
5707
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "text", className: "jp-agent-settings-input", value: vllmEndpoint, onChange: (e) => setVllmEndpoint(e.target.value), placeholder: "https://openrouter.ai/api/v1" })),
5708
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5709
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" }, "API \uD0A4 (\uC120\uD0DD\uC0AC\uD56D)"),
5710
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "password", className: "jp-agent-settings-input", value: vllmApiKey, onChange: (e) => setVllmApiKey(e.target.value), placeholder: "API \uD0A4\uAC00 \uD544\uC694\uD55C \uACBD\uC6B0 \uC785\uB825" })),
5711
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5712
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" }, "\uBAA8\uB378 \uC774\uB984"),
5713
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "text", className: "jp-agent-settings-input", value: vllmModel, onChange: (e) => setVllmModel(e.target.value), placeholder: "meta-llama/Llama-2-7b-chat-hf" })),
5714
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5715
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" }, "Temperature"),
5716
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "number", className: "jp-agent-settings-input", value: vllmTemperature, onChange: (e) => setVllmTemperature(Math.max(0, Math.min(2, parseFloat(e.target.value) || 0))), min: 0, max: 2, step: 0.1, style: { width: '100px' } }),
5717
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { color: '#666', marginLeft: '8px' } }, "0.0 = \uACB0\uC815\uC801, 1.0+ = \uCC3D\uC758\uC801")),
5718
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5719
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-checkbox" },
5720
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "checkbox", checked: vllmUseResponsesApi, onChange: (e) => setVllmUseResponsesApi(e.target.checked) }),
5721
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "Use Responses API (/v1/responses)")),
5722
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { color: '#666', display: 'block', marginTop: '4px' } }, "OpenAI Responses API\uB97C \uC9C0\uC6D0\uD558\uB294 \uC5D4\uB4DC\uD3EC\uC778\uD2B8\uC5D0\uC11C \uC0AC\uC6A9")))),
5723
- provider === 'openai' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-provider" },
5724
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h3", null, "OpenAI \uC124\uC815"),
5725
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5726
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" }, "API \uD0A4"),
5727
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "password", className: "jp-agent-settings-input", value: openaiApiKey, onChange: (e) => setOpenaiApiKey(e.target.value), placeholder: "sk-..." })),
5728
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5729
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" }, "\uBAA8\uB378"),
5730
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("select", { className: "jp-agent-settings-select", value: openaiModel, onChange: (e) => setOpenaiModel(e.target.value) },
5731
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gpt-4" }, "GPT-4"),
5732
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gpt-4-turbo" }, "GPT-4 Turbo"),
5733
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gpt-3.5-turbo" }, "GPT-3.5 Turbo"))))),
5734
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-summarization" },
5735
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-summarization-header" },
5736
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label", style: { marginBottom: 0 } }, "\uC694\uC57D\uC6A9 LLM \uC124\uC815"),
5737
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", null, "\uB300\uD654 \uC555\uCD95 (/compact) \uBC0F Agent \uC694\uC57D\uC5D0 \uC0AC\uC6A9")),
5738
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-checkbox" },
5739
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "checkbox", checked: summarizationEnabled, onChange: (e) => setSummarizationEnabled(e.target.checked) }),
5740
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\uBCC4\uB3C4 LLM\uC73C\uB85C \uC694\uC57D \uC218\uD589 (\uBBF8\uC124\uC815 \uC2DC \uAE30\uBCF8 LLM \uC0AC\uC6A9)")),
5741
- summarizationEnabled && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-summarization-content" },
5742
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5743
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" }, "\uD504\uB85C\uBC14\uC774\uB354"),
5744
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("select", { className: "jp-agent-settings-select", value: summarizationProvider, onChange: (e) => setSummarizationProvider(e.target.value) },
5745
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gemini" }, "Gemini (gemini-2.5-flash)"),
5746
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "openai" }, "OpenAI (gpt-4o-mini)"),
5747
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "vllm" }, "vLLM / OpenRouter")),
5748
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { className: "jp-agent-settings-summarization-hint" }, "\uAE30\uBCF8 \uC124\uC815\uC758 API \uD0A4\uB97C \uC0AC\uC6A9\uD569\uB2C8\uB2E4")),
5749
- summarizationProvider === 'vllm' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5750
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" }, "\uBAA8\uB378\uBA85 (\uC120\uD0DD)"),
5751
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "text", className: "jp-agent-settings-input", value: summarizationModel, onChange: (e) => setSummarizationModel(e.target.value), placeholder: "\uC608: mistralai/mistral-7b-instruct" }),
5752
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { className: "jp-agent-settings-summarization-hint" }, "\uBE44\uC6CC\uB450\uBA74 \uAE30\uBCF8 vLLM \uBAA8\uB378 \uC0AC\uC6A9"))),
5753
- summarizationProvider !== 'vllm' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-summarization-default-model" },
5754
- "\uAE30\uBCF8 \uBAA8\uB378: ",
5755
- summarizationProvider === 'gemini' ? 'gemini-2.5-flash' : 'gpt-4o-mini'))))),
5756
5406
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5757
5407
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" }, "\uC790\uB3D9 \uC2E4\uD589 \uC2B9\uC778"),
5758
5408
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-checkbox" },
5759
5409
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "checkbox", checked: autoApprove, onChange: (e) => setAutoApprove(e.target.checked), "data-testid": "auto-approve-checkbox" }),
5760
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\uC2B9\uC778 \uC5C6\uC774 \uBC14\uB85C \uC2E4\uD589 (\uCF54\uB4DC/\uD30C\uC77C/\uC178 \uD3EC\uD568)"))),
5761
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5762
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label", htmlFor: "jp-agent-idle-timeout" },
5763
- "Idle Timeout (\uBD84)",
5764
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { fontWeight: 'normal', marginLeft: '8px', color: '#666' } }, "\uBE44\uD65C\uB3D9 \uC2DC \uC790\uB3D9 \uC885\uB8CC (0 = \uBE44\uD65C\uC131\uD654)")),
5765
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { id: "jp-agent-idle-timeout", type: "number", className: "jp-agent-settings-input", value: idleTimeoutMinutes, onChange: (e) => setIdleTimeoutMinutes(Math.max(0, parseInt(e.target.value) || 0)), min: 0, max: 1440, placeholder: "60", style: { width: '120px' }, "data-testid": "idle-timeout-input" })),
5766
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
5767
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" },
5768
- "\uC5D0\uC774\uC804\uD2B8\uBCC4 System Prompt",
5769
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { fontWeight: 'normal', marginLeft: '8px', color: '#666' } }, "\uAC01 \uC5D0\uC774\uC804\uD2B8\uC758 \uC5ED\uD560\uACFC \uB3D9\uC791\uC744 \uC815\uC758\uD569\uB2C8\uB2E4.")),
5770
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-agent-section", style: { marginTop: '12px', border: '1px solid #ddd', borderRadius: '4px' } },
5771
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-agent-header", style: {
5772
- padding: '8px 12px',
5773
- background: '#f5f5f5',
5774
- cursor: 'pointer',
5775
- display: 'flex',
5776
- justifyContent: 'space-between',
5777
- alignItems: 'center'
5778
- }, onClick: () => setExpandedAgents(prev => ({ ...prev, planner: !prev.planner })) },
5779
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { style: { fontWeight: 'bold', display: 'flex', alignItems: 'center', gap: '6px' } },
5780
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_GpsFixed__WEBPACK_IMPORTED_MODULE_7__["default"], { sx: { fontSize: 18 } }),
5781
- " Planner (Supervisor)"),
5782
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, expandedAgents.planner ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ExpandMore__WEBPACK_IMPORTED_MODULE_8__["default"], { sx: { fontSize: 18 } }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ChevronRight__WEBPACK_IMPORTED_MODULE_9__["default"], { sx: { fontSize: 18 } }))),
5783
- expandedAgents.planner && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { style: { padding: '12px' } },
5784
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { color: '#666', display: 'block', marginBottom: '8px' } }, "\uC791\uC5C5 \uACC4\uD68D \uBC0F \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8 \uC704\uC784\uC744 \uB2F4\uB2F9\uD569\uB2C8\uB2E4."),
5785
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("textarea", { className: "jp-agent-settings-input jp-agent-settings-textarea", value: agentPrompts.planner || '', onChange: (e) => setAgentPrompts(prev => ({ ...prev, planner: e.target.value })), rows: 6 }),
5786
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-agent-settings-button jp-agent-settings-button-secondary jp-agent-settings-button-compact", style: { marginTop: '8px' }, onClick: () => defaultPrompts && setAgentPrompts(prev => ({ ...prev, planner: defaultPrompts.planner })), disabled: isLoadingPrompts }, isLoadingPrompts ? '로딩중...' : '기본값으로 되돌리기')))),
5787
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-agent-section", style: { marginTop: '8px', border: '1px solid #ddd', borderRadius: '4px' } },
5788
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-agent-header", style: {
5789
- padding: '8px 12px',
5790
- background: '#f5f5f5',
5791
- cursor: 'pointer',
5792
- display: 'flex',
5793
- justifyContent: 'space-between',
5794
- alignItems: 'center'
5795
- }, onClick: () => setExpandedAgents(prev => ({ ...prev, python_developer: !prev.python_developer })) },
5796
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { style: { fontWeight: 'bold', display: 'flex', alignItems: 'center', gap: '6px' } },
5797
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_Code__WEBPACK_IMPORTED_MODULE_4__["default"], { sx: { fontSize: 18 } }),
5798
- " Python Developer"),
5799
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, expandedAgents.python_developer ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ExpandMore__WEBPACK_IMPORTED_MODULE_8__["default"], { sx: { fontSize: 18 } }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ChevronRight__WEBPACK_IMPORTED_MODULE_9__["default"], { sx: { fontSize: 18 } }))),
5800
- expandedAgents.python_developer && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { style: { padding: '12px' } },
5801
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { color: '#666', display: 'block', marginBottom: '8px' } }, "Python \uCF54\uB4DC \uC2E4\uD589, \uB370\uC774\uD130 \uBD84\uC11D, \uC2DC\uAC01\uD654, ML \uBAA8\uB378\uB9C1\uC744 \uB2F4\uB2F9\uD569\uB2C8\uB2E4."),
5802
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("textarea", { className: "jp-agent-settings-input jp-agent-settings-textarea", value: agentPrompts.python_developer || '', onChange: (e) => setAgentPrompts(prev => ({ ...prev, python_developer: e.target.value })), rows: 6 }),
5803
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-agent-settings-button jp-agent-settings-button-secondary jp-agent-settings-button-compact", style: { marginTop: '8px' }, onClick: () => defaultPrompts && setAgentPrompts(prev => ({ ...prev, python_developer: defaultPrompts.python_developer })), disabled: isLoadingPrompts }, isLoadingPrompts ? '로딩중...' : '기본값으로 되돌리기')))),
5804
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-agent-section", style: { marginTop: '8px', border: '1px solid #ddd', borderRadius: '4px' } },
5805
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-agent-header", style: {
5806
- padding: '8px 12px',
5807
- background: '#f5f5f5',
5808
- cursor: 'pointer',
5809
- display: 'flex',
5810
- justifyContent: 'space-between',
5811
- alignItems: 'center'
5812
- }, onClick: () => setExpandedAgents(prev => ({ ...prev, researcher: !prev.researcher })) },
5813
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { style: { fontWeight: 'bold', display: 'flex', alignItems: 'center', gap: '6px' } },
5814
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_Search__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { fontSize: 18 } }),
5815
- " Researcher"),
5816
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, expandedAgents.researcher ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ExpandMore__WEBPACK_IMPORTED_MODULE_8__["default"], { sx: { fontSize: 18 } }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ChevronRight__WEBPACK_IMPORTED_MODULE_9__["default"], { sx: { fontSize: 18 } }))),
5817
- expandedAgents.researcher && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { style: { padding: '12px' } },
5818
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { color: '#666', display: 'block', marginBottom: '8px' } }, "\uD30C\uC77C \uAC80\uC0C9, \uCF54\uB4DC \uBD84\uC11D, Qdrant RAG \uAC80\uC0C9\uC744 \uB2F4\uB2F9\uD569\uB2C8\uB2E4 (READ-ONLY)."),
5819
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("textarea", { className: "jp-agent-settings-input jp-agent-settings-textarea", value: agentPrompts.researcher || '', onChange: (e) => setAgentPrompts(prev => ({ ...prev, researcher: e.target.value })), rows: 6 }),
5820
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-agent-settings-button jp-agent-settings-button-secondary jp-agent-settings-button-compact", style: { marginTop: '8px' }, onClick: () => defaultPrompts && setAgentPrompts(prev => ({ ...prev, researcher: defaultPrompts.researcher })), disabled: isLoadingPrompts }, isLoadingPrompts ? '로딩중...' : '기본값으로 되돌리기')))),
5821
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-agent-section", style: { marginTop: '8px', border: '1px solid #ddd', borderRadius: '4px' } },
5822
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-agent-header", style: {
5823
- padding: '8px 12px',
5824
- background: '#f5f5f5',
5825
- cursor: 'pointer',
5826
- display: 'flex',
5827
- justifyContent: 'space-between',
5828
- alignItems: 'center'
5829
- }, onClick: () => setExpandedAgents(prev => ({ ...prev, athena_query: !prev.athena_query })) },
5830
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { style: { fontWeight: 'bold', display: 'flex', alignItems: 'center', gap: '6px' } },
5831
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_Analytics__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { fontSize: 18 } }),
5832
- " Athena Query"),
5833
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, expandedAgents.athena_query ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ExpandMore__WEBPACK_IMPORTED_MODULE_8__["default"], { sx: { fontSize: 18 } }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ChevronRight__WEBPACK_IMPORTED_MODULE_9__["default"], { sx: { fontSize: 18 } }))),
5834
- expandedAgents.athena_query && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { style: { padding: '12px' } },
5835
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { color: '#666', display: 'block', marginBottom: '8px' } }, "Qdrant RAG\uB97C \uD65C\uC6A9\uD55C Athena SQL \uCFFC\uB9AC \uC0DD\uC131\uC744 \uB2F4\uB2F9\uD569\uB2C8\uB2E4 (Python Developer\uC5D0\uC11C \uD638\uCD9C)."),
5836
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("textarea", { className: "jp-agent-settings-input jp-agent-settings-textarea", value: agentPrompts.athena_query || '', onChange: (e) => setAgentPrompts(prev => ({ ...prev, athena_query: e.target.value })), rows: 6 }),
5837
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-agent-settings-button jp-agent-settings-button-secondary jp-agent-settings-button-compact", style: { marginTop: '8px' }, onClick: () => defaultPrompts && setAgentPrompts(prev => ({ ...prev, athena_query: defaultPrompts.athena_query })), disabled: isLoadingPrompts }, isLoadingPrompts ? '로딩중...' : '기본값으로 되돌리기')))),
5838
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { style: { marginTop: '12px' } },
5839
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-agent-settings-button jp-agent-settings-button-secondary", onClick: () => {
5840
- if (defaultPrompts) {
5841
- setAgentPrompts({
5842
- planner: defaultPrompts.planner,
5843
- python_developer: defaultPrompts.python_developer,
5844
- researcher: defaultPrompts.researcher,
5845
- athena_query: defaultPrompts.athena_query,
5846
- });
5847
- }
5848
- }, disabled: isLoadingPrompts }, isLoadingPrompts ? '로딩중...' : '모든 프롬프트 기본값으로 되돌리기')))),
5410
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\uC2B9\uC778 \uC5C6\uC774 \uBC14\uB85C \uC2E4\uD589 (\uCF54\uB4DC/\uD30C\uC77C/\uC178 \uD3EC\uD568)")),
5411
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { color: '#666', display: 'block', marginTop: '4px', marginLeft: '24px' } }, "\uD65C\uC131\uD654 \uC2DC Human-in-the-Loop \uC2B9\uC778 \uB2E8\uACC4\uB97C \uAC74\uB108\uB701\uB2C8\uB2E4"))),
5849
5412
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-footer" },
5850
5413
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-settings-button jp-agent-settings-button-secondary", onClick: onClose }, "\uCDE8\uC18C"),
5851
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-settings-button jp-agent-settings-button-test", onClick: handleTest, disabled: isTesting }, isTesting ? '테스트 중...' : 'API 테스트'),
5852
5414
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-settings-button jp-agent-settings-button-primary", onClick: handleSave }, "\uC800\uC7A5")))));
5853
5415
  };
5854
5416
 
@@ -7919,7 +7481,6 @@ __webpack_require__.r(__webpack_exports__);
7919
7481
  * Plugin namespace
7920
7482
  */
7921
7483
  const PLUGIN_ID = '@hdsp-agent/idle-monitor';
7922
- const CONFIG_STORAGE_KEY = 'hdsp-agent-llm-config';
7923
7484
  /**
7924
7485
  * Default idle timeout (in minutes)
7925
7486
  */
@@ -7946,81 +7507,61 @@ class IdleMonitorService {
7946
7507
  */
7947
7508
  this.mouseMoveThrottleTime = 0;
7948
7509
  this.lastActivityTime = Date.now();
7949
- // Initialize timeout from localStorage config
7950
- this.idleTimeoutMinutes = this.loadTimeoutFromConfig();
7951
- this.isDisabled = this.idleTimeoutMinutes === 0;
7510
+ // Start with default (60 minutes), then fetch from server
7511
+ this.idleTimeoutMinutes = DEFAULT_IDLE_TIMEOUT_MINUTES;
7512
+ this.isDisabled = false;
7952
7513
  this.idleTimeoutMs = this.idleTimeoutMinutes * 60 * 1000;
7953
- // Warning period: 25% of timeout or minimum 30 seconds
7954
7514
  const warningPeriod = Math.max(MIN_WARNING_PERIOD_MS, this.idleTimeoutMs * 0.25);
7955
7515
  this.warningStartMs = this.idleTimeoutMs - warningPeriod;
7956
7516
  this.setupActivityListeners();
7957
- this.setupStorageListener();
7958
7517
  this.createCountdownElement();
7518
+ // Fetch actual timeout from Agent Server
7519
+ this.fetchIdleTimeoutFromServer();
7959
7520
  if (!this.isDisabled) {
7960
7521
  this.startIdleCheck();
7961
7522
  }
7962
7523
  console.log('[IdleMonitor] Service initialized. Idle timeout:', this.idleTimeoutMinutes, 'minutes, warning at:', Math.round(this.warningStartMs / 1000), 'sec', this.isDisabled ? '(DISABLED)' : '');
7963
7524
  }
7964
7525
  /**
7965
- * Load timeout configuration from localStorage
7526
+ * Fetch idle timeout setting from Agent Server
7966
7527
  */
7967
- loadTimeoutFromConfig() {
7528
+ async fetchIdleTimeoutFromServer() {
7968
7529
  try {
7969
- const stored = localStorage.getItem(CONFIG_STORAGE_KEY);
7970
- if (stored) {
7971
- const config = JSON.parse(stored);
7972
- if (typeof config.idleTimeoutMinutes === 'number') {
7973
- return config.idleTimeoutMinutes;
7974
- }
7530
+ const serverRoot = _jupyterlab_coreutils__WEBPACK_IMPORTED_MODULE_2__.PageConfig.getBaseUrl();
7531
+ const response = await fetch(`${serverRoot}hdsp-agent/config/admin`);
7532
+ if (!response.ok) {
7533
+ console.warn('[IdleMonitor] Failed to fetch config from server:', response.status);
7534
+ return;
7975
7535
  }
7976
- }
7977
- catch (e) {
7978
- console.warn('[IdleMonitor] Failed to load config from localStorage:', e);
7979
- }
7980
- return DEFAULT_IDLE_TIMEOUT_MINUTES;
7981
- }
7982
- /**
7983
- * Setup listener for localStorage changes (config updates from settings panel)
7984
- */
7985
- setupStorageListener() {
7986
- window.addEventListener('storage', (e) => {
7987
- if (e.key === CONFIG_STORAGE_KEY) {
7988
- const newTimeout = this.loadTimeoutFromConfig();
7989
- if (newTimeout !== this.idleTimeoutMinutes) {
7990
- this.updateTimeout(newTimeout);
7536
+ const config = await response.json();
7537
+ const serverTimeoutSeconds = config.idleTimeout;
7538
+ if (typeof serverTimeoutSeconds === 'number' && serverTimeoutSeconds > 0) {
7539
+ // Server sends seconds, convert to minutes
7540
+ const newTimeoutMinutes = Math.ceil(serverTimeoutSeconds / 60);
7541
+ if (newTimeoutMinutes !== this.idleTimeoutMinutes) {
7542
+ this.updateIdleTimeout(newTimeoutMinutes);
7543
+ console.log('[IdleMonitor] Updated timeout from server:', newTimeoutMinutes, 'minutes');
7991
7544
  }
7992
7545
  }
7993
- });
7994
- // Also listen for custom event (same-tab config changes)
7995
- window.addEventListener('hdsp-config-updated', () => {
7996
- const newTimeout = this.loadTimeoutFromConfig();
7997
- if (newTimeout !== this.idleTimeoutMinutes) {
7998
- this.updateTimeout(newTimeout);
7546
+ else if (serverTimeoutSeconds === 0) {
7547
+ // 0 means disabled
7548
+ this.isDisabled = true;
7549
+ this.stopIdleCheck();
7550
+ console.log('[IdleMonitor] Idle timeout disabled by server config');
7999
7551
  }
8000
- });
7552
+ }
7553
+ catch (error) {
7554
+ console.warn('[IdleMonitor] Error fetching config from server:', error);
7555
+ }
8001
7556
  }
8002
7557
  /**
8003
- * Update timeout dynamically
7558
+ * Update idle timeout dynamically
8004
7559
  */
8005
- updateTimeout(minutes) {
8006
- const wasDisabled = this.isDisabled;
7560
+ updateIdleTimeout(minutes) {
8007
7561
  this.idleTimeoutMinutes = minutes;
8008
- this.isDisabled = minutes === 0;
8009
7562
  this.idleTimeoutMs = minutes * 60 * 1000;
8010
- // Warning period: 25% of timeout or minimum 30 seconds
8011
7563
  const warningPeriod = Math.max(MIN_WARNING_PERIOD_MS, this.idleTimeoutMs * 0.25);
8012
7564
  this.warningStartMs = this.idleTimeoutMs - warningPeriod;
8013
- console.log('[IdleMonitor] Timeout updated to:', minutes, 'minutes', this.isDisabled ? '(DISABLED)' : '');
8014
- // Stop idle check if disabled
8015
- if (this.isDisabled) {
8016
- this.stopIdleCheck();
8017
- this.hideCountdown();
8018
- }
8019
- else if (wasDisabled) {
8020
- // Re-enable if was disabled
8021
- this.resetIdleTimer();
8022
- this.startIdleCheck();
8023
- }
8024
7565
  }
8025
7566
  /**
8026
7567
  * Stop idle check interval
@@ -9829,26 +9370,15 @@ __webpack_require__.r(__webpack_exports__);
9829
9370
  /* harmony export */ DEFAULT_PLANNER_PROMPT: () => (/* binding */ DEFAULT_PLANNER_PROMPT),
9830
9371
  /* harmony export */ DEFAULT_PYTHON_DEVELOPER_PROMPT: () => (/* binding */ DEFAULT_PYTHON_DEVELOPER_PROMPT),
9831
9372
  /* harmony export */ DEFAULT_RESEARCHER_PROMPT: () => (/* binding */ DEFAULT_RESEARCHER_PROMPT),
9832
- /* harmony export */ buildSingleKeyConfig: () => (/* binding */ buildSingleKeyConfig),
9833
9373
  /* harmony export */ clearCachedPrompts: () => (/* binding */ clearCachedPrompts),
9834
9374
  /* harmony export */ clearLLMConfig: () => (/* binding */ clearLLMConfig),
9835
9375
  /* harmony export */ fetchDefaultPrompts: () => (/* binding */ fetchDefaultPrompts),
9836
- /* harmony export */ getAvailableKeyCount: () => (/* binding */ getAvailableKeyCount),
9837
9376
  /* harmony export */ getCachedPrompts: () => (/* binding */ getCachedPrompts),
9838
- /* harmony export */ getCurrentKeyIndex: () => (/* binding */ getCurrentKeyIndex),
9839
9377
  /* harmony export */ getDefaultLLMConfig: () => (/* binding */ getDefaultLLMConfig),
9840
9378
  /* harmony export */ getLLMConfig: () => (/* binding */ getLLMConfig),
9841
- /* harmony export */ getRandomApiKey: () => (/* binding */ getRandomApiKey),
9842
- /* harmony export */ getValidApiKeysCount: () => (/* binding */ getValidApiKeysCount),
9843
- /* harmony export */ getValidGeminiKeys: () => (/* binding */ getValidGeminiKeys),
9844
- /* harmony export */ handleRateLimitError: () => (/* binding */ handleRateLimitError),
9845
- /* harmony export */ hasAvailableKeys: () => (/* binding */ hasAvailableKeys),
9846
9379
  /* harmony export */ hasValidApiKey: () => (/* binding */ hasValidApiKey),
9847
9380
  /* harmony export */ initializePrompts: () => (/* binding */ initializePrompts),
9848
- /* harmony export */ isRateLimitError: () => (/* binding */ isRateLimitError),
9849
9381
  /* harmony export */ maskApiKey: () => (/* binding */ maskApiKey),
9850
- /* harmony export */ resetKeyRotation: () => (/* binding */ resetKeyRotation),
9851
- /* harmony export */ rotateToNextKey: () => (/* binding */ rotateToNextKey),
9852
9382
  /* harmony export */ saveLLMConfig: () => (/* binding */ saveLLMConfig),
9853
9383
  /* harmony export */ testApiKey: () => (/* binding */ testApiKey)
9854
9384
  /* harmony export */ });
@@ -9943,14 +9473,8 @@ async function initializePrompts() {
9943
9473
  };
9944
9474
  }
9945
9475
  // ═══════════════════════════════════════════════════════════════════════════
9946
- // Key Rotation State (in-memory, not persisted)
9476
+ // LocalStorage Functions
9947
9477
  // ═══════════════════════════════════════════════════════════════════════════
9948
- /** Current key index for rotation (per-session, not persisted) */
9949
- let currentKeyIndex = 0;
9950
- /** Set of rate-limited key indices (reset after successful request) */
9951
- const rateLimitedKeys = new Set();
9952
- /** Maximum retry attempts with different keys */
9953
- const MAX_KEY_ROTATION_ATTEMPTS = 10;
9954
9478
  /**
9955
9479
  * Get the current LLM configuration from localStorage
9956
9480
  */
@@ -9989,92 +9513,22 @@ function clearLLMConfig() {
9989
9513
  console.log('[ApiKeyManager] Config cleared from localStorage');
9990
9514
  }
9991
9515
  /**
9992
- * Check if API key is configured for the current provider
9516
+ * Check if API key is configured - DEPRECATED
9517
+ * All LLM settings now come from Agent Server
9993
9518
  */
9994
- function hasValidApiKey(config) {
9995
- if (!config)
9996
- return false;
9997
- switch (config.provider) {
9998
- case 'gemini':
9999
- // Check both single key and multiple keys
10000
- const hasMainKey = !!(config.gemini?.apiKey && config.gemini.apiKey.trim());
10001
- const hasArrayKeys = !!(config.gemini?.apiKeys && config.gemini.apiKeys.some(k => k && k.trim()));
10002
- return hasMainKey || hasArrayKeys;
10003
- case 'openai':
10004
- return !!(config.openai?.apiKey && config.openai.apiKey.trim());
10005
- case 'vllm':
10006
- // vLLM may not require API key
10007
- return true;
10008
- default:
10009
- return false;
10010
- }
9519
+ function hasValidApiKey(_config) {
9520
+ // Always return true - API keys are managed by Agent Server
9521
+ return true;
10011
9522
  }
10012
9523
  /**
10013
- * Get default LLM configuration
10014
- * Uses cached prompts from API if available, otherwise uses placeholders
9524
+ * Get default LLM configuration (simplified - user preferences only)
10015
9525
  */
10016
9526
  function getDefaultLLMConfig() {
10017
- const prompts = getCachedPrompts();
10018
9527
  return {
10019
- provider: 'gemini',
10020
- gemini: {
10021
- apiKey: '',
10022
- apiKeys: [],
10023
- model: 'gemini-2.5-flash'
10024
- },
10025
- openai: {
10026
- apiKey: '',
10027
- model: 'gpt-4'
10028
- },
10029
- vllm: {
10030
- endpoint: 'http://localhost:8000/v1',
10031
- model: 'default'
10032
- },
10033
- agentPrompts: prompts ? {
10034
- planner: prompts.planner,
10035
- python_developer: prompts.python_developer,
10036
- researcher: prompts.researcher,
10037
- athena_query: prompts.athena_query,
10038
- } : { ...DEFAULT_AGENT_PROMPTS },
9528
+ workspaceRoot: '',
10039
9529
  autoApprove: false
10040
9530
  };
10041
9531
  }
10042
- /**
10043
- * Get a random API key from the list (for load balancing)
10044
- */
10045
- function getRandomApiKey(config) {
10046
- if (config.provider === 'gemini' && config.gemini) {
10047
- const keys = config.gemini.apiKeys.filter(k => k && k.trim());
10048
- if (keys.length === 0) {
10049
- return config.gemini.apiKey || null;
10050
- }
10051
- // Random selection for load balancing
10052
- const randomIndex = Math.floor(Math.random() * keys.length);
10053
- return keys[randomIndex];
10054
- }
10055
- if (config.provider === 'openai' && config.openai) {
10056
- return config.openai.apiKey || null;
10057
- }
10058
- if (config.provider === 'vllm' && config.vllm) {
10059
- return config.vllm.apiKey || null;
10060
- }
10061
- return null;
10062
- }
10063
- /**
10064
- * Get all valid API keys count
10065
- */
10066
- function getValidApiKeysCount(config) {
10067
- if (config.provider === 'gemini' && config.gemini) {
10068
- return config.gemini.apiKeys.filter(k => k && k.trim()).length;
10069
- }
10070
- if (config.provider === 'openai' && config.openai) {
10071
- return config.openai.apiKey && config.openai.apiKey.trim() ? 1 : 0;
10072
- }
10073
- if (config.provider === 'vllm') {
10074
- return 1; // vLLM doesn't require API key
10075
- }
10076
- return 0;
10077
- }
10078
9532
  /**
10079
9533
  * Mask API key for display (show first 4 and last 4 characters)
10080
9534
  */
@@ -10084,159 +9538,14 @@ function maskApiKey(key) {
10084
9538
  return `${key.slice(0, 4)}...${key.slice(-4)}`;
10085
9539
  }
10086
9540
  /**
10087
- * Test API key by making a simple request
10088
- */
10089
- async function testApiKey(config) {
10090
- try {
10091
- // Simple validation - just check if key exists
10092
- if (!hasValidApiKey(config)) {
10093
- return { success: false, message: 'API key not configured' };
10094
- }
10095
- // For more thorough testing, you could make a minimal API call here
10096
- // But for now, just validate format
10097
- const key = config[config.provider];
10098
- if (config.provider === 'gemini' && key?.apiKey) {
10099
- if (!key.apiKey.startsWith('AIza')) {
10100
- return { success: false, message: 'Invalid Gemini API key format (should start with AIza)' };
10101
- }
10102
- }
10103
- if (config.provider === 'openai' && key?.apiKey) {
10104
- if (!key.apiKey.startsWith('sk-')) {
10105
- return { success: false, message: 'Invalid OpenAI API key format (should start with sk-)' };
10106
- }
10107
- }
10108
- return { success: true, message: 'API key format is valid' };
10109
- }
10110
- catch (e) {
10111
- return { success: false, message: `Error: ${e}` };
10112
- }
10113
- }
10114
- // ═══════════════════════════════════════════════════════════════════════════
10115
- // Key Rotation Functions (Financial Security Compliance)
10116
- // ═══════════════════════════════════════════════════════════════════════════
10117
- /**
10118
- * Get all valid Gemini API keys from config
10119
- */
10120
- function getValidGeminiKeys(config) {
10121
- if (config.provider !== 'gemini' || !config.gemini) {
10122
- return [];
10123
- }
10124
- const keys = config.gemini.apiKeys?.filter(k => k && k.trim()) || [];
10125
- // Fallback to single apiKey if no array
10126
- if (keys.length === 0 && config.gemini.apiKey && config.gemini.apiKey.trim()) {
10127
- return [config.gemini.apiKey];
10128
- }
10129
- return keys;
10130
- }
10131
- /**
10132
- * Get current key index for rotation tracking
10133
- */
10134
- function getCurrentKeyIndex() {
10135
- return currentKeyIndex;
10136
- }
10137
- /**
10138
- * Reset key rotation state (call after successful request)
10139
- */
10140
- function resetKeyRotation() {
10141
- rateLimitedKeys.clear();
10142
- console.log('[ApiKeyManager] Key rotation state reset');
10143
- }
10144
- /**
10145
- * Mark current key as rate-limited and rotate to next available key
10146
- * @returns true if rotation successful, false if all keys exhausted
10147
- */
10148
- function rotateToNextKey(config) {
10149
- const keys = getValidGeminiKeys(config);
10150
- if (keys.length <= 1) {
10151
- console.log('[ApiKeyManager] Cannot rotate - only one key available');
10152
- return false;
10153
- }
10154
- // Mark current key as rate-limited
10155
- rateLimitedKeys.add(currentKeyIndex);
10156
- console.log(`[ApiKeyManager] Key ${currentKeyIndex + 1} marked as rate-limited`);
10157
- // Find next available key
10158
- for (let i = 0; i < keys.length; i++) {
10159
- const nextIndex = (currentKeyIndex + 1 + i) % keys.length;
10160
- if (!rateLimitedKeys.has(nextIndex)) {
10161
- currentKeyIndex = nextIndex;
10162
- console.log(`[ApiKeyManager] Rotated to key ${currentKeyIndex + 1}/${keys.length}`);
10163
- return true;
10164
- }
10165
- }
10166
- // All keys rate-limited
10167
- console.log('[ApiKeyManager] All keys rate-limited');
10168
- return false;
10169
- }
10170
- /**
10171
- * Check if there are available keys (not all rate-limited)
10172
- */
10173
- function hasAvailableKeys(config) {
10174
- const keys = getValidGeminiKeys(config);
10175
- return keys.length > rateLimitedKeys.size;
10176
- }
10177
- /**
10178
- * Get count of remaining available keys
10179
- */
10180
- function getAvailableKeyCount(config) {
10181
- const keys = getValidGeminiKeys(config);
10182
- return Math.max(0, keys.length - rateLimitedKeys.size);
10183
- }
10184
- /**
10185
- * Build config with SINGLE current API key for server request.
10186
- * Server receives only one key - rotation is handled by frontend.
10187
- */
10188
- function buildSingleKeyConfig(config) {
10189
- if (config.provider !== 'gemini' || !config.gemini) {
10190
- // Non-Gemini providers - return as-is (no rotation)
10191
- return config;
10192
- }
10193
- const keys = getValidGeminiKeys(config);
10194
- if (keys.length === 0) {
10195
- return config;
10196
- }
10197
- // Ensure currentKeyIndex is within bounds
10198
- if (currentKeyIndex >= keys.length) {
10199
- currentKeyIndex = 0;
10200
- }
10201
- const currentKey = keys[currentKeyIndex];
10202
- console.log(`[ApiKeyManager] Using key ${currentKeyIndex + 1}/${keys.length}`);
10203
- // Return config with single apiKey (server only uses apiKey field)
10204
- return {
10205
- ...config,
10206
- gemini: {
10207
- ...config.gemini,
10208
- apiKey: currentKey,
10209
- // Don't send apiKeys array to server (security)
10210
- apiKeys: [],
10211
- },
10212
- };
10213
- }
10214
- /**
10215
- * Handle rate limit error with automatic key rotation
10216
- * @returns New config with rotated key, or null if all keys exhausted
10217
- */
10218
- function handleRateLimitError(config) {
10219
- if (config.provider !== 'gemini') {
10220
- // Non-Gemini providers don't support rotation
10221
- return null;
10222
- }
10223
- const rotated = rotateToNextKey(config);
10224
- if (!rotated) {
10225
- console.log('[ApiKeyManager] Rate limit: All keys exhausted');
10226
- return null;
10227
- }
10228
- return buildSingleKeyConfig(config);
10229
- }
10230
- /**
10231
- * Check if error is a rate limit error (429)
9541
+ * Test API key - DEPRECATED
9542
+ * All LLM settings now come from Agent Server
10232
9543
  */
10233
- function isRateLimitError(error) {
10234
- const errorMsg = typeof error === 'string' ? error : error.message;
10235
- return errorMsg.includes('RATE_LIMIT_EXCEEDED') ||
10236
- errorMsg.includes('429') ||
10237
- errorMsg.toLowerCase().includes('quota exceeded') ||
10238
- errorMsg.toLowerCase().includes('rate limit');
9544
+ async function testApiKey(_config) {
9545
+ // API key testing should be done via Agent Server
9546
+ return { success: true, message: 'API keys are managed by Agent Server' };
10239
9547
  }
9548
+ // Key rotation functions removed - API keys are now managed by Agent Server
10240
9549
 
10241
9550
 
10242
9551
  /***/ },
@@ -10251,13 +9560,11 @@ __webpack_require__.r(__webpack_exports__);
10251
9560
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
10252
9561
  /* harmony export */ ApiService: () => (/* binding */ ApiService)
10253
9562
  /* harmony export */ });
10254
- /* harmony import */ var _ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ApiKeyManager */ "./lib/services/ApiKeyManager.js");
10255
- /* harmony import */ var _jupyterlab_coreutils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @jupyterlab/coreutils */ "webpack/sharing/consume/default/@jupyterlab/coreutils");
10256
- /* harmony import */ var _jupyterlab_coreutils__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_jupyterlab_coreutils__WEBPACK_IMPORTED_MODULE_1__);
9563
+ /* harmony import */ var _jupyterlab_coreutils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @jupyterlab/coreutils */ "webpack/sharing/consume/default/@jupyterlab/coreutils");
9564
+ /* harmony import */ var _jupyterlab_coreutils__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_jupyterlab_coreutils__WEBPACK_IMPORTED_MODULE_0__);
10257
9565
  /**
10258
9566
  * API Service Layer for REST communication with backend
10259
9567
  */
10260
-
10261
9568
  // ✅ 핵심 변경 1: ServerConnection 대신 PageConfig 임포트
10262
9569
 
10263
9570
  class ApiService {
@@ -10271,10 +9578,10 @@ class ApiService {
10271
9578
  else {
10272
9579
  // ✅ 핵심 변경 2: ServerConnection 대신 PageConfig로 URL 가져오기
10273
9580
  // PageConfig.getBaseUrl()은 '/user/아이디/프로젝트/' 형태의 주소를 정확히 가져옵니다.
10274
- const serverRoot = _jupyterlab_coreutils__WEBPACK_IMPORTED_MODULE_1__.PageConfig.getBaseUrl();
9581
+ const serverRoot = _jupyterlab_coreutils__WEBPACK_IMPORTED_MODULE_0__.PageConfig.getBaseUrl();
10275
9582
  // 3. 경로 합치기
10276
9583
  // 결과: /user/453467/pl2wadmprj/hdsp-agent
10277
- this.baseUrl = _jupyterlab_coreutils__WEBPACK_IMPORTED_MODULE_1__.URLExt.join(serverRoot, 'hdsp-agent');
9584
+ this.baseUrl = _jupyterlab_coreutils__WEBPACK_IMPORTED_MODULE_0__.URLExt.join(serverRoot, 'hdsp-agent');
10278
9585
  }
10279
9586
  console.log('[ApiService] Base URL initialized:', this.baseUrl); // 디버깅용 로그
10280
9587
  }
@@ -10334,88 +9641,41 @@ class ApiService {
10334
9641
  }
10335
9642
  }
10336
9643
  // ═══════════════════════════════════════════════════════════════════════════
10337
- // Global Rate Limit Handling with Key Rotation
9644
+ // API Request Helper
10338
9645
  // ═══════════════════════════════════════════════════════════════════════════
10339
9646
  /**
10340
- * Fetch wrapper with automatic API key rotation on rate limit (429)
9647
+ * Simple fetch wrapper for POST requests
10341
9648
  *
10342
- * NOTE (Financial Security Compliance):
10343
- * - API key rotation is handled by frontend (not server)
10344
- * - Server receives ONLY ONE key per request
10345
- * - On 429 rate limit, frontend rotates key and retries with next key
9649
+ * NOTE: API keys are now managed by Agent Server's AdminConfig.
9650
+ * Client only sends workspaceRoot and autoApprove preferences.
10346
9651
  */
10347
9652
  async fetchWithKeyRotation(url, request, options) {
10348
- const MAX_RETRIES = 10;
10349
- const originalConfig = request.llmConfig;
10350
- let lastError = null;
10351
- for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
10352
- // Build request with single key for this attempt
10353
- const requestToSend = originalConfig
10354
- ? { ...request, llmConfig: (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.buildSingleKeyConfig)(originalConfig) }
10355
- : request;
9653
+ try {
9654
+ const response = await fetch(url, {
9655
+ method: 'POST',
9656
+ headers: this.getHeaders(),
9657
+ credentials: 'include',
9658
+ body: JSON.stringify(request)
9659
+ });
9660
+ if (response.ok) {
9661
+ return response.json();
9662
+ }
9663
+ // Handle error response
9664
+ const errorText = await response.text();
9665
+ let errorMessage = options?.defaultErrorMessage || 'API 요청 실패';
10356
9666
  try {
10357
- const response = await fetch(url, {
10358
- method: 'POST',
10359
- headers: this.getHeaders(),
10360
- credentials: 'include',
10361
- body: JSON.stringify(requestToSend)
10362
- });
10363
- if (response.ok) {
10364
- // Success - reset key rotation state
10365
- (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.resetKeyRotation)();
10366
- return response.json();
10367
- }
10368
- // Handle error response
10369
- const errorText = await response.text();
10370
- // Check if rate limit error
10371
- if ((0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.isRateLimitError)(errorText) && originalConfig) {
10372
- console.log(`[ApiService] Rate limit on attempt ${attempt + 1}, rotating key...`);
10373
- // Try to rotate to next key
10374
- const rotatedConfig = (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.handleRateLimitError)(originalConfig);
10375
- if (rotatedConfig) {
10376
- // Notify UI about key rotation
10377
- const keys = (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.getValidGeminiKeys)(originalConfig);
10378
- if (options?.onKeyRotation) {
10379
- options.onKeyRotation((0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.getCurrentKeyIndex)(), keys.length);
10380
- }
10381
- continue; // Try next key
10382
- }
10383
- else {
10384
- // All keys exhausted
10385
- throw new Error('모든 API 키가 Rate Limit 상태입니다. 잠시 후 다시 시도해주세요.');
10386
- }
10387
- }
10388
- // Not a rate limit error - parse and throw
10389
- let errorMessage = options?.defaultErrorMessage || 'API 요청 실패';
10390
- try {
10391
- const errorJson = JSON.parse(errorText);
10392
- errorMessage = errorJson.detail || errorJson.error || errorJson.message || errorMessage;
10393
- }
10394
- catch (e) {
10395
- errorMessage = errorText || errorMessage;
10396
- }
10397
- throw new Error(errorMessage);
9667
+ const errorJson = JSON.parse(errorText);
9668
+ errorMessage = errorJson.detail || errorJson.error || errorJson.message || errorMessage;
10398
9669
  }
10399
- catch (error) {
10400
- const errorMsg = error instanceof Error ? error.message : String(error);
10401
- lastError = error instanceof Error ? error : new Error(errorMsg);
10402
- // If it's a rate limit error from the catch block, check rotation
10403
- if ((0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.isRateLimitError)(errorMsg) && originalConfig) {
10404
- const rotatedConfig = (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.handleRateLimitError)(originalConfig);
10405
- if (rotatedConfig) {
10406
- const keys = (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.getValidGeminiKeys)(originalConfig);
10407
- if (options?.onKeyRotation) {
10408
- options.onKeyRotation((0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.getCurrentKeyIndex)(), keys.length);
10409
- }
10410
- continue;
10411
- }
10412
- throw new Error('모든 API 키가 Rate Limit 상태입니다. 잠시 후 다시 시도해주세요.');
10413
- }
10414
- // Not a rate limit error, throw immediately
10415
- throw error;
9670
+ catch (e) {
9671
+ errorMessage = errorText || errorMessage;
10416
9672
  }
9673
+ throw new Error(errorMessage);
9674
+ }
9675
+ catch (error) {
9676
+ const errorMsg = error instanceof Error ? error.message : String(error);
9677
+ throw error instanceof Error ? error : new Error(errorMsg);
10417
9678
  }
10418
- throw lastError || new Error('Maximum retry attempts exceeded');
10419
9679
  }
10420
9680
  /**
10421
9681
  * Execute cell action (explain, fix, custom)
@@ -10444,43 +9704,11 @@ class ApiService {
10444
9704
  /**
10445
9705
  * 단순 Chat용 스트리밍 - /chat/stream 사용
10446
9706
  * 원래 main 브랜치의 구현 복원 - LangChain 에이전트 없이 단순 Q&A
9707
+ *
9708
+ * NOTE: API keys are now managed by Agent Server's AdminConfig.
10447
9709
  */
10448
9710
  async sendChatStream(request, onChunk, onMetadata, abortSignal, onDebug) {
10449
- const MAX_RETRIES = 10;
10450
- let currentConfig = request.llmConfig;
10451
- let lastError = null;
10452
- for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
10453
- const requestToSend = currentConfig
10454
- ? { ...request, llmConfig: (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.buildSingleKeyConfig)(currentConfig) }
10455
- : request;
10456
- try {
10457
- await this.sendChatStreamInternal(requestToSend, onChunk, onMetadata, abortSignal, onDebug);
10458
- (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.resetKeyRotation)();
10459
- return;
10460
- }
10461
- catch (error) {
10462
- // Check if it's an abort error
10463
- if (error instanceof Error && error.name === 'AbortError') {
10464
- console.log('[ApiService] Chat stream aborted by user');
10465
- throw error;
10466
- }
10467
- const errorMsg = error instanceof Error ? error.message : String(error);
10468
- lastError = error instanceof Error ? error : new Error(errorMsg);
10469
- if ((0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.isRateLimitError)(errorMsg) && request.llmConfig) {
10470
- console.log(`[ApiService] Chat rate limit on attempt ${attempt + 1}, trying next key...`);
10471
- const rotatedConfig = (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.handleRateLimitError)(request.llmConfig);
10472
- if (rotatedConfig) {
10473
- currentConfig = request.llmConfig;
10474
- continue;
10475
- }
10476
- else {
10477
- throw new Error('모든 API 키가 Rate Limit 상태입니다. 잠시 후 다시 시도해주세요.');
10478
- }
10479
- }
10480
- throw error;
10481
- }
10482
- }
10483
- throw lastError || new Error('Maximum retry attempts exceeded');
9711
+ await this.sendChatStreamInternal(request, onChunk, onMetadata, abortSignal, onDebug);
10484
9712
  }
10485
9713
  /**
10486
9714
  * 단순 Chat 스트리밍 내부 구현 - /chat/stream 엔드포인트 사용
@@ -10550,65 +9778,13 @@ class ApiService {
10550
9778
  /**
10551
9779
  * Agent V2용 스트리밍 - /agent/langchain/stream 사용
10552
9780
  * LangChain Deep Agent (HITL, Todo, 도구 실행 등)
9781
+ *
9782
+ * NOTE: API keys are now managed by Agent Server's AdminConfig.
10553
9783
  */
10554
9784
  async sendAgentV2Stream(request, onChunk, onMetadata, onDebug, onInterrupt, onTodos, onDebugClear, onToolCall, onComplete, // Callback to capture thread_id for context persistence
10555
9785
  threadId // Optional thread_id to continue existing conversation
10556
9786
  ) {
10557
- // Maximum retry attempts (should match number of keys)
10558
- const MAX_RETRIES = 10;
10559
- let currentConfig = request.llmConfig;
10560
- let lastError = null;
10561
- for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
10562
- // Build request with single key for this attempt
10563
- const requestToSend = currentConfig
10564
- ? { ...request, llmConfig: (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.buildSingleKeyConfig)(currentConfig) }
10565
- : request;
10566
- try {
10567
- await this.sendAgentV2StreamInternal(requestToSend, onChunk, onMetadata, onDebug, onInterrupt, onTodos, onDebugClear, onToolCall, onComplete, threadId);
10568
- // Success - reset key rotation state
10569
- (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.resetKeyRotation)();
10570
- return;
10571
- }
10572
- catch (error) {
10573
- const errorMsg = error instanceof Error ? error.message : String(error);
10574
- lastError = error instanceof Error ? error : new Error(errorMsg);
10575
- // Check if rate limit error and we have config to rotate
10576
- if ((0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.isRateLimitError)(errorMsg) && request.llmConfig) {
10577
- console.log(`[ApiService] Rate limit on attempt ${attempt + 1}, trying next key...`);
10578
- // Try to rotate to next key using ORIGINAL config (with all keys)
10579
- const rotatedConfig = (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.handleRateLimitError)(request.llmConfig);
10580
- if (rotatedConfig) {
10581
- // Update currentConfig with the rotated key for next attempt
10582
- // Note: rotatedConfig already has single key, but we need full config for next rotation
10583
- currentConfig = request.llmConfig;
10584
- continue; // Try next key
10585
- }
10586
- else {
10587
- // All keys exhausted
10588
- throw new Error('모든 API 키가 Rate Limit 상태입니다. 잠시 후 다시 시도해주세요.');
10589
- }
10590
- }
10591
- // Not a rate limit error, throw immediately
10592
- throw error;
10593
- }
10594
- }
10595
- // Should not reach here, but just in case
10596
- throw lastError || new Error('Maximum retry attempts exceeded');
10597
- }
10598
- /**
10599
- * Build request with single API key for server
10600
- * (Key rotation is managed by frontend for financial security compliance)
10601
- */
10602
- buildRequestWithSingleKey(request) {
10603
- if (!request.llmConfig) {
10604
- return request;
10605
- }
10606
- // Build config with single key (server only uses apiKey field)
10607
- const singleKeyConfig = (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.buildSingleKeyConfig)(request.llmConfig);
10608
- return {
10609
- ...request,
10610
- llmConfig: singleKeyConfig
10611
- };
9787
+ await this.sendAgentV2StreamInternal(request, onChunk, onMetadata, onDebug, onInterrupt, onTodos, onDebugClear, onToolCall, onComplete, threadId);
10612
9788
  }
10613
9789
  /**
10614
9790
  * 기존 sendMessageStream 유지 (하위 호환성)
@@ -13519,4 +12695,4 @@ __webpack_require__.r(__webpack_exports__);
13519
12695
  /***/ }
13520
12696
 
13521
12697
  }]);
13522
- //# sourceMappingURL=lib_index_js.df05d90f366bfd5fa023.js.map
12698
+ //# sourceMappingURL=lib_index_js.cc0a7158a5e3de7f22f7.js.map