hdsp-jupyter-extension 2.0.26__py3-none-any.whl → 2.0.28__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agent_server/context_providers/__init__.py +4 -2
- agent_server/context_providers/actions.py +73 -7
- agent_server/context_providers/file.py +23 -23
- agent_server/langchain/__init__.py +2 -2
- agent_server/langchain/agent.py +18 -251
- agent_server/langchain/agent_factory.py +26 -4
- agent_server/langchain/agent_prompts/planner_prompt.py +22 -35
- agent_server/langchain/custom_middleware.py +278 -43
- agent_server/langchain/llm_factory.py +102 -54
- agent_server/langchain/logging_utils.py +1 -1
- agent_server/langchain/middleware/__init__.py +5 -0
- agent_server/langchain/middleware/code_history_middleware.py +126 -37
- agent_server/langchain/middleware/content_injection_middleware.py +110 -0
- agent_server/langchain/middleware/subagent_events.py +88 -9
- agent_server/langchain/middleware/subagent_middleware.py +518 -240
- agent_server/langchain/prompts.py +5 -22
- agent_server/langchain/state_schema.py +44 -0
- agent_server/langchain/tools/jupyter_tools.py +4 -5
- agent_server/langchain/tools/tool_registry.py +6 -0
- agent_server/routers/chat.py +305 -2
- agent_server/routers/config.py +193 -8
- agent_server/routers/config_schema.py +254 -0
- agent_server/routers/context.py +31 -8
- agent_server/routers/langchain_agent.py +310 -153
- hdsp_agent_core/managers/config_manager.py +100 -1
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/build_log.json +1 -1
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/package.json +2 -2
- hdsp_jupyter_extension-2.0.26.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.b5e4416b4e07ec087aad.js → hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.55727265b00191e68d9a.js +479 -15
- hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.55727265b00191e68d9a.js.map +1 -0
- jupyter_ext/labextension/static/lib_index_js.67505497667f9c0a763d.js → hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.df05d90f366bfd5fa023.js +1287 -190
- hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.df05d90f366bfd5fa023.js.map +1 -0
- hdsp_jupyter_extension-2.0.26.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.0fe2dcbbd176ee0efceb.js → hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.08fce819ee32e9d25175.js +3 -3
- jupyter_ext/labextension/static/remoteEntry.0fe2dcbbd176ee0efceb.js.map → hdsp_jupyter_extension-2.0.28.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.08fce819ee32e9d25175.js.map +1 -1
- {hdsp_jupyter_extension-2.0.26.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/METADATA +1 -1
- {hdsp_jupyter_extension-2.0.26.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/RECORD +66 -64
- jupyter_ext/_version.py +1 -1
- jupyter_ext/handlers.py +41 -0
- jupyter_ext/labextension/build_log.json +1 -1
- jupyter_ext/labextension/package.json +2 -2
- jupyter_ext/labextension/static/{frontend_styles_index_js.b5e4416b4e07ec087aad.js → frontend_styles_index_js.55727265b00191e68d9a.js} +479 -15
- jupyter_ext/labextension/static/frontend_styles_index_js.55727265b00191e68d9a.js.map +1 -0
- hdsp_jupyter_extension-2.0.26.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.67505497667f9c0a763d.js → jupyter_ext/labextension/static/lib_index_js.df05d90f366bfd5fa023.js +1287 -190
- jupyter_ext/labextension/static/lib_index_js.df05d90f366bfd5fa023.js.map +1 -0
- jupyter_ext/labextension/static/{remoteEntry.0fe2dcbbd176ee0efceb.js → remoteEntry.08fce819ee32e9d25175.js} +3 -3
- hdsp_jupyter_extension-2.0.26.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.0fe2dcbbd176ee0efceb.js.map → jupyter_ext/labextension/static/remoteEntry.08fce819ee32e9d25175.js.map +1 -1
- agent_server/langchain/middleware/description_injector.py +0 -150
- hdsp_jupyter_extension-2.0.26.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.b5e4416b4e07ec087aad.js.map +0 -1
- hdsp_jupyter_extension-2.0.26.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.67505497667f9c0a763d.js.map +0 -1
- jupyter_ext/labextension/static/frontend_styles_index_js.b5e4416b4e07ec087aad.js.map +0 -1
- jupyter_ext/labextension/static/lib_index_js.67505497667f9c0a763d.js.map +0 -1
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80.c095373419d05e6f141a.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80.c095373419d05e6f141a.js.map +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b81.61e75fb98ecff46cf836.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b81.61e75fb98ecff46cf836.js.map +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/style.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195.e2553aab0c3963b83dd7.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195.e2553aab0c3963b83dd7.js.map +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js.map +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js.map +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js.map +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js.map +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js +0 -0
- {hdsp_jupyter_extension-2.0.26.data → hdsp_jupyter_extension-2.0.28.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js.map +0 -0
- {hdsp_jupyter_extension-2.0.26.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/WHEEL +0 -0
- {hdsp_jupyter_extension-2.0.26.dist-info → hdsp_jupyter_extension-2.0.28.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,560 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
(self["webpackChunkhdsp_agent"] = self["webpackChunkhdsp_agent"] || []).push([["lib_index_js"],{
|
|
3
3
|
|
|
4
|
+
/***/ "./lib/components/AdminSettingsPanel.js"
|
|
5
|
+
/*!**********************************************!*\
|
|
6
|
+
!*** ./lib/components/AdminSettingsPanel.js ***!
|
|
7
|
+
\**********************************************/
|
|
8
|
+
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
9
|
+
|
|
10
|
+
__webpack_require__.r(__webpack_exports__);
|
|
11
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
12
|
+
/* harmony export */ AdminSettingsPanel: () => (/* binding */ AdminSettingsPanel)
|
|
13
|
+
/* harmony export */ });
|
|
14
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react");
|
|
15
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
16
|
+
/* 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");
|
|
17
|
+
/* 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");
|
|
18
|
+
/* 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");
|
|
19
|
+
/* 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");
|
|
20
|
+
/* 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");
|
|
21
|
+
/* 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");
|
|
22
|
+
/* 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");
|
|
23
|
+
/* 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");
|
|
24
|
+
/* 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");
|
|
25
|
+
/* harmony import */ var _services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../services/ApiKeyManager */ "./lib/services/ApiKeyManager.js");
|
|
26
|
+
/**
|
|
27
|
+
* Admin Settings Panel Component
|
|
28
|
+
*
|
|
29
|
+
* Comprehensive configuration for administrators:
|
|
30
|
+
* - LLM Provider settings (API keys, models, endpoints)
|
|
31
|
+
* - Summarization LLM settings
|
|
32
|
+
* - Embedding configuration
|
|
33
|
+
* - RAG/Qdrant settings
|
|
34
|
+
* - User preferences (also editable by admin)
|
|
35
|
+
* - Agent prompts
|
|
36
|
+
* - Server settings
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
const AdminSettingsPanel = ({ onClose, onSave, apiService }) => {
|
|
50
|
+
const [isLoading, setIsLoading] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(true);
|
|
51
|
+
const [isSaving, setIsSaving] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
52
|
+
const [error, setError] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
53
|
+
// Collapsible sections
|
|
54
|
+
const [expandedSections, setExpandedSections] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({
|
|
55
|
+
llm: true,
|
|
56
|
+
summarization: false,
|
|
57
|
+
embedding: false,
|
|
58
|
+
rag: false,
|
|
59
|
+
user: true,
|
|
60
|
+
server: false,
|
|
61
|
+
prompts: false
|
|
62
|
+
});
|
|
63
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
64
|
+
// LLM Provider Settings
|
|
65
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
66
|
+
const [provider, setProvider] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('gemini');
|
|
67
|
+
// Gemini
|
|
68
|
+
const [geminiApiKeys, setGeminiApiKeys] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(['']);
|
|
69
|
+
const [geminiModel, setGeminiModel] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('gemini-2.5-flash');
|
|
70
|
+
// vLLM / OpenRouter
|
|
71
|
+
const [vllmEndpoint, setVllmEndpoint] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('https://openrouter.ai/api/v1');
|
|
72
|
+
const [vllmApiKey, setVllmApiKey] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
|
|
73
|
+
const [vllmModel, setVllmModel] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('openai/gpt-4o');
|
|
74
|
+
const [vllmUseResponsesApi, setVllmUseResponsesApi] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
75
|
+
// OpenAI
|
|
76
|
+
const [openaiApiKey, setOpenaiApiKey] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
|
|
77
|
+
const [openaiModel, setOpenaiModel] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('gpt-4o');
|
|
78
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
79
|
+
// Summarization LLM
|
|
80
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
81
|
+
const [summarizationEnabled, setSummarizationEnabled] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
82
|
+
const [summarizationProvider, setSummarizationProvider] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('gemini');
|
|
83
|
+
const [summarizationModel, setSummarizationModel] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
|
|
84
|
+
const [summarizationEndpoint, setSummarizationEndpoint] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
|
|
85
|
+
const [summarizationApiKey, setSummarizationApiKey] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
|
|
86
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
87
|
+
// Embedding Settings
|
|
88
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
89
|
+
const [embeddingProvider, setEmbeddingProvider] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('openai');
|
|
90
|
+
const [embeddingModel, setEmbeddingModel] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('text-embedding-3-small');
|
|
91
|
+
const [embeddingApiKey, setEmbeddingApiKey] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
|
|
92
|
+
const [embeddingEndpoint, setEmbeddingEndpoint] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
|
|
93
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
94
|
+
// RAG Settings
|
|
95
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
96
|
+
const [qdrantUrl, setQdrantUrl] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('http://localhost:6333');
|
|
97
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
98
|
+
// User Settings (also editable by admin)
|
|
99
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
100
|
+
const [workspaceRoot, setWorkspaceRoot] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
|
|
101
|
+
const [temperature, setTemperature] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(0.7);
|
|
102
|
+
const [autoApprove, setAutoApprove] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
103
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
104
|
+
// Server Settings
|
|
105
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
106
|
+
const [agentServerUrl, setAgentServerUrl] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('http://localhost:8000');
|
|
107
|
+
const [agentServerTimeout, setAgentServerTimeout] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(120);
|
|
108
|
+
const [idleTimeout, setIdleTimeout] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(300);
|
|
109
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
110
|
+
// Agent Prompts
|
|
111
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
112
|
+
const [agentPrompts, setAgentPrompts] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({});
|
|
113
|
+
const [expandedAgents, setExpandedAgents] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({});
|
|
114
|
+
const [defaultPrompts, setDefaultPrompts] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)((0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.getCachedPrompts)());
|
|
115
|
+
const [isLoadingPrompts, setIsLoadingPrompts] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(!(0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.getCachedPrompts)());
|
|
116
|
+
// Test state
|
|
117
|
+
const [isTesting, setIsTesting] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
118
|
+
const [testResults, setTestResults] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({});
|
|
119
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
120
|
+
// Load Config
|
|
121
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
122
|
+
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
123
|
+
loadConfig();
|
|
124
|
+
if (!defaultPrompts) {
|
|
125
|
+
setIsLoadingPrompts(true);
|
|
126
|
+
(0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.fetchDefaultPrompts)().then(prompts => {
|
|
127
|
+
setDefaultPrompts(prompts);
|
|
128
|
+
setIsLoadingPrompts(false);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}, []);
|
|
132
|
+
const loadConfig = async () => {
|
|
133
|
+
try {
|
|
134
|
+
setIsLoading(true);
|
|
135
|
+
// Load admin config
|
|
136
|
+
const adminConfig = await apiService.getAdminConfig();
|
|
137
|
+
// Provider
|
|
138
|
+
setProvider(adminConfig.provider);
|
|
139
|
+
// Gemini
|
|
140
|
+
if (adminConfig.gemini) {
|
|
141
|
+
const keys = adminConfig.gemini.apiKeys?.length
|
|
142
|
+
? adminConfig.gemini.apiKeys
|
|
143
|
+
: adminConfig.gemini.apiKey ? [adminConfig.gemini.apiKey] : [''];
|
|
144
|
+
setGeminiApiKeys(keys);
|
|
145
|
+
setGeminiModel(adminConfig.gemini.model || 'gemini-2.5-flash');
|
|
146
|
+
}
|
|
147
|
+
// vLLM
|
|
148
|
+
if (adminConfig.vllm) {
|
|
149
|
+
setVllmEndpoint(adminConfig.vllm.endpoint || 'https://openrouter.ai/api/v1');
|
|
150
|
+
setVllmApiKey(adminConfig.vllm.apiKey || '');
|
|
151
|
+
setVllmModel(adminConfig.vllm.model || 'openai/gpt-4o');
|
|
152
|
+
setVllmUseResponsesApi(Boolean(adminConfig.vllm.useResponsesApi));
|
|
153
|
+
}
|
|
154
|
+
// OpenAI
|
|
155
|
+
if (adminConfig.openai) {
|
|
156
|
+
setOpenaiApiKey(adminConfig.openai.apiKey || '');
|
|
157
|
+
setOpenaiModel(adminConfig.openai.model || 'gpt-4o');
|
|
158
|
+
}
|
|
159
|
+
// Summarization
|
|
160
|
+
if (adminConfig.summarization) {
|
|
161
|
+
setSummarizationEnabled(Boolean(adminConfig.summarization.enabled));
|
|
162
|
+
setSummarizationProvider(adminConfig.summarization.provider || 'gemini');
|
|
163
|
+
setSummarizationModel(adminConfig.summarization.model || '');
|
|
164
|
+
setSummarizationEndpoint(adminConfig.summarization.endpoint || '');
|
|
165
|
+
setSummarizationApiKey(adminConfig.summarization.apiKey || '');
|
|
166
|
+
}
|
|
167
|
+
// Embedding
|
|
168
|
+
if (adminConfig.embedding) {
|
|
169
|
+
setEmbeddingProvider(adminConfig.embedding.provider || 'openai');
|
|
170
|
+
setEmbeddingModel(adminConfig.embedding.model || 'text-embedding-3-small');
|
|
171
|
+
setEmbeddingApiKey(adminConfig.embedding.apiKey || '');
|
|
172
|
+
setEmbeddingEndpoint(adminConfig.embedding.endpoint || '');
|
|
173
|
+
}
|
|
174
|
+
// RAG
|
|
175
|
+
if (adminConfig.rag) {
|
|
176
|
+
setQdrantUrl(adminConfig.rag.qdrantUrl || 'http://localhost:6333');
|
|
177
|
+
}
|
|
178
|
+
// Server
|
|
179
|
+
setAgentServerUrl(adminConfig.agentServerUrl || 'http://localhost:8000');
|
|
180
|
+
setAgentServerTimeout(adminConfig.agentServerTimeout || 120);
|
|
181
|
+
setIdleTimeout(adminConfig.idleTimeout || 300);
|
|
182
|
+
// Prompts
|
|
183
|
+
if (adminConfig.prompts) {
|
|
184
|
+
setAgentPrompts(adminConfig.prompts);
|
|
185
|
+
}
|
|
186
|
+
// Load user config
|
|
187
|
+
const userConfig = await apiService.getUserConfig();
|
|
188
|
+
setWorkspaceRoot(userConfig.workspaceRoot || '');
|
|
189
|
+
setTemperature(userConfig.temperature ?? 0.7);
|
|
190
|
+
setAutoApprove(Boolean(userConfig.autoApprove));
|
|
191
|
+
setError(null);
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
setError(`설정을 불러오는데 실패했습니다: ${err}`);
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
setIsLoading(false);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
201
|
+
// Save Config
|
|
202
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
203
|
+
const handleSave = async () => {
|
|
204
|
+
try {
|
|
205
|
+
setIsSaving(true);
|
|
206
|
+
// Admin config
|
|
207
|
+
const adminConfig = {
|
|
208
|
+
provider,
|
|
209
|
+
gemini: {
|
|
210
|
+
apiKey: geminiApiKeys[0] || '',
|
|
211
|
+
apiKeys: geminiApiKeys.filter(k => k && k.trim()),
|
|
212
|
+
model: geminiModel
|
|
213
|
+
},
|
|
214
|
+
openai: {
|
|
215
|
+
apiKey: openaiApiKey,
|
|
216
|
+
model: openaiModel
|
|
217
|
+
},
|
|
218
|
+
vllm: {
|
|
219
|
+
endpoint: vllmEndpoint,
|
|
220
|
+
apiKey: vllmApiKey,
|
|
221
|
+
model: vllmModel,
|
|
222
|
+
useResponsesApi: vllmUseResponsesApi
|
|
223
|
+
},
|
|
224
|
+
summarization: {
|
|
225
|
+
enabled: summarizationEnabled,
|
|
226
|
+
provider: summarizationProvider,
|
|
227
|
+
model: summarizationModel || undefined,
|
|
228
|
+
...(summarizationProvider === 'vllm' ? {
|
|
229
|
+
endpoint: summarizationEndpoint,
|
|
230
|
+
apiKey: summarizationApiKey
|
|
231
|
+
} : {})
|
|
232
|
+
},
|
|
233
|
+
embedding: {
|
|
234
|
+
provider: embeddingProvider,
|
|
235
|
+
model: embeddingModel,
|
|
236
|
+
apiKey: embeddingApiKey || undefined,
|
|
237
|
+
...(embeddingProvider === 'vllm' ? { endpoint: embeddingEndpoint } : {})
|
|
238
|
+
},
|
|
239
|
+
rag: {
|
|
240
|
+
qdrantUrl,
|
|
241
|
+
collectionName: '' // Will be set by agent
|
|
242
|
+
},
|
|
243
|
+
agentServerUrl,
|
|
244
|
+
agentServerTimeout,
|
|
245
|
+
prompts: agentPrompts,
|
|
246
|
+
idleTimeout
|
|
247
|
+
};
|
|
248
|
+
await apiService.updateAdminConfig(adminConfig);
|
|
249
|
+
// User config
|
|
250
|
+
await apiService.updateUserConfig({
|
|
251
|
+
workspaceRoot: workspaceRoot.trim(),
|
|
252
|
+
temperature,
|
|
253
|
+
autoApprove
|
|
254
|
+
});
|
|
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
|
+
},
|
|
272
|
+
workspaceRoot: workspaceRoot.trim(),
|
|
273
|
+
autoApprove,
|
|
274
|
+
agentPrompts
|
|
275
|
+
};
|
|
276
|
+
(0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.saveLLMConfig)(llmConfigForLocalStorage);
|
|
277
|
+
console.log('[AdminSettingsPanel] Config saved to localStorage for Chat mode');
|
|
278
|
+
onSave(adminConfig);
|
|
279
|
+
onClose();
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
setError(`설정 저장에 실패했습니다: ${err}`);
|
|
283
|
+
}
|
|
284
|
+
finally {
|
|
285
|
+
setIsSaving(false);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
289
|
+
// Helpers
|
|
290
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
291
|
+
const toggleSection = (key) => {
|
|
292
|
+
setExpandedSections(prev => ({ ...prev, [key]: !prev[key] }));
|
|
293
|
+
};
|
|
294
|
+
const handleAddKey = () => {
|
|
295
|
+
if (geminiApiKeys.length < 10) {
|
|
296
|
+
setGeminiApiKeys([...geminiApiKeys, '']);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
const handleRemoveKey = (index) => {
|
|
300
|
+
if (geminiApiKeys.length > 1) {
|
|
301
|
+
setGeminiApiKeys(geminiApiKeys.filter((_, i) => i !== index));
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
const handleKeyChange = (index, value) => {
|
|
305
|
+
const newKeys = [...geminiApiKeys];
|
|
306
|
+
newKeys[index] = value;
|
|
307
|
+
setGeminiApiKeys(newKeys);
|
|
308
|
+
};
|
|
309
|
+
const validKeyCount = geminiApiKeys.filter(k => k && k.trim()).length;
|
|
310
|
+
const handleTest = async () => {
|
|
311
|
+
setIsTesting(true);
|
|
312
|
+
setTestResults({});
|
|
313
|
+
if (provider === 'gemini') {
|
|
314
|
+
geminiApiKeys.forEach((key, index) => {
|
|
315
|
+
if (key && key.trim()) {
|
|
316
|
+
setTestResults(prev => ({ ...prev, [index]: key.startsWith('AIza') ? 'success' : 'error' }));
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
else if (provider === 'openai') {
|
|
321
|
+
setTestResults({ 0: openaiApiKey.startsWith('sk-') ? 'success' : 'error' });
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
setTestResults({ 0: vllmEndpoint ? 'success' : 'error' });
|
|
325
|
+
}
|
|
326
|
+
setIsTesting(false);
|
|
327
|
+
};
|
|
328
|
+
const getTestStatusIcon = (index) => {
|
|
329
|
+
const status = testResults[index];
|
|
330
|
+
if (status === 'testing')
|
|
331
|
+
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_HourglassEmpty__WEBPACK_IMPORTED_MODULE_3__["default"], { sx: { fontSize: 16, color: 'info.main' } });
|
|
332
|
+
if (status === 'success')
|
|
333
|
+
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_CheckCircle__WEBPACK_IMPORTED_MODULE_1__["default"], { sx: { fontSize: 16, color: 'success.main' } });
|
|
334
|
+
if (status === 'error')
|
|
335
|
+
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_Error__WEBPACK_IMPORTED_MODULE_2__["default"], { sx: { fontSize: 16, color: 'error.main' } });
|
|
336
|
+
return null;
|
|
337
|
+
};
|
|
338
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
339
|
+
// Section Header Component
|
|
340
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
341
|
+
const SectionHeader = ({ title, sectionKey, subtitle }) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section-header", onClick: () => toggleSection(sectionKey) },
|
|
342
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section-title" },
|
|
343
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-section-icon" }, expandedSections[sectionKey] ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ExpandMore__WEBPACK_IMPORTED_MODULE_8__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ChevronRight__WEBPACK_IMPORTED_MODULE_9__["default"], null)),
|
|
344
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, title),
|
|
345
|
+
subtitle && react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-section-subtitle" }, subtitle))));
|
|
346
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
347
|
+
// Render
|
|
348
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
349
|
+
if (isLoading) {
|
|
350
|
+
return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-overlay" },
|
|
351
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-dialog jp-admin-dialog" },
|
|
352
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-content", style: { textAlign: 'center', padding: '40px' } },
|
|
353
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_HourglassEmpty__WEBPACK_IMPORTED_MODULE_3__["default"], { sx: { fontSize: 48, color: 'info.main' } }),
|
|
354
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", null, "\uC124\uC815\uC744 \uBD88\uB7EC\uC624\uB294 \uC911...")))));
|
|
355
|
+
}
|
|
356
|
+
return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-overlay" },
|
|
357
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-dialog jp-admin-dialog" },
|
|
358
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-header" },
|
|
359
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", null, "\uAD00\uB9AC\uC790 \uC124\uC815"),
|
|
360
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-settings-close", onClick: onClose, title: "\uB2EB\uAE30" }, "\u00D7")),
|
|
361
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-content" },
|
|
362
|
+
error && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-error" }, error)),
|
|
363
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section" },
|
|
364
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(SectionHeader, { title: "LLM \uC124\uC815", sectionKey: "llm", subtitle: "\uBA54\uC778 \uC5B8\uC5B4 \uBAA8\uB378" }),
|
|
365
|
+
expandedSections.llm && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section-content" },
|
|
366
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
367
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "\uD504\uB85C\uBC14\uC774\uB354"),
|
|
368
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("select", { className: "jp-admin-select", value: provider, onChange: (e) => setProvider(e.target.value) },
|
|
369
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gemini" }, "Google Gemini"),
|
|
370
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "vllm" }, "vLLM / OpenRouter"),
|
|
371
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "openai" }, "OpenAI"))),
|
|
372
|
+
provider === 'gemini' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
|
|
373
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
374
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" },
|
|
375
|
+
"API \uD0A4 (",
|
|
376
|
+
validKeyCount,
|
|
377
|
+
"/10)",
|
|
378
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-label-hint" }, "Rate limit \uC2DC \uC790\uB3D9 \uB85C\uD14C\uC774\uC158")),
|
|
379
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-keys-container" },
|
|
380
|
+
geminiApiKeys.map((key, index) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { key: index, className: "jp-admin-key-row" },
|
|
381
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-key-index" },
|
|
382
|
+
index + 1,
|
|
383
|
+
"."),
|
|
384
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "password", className: "jp-admin-input", value: key, onChange: (e) => handleKeyChange(index, e.target.value), placeholder: "AIza..." }),
|
|
385
|
+
getTestStatusIcon(index),
|
|
386
|
+
geminiApiKeys.length > 1 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-admin-btn-remove", onClick: () => handleRemoveKey(index) }, "\u00D7"))))),
|
|
387
|
+
geminiApiKeys.length < 10 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-admin-btn-add", onClick: handleAddKey }, "+ \uD0A4 \uCD94\uAC00")))),
|
|
388
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
389
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "\uBAA8\uB378"),
|
|
390
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("select", { className: "jp-admin-select", value: geminiModel, onChange: (e) => setGeminiModel(e.target.value) },
|
|
391
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gemini-2.5-flash" }, "Gemini 2.5 Flash"),
|
|
392
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gemini-2.5-pro" }, "Gemini 2.5 Pro"))))),
|
|
393
|
+
provider === 'vllm' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
|
|
394
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
395
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "API Base URL"),
|
|
396
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "text", className: "jp-admin-input", value: vllmEndpoint, onChange: (e) => setVllmEndpoint(e.target.value), placeholder: "https://openrouter.ai/api/v1" })),
|
|
397
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
398
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "API \uD0A4"),
|
|
399
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "password", className: "jp-admin-input", value: vllmApiKey, onChange: (e) => setVllmApiKey(e.target.value), placeholder: "sk-or-..." })),
|
|
400
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
401
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "\uBAA8\uB378"),
|
|
402
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "text", className: "jp-admin-input", value: vllmModel, onChange: (e) => setVllmModel(e.target.value), placeholder: "openai/gpt-4o" })),
|
|
403
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
404
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-checkbox" },
|
|
405
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "checkbox", checked: vllmUseResponsesApi, onChange: (e) => setVllmUseResponsesApi(e.target.checked) }),
|
|
406
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "Use Responses API (/v1/responses)"))))),
|
|
407
|
+
provider === 'openai' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
|
|
408
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
409
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "API \uD0A4"),
|
|
410
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "password", className: "jp-admin-input", value: openaiApiKey, onChange: (e) => setOpenaiApiKey(e.target.value), placeholder: "sk-..." })),
|
|
411
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
412
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "\uBAA8\uB378"),
|
|
413
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("select", { className: "jp-admin-select", value: openaiModel, onChange: (e) => setOpenaiModel(e.target.value) },
|
|
414
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gpt-4o" }, "GPT-4o"),
|
|
415
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gpt-4o-mini" }, "GPT-4o Mini"),
|
|
416
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gpt-4-turbo" }, "GPT-4 Turbo")))))))),
|
|
417
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section" },
|
|
418
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(SectionHeader, { title: "\uC0AC\uC6A9\uC790 \uC124\uC815", sectionKey: "user", subtitle: "\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4, \uC628\uB3C4, \uC790\uB3D9 \uC2B9\uC778" }),
|
|
419
|
+
expandedSections.user && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section-content" },
|
|
420
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
421
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" },
|
|
422
|
+
"\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8",
|
|
423
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-label-hint" }, "\uBE44\uC6B0\uBA74 \uD604\uC7AC \uB178\uD2B8\uBD81 \uD3F4\uB354 \uAE30\uC900")),
|
|
424
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "text", className: "jp-admin-input", value: workspaceRoot, onChange: (e) => setWorkspaceRoot(e.target.value), placeholder: "/path/to/project" })),
|
|
425
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
426
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" },
|
|
427
|
+
"Temperature",
|
|
428
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-label-hint" }, "0.0 = \uACB0\uC815\uC801, 1.0+ = \uCC3D\uC758\uC801")),
|
|
429
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-slider-row" },
|
|
430
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "range", min: 0, max: 2, step: 0.1, value: temperature, onChange: (e) => setTemperature(parseFloat(e.target.value)), className: "jp-admin-slider" }),
|
|
431
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "number", className: "jp-admin-input jp-admin-input-small", value: temperature, onChange: (e) => setTemperature(Math.max(0, Math.min(2, parseFloat(e.target.value) || 0))), min: 0, max: 2, step: 0.1 }))),
|
|
432
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
433
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-checkbox" },
|
|
434
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "checkbox", checked: autoApprove, onChange: (e) => setAutoApprove(e.target.checked) }),
|
|
435
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\uC790\uB3D9 \uC2E4\uD589 \uC2B9\uC778 (\uCF54\uB4DC/\uD30C\uC77C/\uC178 \uD3EC\uD568)")),
|
|
436
|
+
autoApprove && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-warning" }, "\uC8FC\uC758: \uC5D0\uC774\uC804\uD2B8\uAC00 \uC0DD\uC131\uD55C \uCF54\uB4DC\uAC00 \uC790\uB3D9\uC73C\uB85C \uC2E4\uD589\uB429\uB2C8\uB2E4.")))))),
|
|
437
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section" },
|
|
438
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(SectionHeader, { title: "\uC694\uC57D\uC6A9 LLM", sectionKey: "summarization", subtitle: "\uB300\uD654 \uC555\uCD95 \uC2DC \uC0AC\uC6A9" }),
|
|
439
|
+
expandedSections.summarization && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section-content" },
|
|
440
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
441
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-checkbox" },
|
|
442
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "checkbox", checked: summarizationEnabled, onChange: (e) => setSummarizationEnabled(e.target.checked) }),
|
|
443
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\uBCC4\uB3C4 LLM\uC73C\uB85C \uC694\uC57D \uC218\uD589 (\uBBF8\uC124\uC815 \uC2DC \uBA54\uC778 LLM \uC0AC\uC6A9)"))),
|
|
444
|
+
summarizationEnabled && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
|
|
445
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
446
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "\uD504\uB85C\uBC14\uC774\uB354"),
|
|
447
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("select", { className: "jp-admin-select", value: summarizationProvider, onChange: (e) => setSummarizationProvider(e.target.value) },
|
|
448
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gemini" }, "Gemini (\uBA54\uC778 \uC124\uC815 \uC0AC\uC6A9)"),
|
|
449
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "openai" }, "OpenAI (\uBA54\uC778 \uC124\uC815 \uC0AC\uC6A9)"),
|
|
450
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "vllm" }, "vLLM / OpenRouter (\uBCC4\uB3C4 \uC124\uC815)"))),
|
|
451
|
+
summarizationProvider === 'vllm' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
|
|
452
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
453
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "API Base URL"),
|
|
454
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "text", className: "jp-admin-input", value: summarizationEndpoint, onChange: (e) => setSummarizationEndpoint(e.target.value), placeholder: "https://openrouter.ai/api/v1" })),
|
|
455
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
456
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "API \uD0A4"),
|
|
457
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "password", className: "jp-admin-input", value: summarizationApiKey, onChange: (e) => setSummarizationApiKey(e.target.value), placeholder: "sk-or-..." })))),
|
|
458
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
459
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" },
|
|
460
|
+
"\uBAA8\uB378",
|
|
461
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-label-hint" }, "\uBE44\uC6B0\uBA74 \uAE30\uBCF8 \uBAA8\uB378 \uC0AC\uC6A9")),
|
|
462
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "text", className: "jp-admin-input", value: summarizationModel, onChange: (e) => setSummarizationModel(e.target.value), placeholder: summarizationProvider === 'vllm' ? 'openai/gpt-4o-mini' : '' }))))))),
|
|
463
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section" },
|
|
464
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(SectionHeader, { title: "\uC784\uBCA0\uB529 \uC124\uC815", sectionKey: "embedding", subtitle: "RAG \uBCA1\uD130 \uAC80\uC0C9\uC6A9" }),
|
|
465
|
+
expandedSections.embedding && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section-content" },
|
|
466
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
467
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "\uD504\uB85C\uBC14\uC774\uB354"),
|
|
468
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("select", { className: "jp-admin-select", value: embeddingProvider, onChange: (e) => setEmbeddingProvider(e.target.value) },
|
|
469
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "openai" }, "OpenAI"),
|
|
470
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gemini" }, "Gemini"),
|
|
471
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "vllm" }, "vLLM / OpenRouter (\uBCC4\uB3C4 \uC124\uC815)"))),
|
|
472
|
+
embeddingProvider === 'vllm' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
473
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "API Base URL"),
|
|
474
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "text", className: "jp-admin-input", value: embeddingEndpoint, onChange: (e) => setEmbeddingEndpoint(e.target.value), placeholder: "https://api.openai.com/v1" }))),
|
|
475
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
476
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "API \uD0A4"),
|
|
477
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "password", className: "jp-admin-input", value: embeddingApiKey, onChange: (e) => setEmbeddingApiKey(e.target.value), placeholder: embeddingProvider === 'openai' ? 'sk-... (비우면 메인 OpenAI 키 사용)' : 'API 키' })),
|
|
478
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
479
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "\uBAA8\uB378"),
|
|
480
|
+
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
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section" },
|
|
488
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(SectionHeader, { title: "\uC11C\uBC84 \uC124\uC815", sectionKey: "server", subtitle: "Agent \uC11C\uBC84, \uD0C0\uC784\uC544\uC6C3" }),
|
|
489
|
+
expandedSections.server && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section-content" },
|
|
490
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row" },
|
|
491
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "Agent Server URL"),
|
|
492
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "text", className: "jp-admin-input", value: agentServerUrl, onChange: (e) => setAgentServerUrl(e.target.value), placeholder: "http://localhost:8000" })),
|
|
493
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-row jp-admin-row-inline" },
|
|
494
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-inline-field" },
|
|
495
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "\uC694\uCCAD Timeout (\uCD08)"),
|
|
496
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "number", className: "jp-admin-input jp-admin-input-small", value: agentServerTimeout, onChange: (e) => setAgentServerTimeout(parseInt(e.target.value) || 120), min: 10, max: 600 })),
|
|
497
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-inline-field" },
|
|
498
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-admin-label" }, "Idle Timeout (\uCD08)"),
|
|
499
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "number", className: "jp-admin-input jp-admin-input-small", value: idleTimeout, onChange: (e) => setIdleTimeout(Math.max(0, parseInt(e.target.value) || 0)), min: 0, max: 86400 })))))),
|
|
500
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section" },
|
|
501
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(SectionHeader, { title: "\uC5D0\uC774\uC804\uD2B8 \uD504\uB86C\uD504\uD2B8", sectionKey: "prompts", subtitle: "\uC2DC\uC2A4\uD15C \uD504\uB86C\uD504\uD2B8 \uCEE4\uC2A4\uD130\uB9C8\uC774\uC9D5" }),
|
|
502
|
+
expandedSections.prompts && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-section-content" },
|
|
503
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-prompt-card" },
|
|
504
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-prompt-header", onClick: () => setExpandedAgents(prev => ({ ...prev, planner: !prev.planner })) },
|
|
505
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-prompt-icon" },
|
|
506
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_GpsFixed__WEBPACK_IMPORTED_MODULE_7__["default"], { sx: { fontSize: 18 } })),
|
|
507
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "Planner (Supervisor)"),
|
|
508
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-prompt-chevron" }, expandedAgents.planner ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ExpandMore__WEBPACK_IMPORTED_MODULE_8__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ChevronRight__WEBPACK_IMPORTED_MODULE_9__["default"], null))),
|
|
509
|
+
expandedAgents.planner && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-prompt-body" },
|
|
510
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("textarea", { className: "jp-admin-textarea", value: agentPrompts.planner || '', onChange: (e) => setAgentPrompts(prev => ({ ...prev, planner: e.target.value })), rows: 6 }),
|
|
511
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-admin-btn-reset", onClick: () => defaultPrompts && setAgentPrompts(prev => ({ ...prev, planner: defaultPrompts.planner })), disabled: isLoadingPrompts }, isLoadingPrompts ? '로딩중...' : '기본값 복원')))),
|
|
512
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-prompt-card" },
|
|
513
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-prompt-header", onClick: () => setExpandedAgents(prev => ({ ...prev, python_developer: !prev.python_developer })) },
|
|
514
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-prompt-icon" },
|
|
515
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_Code__WEBPACK_IMPORTED_MODULE_4__["default"], { sx: { fontSize: 18 } })),
|
|
516
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "Python Developer"),
|
|
517
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-prompt-chevron" }, expandedAgents.python_developer ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ExpandMore__WEBPACK_IMPORTED_MODULE_8__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ChevronRight__WEBPACK_IMPORTED_MODULE_9__["default"], null))),
|
|
518
|
+
expandedAgents.python_developer && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-prompt-body" },
|
|
519
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("textarea", { className: "jp-admin-textarea", value: agentPrompts.python_developer || '', onChange: (e) => setAgentPrompts(prev => ({ ...prev, python_developer: e.target.value })), rows: 6 }),
|
|
520
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-admin-btn-reset", onClick: () => defaultPrompts && setAgentPrompts(prev => ({ ...prev, python_developer: defaultPrompts.python_developer })), disabled: isLoadingPrompts }, isLoadingPrompts ? '로딩중...' : '기본값 복원')))),
|
|
521
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-prompt-card" },
|
|
522
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-prompt-header", onClick: () => setExpandedAgents(prev => ({ ...prev, researcher: !prev.researcher })) },
|
|
523
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-prompt-icon" },
|
|
524
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_Search__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { fontSize: 18 } })),
|
|
525
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "Researcher"),
|
|
526
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-prompt-chevron" }, expandedAgents.researcher ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ExpandMore__WEBPACK_IMPORTED_MODULE_8__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ChevronRight__WEBPACK_IMPORTED_MODULE_9__["default"], null))),
|
|
527
|
+
expandedAgents.researcher && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-prompt-body" },
|
|
528
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("textarea", { className: "jp-admin-textarea", value: agentPrompts.researcher || '', onChange: (e) => setAgentPrompts(prev => ({ ...prev, researcher: e.target.value })), rows: 6 }),
|
|
529
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-admin-btn-reset", onClick: () => defaultPrompts && setAgentPrompts(prev => ({ ...prev, researcher: defaultPrompts.researcher })), disabled: isLoadingPrompts }, isLoadingPrompts ? '로딩중...' : '기본값 복원')))),
|
|
530
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-prompt-card" },
|
|
531
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-prompt-header", onClick: () => setExpandedAgents(prev => ({ ...prev, athena_query: !prev.athena_query })) },
|
|
532
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-prompt-icon" },
|
|
533
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_Analytics__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { fontSize: 18 } })),
|
|
534
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "Athena Query"),
|
|
535
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-admin-prompt-chevron" }, expandedAgents.athena_query ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ExpandMore__WEBPACK_IMPORTED_MODULE_8__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ChevronRight__WEBPACK_IMPORTED_MODULE_9__["default"], null))),
|
|
536
|
+
expandedAgents.athena_query && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-admin-prompt-body" },
|
|
537
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("textarea", { className: "jp-admin-textarea", value: agentPrompts.athena_query || '', onChange: (e) => setAgentPrompts(prev => ({ ...prev, athena_query: e.target.value })), rows: 6 }),
|
|
538
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-admin-btn-reset", onClick: () => defaultPrompts && setAgentPrompts(prev => ({ ...prev, athena_query: defaultPrompts.athena_query })), disabled: isLoadingPrompts }, isLoadingPrompts ? '로딩중...' : '기본값 복원')))),
|
|
539
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-admin-btn-reset-all", onClick: () => {
|
|
540
|
+
if (defaultPrompts) {
|
|
541
|
+
setAgentPrompts({
|
|
542
|
+
planner: defaultPrompts.planner,
|
|
543
|
+
python_developer: defaultPrompts.python_developer,
|
|
544
|
+
researcher: defaultPrompts.researcher,
|
|
545
|
+
athena_query: defaultPrompts.athena_query,
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
}, disabled: isLoadingPrompts }, isLoadingPrompts ? '로딩중...' : '모든 프롬프트 기본값 복원'))))),
|
|
549
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-footer" },
|
|
550
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-settings-button jp-agent-settings-button-secondary", onClick: onClose }, "\uCDE8\uC18C"),
|
|
551
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-settings-button jp-agent-settings-button-test", onClick: handleTest, disabled: isTesting }, isTesting ? '테스트 중...' : 'API 테스트'),
|
|
552
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-settings-button jp-agent-settings-button-primary", onClick: handleSave, disabled: isSaving }, isSaving ? '저장 중...' : '저장')))));
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
/***/ },
|
|
557
|
+
|
|
4
558
|
/***/ "./lib/components/AgentPanel.js"
|
|
5
559
|
/*!**************************************!*\
|
|
6
560
|
!*** ./lib/components/AgentPanel.js ***!
|
|
@@ -18,15 +572,17 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
18
572
|
/* harmony import */ var _jupyterlab_notebook__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @jupyterlab/notebook */ "webpack/sharing/consume/default/@jupyterlab/notebook");
|
|
19
573
|
/* harmony import */ var _jupyterlab_notebook__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_jupyterlab_notebook__WEBPACK_IMPORTED_MODULE_2__);
|
|
20
574
|
/* harmony import */ var _SettingsPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./SettingsPanel */ "./lib/components/SettingsPanel.js");
|
|
21
|
-
/* harmony import */ var
|
|
22
|
-
/* harmony import */ var
|
|
23
|
-
/* harmony import */ var
|
|
24
|
-
/* harmony import */ var
|
|
25
|
-
/* harmony import */ var
|
|
26
|
-
/* harmony import */ var
|
|
27
|
-
/* harmony import */ var
|
|
28
|
-
/* harmony import */ var
|
|
29
|
-
/* harmony import */ var
|
|
575
|
+
/* harmony import */ var _AdminSettingsPanel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./AdminSettingsPanel */ "./lib/components/AdminSettingsPanel.js");
|
|
576
|
+
/* harmony import */ var _UserSettingsPanel__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./UserSettingsPanel */ "./lib/components/UserSettingsPanel.js");
|
|
577
|
+
/* harmony import */ var _services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../services/ApiKeyManager */ "./lib/services/ApiKeyManager.js");
|
|
578
|
+
/* harmony import */ var _utils_markdownRenderer__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../utils/markdownRenderer */ "./lib/utils/markdownRenderer.js");
|
|
579
|
+
/* harmony import */ var _FileSelectionDialog__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./FileSelectionDialog */ "./lib/components/FileSelectionDialog.js");
|
|
580
|
+
/* harmony import */ var _StreamingMessage__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./StreamingMessage */ "./lib/components/StreamingMessage.js");
|
|
581
|
+
/* harmony import */ var _ContextAutocomplete__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./ContextAutocomplete */ "./lib/components/ContextAutocomplete.js");
|
|
582
|
+
/* harmony import */ var _utils_icons__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../utils/icons */ "./lib/utils/icons.js");
|
|
583
|
+
/* harmony import */ var _mui_icons_material_ExpandLess__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! @mui/icons-material/ExpandLess */ "./node_modules/@mui/icons-material/esm/ExpandLess.js");
|
|
584
|
+
/* harmony import */ var _mui_icons_material_ExpandMore__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! @mui/icons-material/ExpandMore */ "./node_modules/@mui/icons-material/esm/ExpandMore.js");
|
|
585
|
+
/* harmony import */ var _logoSvg__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../logoSvg */ "./lib/logoSvg.js");
|
|
30
586
|
/**
|
|
31
587
|
* Agent Panel - Main sidebar panel for Jupyter Agent
|
|
32
588
|
* Cursor AI Style: Unified Chat + Agent Interface
|
|
@@ -34,6 +590,8 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
34
590
|
|
|
35
591
|
|
|
36
592
|
|
|
593
|
+
// Fallback for legacy mode
|
|
594
|
+
|
|
37
595
|
|
|
38
596
|
|
|
39
597
|
|
|
@@ -48,7 +606,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
48
606
|
// 탭바 아이콘 생성
|
|
49
607
|
const hdspTabIcon = new _jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_1__.LabIcon({
|
|
50
608
|
name: 'hdsp-agent:tab-icon',
|
|
51
|
-
svgstr:
|
|
609
|
+
svgstr: _logoSvg__WEBPACK_IMPORTED_MODULE_14__.tabbarLogoSvg
|
|
52
610
|
});
|
|
53
611
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
54
612
|
// Python 파일 에러 감지 및 처리 유틸리티
|
|
@@ -156,9 +714,12 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
156
714
|
const [streamingMessageId, setStreamingMessageId] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
157
715
|
const [conversationId, setConversationId] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
|
|
158
716
|
const [showSettings, setShowSettings] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
717
|
+
const [isAdmin, setIsAdmin] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); // null = loading, false/true = resolved
|
|
159
718
|
const [llmConfig, setLlmConfig] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
160
719
|
// Agent 실행 상태
|
|
161
720
|
const [isAgentRunning, setIsAgentRunning] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
721
|
+
// Compact 압축 상태
|
|
722
|
+
const [isCompacting, setIsCompacting] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
162
723
|
// 입력 모드 (Cursor AI 스타일) - 로컬 스토리지에서 복원
|
|
163
724
|
// Note: 'agent_v2'는 'agent'로 마이그레이션
|
|
164
725
|
const [inputMode, setInputMode] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
|
|
@@ -249,6 +810,8 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
249
810
|
// Todo list state (from TodoListMiddleware)
|
|
250
811
|
const [todos, setTodos] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);
|
|
251
812
|
const [isTodoExpanded, setIsTodoExpanded] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
813
|
+
// When true, render in_progress todos as cancelled (survives late SSE overwrites)
|
|
814
|
+
const [isTodoStopped, setIsTodoStopped] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
252
815
|
// Agent thread ID for context persistence across cycles (SummarizationMiddleware support)
|
|
253
816
|
const [agentThreadId, setAgentThreadId] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
254
817
|
// AbortController for stopping long-running tasks
|
|
@@ -603,10 +1166,30 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
603
1166
|
setInputMode(mode);
|
|
604
1167
|
}
|
|
605
1168
|
}));
|
|
606
|
-
// Load config on mount
|
|
1169
|
+
// Load config and check admin mode on mount
|
|
607
1170
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
608
1171
|
loadConfig();
|
|
1172
|
+
// Check admin mode for settings panel selection
|
|
1173
|
+
const checkAdminMode = async () => {
|
|
1174
|
+
try {
|
|
1175
|
+
const result = await apiService.checkIsAdmin();
|
|
1176
|
+
setIsAdmin(result.isAdmin);
|
|
1177
|
+
console.log('[AgentPanel] Admin mode check:', result.isAdmin, result.reason);
|
|
1178
|
+
}
|
|
1179
|
+
catch (err) {
|
|
1180
|
+
console.warn('[AgentPanel] Failed to check admin mode, defaulting to user mode:', err);
|
|
1181
|
+
setIsAdmin(false); // Default to user mode on error
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1184
|
+
checkAdminMode();
|
|
609
1185
|
}, []);
|
|
1186
|
+
// Force chat mode for non-admin users (agent mode is admin-only for now)
|
|
1187
|
+
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
1188
|
+
if (isAdmin === false && inputMode !== 'chat') {
|
|
1189
|
+
console.log('[AgentPanel] Non-admin user detected, forcing chat mode');
|
|
1190
|
+
setInputMode('chat');
|
|
1191
|
+
}
|
|
1192
|
+
}, [isAdmin]);
|
|
610
1193
|
// Reset expand state when debug status changes
|
|
611
1194
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
612
1195
|
setIsDebugExpanded(false);
|
|
@@ -706,10 +1289,10 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
706
1289
|
// Remove lastActiveNotebook state - just use currentWidget directly
|
|
707
1290
|
const loadConfig = () => {
|
|
708
1291
|
// Load from localStorage using ApiKeyManager
|
|
709
|
-
const config = (0,
|
|
1292
|
+
const config = (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.getLLMConfig)();
|
|
710
1293
|
if (!config) {
|
|
711
1294
|
console.log('[AgentPanel] No config in localStorage, using default');
|
|
712
|
-
const defaultConfig = (0,
|
|
1295
|
+
const defaultConfig = (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.getDefaultLLMConfig)();
|
|
713
1296
|
setLlmConfig(defaultConfig);
|
|
714
1297
|
return;
|
|
715
1298
|
}
|
|
@@ -923,9 +1506,9 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
923
1506
|
const handleSaveConfig = (config) => {
|
|
924
1507
|
console.log('[AgentPanel] Saving config to localStorage');
|
|
925
1508
|
console.log('Provider:', config.provider);
|
|
926
|
-
console.log('API Key configured:', (0,
|
|
1509
|
+
console.log('API Key configured:', (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.hasValidApiKey)(config) ? '✓ Yes' : '✗ No');
|
|
927
1510
|
// Save to localStorage using ApiKeyManager
|
|
928
|
-
(0,
|
|
1511
|
+
(0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.saveLLMConfig)(config);
|
|
929
1512
|
// Update state
|
|
930
1513
|
setLlmConfig(config);
|
|
931
1514
|
console.log('[AgentPanel] Config saved successfully');
|
|
@@ -963,7 +1546,15 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
963
1546
|
*/
|
|
964
1547
|
const stopCurrentTask = async () => {
|
|
965
1548
|
console.log('[AgentPanel] Stopping current task...');
|
|
966
|
-
// 1.
|
|
1549
|
+
// 1. Mark todos as stopped — rendering will show in_progress as cancelled
|
|
1550
|
+
setIsTodoStopped(true);
|
|
1551
|
+
// 2. Abort any ongoing fetch requests
|
|
1552
|
+
if (abortControllerRef.current) {
|
|
1553
|
+
abortControllerRef.current.abort();
|
|
1554
|
+
abortControllerRef.current = null;
|
|
1555
|
+
console.log('[AgentPanel] Aborted fetch request');
|
|
1556
|
+
}
|
|
1557
|
+
// 2. Cancel the server-side agent execution
|
|
967
1558
|
if (agentThreadId) {
|
|
968
1559
|
try {
|
|
969
1560
|
const result = await apiService.cancelAgent(agentThreadId);
|
|
@@ -973,12 +1564,6 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
973
1564
|
console.warn('[AgentPanel] Error cancelling server-side agent:', error);
|
|
974
1565
|
}
|
|
975
1566
|
}
|
|
976
|
-
// 2. Abort any ongoing fetch requests
|
|
977
|
-
if (abortControllerRef.current) {
|
|
978
|
-
abortControllerRef.current.abort();
|
|
979
|
-
abortControllerRef.current = null;
|
|
980
|
-
console.log('[AgentPanel] Aborted fetch request');
|
|
981
|
-
}
|
|
982
1567
|
// 3. Interrupt the Jupyter kernel if executing (try all possible kernels)
|
|
983
1568
|
try {
|
|
984
1569
|
// Try notebook kernel first
|
|
@@ -1014,7 +1599,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
1014
1599
|
setAgentThreadId(null);
|
|
1015
1600
|
// 6. Update current in_progress todo to show it was stopped
|
|
1016
1601
|
setTodos(prev => prev.map(todo => todo.status === 'in_progress'
|
|
1017
|
-
? { ...todo,
|
|
1602
|
+
? { ...todo, status: 'cancelled' }
|
|
1018
1603
|
: todo));
|
|
1019
1604
|
// Show notification
|
|
1020
1605
|
showNotification('작업이 중단되었습니다.', 'info');
|
|
@@ -1022,15 +1607,25 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
1022
1607
|
};
|
|
1023
1608
|
// Auto-scroll state refs for smooth scrolling
|
|
1024
1609
|
const scrollRafRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
|
|
1025
|
-
const
|
|
1610
|
+
const lastScrollHeightRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(0);
|
|
1611
|
+
const wasStreamingRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);
|
|
1026
1612
|
const userScrolledUpRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);
|
|
1613
|
+
// Flag to suppress scroll-event detection during programmatic scrolls
|
|
1614
|
+
const isProgrammaticScrollRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);
|
|
1027
1615
|
// Track user scroll to detect if they scrolled up
|
|
1616
|
+
// During streaming, only real user scroll (wheel/touch) should disable auto-scroll.
|
|
1617
|
+
// Programmatic scrolls and rendering-induced scroll events are ignored.
|
|
1028
1618
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
1029
1619
|
const container = messagesContainerRef.current;
|
|
1030
1620
|
if (!container)
|
|
1031
1621
|
return;
|
|
1032
1622
|
const handleScroll = () => {
|
|
1033
|
-
|
|
1623
|
+
// Skip detection if this was triggered by our programmatic scroll
|
|
1624
|
+
if (isProgrammaticScrollRef.current) {
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
// Use wider threshold (200px) to tolerate rendering height fluctuations
|
|
1628
|
+
const isNearBottom = container.scrollHeight - container.scrollTop - container.clientHeight < 200;
|
|
1034
1629
|
userScrolledUpRef.current = !isNearBottom;
|
|
1035
1630
|
};
|
|
1036
1631
|
container.addEventListener('scroll', handleScroll, { passive: true });
|
|
@@ -1041,38 +1636,58 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
1041
1636
|
const container = messagesContainerRef.current;
|
|
1042
1637
|
if (!container)
|
|
1043
1638
|
return;
|
|
1044
|
-
//
|
|
1639
|
+
// 스트리밍 종료 감지: 이전에 스트리밍 중이었고 지금은 아닌 경우
|
|
1640
|
+
const streamingJustEnded = wasStreamingRef.current && !isStreaming;
|
|
1641
|
+
wasStreamingRef.current = isStreaming;
|
|
1642
|
+
// If user scrolled up, don't auto-scroll (단, 스트리밍 종료 시는 예외)
|
|
1045
1643
|
if (userScrolledUpRef.current && isStreaming) {
|
|
1046
1644
|
return;
|
|
1047
1645
|
}
|
|
1048
|
-
//
|
|
1049
|
-
const
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1646
|
+
// Use wider threshold during streaming (rendering can cause temporary height changes)
|
|
1647
|
+
const threshold = isStreaming ? 200 : 100;
|
|
1648
|
+
const isNearBottom = container.scrollHeight - container.scrollTop - container.clientHeight < threshold;
|
|
1649
|
+
if (isStreaming) {
|
|
1650
|
+
// 스트리밍 중: 높이가 20px 이상 변화했을 때만 스크롤
|
|
1651
|
+
const scrollHeight = container.scrollHeight;
|
|
1652
|
+
const heightChanged = scrollHeight - lastScrollHeightRef.current > 20;
|
|
1653
|
+
if (heightChanged) {
|
|
1054
1654
|
if (scrollRafRef.current) {
|
|
1055
1655
|
cancelAnimationFrame(scrollRafRef.current);
|
|
1056
1656
|
}
|
|
1057
1657
|
scrollRafRef.current = requestAnimationFrame(() => {
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
}
|
|
1658
|
+
isProgrammaticScrollRef.current = true;
|
|
1659
|
+
container.scrollTo({
|
|
1660
|
+
top: container.scrollHeight,
|
|
1661
|
+
behavior: 'smooth'
|
|
1662
|
+
});
|
|
1064
1663
|
scrollRafRef.current = null;
|
|
1664
|
+
setTimeout(() => {
|
|
1665
|
+
isProgrammaticScrollRef.current = false;
|
|
1666
|
+
}, 150);
|
|
1065
1667
|
});
|
|
1668
|
+
lastScrollHeightRef.current = scrollHeight;
|
|
1066
1669
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1670
|
+
}
|
|
1671
|
+
else if (streamingJustEnded) {
|
|
1672
|
+
// 스트리밍 종료 시: 약간의 딜레이 후 최종 스크롤 (렌더링 완료 대기)
|
|
1673
|
+
console.log('[AutoScroll] Streaming ended, scrolling to bottom');
|
|
1674
|
+
setTimeout(() => {
|
|
1675
|
+
if (container) {
|
|
1676
|
+
container.scrollTo({
|
|
1677
|
+
top: container.scrollHeight,
|
|
1678
|
+
behavior: 'smooth'
|
|
1679
|
+
});
|
|
1680
|
+
}
|
|
1681
|
+
}, 100);
|
|
1682
|
+
userScrolledUpRef.current = false;
|
|
1683
|
+
lastScrollHeightRef.current = 0; // 리셋
|
|
1684
|
+
}
|
|
1685
|
+
else if (isNearBottom) {
|
|
1686
|
+
// 하단 근처일 때: 스크롤
|
|
1687
|
+
container.scrollTo({
|
|
1688
|
+
top: container.scrollHeight,
|
|
1689
|
+
behavior: 'smooth'
|
|
1690
|
+
});
|
|
1076
1691
|
}
|
|
1077
1692
|
// Cleanup RAF on unmount
|
|
1078
1693
|
return () => {
|
|
@@ -1108,11 +1723,11 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
1108
1723
|
let currentConfig = llmConfig;
|
|
1109
1724
|
if (!currentConfig) {
|
|
1110
1725
|
// Config not loaded yet, try to load from localStorage
|
|
1111
|
-
currentConfig = (0,
|
|
1726
|
+
currentConfig = (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.getLLMConfig)() || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.getDefaultLLMConfig)();
|
|
1112
1727
|
setLlmConfig(currentConfig);
|
|
1113
1728
|
}
|
|
1114
1729
|
// Check API key using ApiKeyManager
|
|
1115
|
-
const hasApiKey = (0,
|
|
1730
|
+
const hasApiKey = (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.hasValidApiKey)(currentConfig);
|
|
1116
1731
|
const autoApproveEnabled = getAutoApproveEnabled(currentConfig);
|
|
1117
1732
|
if (!hasApiKey) {
|
|
1118
1733
|
// Show error message and open settings
|
|
@@ -1139,6 +1754,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
1139
1754
|
}
|
|
1140
1755
|
setIsLoading(true);
|
|
1141
1756
|
setIsStreaming(true);
|
|
1757
|
+
setIsTodoStopped(false);
|
|
1142
1758
|
// Clear todos only when starting a new task (all completed or no todos)
|
|
1143
1759
|
// Keep todos if there are pending/in_progress items (continuation of current task)
|
|
1144
1760
|
const hasActiveTodos = todos.some(t => t.status === 'pending' || t.status === 'in_progress');
|
|
@@ -1147,9 +1763,11 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
1147
1763
|
}
|
|
1148
1764
|
setDebugStatus(null);
|
|
1149
1765
|
// Clear the data attribute and ref after using it
|
|
1150
|
-
if (
|
|
1151
|
-
textarea.removeAttribute('data-llm-prompt');
|
|
1766
|
+
if (llmPrompt) {
|
|
1152
1767
|
pendingLlmPromptRef.current = null;
|
|
1768
|
+
if (textarea) {
|
|
1769
|
+
textarea.removeAttribute('data-llm-prompt');
|
|
1770
|
+
}
|
|
1153
1771
|
}
|
|
1154
1772
|
// Create assistant message ID for streaming updates
|
|
1155
1773
|
const assistantMessageId = makeMessageId('assistant');
|
|
@@ -1192,7 +1810,11 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
1192
1810
|
}
|
|
1193
1811
|
},
|
|
1194
1812
|
// AbortSignal for stopping the stream
|
|
1195
|
-
abortControllerRef.current.signal
|
|
1813
|
+
abortControllerRef.current.signal,
|
|
1814
|
+
// onDebug callback for status messages (auto-compact, etc.)
|
|
1815
|
+
(status) => {
|
|
1816
|
+
setDebugStatus(status);
|
|
1817
|
+
});
|
|
1196
1818
|
}
|
|
1197
1819
|
else {
|
|
1198
1820
|
// Agent V2 모드 - /agent/langchain/stream 사용 (HITL, Todo, 도구 실행)
|
|
@@ -2159,7 +2781,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
2159
2781
|
};
|
|
2160
2782
|
}
|
|
2161
2783
|
};
|
|
2162
|
-
const executeCodeViaSubprocess = async (code, timeout) => {
|
|
2784
|
+
const executeCodeViaSubprocess = async (code, timeout, onOutput) => {
|
|
2163
2785
|
const isShell = isShellCell(code);
|
|
2164
2786
|
const command = isShell ? extractShellCommand(code) : buildPythonCommand(code);
|
|
2165
2787
|
if (!command) {
|
|
@@ -2173,7 +2795,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
2173
2795
|
execution_method: 'subprocess'
|
|
2174
2796
|
};
|
|
2175
2797
|
}
|
|
2176
|
-
const result = await executeSubprocessCommand(command, timeout);
|
|
2798
|
+
const result = await executeSubprocessCommand(command, timeout, onOutput);
|
|
2177
2799
|
return {
|
|
2178
2800
|
...result,
|
|
2179
2801
|
execution_method: 'subprocess'
|
|
@@ -2469,7 +3091,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
2469
3091
|
else if (action === 'search_files_tool') {
|
|
2470
3092
|
setDebugStatus({ status: `파일 검색 중: ${args?.pattern || ''}`, icon: 'search' });
|
|
2471
3093
|
try {
|
|
2472
|
-
const currentLlmConfig = llmConfig || (0,
|
|
3094
|
+
const currentLlmConfig = llmConfig || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.getLLMConfig)() || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.getDefaultLLMConfig)();
|
|
2473
3095
|
const searchResult = await apiService.searchWorkspace({
|
|
2474
3096
|
pattern: args?.pattern || '',
|
|
2475
3097
|
file_types: args?.file_pattern ? [args.file_pattern] : ['*'],
|
|
@@ -2532,7 +3154,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
2532
3154
|
}, (nextInterrupt) => {
|
|
2533
3155
|
interrupted = true;
|
|
2534
3156
|
approvalPendingRef.current = true;
|
|
2535
|
-
const autoApproveEnabled = getAutoApproveEnabled(llmConfig || (0,
|
|
3157
|
+
const autoApproveEnabled = getAutoApproveEnabled(llmConfig || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.getLLMConfig)() || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.getDefaultLLMConfig)());
|
|
2536
3158
|
// Handle next interrupt (could be another search or code execution)
|
|
2537
3159
|
if (nextInterrupt.action === 'search_notebook_cells_tool'
|
|
2538
3160
|
|| nextInterrupt.action === 'check_resource_tool'
|
|
@@ -2959,7 +3581,11 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
2959
3581
|
const code = interrupt.args.code;
|
|
2960
3582
|
if (!shouldExecuteInNotebook(code)) {
|
|
2961
3583
|
setDebugStatus({ status: '서브프로세스로 코드 실행 중...', icon: 'terminal' });
|
|
2962
|
-
const
|
|
3584
|
+
const subCmd = isShellCell(code) ? extractShellCommand(code) : buildPythonCommand(code);
|
|
3585
|
+
const subOutputId = subCmd ? createCommandOutputMessage(subCmd) : null;
|
|
3586
|
+
const execResult = await executeCodeViaSubprocess(code, interrupt.args?.timeout, subOutputId
|
|
3587
|
+
? (chunk) => appendCommandOutputMessage(subOutputId, chunk.text, chunk.stream)
|
|
3588
|
+
: undefined);
|
|
2963
3589
|
resumeDecision = 'edit';
|
|
2964
3590
|
resumeArgs = {
|
|
2965
3591
|
code,
|
|
@@ -2979,7 +3605,11 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
2979
3605
|
const notebook = getActiveNotebookPanel();
|
|
2980
3606
|
if (!notebook) {
|
|
2981
3607
|
setDebugStatus({ status: '노트북이 없어 서브프로세스로 실행 중...', icon: 'terminal' });
|
|
2982
|
-
const
|
|
3608
|
+
const nbCmd = isShellCell(code) ? extractShellCommand(code) : buildPythonCommand(code);
|
|
3609
|
+
const nbOutputId = nbCmd ? createCommandOutputMessage(nbCmd) : null;
|
|
3610
|
+
const execResult = await executeCodeViaSubprocess(code, interrupt.args?.timeout, nbOutputId
|
|
3611
|
+
? (chunk) => appendCommandOutputMessage(nbOutputId, chunk.text, chunk.stream)
|
|
3612
|
+
: undefined);
|
|
2983
3613
|
resumeDecision = 'edit';
|
|
2984
3614
|
resumeArgs = {
|
|
2985
3615
|
code,
|
|
@@ -2990,7 +3620,11 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
2990
3620
|
const cellIndex = insertCell(notebook, 'code', code);
|
|
2991
3621
|
if (cellIndex === null) {
|
|
2992
3622
|
setDebugStatus({ status: '노트북 모델이 없어 서브프로세스로 실행 중...', icon: 'terminal' });
|
|
2993
|
-
const
|
|
3623
|
+
const mdlCmd = isShellCell(code) ? extractShellCommand(code) : buildPythonCommand(code);
|
|
3624
|
+
const mdlOutputId = mdlCmd ? createCommandOutputMessage(mdlCmd) : null;
|
|
3625
|
+
const execResult = await executeCodeViaSubprocess(code, interrupt.args?.timeout, mdlOutputId
|
|
3626
|
+
? (chunk) => appendCommandOutputMessage(mdlOutputId, chunk.text, chunk.stream)
|
|
3627
|
+
: undefined);
|
|
2994
3628
|
resumeDecision = 'edit';
|
|
2995
3629
|
resumeArgs = {
|
|
2996
3630
|
code,
|
|
@@ -3081,7 +3715,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
3081
3715
|
}, (nextInterrupt) => {
|
|
3082
3716
|
interrupted = true;
|
|
3083
3717
|
approvalPendingRef.current = true;
|
|
3084
|
-
const autoApproveEnabled = getAutoApproveEnabled(llmConfig || (0,
|
|
3718
|
+
const autoApproveEnabled = getAutoApproveEnabled(llmConfig || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.getLLMConfig)() || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.getDefaultLLMConfig)());
|
|
3085
3719
|
// Auto-approve search/file/resource tools
|
|
3086
3720
|
if (nextInterrupt.action === 'search_notebook_cells_tool'
|
|
3087
3721
|
|| nextInterrupt.action === 'check_resource_tool'
|
|
@@ -3391,19 +4025,16 @@ SyntaxError: '(' was never closed
|
|
|
3391
4025
|
const newCursorPosition = e.target.selectionStart || 0;
|
|
3392
4026
|
setInput(newValue);
|
|
3393
4027
|
setCursorPosition(newCursorPosition);
|
|
3394
|
-
// Check if we should show autocomplete (when typing @ or
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
else {
|
|
3405
|
-
setShowAutocomplete(false);
|
|
3406
|
-
}
|
|
4028
|
+
// Check if we should show autocomplete (when typing @ or / commands)
|
|
4029
|
+
// Find the start of the current word (from cursor back to whitespace)
|
|
4030
|
+
let wordStart = newCursorPosition;
|
|
4031
|
+
while (wordStart > 0 && !/\s/.test(newValue[wordStart - 1])) {
|
|
4032
|
+
wordStart--;
|
|
4033
|
+
}
|
|
4034
|
+
const currentWord = newValue.slice(wordStart, newCursorPosition);
|
|
4035
|
+
// Show autocomplete if current word starts with @ or /
|
|
4036
|
+
if (currentWord.startsWith('@') || currentWord.startsWith('/')) {
|
|
4037
|
+
setShowAutocomplete(true);
|
|
3407
4038
|
}
|
|
3408
4039
|
else {
|
|
3409
4040
|
setShowAutocomplete(false);
|
|
@@ -3412,8 +4043,8 @@ SyntaxError: '(' was never closed
|
|
|
3412
4043
|
// Handle autocomplete selection
|
|
3413
4044
|
// viaEnter: true = Enter key or click (final selection), false = Tab key (navigation)
|
|
3414
4045
|
const handleAutocompleteSelect = async (option, viaEnter) => {
|
|
3415
|
-
// Check if this is an action command (like
|
|
3416
|
-
if (option.id === '
|
|
4046
|
+
// Check if this is an action command (like /reset, /compact)
|
|
4047
|
+
if (option.id === '/reset' && option.is_complete) {
|
|
3417
4048
|
// Execute reset action
|
|
3418
4049
|
await executeResetAction();
|
|
3419
4050
|
setShowAutocomplete(false);
|
|
@@ -3421,6 +4052,23 @@ SyntaxError: '(' was never closed
|
|
|
3421
4052
|
setInput('');
|
|
3422
4053
|
return;
|
|
3423
4054
|
}
|
|
4055
|
+
if (option.id === '/compact' && option.is_complete) {
|
|
4056
|
+
// Hide autocomplete immediately before async operation
|
|
4057
|
+
setShowAutocomplete(false);
|
|
4058
|
+
setHasAutocompleteOptions(false);
|
|
4059
|
+
setInput('');
|
|
4060
|
+
// Execute compact action
|
|
4061
|
+
await executeCompactAction();
|
|
4062
|
+
return;
|
|
4063
|
+
}
|
|
4064
|
+
if (option.id === '/help' && option.is_complete) {
|
|
4065
|
+
// Execute help action (no async, just display help message)
|
|
4066
|
+
executeHelpAction();
|
|
4067
|
+
setShowAutocomplete(false);
|
|
4068
|
+
setHasAutocompleteOptions(false);
|
|
4069
|
+
setInput('');
|
|
4070
|
+
return;
|
|
4071
|
+
}
|
|
3424
4072
|
if (!textareaRef.current)
|
|
3425
4073
|
return;
|
|
3426
4074
|
const text = input;
|
|
@@ -3453,7 +4101,7 @@ SyntaxError: '(' was never closed
|
|
|
3453
4101
|
}
|
|
3454
4102
|
}, 0);
|
|
3455
4103
|
};
|
|
3456
|
-
// Execute
|
|
4104
|
+
// Execute /reset action
|
|
3457
4105
|
const executeResetAction = async () => {
|
|
3458
4106
|
try {
|
|
3459
4107
|
// Reset both agent thread and conversation
|
|
@@ -3494,6 +4142,141 @@ SyntaxError: '(' was never closed
|
|
|
3494
4142
|
setMessages(prev => [...prev, errorMessage]);
|
|
3495
4143
|
}
|
|
3496
4144
|
};
|
|
4145
|
+
// Execute /compact action - LLM-based conversation summarization
|
|
4146
|
+
const executeCompactAction = async () => {
|
|
4147
|
+
const convId = agentThreadId || conversationId;
|
|
4148
|
+
if (!convId) {
|
|
4149
|
+
const noSessionMessage = {
|
|
4150
|
+
id: `compact-info-${Date.now()}`,
|
|
4151
|
+
role: 'assistant',
|
|
4152
|
+
content: '세션이 없습니다. 대화를 먼저 시작해주세요.',
|
|
4153
|
+
timestamp: Date.now(),
|
|
4154
|
+
};
|
|
4155
|
+
setMessages(prev => [...prev, noSessionMessage]);
|
|
4156
|
+
return;
|
|
4157
|
+
}
|
|
4158
|
+
try {
|
|
4159
|
+
setIsCompacting(true);
|
|
4160
|
+
// Show compacting status (same as auto-compact)
|
|
4161
|
+
setDebugStatus({ status: '대화 컨텍스트 요약 중...', icon: 'thinking' });
|
|
4162
|
+
// Call compact API
|
|
4163
|
+
const result = await apiService.compactConversation(convId, llmConfig || undefined);
|
|
4164
|
+
if (result.success) {
|
|
4165
|
+
// Show completion status with message count (Claude Code style - status only, no chat message)
|
|
4166
|
+
const statusMsg = result.originalMessages && result.compressedMessages
|
|
4167
|
+
? `대화가 압축되었습니다. (${result.originalMessages}개 → ${result.compressedMessages}개 메시지)`
|
|
4168
|
+
: '대화가 압축되었습니다.';
|
|
4169
|
+
setDebugStatus({ status: statusMsg, icon: 'check' });
|
|
4170
|
+
// Clear status after delay (summary stays in context only, not shown to user)
|
|
4171
|
+
setTimeout(() => setDebugStatus(null), 3000);
|
|
4172
|
+
}
|
|
4173
|
+
else {
|
|
4174
|
+
// Add error message
|
|
4175
|
+
setDebugStatus(null);
|
|
4176
|
+
const errorMessage = {
|
|
4177
|
+
id: `compact-error-${Date.now()}`,
|
|
4178
|
+
role: 'assistant',
|
|
4179
|
+
content: result.message,
|
|
4180
|
+
timestamp: Date.now(),
|
|
4181
|
+
};
|
|
4182
|
+
setMessages(prev => [...prev, errorMessage]);
|
|
4183
|
+
}
|
|
4184
|
+
console.log('[AgentPanel] Compact complete:', result);
|
|
4185
|
+
}
|
|
4186
|
+
catch (error) {
|
|
4187
|
+
console.error('[AgentPanel] Failed to compact conversation:', error);
|
|
4188
|
+
// Clear status and show error
|
|
4189
|
+
setDebugStatus(null);
|
|
4190
|
+
const errorMessage = {
|
|
4191
|
+
id: `compact-error-${Date.now()}`,
|
|
4192
|
+
role: 'assistant',
|
|
4193
|
+
content: `압축 실패: ${error}`,
|
|
4194
|
+
timestamp: Date.now(),
|
|
4195
|
+
};
|
|
4196
|
+
setMessages(prev => [...prev, errorMessage]);
|
|
4197
|
+
}
|
|
4198
|
+
finally {
|
|
4199
|
+
setIsCompacting(false);
|
|
4200
|
+
}
|
|
4201
|
+
};
|
|
4202
|
+
// Execute /help action - Display help guide (no LLM call)
|
|
4203
|
+
const executeHelpAction = () => {
|
|
4204
|
+
const helpContent = `## HDSP Agent 사용 가이드
|
|
4205
|
+
|
|
4206
|
+
### 사용 가능한 명령어
|
|
4207
|
+
|
|
4208
|
+
| 명령어 | 설명 |
|
|
4209
|
+
|--------|------|
|
|
4210
|
+
| \`/help\` | 이 도움말 표시 |
|
|
4211
|
+
| \`/reset\` | 세션 초기화 및 대화 기록 삭제 |
|
|
4212
|
+
| \`/compact\` | 대화 기록 요약 (컨텍스트 절약) |
|
|
4213
|
+
| \`@file:경로\` | 파일 내용을 프롬프트에 포함 |
|
|
4214
|
+
|
|
4215
|
+
### 현재 사용 가능한 기능
|
|
4216
|
+
|
|
4217
|
+
현재는 **Chat 모드**만 사용 가능합니다. Chat 모드에서는 AI와 자유롭게 대화하며 질문하고 답변을 받을 수 있습니다.
|
|
4218
|
+
|
|
4219
|
+
### Agent 모드 (출시 예정!)
|
|
4220
|
+
|
|
4221
|
+
**Agent 모드**가 곧 오픈될 예정입니다! Agent 모드에서는 AI가 단순 대화를 넘어 실제 작업을 수행합니다:
|
|
4222
|
+
|
|
4223
|
+
- **코드 자동 실행**: 데이터 분석, 시각화, 모델 학습 등을 직접 실행
|
|
4224
|
+
- **파일 읽기/수정**: 프로젝트 파일을 읽고 수정하여 코드 개선
|
|
4225
|
+
- **셸 명령 실행**: 패키지 설치, 스크립트 실행 등 터미널 작업 수행
|
|
4226
|
+
- **멀티스텝 작업**: 복잡한 작업을 여러 단계로 나눠 자동 처리
|
|
4227
|
+
- **오류 자동 수정**: 코드 실행 중 오류 발생 시 자동으로 분석하고 수정
|
|
4228
|
+
|
|
4229
|
+
기대해 주세요!
|
|
4230
|
+
|
|
4231
|
+
### 노트북 셀 아이콘 버튼
|
|
4232
|
+
|
|
4233
|
+
Jupyter 노트북의 각 코드 셀 옆에 있는 아이콘 버튼을 활용하세요:
|
|
4234
|
+
|
|
4235
|
+
| 아이콘 | 기능 | 설명 |
|
|
4236
|
+
|--------|------|------|
|
|
4237
|
+
| 💬 | **질문하기** | 선택한 셀 코드에 대해 자유롭게 질문 |
|
|
4238
|
+
| ✏️ | **수정 제안** | 코드 개선, 버그 수정, 최적화 제안 요청 |
|
|
4239
|
+
| 📖 | **코드 설명** | 코드의 동작 원리와 로직을 상세히 설명 |
|
|
4240
|
+
|
|
4241
|
+
버튼 클릭 시 해당 셀의 코드가 자동으로 대화창에 포함됩니다.
|
|
4242
|
+
|
|
4243
|
+
### 파일 참조 사용법
|
|
4244
|
+
|
|
4245
|
+
입력창에 \`@file:\`을 입력하면 자동완성으로 파일을 선택할 수 있습니다.
|
|
4246
|
+
|
|
4247
|
+
**예시:**
|
|
4248
|
+
- \`@file:main.py 이 코드를 분석해줘\`
|
|
4249
|
+
- \`@file:src/utils.py 테스트 코드 작성해줘\`
|
|
4250
|
+
|
|
4251
|
+
### 키보드 단축키
|
|
4252
|
+
|
|
4253
|
+
| 단축키 | 동작 |
|
|
4254
|
+
|--------|------|
|
|
4255
|
+
| \`Enter\` | 메시지 전송 |
|
|
4256
|
+
| \`Shift+Enter\` | 줄바꿈 |
|
|
4257
|
+
| \`Escape\` | 스트리밍 중지 / 자동완성 닫기 |
|
|
4258
|
+
|
|
4259
|
+
### 설정
|
|
4260
|
+
|
|
4261
|
+
- 우측 상단 ⚙️ 아이콘으로 설정 패널 열기
|
|
4262
|
+
- **워크스페이스**: 파일 경로 기준 폴더 설정
|
|
4263
|
+
- **Temperature**: LLM 응답 다양성 조절 (0.0 = 일관됨, 1.0+ = 창의적)
|
|
4264
|
+
|
|
4265
|
+
### 팁
|
|
4266
|
+
|
|
4267
|
+
- 긴 대화 시 \`/compact\`로 컨텍스트 절약
|
|
4268
|
+
- 에러 발생 시 \`/reset\`으로 세션 초기화
|
|
4269
|
+
- 코드 분석 시 \`@file:\`로 파일 직접 참조
|
|
4270
|
+
- 노트북 셀의 아이콘 버튼으로 빠르게 코드 질문하기`;
|
|
4271
|
+
const helpMessage = {
|
|
4272
|
+
id: `help-${Date.now()}`,
|
|
4273
|
+
role: 'assistant',
|
|
4274
|
+
content: helpContent,
|
|
4275
|
+
timestamp: Date.now(),
|
|
4276
|
+
};
|
|
4277
|
+
setMessages(prev => [...prev, helpMessage]);
|
|
4278
|
+
console.log('[AgentPanel] Help displayed');
|
|
4279
|
+
};
|
|
3497
4280
|
const handleKeyDown = (e) => {
|
|
3498
4281
|
// If autocomplete is visible AND has options, let it handle Tab/Enter
|
|
3499
4282
|
if (showAutocomplete && hasAutocompleteOptions) {
|
|
@@ -3521,25 +4304,27 @@ SyntaxError: '(' was never closed
|
|
|
3521
4304
|
e.preventDefault();
|
|
3522
4305
|
handleSendMessage();
|
|
3523
4306
|
}
|
|
3524
|
-
// Shift+Tab: 모드 전환 (chat ↔ agent)
|
|
3525
|
-
if (e.key === 'Tab' && e.shiftKey) {
|
|
4307
|
+
// Shift+Tab: 모드 전환 (chat ↔ agent) - admin only
|
|
4308
|
+
if (e.key === 'Tab' && e.shiftKey && isAdmin) {
|
|
3526
4309
|
e.preventDefault();
|
|
3527
4310
|
setInputMode(prev => prev === 'chat' ? 'agent' : 'chat');
|
|
3528
4311
|
return;
|
|
3529
4312
|
}
|
|
3530
|
-
// Cmd/Ctrl + . : 모드 전환 (대체 단축키)
|
|
3531
|
-
if (e.key === '.' && (e.metaKey || e.ctrlKey)) {
|
|
4313
|
+
// Cmd/Ctrl + . : 모드 전환 (대체 단축키) - admin only
|
|
4314
|
+
if (e.key === '.' && (e.metaKey || e.ctrlKey) && isAdmin) {
|
|
3532
4315
|
e.preventDefault();
|
|
3533
4316
|
setInputMode(prev => prev === 'chat' ? 'agent' : 'chat');
|
|
3534
4317
|
}
|
|
3535
|
-
// Tab (without Shift): Agent 모드일 때 드롭다운 토글
|
|
3536
|
-
if (e.key === 'Tab' && !e.shiftKey && inputMode !== 'chat') {
|
|
4318
|
+
// Tab (without Shift): Agent 모드일 때 드롭다운 토글 - admin only
|
|
4319
|
+
if (e.key === 'Tab' && !e.shiftKey && inputMode !== 'chat' && isAdmin) {
|
|
3537
4320
|
e.preventDefault();
|
|
3538
4321
|
setShowModeDropdown(prev => !prev);
|
|
3539
4322
|
}
|
|
3540
4323
|
};
|
|
3541
|
-
// 모드 토글 함수 (chat ↔ agent)
|
|
4324
|
+
// 모드 토글 함수 (chat ↔ agent) - admin only
|
|
3542
4325
|
const toggleMode = () => {
|
|
4326
|
+
if (!isAdmin)
|
|
4327
|
+
return; // Non-admin users can only use chat mode
|
|
3543
4328
|
setInputMode(prev => prev === 'chat' ? 'agent' : 'chat');
|
|
3544
4329
|
setShowModeDropdown(false);
|
|
3545
4330
|
};
|
|
@@ -3582,7 +4367,7 @@ SyntaxError: '(' was never closed
|
|
|
3582
4367
|
if (!debugStatus || typeof debugStatus === 'string')
|
|
3583
4368
|
return '';
|
|
3584
4369
|
if (debugStatus.icon) {
|
|
3585
|
-
return (0,
|
|
4370
|
+
return (0,_utils_icons__WEBPACK_IMPORTED_MODULE_11__.getIconByName)(debugStatus.icon);
|
|
3586
4371
|
}
|
|
3587
4372
|
return '';
|
|
3588
4373
|
};
|
|
@@ -3606,11 +4391,33 @@ SyntaxError: '(' was never closed
|
|
|
3606
4391
|
return null;
|
|
3607
4392
|
};
|
|
3608
4393
|
const statusText = getStatusText();
|
|
3609
|
-
|
|
4394
|
+
// When stopped, treat in_progress as cancelled for rendering
|
|
4395
|
+
const displayTodos = isTodoStopped
|
|
4396
|
+
? todos.map(t => t.status === 'in_progress' ? { ...t, status: 'cancelled' } : t)
|
|
4397
|
+
: todos;
|
|
4398
|
+
const hasActiveTodos = displayTodos.some(todo => todo.status === 'pending' || todo.status === 'in_progress');
|
|
3610
4399
|
return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-panel" },
|
|
3611
|
-
showSettings && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
4400
|
+
showSettings && (isAdmin === true ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_AdminSettingsPanel__WEBPACK_IMPORTED_MODULE_4__.AdminSettingsPanel, { onClose: () => setShowSettings(false), onSave: (config) => {
|
|
4401
|
+
// Refresh llmConfig from localStorage after admin save
|
|
4402
|
+
loadConfig();
|
|
4403
|
+
console.log('[AgentPanel] Admin config saved');
|
|
4404
|
+
}, apiService: apiService })) : isAdmin === false ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_UserSettingsPanel__WEBPACK_IMPORTED_MODULE_5__.UserSettingsPanel, { onClose: () => setShowSettings(false), onSave: (config) => {
|
|
4405
|
+
// Update llmConfig with user settings
|
|
4406
|
+
if (llmConfig) {
|
|
4407
|
+
const updatedConfig = {
|
|
4408
|
+
...llmConfig,
|
|
4409
|
+
workspaceRoot: config.workspaceRoot,
|
|
4410
|
+
autoApprove: config.autoApprove
|
|
4411
|
+
};
|
|
4412
|
+
(0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_6__.saveLLMConfig)(updatedConfig);
|
|
4413
|
+
setLlmConfig(updatedConfig);
|
|
4414
|
+
}
|
|
4415
|
+
console.log('[AgentPanel] User config saved');
|
|
4416
|
+
}, apiService: apiService })) : (
|
|
4417
|
+
// Fallback to legacy SettingsPanel while checking admin mode
|
|
4418
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_SettingsPanel__WEBPACK_IMPORTED_MODULE_3__.SettingsPanel, { onClose: () => setShowSettings(false), onSave: handleSaveConfig, currentConfig: llmConfig || undefined }))),
|
|
3612
4419
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-header" },
|
|
3613
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-header-logo", dangerouslySetInnerHTML: { __html:
|
|
4420
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-header-logo", dangerouslySetInnerHTML: { __html: _logoSvg__WEBPACK_IMPORTED_MODULE_14__.headerLogoSvg } }),
|
|
3614
4421
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-header-buttons" },
|
|
3615
4422
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-clear-button", onClick: clearChat, title: "\uB300\uD654 \uCD08\uAE30\uD654" },
|
|
3616
4423
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
|
|
@@ -3717,7 +4524,7 @@ SyntaxError: '(' was never closed
|
|
|
3717
4524
|
const decision = msg.metadata?.interrupt?.decision;
|
|
3718
4525
|
const autoApproved = msg.metadata?.interrupt?.autoApproved;
|
|
3719
4526
|
// 자동 승인일 때는 코드블럭 헤더에 배지가 표시되므로 actionHtml에는 표시하지 않음
|
|
3720
|
-
const resolvedIcon = autoApproved ? '' : (decision === 'reject' ?
|
|
4527
|
+
const resolvedIcon = autoApproved ? '' : (decision === 'reject' ? _utils_icons__WEBPACK_IMPORTED_MODULE_11__.Icons.rejectCircle : _utils_icons__WEBPACK_IMPORTED_MODULE_11__.Icons.doubleCheck);
|
|
3721
4528
|
const resolvedClass = autoApproved ? '' : (decision === 'reject' ? 'jp-agent-interrupt-actions--rejected' : 'jp-agent-interrupt-actions--resolved');
|
|
3722
4529
|
const actionHtml = resolved && !autoApproved
|
|
3723
4530
|
? `<div class="jp-agent-interrupt-actions ${resolvedClass}">${resolvedIcon}</div>`
|
|
@@ -3725,8 +4532,8 @@ SyntaxError: '(' was never closed
|
|
|
3725
4532
|
? '' // 자동 승인일 때는 actionHtml 비움 (코드블럭 헤더에 배지 표시)
|
|
3726
4533
|
: `
|
|
3727
4534
|
<div class="code-block-actions jp-agent-interrupt-actions">
|
|
3728
|
-
<button class="jp-agent-interrupt-approve-btn" data-action="approve" title="승인">${
|
|
3729
|
-
<button class="jp-agent-interrupt-reject-btn" data-action="reject" title="거부">${
|
|
4535
|
+
<button class="jp-agent-interrupt-approve-btn" data-action="approve" title="승인">${_utils_icons__WEBPACK_IMPORTED_MODULE_11__.Icons.approveCircle}</button>
|
|
4536
|
+
<button class="jp-agent-interrupt-reject-btn" data-action="reject" title="거부">${_utils_icons__WEBPACK_IMPORTED_MODULE_11__.Icons.rejectCircle}</button>
|
|
3730
4537
|
</div>
|
|
3731
4538
|
`;
|
|
3732
4539
|
// Get tool description from args (for jupyter_cell_tool)
|
|
@@ -3734,15 +4541,15 @@ SyntaxError: '(' was never closed
|
|
|
3734
4541
|
const renderedHtml = (() => {
|
|
3735
4542
|
// markdown_tool: render content as HTML directly, not as code block
|
|
3736
4543
|
let html = isMarkdownTool
|
|
3737
|
-
? (0,
|
|
3738
|
-
: (0,
|
|
4544
|
+
? (0,_utils_markdownRenderer__WEBPACK_IMPORTED_MODULE_7__.formatMarkdownToHtml)(snippet)
|
|
4545
|
+
: (0,_utils_markdownRenderer__WEBPACK_IMPORTED_MODULE_7__.formatMarkdownToHtml)(`\n\`\`\`${language}\n${snippet}\n\`\`\``);
|
|
3739
4546
|
if (isWriteFile && writePath) {
|
|
3740
4547
|
const safePath = escapeHtml(writePath);
|
|
3741
4548
|
html = html.replace(/<span class="code-block-language">[^<]*<\/span>/, `<span class="code-block-language jp-agent-interrupt-path">${safePath}</span>`);
|
|
3742
4549
|
}
|
|
3743
4550
|
if ((isEditFile || isMultiEditFile) && editPath) {
|
|
3744
4551
|
const safePath = escapeHtml(editPath);
|
|
3745
|
-
const editIcon =
|
|
4552
|
+
const editIcon = _utils_icons__WEBPACK_IMPORTED_MODULE_11__.Icons.edit;
|
|
3746
4553
|
html = html.replace(/<span class="code-block-language">[^<]*<\/span>/, `<span class="code-block-language jp-agent-interrupt-path">${editIcon} ${safePath}</span>`);
|
|
3747
4554
|
}
|
|
3748
4555
|
// actionHtml이 비어있지 않을 때만 추가
|
|
@@ -3769,12 +4576,12 @@ SyntaxError: '(' was never closed
|
|
|
3769
4576
|
.replace(/\n/g, ' ') // Remove actual newlines
|
|
3770
4577
|
.replace(/\s+/g, ' ') // Collapse multiple spaces
|
|
3771
4578
|
.trim();
|
|
3772
|
-
result += `<div class="jp-agent-code-description">${
|
|
4579
|
+
result += `<div class="jp-agent-code-description">${_utils_icons__WEBPACK_IMPORTED_MODULE_11__.Icons.description} ${safeDescription}</div>`;
|
|
3773
4580
|
}
|
|
3774
4581
|
// 2. 승인 메시지 (자동 승인이 아닐 때만) - 코드 블록 바로 위
|
|
3775
4582
|
if (!msg.metadata?.interrupt?.autoApproved && msg.content) {
|
|
3776
4583
|
const safeContent = escapeHtml(msg.content);
|
|
3777
|
-
result += `<div class="jp-agent-interrupt-description">${
|
|
4584
|
+
result += `<div class="jp-agent-interrupt-description">${_utils_icons__WEBPACK_IMPORTED_MODULE_11__.Icons.approvalWarning} ${safeContent}</div>`;
|
|
3778
4585
|
}
|
|
3779
4586
|
// 3. 코드 블록
|
|
3780
4587
|
result += codeHtml;
|
|
@@ -3798,7 +4605,7 @@ SyntaxError: '(' was never closed
|
|
|
3798
4605
|
})())))) : msg.role === 'assistant' ? (
|
|
3799
4606
|
// Assistant(AI) 메시지: 스트리밍 지원 마크다운 렌더링
|
|
3800
4607
|
// 이벤트 위임은 상위 messages-container에서 처리됨
|
|
3801
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
4608
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_StreamingMessage__WEBPACK_IMPORTED_MODULE_9__.StreamingMessage, { content: msg.content, isStreaming: streamingMessageId === msg.id, onInsertAbove: handleInsertCodeAbove, onInsertBelow: handleInsertCodeBelow, onInsertAsCell: handleInsertCodeAsCell, cellActionsEnabled: !!getActiveNotebookPanel() })) : (
|
|
3802
4609
|
// User(사용자) 메시지: 텍스트 그대로 줄바꿈만 처리
|
|
3803
4610
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { style: { whiteSpace: 'pre-wrap' } }, msg.content)))));
|
|
3804
4611
|
}
|
|
@@ -3846,8 +4653,11 @@ SyntaxError: '(' was never closed
|
|
|
3846
4653
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("svg", { className: `jp-agent-todo-expand-icon ${isTodoExpanded ? 'jp-agent-todo-expand-icon--expanded' : ''}`, viewBox: "0 0 16 16", fill: "currentColor", width: "12", height: "12" },
|
|
3847
4654
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("path", { d: "M6 12l4-4-4-4" })),
|
|
3848
4655
|
(() => {
|
|
3849
|
-
const currentTodo = todos.find(t => t.status === 'in_progress') || todos.find(t => t.status === 'pending');
|
|
3850
4656
|
const isStillWorking = isStreaming || isLoading || isAgentRunning;
|
|
4657
|
+
if (isTodoStopped) {
|
|
4658
|
+
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-todo-compact-current" }, "\uC791\uC5C5 \uC911\uB2E8\uB428");
|
|
4659
|
+
}
|
|
4660
|
+
const currentTodo = displayTodos.find(t => t.status === 'in_progress') || displayTodos.find(t => t.status === 'pending');
|
|
3851
4661
|
if (currentTodo) {
|
|
3852
4662
|
return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
|
|
3853
4663
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-todo-compact-spinner" }),
|
|
@@ -3859,10 +4669,10 @@ SyntaxError: '(' was never closed
|
|
|
3859
4669
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-todo-compact-current" }, "\uC791\uC5C5 \uB9C8\uBB34\uB9AC \uC911...")));
|
|
3860
4670
|
}
|
|
3861
4671
|
else {
|
|
3862
|
-
return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-todo-compact-current", dangerouslySetInnerHTML: { __html: `${
|
|
4672
|
+
return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-todo-compact-current", dangerouslySetInnerHTML: { __html: `${_utils_icons__WEBPACK_IMPORTED_MODULE_11__.Icons.check} 모든 작업 완료` } }));
|
|
3863
4673
|
}
|
|
3864
4674
|
})()),
|
|
3865
|
-
(isStreaming || isLoading || isAgentRunning ||
|
|
4675
|
+
!isTodoStopped && (isStreaming || isLoading || isAgentRunning || displayTodos.some(t => t.status === 'in_progress')) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-todo-stop-btn", onClick: (e) => {
|
|
3866
4676
|
e.stopPropagation();
|
|
3867
4677
|
console.log('[AgentPanel] Stop button clicked');
|
|
3868
4678
|
stopCurrentTask();
|
|
@@ -3879,12 +4689,14 @@ SyntaxError: '(' was never closed
|
|
|
3879
4689
|
getDebugStatusIcon() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-debug-icon", dangerouslySetInnerHTML: { __html: getDebugStatusIcon() } })),
|
|
3880
4690
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: `jp-agent-debug-text${isDebugStatusExpandable() ? ' jp-agent-debug-text--expandable' : ''}`, onClick: isDebugStatusExpandable() ? () => setIsDebugExpanded(!isDebugExpanded) : undefined, title: isDebugStatusExpandable() ? (isDebugExpanded ? '접기' : '펼치기') : undefined },
|
|
3881
4691
|
statusText,
|
|
3882
|
-
isDebugStatusExpandable() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-debug-expand-icon" }, isDebugExpanded ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
3883
|
-
isTodoExpanded && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-todo-expanded" },
|
|
4692
|
+
isDebugStatusExpandable() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-debug-expand-icon" }, isDebugExpanded ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ExpandLess__WEBPACK_IMPORTED_MODULE_12__["default"], { sx: { fontSize: 14 } }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ExpandMore__WEBPACK_IMPORTED_MODULE_13__["default"], { sx: { fontSize: 14 } }))))))),
|
|
4693
|
+
isTodoExpanded && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-todo-expanded" }, displayTodos.map((todo, index) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { key: index, className: `jp-agent-todo-item jp-agent-todo-item--${todo.status}` },
|
|
3884
4694
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-todo-item-indicator" },
|
|
3885
4695
|
todo.status === 'completed' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("svg", { viewBox: "0 0 16 16", fill: "currentColor", width: "12", height: "12" },
|
|
3886
4696
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("path", { d: "M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z" }))),
|
|
3887
4697
|
todo.status === 'in_progress' && react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-todo-item-spinner" }),
|
|
4698
|
+
todo.status === 'cancelled' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("svg", { viewBox: "0 0 16 16", fill: "currentColor", width: "12", height: "12" },
|
|
4699
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("path", { d: "M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z" }))),
|
|
3888
4700
|
todo.status === 'pending' && react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-todo-item-number" }, index + 1)),
|
|
3889
4701
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: `jp-agent-todo-item-text ${todo.status === 'completed' ? 'jp-agent-todo-item-text--done' : ''}` }, todo.content)))))))),
|
|
3890
4702
|
statusText && !hasActiveTodos && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `jp-agent-message jp-agent-message-debug jp-agent-message-debug--above-input${statusText.startsWith('오류:') ? ' jp-agent-message-debug-error' : ''}` },
|
|
@@ -3892,23 +4704,21 @@ SyntaxError: '(' was never closed
|
|
|
3892
4704
|
getDebugStatusIcon() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-debug-icon", dangerouslySetInnerHTML: { __html: getDebugStatusIcon() } })),
|
|
3893
4705
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: `jp-agent-debug-text${isDebugStatusExpandable() ? ' jp-agent-debug-text--expandable' : ''}`, onClick: isDebugStatusExpandable() ? () => setIsDebugExpanded(!isDebugExpanded) : undefined, title: isDebugStatusExpandable() ? (isDebugExpanded ? '접기' : '펼치기') : undefined },
|
|
3894
4706
|
statusText,
|
|
3895
|
-
isDebugStatusExpandable() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-debug-expand-icon" }, isDebugExpanded ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
4707
|
+
isDebugStatusExpandable() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-debug-expand-icon" }, isDebugExpanded ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ExpandLess__WEBPACK_IMPORTED_MODULE_12__["default"], { sx: { fontSize: 14 } }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_ExpandMore__WEBPACK_IMPORTED_MODULE_13__["default"], { sx: { fontSize: 14 } }))))))),
|
|
3896
4708
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-input-container" },
|
|
3897
4709
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-input-wrapper", style: { position: 'relative' } },
|
|
3898
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
4710
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ContextAutocomplete__WEBPACK_IMPORTED_MODULE_10__.ContextAutocomplete, { apiService: apiService, inputValue: input, cursorPosition: cursorPosition, baseDir: notebookTracker?.currentWidget?.context?.path ?
|
|
3899
4711
|
notebookTracker.currentWidget.context.path.replace(/[^/]*$/, '') : undefined, onSelect: handleAutocompleteSelect, onClose: () => { setShowAutocomplete(false); setHasAutocompleteOptions(false); }, onOptionsChange: setHasAutocompleteOptions, visible: showAutocomplete, anchorEl: textareaRef.current }),
|
|
3900
4712
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("textarea", { ref: textareaRef, className: `jp-agent-input ${inputMode !== 'chat' ? 'jp-agent-input--agent-mode' : ''} ${isRejectionMode ? 'jp-agent-input--rejection-mode' : ''}`, value: input, onChange: handleInputChange, onKeyDown: handleKeyDown, onSelect: (e) => setCursorPosition(e.target.selectionStart || 0), placeholder: isRejectionMode
|
|
3901
4713
|
? '다른 방향 제시'
|
|
3902
4714
|
: (inputMode === 'agent'
|
|
3903
4715
|
? '노트북 작업을 입력하세요... (예: @file:path로 파일 참조)'
|
|
3904
|
-
: '메시지를 입력하세요... (@file:path로 파일 참조)'),
|
|
3905
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-button-container" },
|
|
3906
|
-
|
|
3907
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-send-button", onClick: handleSendMessage, disabled: (!input.trim() && !isRejectionMode) || isLoading || isStreaming || isAgentRunning, title: isRejectionMode ? "거부 전송 (Enter)" : "전송 (Enter)" }, isAgentRunning ? '실행 중...' : (isRejectionMode ? '거부' : '전송')))),
|
|
3911
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-mode-bar" },
|
|
4716
|
+
: '메시지를 입력하세요... (@file:path로 파일 참조)'), disabled: isLoading || isAgentRunning }),
|
|
4717
|
+
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
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "14", height: "14" },
|
|
4719
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("rect", { x: "6", y: "6", width: "12", height: "12", rx: "1" })),
|
|
4720
|
+
"\uC911\uB2E8")) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-send-button", onClick: handleSendMessage, disabled: (!input.trim() && !isRejectionMode) || isLoading || isStreaming || isAgentRunning, title: isRejectionMode ? "거부 전송 (Enter)" : "전송 (Enter)" }, isAgentRunning ? '실행 중...' : (isRejectionMode ? '거부' : '전송'))))),
|
|
4721
|
+
isAdmin && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-mode-bar" },
|
|
3912
4722
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-mode-toggle-container" },
|
|
3913
4723
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: `jp-agent-mode-toggle ${inputMode !== 'chat' ? 'jp-agent-mode-toggle--active' : ''}`, onClick: toggleMode, title: `${inputMode === 'chat' ? 'Chat' : 'Agent'} 모드 (⇧Tab)` },
|
|
3914
4724
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("svg", { className: "jp-agent-mode-icon", viewBox: inputMode === 'chat' ? "0 0 16 16" : "0 -960 960 960", fill: "currentColor", width: "14", height: "14" }, inputMode === 'chat' ? (
|
|
@@ -3931,8 +4741,8 @@ SyntaxError: '(' was never closed
|
|
|
3931
4741
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "Agent"),
|
|
3932
4742
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-mode-shortcut" }, "\uB178\uD2B8\uBD81 \uC790\uB3D9 \uC2E4\uD589"))))),
|
|
3933
4743
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-mode-hints" },
|
|
3934
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-mode-hint" }, "\u21E7Tab \uBAA8\uB4DC \uC804\uD658")))),
|
|
3935
|
-
fileSelectionMetadata && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
4744
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-mode-hint" }, "\u21E7Tab \uBAA8\uB4DC \uC804\uD658"))))),
|
|
4745
|
+
fileSelectionMetadata && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_FileSelectionDialog__WEBPACK_IMPORTED_MODULE_8__.FileSelectionDialog, { filename: fileSelectionMetadata.pattern, options: fileSelectionMetadata.options, message: fileSelectionMetadata.message, onSelect: handleFileSelect, onCancel: handleFileSelectCancel }))));
|
|
3936
4746
|
});
|
|
3937
4747
|
ChatPanel.displayName = 'ChatPanel';
|
|
3938
4748
|
/**
|
|
@@ -4150,8 +4960,8 @@ function getPartialCommand(text, cursorPosition) {
|
|
|
4150
4960
|
start--;
|
|
4151
4961
|
}
|
|
4152
4962
|
const word = text.slice(start, cursorPosition);
|
|
4153
|
-
// Check if it looks like a context command
|
|
4154
|
-
if (word.startsWith('@')) {
|
|
4963
|
+
// Check if it looks like a context command (@ for context, / for actions)
|
|
4964
|
+
if (word.startsWith('@') || word.startsWith('/')) {
|
|
4155
4965
|
return word;
|
|
4156
4966
|
}
|
|
4157
4967
|
return null;
|
|
@@ -4662,9 +5472,12 @@ const SettingsPanel = ({ onClose, onSave, currentConfig }) => {
|
|
|
4662
5472
|
const [autoApprove, setAutoApprove] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(Boolean(initConfig.autoApprove));
|
|
4663
5473
|
const [idleTimeoutMinutes, setIdleTimeoutMinutes] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.idleTimeoutMinutes ?? 60);
|
|
4664
5474
|
// Multi-Agent settings
|
|
4665
|
-
const [agentMode, setAgentMode] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.agentMode || 'single');
|
|
4666
5475
|
const [agentPrompts, setAgentPrompts] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(initConfig.agentPrompts || {});
|
|
4667
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 || '');
|
|
4668
5481
|
// Default prompts fetched from API
|
|
4669
5482
|
const [defaultPrompts, setDefaultPrompts] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)((0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.getCachedPrompts)());
|
|
4670
5483
|
const [isLoadingPrompts, setIsLoadingPrompts] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(!(0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.getCachedPrompts)());
|
|
@@ -4704,7 +5517,6 @@ const SettingsPanel = ({ onClose, onSave, currentConfig }) => {
|
|
|
4704
5517
|
setWorkspaceRoot(currentConfig.workspaceRoot || '');
|
|
4705
5518
|
setAutoApprove(Boolean(currentConfig.autoApprove));
|
|
4706
5519
|
setIdleTimeoutMinutes(currentConfig.idleTimeoutMinutes ?? 60);
|
|
4707
|
-
setAgentMode(currentConfig.agentMode || 'single');
|
|
4708
5520
|
// Use currentConfig prompts, or cached defaults, or empty object
|
|
4709
5521
|
const cachedDefaults = (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_10__.getCachedPrompts)();
|
|
4710
5522
|
setAgentPrompts(currentConfig.agentPrompts || (cachedDefaults ? {
|
|
@@ -4713,6 +5525,10 @@ const SettingsPanel = ({ onClose, onSave, currentConfig }) => {
|
|
|
4713
5525
|
researcher: cachedDefaults.researcher,
|
|
4714
5526
|
athena_query: cachedDefaults.athena_query,
|
|
4715
5527
|
} : {}));
|
|
5528
|
+
// Summarization settings
|
|
5529
|
+
setSummarizationEnabled(Boolean(currentConfig.summarization?.enabled));
|
|
5530
|
+
setSummarizationProvider(currentConfig.summarization?.provider || 'gemini');
|
|
5531
|
+
setSummarizationModel(currentConfig.summarization?.model || '');
|
|
4716
5532
|
}
|
|
4717
5533
|
}, [currentConfig]);
|
|
4718
5534
|
// Helper: Build LLM config from state
|
|
@@ -4736,8 +5552,12 @@ const SettingsPanel = ({ onClose, onSave, currentConfig }) => {
|
|
|
4736
5552
|
},
|
|
4737
5553
|
workspaceRoot: workspaceRoot.trim() ? workspaceRoot.trim() : undefined,
|
|
4738
5554
|
systemPrompt: systemPrompt && systemPrompt.trim() ? systemPrompt : undefined,
|
|
4739
|
-
agentMode,
|
|
4740
5555
|
agentPrompts,
|
|
5556
|
+
summarization: summarizationEnabled ? {
|
|
5557
|
+
enabled: true,
|
|
5558
|
+
provider: summarizationProvider,
|
|
5559
|
+
model: summarizationModel.trim() || undefined,
|
|
5560
|
+
} : undefined,
|
|
4741
5561
|
autoApprove,
|
|
4742
5562
|
idleTimeoutMinutes
|
|
4743
5563
|
});
|
|
@@ -4911,6 +5731,28 @@ const SettingsPanel = ({ onClose, onSave, currentConfig }) => {
|
|
|
4911
5731
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gpt-4" }, "GPT-4"),
|
|
4912
5732
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "gpt-4-turbo" }, "GPT-4 Turbo"),
|
|
4913
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'))))),
|
|
4914
5756
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
|
|
4915
5757
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" }, "\uC790\uB3D9 \uC2E4\uD589 \uC2B9\uC778"),
|
|
4916
5758
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-checkbox" },
|
|
@@ -4922,21 +5764,6 @@ const SettingsPanel = ({ onClose, onSave, currentConfig }) => {
|
|
|
4922
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)")),
|
|
4923
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" })),
|
|
4924
5766
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
|
|
4925
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" }, "\uC5D0\uC774\uC804\uD2B8 \uBAA8\uB4DC"),
|
|
4926
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("select", { className: "jp-agent-settings-select", value: agentMode, onChange: (e) => setAgentMode(e.target.value) },
|
|
4927
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "single" }, "Single Agent (\uBAA8\uB4E0 \uB3C4\uAD6C \uD1B5\uD569)"),
|
|
4928
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", { value: "multi" }, "Multi-Agent (Planner + Subagents)")),
|
|
4929
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { color: '#666', marginTop: '4px', display: 'block' } }, agentMode === 'single'
|
|
4930
|
-
? '단일 에이전트가 모든 작업을 처리합니다.'
|
|
4931
|
-
: 'Planner가 Python Developer, Researcher 등 전문 에이전트에게 작업을 위임합니다.')),
|
|
4932
|
-
agentMode === 'single' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
|
|
4933
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" },
|
|
4934
|
-
"System Prompt",
|
|
4935
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { fontWeight: 'normal', marginLeft: '8px', color: '#666' } }, "\uB2E8\uC77C \uC5D0\uC774\uC804\uD2B8\uC5D0 \uC801\uC6A9\uB429\uB2C8\uB2E4.")),
|
|
4936
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("textarea", { className: "jp-agent-settings-input jp-agent-settings-textarea", value: systemPrompt, onChange: (e) => setSystemPrompt(e.target.value), rows: 8 }),
|
|
4937
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-inline-actions" },
|
|
4938
|
-
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", onClick: () => defaultPrompts && setSystemPrompt(defaultPrompts.single), disabled: isLoadingPrompts }, isLoadingPrompts ? '로딩중...' : '기본값으로 되돌리기')))),
|
|
4939
|
-
agentMode === 'multi' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
|
|
4940
5767
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" },
|
|
4941
5768
|
"\uC5D0\uC774\uC804\uD2B8\uBCC4 System Prompt",
|
|
4942
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.")),
|
|
@@ -5011,9 +5838,6 @@ const SettingsPanel = ({ onClose, onSave, currentConfig }) => {
|
|
|
5011
5838
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { style: { marginTop: '12px' } },
|
|
5012
5839
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { type: "button", className: "jp-agent-settings-button jp-agent-settings-button-secondary", onClick: () => {
|
|
5013
5840
|
if (defaultPrompts) {
|
|
5014
|
-
// Reset single-mode prompt
|
|
5015
|
-
setSystemPrompt(defaultPrompts.single);
|
|
5016
|
-
// Reset multi-agent prompts
|
|
5017
5841
|
setAgentPrompts({
|
|
5018
5842
|
planner: defaultPrompts.planner,
|
|
5019
5843
|
python_developer: defaultPrompts.python_developer,
|
|
@@ -5021,7 +5845,7 @@ const SettingsPanel = ({ onClose, onSave, currentConfig }) => {
|
|
|
5021
5845
|
athena_query: defaultPrompts.athena_query,
|
|
5022
5846
|
});
|
|
5023
5847
|
}
|
|
5024
|
-
}, disabled: isLoadingPrompts }, isLoadingPrompts ? '로딩중...' : '모든 프롬프트 기본값으로 되돌리기'))))
|
|
5848
|
+
}, disabled: isLoadingPrompts }, isLoadingPrompts ? '로딩중...' : '모든 프롬프트 기본값으로 되돌리기')))),
|
|
5025
5849
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-footer" },
|
|
5026
5850
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-settings-button jp-agent-settings-button-secondary", onClick: onClose }, "\uCDE8\uC18C"),
|
|
5027
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 테스트'),
|
|
@@ -5133,6 +5957,9 @@ const StreamingMessage = ({ content, isStreaming = false, className = '', deboun
|
|
|
5133
5957
|
const rendererRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
|
|
5134
5958
|
const lastContentRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)('');
|
|
5135
5959
|
const [codeToolbarDefs, setCodeToolbarDefs] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);
|
|
5960
|
+
// Throttle refs for native Rendermime streaming render
|
|
5961
|
+
const pendingRenderRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
|
|
5962
|
+
const latestContentForRender = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)('');
|
|
5136
5963
|
// Synchronously determine renderer type (useMemo instead of useState+useEffect)
|
|
5137
5964
|
// Use fallback renderer for summary JSON (native renderer doesn't parse it)
|
|
5138
5965
|
const useNativeRenderer = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => {
|
|
@@ -5189,7 +6016,11 @@ const StreamingMessage = ({ content, isStreaming = false, className = '', deboun
|
|
|
5189
6016
|
});
|
|
5190
6017
|
setCodeToolbarDefs(newDefs);
|
|
5191
6018
|
}, []);
|
|
6019
|
+
// Track whether renderer node is already mounted in DOM
|
|
6020
|
+
const rendererMountedRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);
|
|
5192
6021
|
// Render content using Rendermime (native JupyterLab renderer)
|
|
6022
|
+
// Key optimization: renderer.node is inserted into the DOM once and stays there.
|
|
6023
|
+
// Subsequent renderModel() calls update it in-place — no DOM replacement, no height collapse.
|
|
5193
6024
|
const renderWithRendermime = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(async (contentToRender) => {
|
|
5194
6025
|
const rmRegistry = getGlobalRenderMimeRegistry();
|
|
5195
6026
|
if (!rmRegistry || !containerRef.current) {
|
|
@@ -5215,16 +6046,19 @@ const StreamingMessage = ({ content, isStreaming = false, className = '', deboun
|
|
|
5215
6046
|
// Create or reuse renderer
|
|
5216
6047
|
if (!rendererRef.current) {
|
|
5217
6048
|
rendererRef.current = rmRegistry.createRenderer(MD_MIME_TYPE);
|
|
6049
|
+
rendererMountedRef.current = false;
|
|
5218
6050
|
}
|
|
5219
6051
|
const renderer = rendererRef.current;
|
|
5220
|
-
//
|
|
6052
|
+
// renderModel() updates renderer.node in-place
|
|
5221
6053
|
await renderer.renderModel(model);
|
|
5222
6054
|
if (renderer.node && containerRef.current) {
|
|
5223
|
-
// Find the content div
|
|
5224
6055
|
const contentDiv = containerRef.current.querySelector('.jp-streaming-content');
|
|
5225
6056
|
if (contentDiv) {
|
|
5226
|
-
|
|
5227
|
-
contentDiv.
|
|
6057
|
+
// Mount renderer node once — subsequent renderModel() calls update it in-place
|
|
6058
|
+
if (!rendererMountedRef.current || !contentDiv.contains(renderer.node)) {
|
|
6059
|
+
contentDiv.replaceChildren(renderer.node);
|
|
6060
|
+
rendererMountedRef.current = true;
|
|
6061
|
+
}
|
|
5228
6062
|
// Apply LaTeX typesetting if available
|
|
5229
6063
|
if (rmRegistry.latexTypesetter) {
|
|
5230
6064
|
rmRegistry.latexTypesetter.typeset(contentDiv);
|
|
@@ -5242,26 +6076,38 @@ const StreamingMessage = ({ content, isStreaming = false, className = '', deboun
|
|
|
5242
6076
|
return false;
|
|
5243
6077
|
}
|
|
5244
6078
|
}, [isStreaming, attachCodeToolbars]);
|
|
5245
|
-
// Effect for rendering with Rendermime
|
|
6079
|
+
// Effect for rendering with Rendermime (throttle pattern during streaming)
|
|
6080
|
+
// Uses throttle instead of debounce: if tokens arrive faster than debounceMs,
|
|
6081
|
+
// debounce would keep resetting the timer and never render. Throttle ensures
|
|
6082
|
+
// rendering happens at most once per debounceMs window.
|
|
5246
6083
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
5247
6084
|
if (!useNativeRenderer || !content) {
|
|
6085
|
+
// Clear pending render when not using native renderer or no content
|
|
6086
|
+
if (pendingRenderRef.current) {
|
|
6087
|
+
clearTimeout(pendingRenderRef.current);
|
|
6088
|
+
pendingRenderRef.current = null;
|
|
6089
|
+
}
|
|
5248
6090
|
return;
|
|
5249
6091
|
}
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
}
|
|
5256
|
-
}
|
|
5257
|
-
else {
|
|
6092
|
+
if (!isStreaming) {
|
|
6093
|
+
// Not streaming: render immediately, clear any pending throttle
|
|
6094
|
+
if (pendingRenderRef.current) {
|
|
6095
|
+
clearTimeout(pendingRenderRef.current);
|
|
6096
|
+
pendingRenderRef.current = null;
|
|
6097
|
+
}
|
|
5258
6098
|
void renderWithRendermime(content);
|
|
6099
|
+
return;
|
|
5259
6100
|
}
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
6101
|
+
// Streaming: throttle — always track latest content, schedule only if none pending
|
|
6102
|
+
latestContentForRender.current = content;
|
|
6103
|
+
if (!pendingRenderRef.current) {
|
|
6104
|
+
pendingRenderRef.current = setTimeout(() => {
|
|
6105
|
+
pendingRenderRef.current = null;
|
|
6106
|
+
void renderWithRendermime(latestContentForRender.current);
|
|
6107
|
+
}, debounceMs);
|
|
6108
|
+
}
|
|
6109
|
+
// No cleanup that clears the timeout — throttle pattern intentionally
|
|
6110
|
+
// lets the scheduled render fire even when content updates again
|
|
5265
6111
|
}, [content, isStreaming, useNativeRenderer, debounceMs, renderWithRendermime]);
|
|
5266
6112
|
// Attach toolbars when streaming stops (only for native renderer)
|
|
5267
6113
|
// Note: Fallback renderer has its own code-block-header with buttons
|
|
@@ -5287,13 +6133,18 @@ const StreamingMessage = ({ content, isStreaming = false, className = '', deboun
|
|
|
5287
6133
|
}
|
|
5288
6134
|
}
|
|
5289
6135
|
}, [useNativeRenderer, renderedHtml, isStreaming, attachCodeToolbars]);
|
|
5290
|
-
// Cleanup renderer and toolbar roots on unmount
|
|
6136
|
+
// Cleanup renderer, throttle timer, and toolbar roots on unmount
|
|
5291
6137
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
5292
6138
|
return () => {
|
|
5293
6139
|
if (rendererRef.current && rendererRef.current.dispose) {
|
|
5294
6140
|
rendererRef.current.dispose();
|
|
5295
6141
|
rendererRef.current = null;
|
|
5296
6142
|
}
|
|
6143
|
+
rendererMountedRef.current = false;
|
|
6144
|
+
if (pendingRenderRef.current) {
|
|
6145
|
+
clearTimeout(pendingRenderRef.current);
|
|
6146
|
+
pendingRenderRef.current = null;
|
|
6147
|
+
}
|
|
5297
6148
|
// Clean up toolbar roots
|
|
5298
6149
|
codeToolbarDefs.forEach(def => {
|
|
5299
6150
|
if (def.root.parentNode) {
|
|
@@ -5468,6 +6319,122 @@ const TaskProgressWidget = ({ taskStatus, onClose, onCancel, onOpenNotebook }) =
|
|
|
5468
6319
|
};
|
|
5469
6320
|
|
|
5470
6321
|
|
|
6322
|
+
/***/ },
|
|
6323
|
+
|
|
6324
|
+
/***/ "./lib/components/UserSettingsPanel.js"
|
|
6325
|
+
/*!*********************************************!*\
|
|
6326
|
+
!*** ./lib/components/UserSettingsPanel.js ***!
|
|
6327
|
+
\*********************************************/
|
|
6328
|
+
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
6329
|
+
|
|
6330
|
+
__webpack_require__.r(__webpack_exports__);
|
|
6331
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
6332
|
+
/* harmony export */ UserSettingsPanel: () => (/* binding */ UserSettingsPanel)
|
|
6333
|
+
/* harmony export */ });
|
|
6334
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react");
|
|
6335
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
6336
|
+
/* harmony import */ var _mui_icons_material_HourglassEmpty__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @mui/icons-material/HourglassEmpty */ "./node_modules/@mui/icons-material/esm/HourglassEmpty.js");
|
|
6337
|
+
/**
|
|
6338
|
+
* User Settings Panel Component
|
|
6339
|
+
*
|
|
6340
|
+
* User preferences configuration:
|
|
6341
|
+
* - Workspace root path
|
|
6342
|
+
* - Temperature setting
|
|
6343
|
+
* - Auto-approve toggle
|
|
6344
|
+
*/
|
|
6345
|
+
|
|
6346
|
+
|
|
6347
|
+
const UserSettingsPanel = ({ onClose, onSave, apiService }) => {
|
|
6348
|
+
const [isLoading, setIsLoading] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(true);
|
|
6349
|
+
const [isSaving, setIsSaving] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
6350
|
+
const [error, setError] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
6351
|
+
// User settings
|
|
6352
|
+
const [workspaceRoot, setWorkspaceRoot] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
|
|
6353
|
+
const [temperature, setTemperature] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(0.7);
|
|
6354
|
+
const [autoApprove, setAutoApprove] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
6355
|
+
// Load config on mount
|
|
6356
|
+
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
6357
|
+
loadConfig();
|
|
6358
|
+
}, []);
|
|
6359
|
+
const loadConfig = async () => {
|
|
6360
|
+
try {
|
|
6361
|
+
setIsLoading(true);
|
|
6362
|
+
const config = await apiService.getUserConfig();
|
|
6363
|
+
setWorkspaceRoot(config.workspaceRoot || '');
|
|
6364
|
+
setTemperature(config.temperature ?? 0.7);
|
|
6365
|
+
setAutoApprove(Boolean(config.autoApprove));
|
|
6366
|
+
setError(null);
|
|
6367
|
+
}
|
|
6368
|
+
catch (err) {
|
|
6369
|
+
setError(`설정을 불러오는데 실패했습니다: ${err}`);
|
|
6370
|
+
}
|
|
6371
|
+
finally {
|
|
6372
|
+
setIsLoading(false);
|
|
6373
|
+
}
|
|
6374
|
+
};
|
|
6375
|
+
const handleSave = async () => {
|
|
6376
|
+
try {
|
|
6377
|
+
setIsSaving(true);
|
|
6378
|
+
const config = {
|
|
6379
|
+
workspaceRoot: workspaceRoot.trim() || '',
|
|
6380
|
+
temperature,
|
|
6381
|
+
autoApprove
|
|
6382
|
+
};
|
|
6383
|
+
await apiService.updateUserConfig(config);
|
|
6384
|
+
onSave(config);
|
|
6385
|
+
onClose();
|
|
6386
|
+
}
|
|
6387
|
+
catch (err) {
|
|
6388
|
+
setError(`설정 저장에 실패했습니다: ${err}`);
|
|
6389
|
+
}
|
|
6390
|
+
finally {
|
|
6391
|
+
setIsSaving(false);
|
|
6392
|
+
}
|
|
6393
|
+
};
|
|
6394
|
+
if (isLoading) {
|
|
6395
|
+
return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-overlay" },
|
|
6396
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-dialog", style: { maxWidth: '400px' } },
|
|
6397
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-content", style: { textAlign: 'center', padding: '40px' } },
|
|
6398
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_HourglassEmpty__WEBPACK_IMPORTED_MODULE_1__["default"], { sx: { fontSize: 48, color: 'info.main' } }),
|
|
6399
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", null, "\uC124\uC815\uC744 \uBD88\uB7EC\uC624\uB294 \uC911...")))));
|
|
6400
|
+
}
|
|
6401
|
+
return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-overlay" },
|
|
6402
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-dialog", style: { maxWidth: '400px' } },
|
|
6403
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-header" },
|
|
6404
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", null, "\uC0AC\uC6A9\uC790 \uC124\uC815"),
|
|
6405
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-settings-close", onClick: onClose, title: "\uB2EB\uAE30" }, "\u00D7")),
|
|
6406
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-content" },
|
|
6407
|
+
error && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-error", style: { color: 'red', marginBottom: '12px' } }, error)),
|
|
6408
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-notice" },
|
|
6409
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\uAC1C\uC778 \uD658\uACBD\uC124\uC815\uC744 \uAD00\uB9AC\uD569\uB2C8\uB2E4.")),
|
|
6410
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
|
|
6411
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label", htmlFor: "jp-user-workspace-root" },
|
|
6412
|
+
"\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8",
|
|
6413
|
+
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")),
|
|
6414
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { id: "jp-user-workspace-root", type: "text", className: "jp-agent-settings-input", value: workspaceRoot, onChange: (e) => setWorkspaceRoot(e.target.value), placeholder: "\uC608: /Users/you/project \uB610\uB294 ./my-project" }),
|
|
6415
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { color: '#666', display: 'block', marginTop: '4px' } }, "\uC808\uB300 \uACBD\uB85C \uB610\uB294 \uC0C1\uB300 \uACBD\uB85C \uBAA8\uB450 \uC0AC\uC6A9 \uAC00\uB2A5\uD569\uB2C8\uB2E4.")),
|
|
6416
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
|
|
6417
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label", htmlFor: "jp-user-temperature" },
|
|
6418
|
+
"Temperature",
|
|
6419
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { fontWeight: 'normal', marginLeft: '8px', color: '#666' } }, "LLM \uC751\uB2F5\uC758 \uCC3D\uC758\uC131 \uC870\uC808")),
|
|
6420
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '12px' } },
|
|
6421
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { id: "jp-user-temperature", type: "range", min: 0, max: 2, step: 0.1, value: temperature, onChange: (e) => setTemperature(parseFloat(e.target.value)), style: { flex: 1 } }),
|
|
6422
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "number", className: "jp-agent-settings-input", value: temperature, onChange: (e) => setTemperature(Math.max(0, Math.min(2, parseFloat(e.target.value) || 0))), min: 0, max: 2, step: 0.1, style: { width: '70px' } })),
|
|
6423
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { color: '#666', display: 'block', marginTop: '4px' } }, "0.0 = \uACB0\uC815\uC801 (\uC77C\uAD00\uB41C \uC751\uB2F5), 1.0+ = \uCC3D\uC758\uC801 (\uB2E4\uC591\uD55C \uC751\uB2F5)")),
|
|
6424
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-group" },
|
|
6425
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-label" }, "\uC790\uB3D9 \uC2E4\uD589 \uC2B9\uC778"),
|
|
6426
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: "jp-agent-settings-checkbox" },
|
|
6427
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "checkbox", checked: autoApprove, onChange: (e) => setAutoApprove(e.target.checked) }),
|
|
6428
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\uC2B9\uC778 \uC5C6\uC774 \uBC14\uB85C \uC2E4\uD589 (\uCF54\uB4DC/\uD30C\uC77C/\uC178 \uD3EC\uD568)")),
|
|
6429
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("small", { style: { color: '#666', display: 'block', marginTop: '4px' } }, autoApprove
|
|
6430
|
+
? '주의: 에이전트가 생성한 코드가 자동으로 실행됩니다.'
|
|
6431
|
+
: '에이전트가 코드 실행, 파일 수정, 셸 명령 실행 전 승인을 요청합니다.'))),
|
|
6432
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-settings-footer" },
|
|
6433
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-settings-button jp-agent-settings-button-secondary", onClick: onClose }, "\uCDE8\uC18C"),
|
|
6434
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-settings-button jp-agent-settings-button-primary", onClick: handleSave, disabled: isSaving }, isSaving ? '저장 중...' : '저장')))));
|
|
6435
|
+
};
|
|
6436
|
+
|
|
6437
|
+
|
|
5471
6438
|
/***/ },
|
|
5472
6439
|
|
|
5473
6440
|
/***/ "./lib/components/code-blocks/CodeToolbar.js"
|
|
@@ -5867,6 +6834,8 @@ function useStreamingMarkdown(content, options = {}) {
|
|
|
5867
6834
|
const lastContentRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)('');
|
|
5868
6835
|
const lastRenderedRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)('');
|
|
5869
6836
|
const renderCountRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(0);
|
|
6837
|
+
// Ref to track latest streaming content for throttle pattern
|
|
6838
|
+
const latestStreamContentRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)('');
|
|
5870
6839
|
// Analyze incomplete blocks
|
|
5871
6840
|
const incompleteBlockInfo = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => analyzeIncompleteMarkdown(content), [content]);
|
|
5872
6841
|
const hasIncompleteBlocks = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => hasAnyIncompleteBlocks(incompleteBlockInfo), [incompleteBlockInfo]);
|
|
@@ -5909,40 +6878,48 @@ function useStreamingMarkdown(content, options = {}) {
|
|
|
5909
6878
|
}
|
|
5910
6879
|
setIsRendering(false);
|
|
5911
6880
|
}, [isStreaming, hasIncompleteBlocks, incompleteBlockInfo, minContentLength]);
|
|
5912
|
-
// Effect for
|
|
6881
|
+
// Effect for throttled rendering during streaming
|
|
6882
|
+
// Uses throttle instead of debounce: debounce resets the timer on every content
|
|
6883
|
+
// change, so if tokens arrive faster than debounceMs, rendering never fires.
|
|
6884
|
+
// Throttle schedules a render and lets it fire even when content updates again,
|
|
6885
|
+
// ensuring at least one render per debounceMs window.
|
|
5913
6886
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
5914
|
-
// Clear any pending timeout
|
|
5915
|
-
if (timeoutRef.current) {
|
|
5916
|
-
clearTimeout(timeoutRef.current);
|
|
5917
|
-
timeoutRef.current = null;
|
|
5918
|
-
}
|
|
5919
6887
|
// Empty content - clear immediately
|
|
5920
6888
|
if (!content) {
|
|
5921
6889
|
setRenderedHtml('');
|
|
5922
6890
|
setIsRendering(false);
|
|
5923
6891
|
lastContentRef.current = '';
|
|
5924
6892
|
lastRenderedRef.current = '';
|
|
6893
|
+
latestStreamContentRef.current = '';
|
|
6894
|
+
if (timeoutRef.current) {
|
|
6895
|
+
clearTimeout(timeoutRef.current);
|
|
6896
|
+
timeoutRef.current = null;
|
|
6897
|
+
}
|
|
5925
6898
|
return;
|
|
5926
6899
|
}
|
|
5927
6900
|
// Force immediate render (e.g., when streaming stops)
|
|
5928
6901
|
if (forceImmediate || !isStreaming) {
|
|
6902
|
+
if (timeoutRef.current) {
|
|
6903
|
+
clearTimeout(timeoutRef.current);
|
|
6904
|
+
timeoutRef.current = null;
|
|
6905
|
+
}
|
|
5929
6906
|
renderContent(content, true);
|
|
5930
6907
|
return;
|
|
5931
6908
|
}
|
|
5932
|
-
//
|
|
6909
|
+
// Streaming: throttle pattern — always track latest content,
|
|
6910
|
+
// only schedule a new render if none is pending
|
|
5933
6911
|
setIsRendering(true);
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
// Cleanup
|
|
5940
|
-
return () => {
|
|
5941
|
-
if (timeoutRef.current) {
|
|
5942
|
-
clearTimeout(timeoutRef.current);
|
|
6912
|
+
latestStreamContentRef.current = content;
|
|
6913
|
+
if (!timeoutRef.current) {
|
|
6914
|
+
// Dynamic delay: shorter for longer content (user is waiting)
|
|
6915
|
+
const dynamicDelay = content.length > 500 ? debounceMs * 0.5 : debounceMs;
|
|
6916
|
+
timeoutRef.current = setTimeout(() => {
|
|
5943
6917
|
timeoutRef.current = null;
|
|
5944
|
-
|
|
5945
|
-
|
|
6918
|
+
renderContent(latestStreamContentRef.current);
|
|
6919
|
+
}, dynamicDelay);
|
|
6920
|
+
}
|
|
6921
|
+
// No cleanup that clears the timeout — throttle pattern intentionally
|
|
6922
|
+
// lets the scheduled render fire even when content updates again
|
|
5946
6923
|
}, [content, isStreaming, forceImmediate, debounceMs, renderContent]);
|
|
5947
6924
|
// Final render when streaming stops
|
|
5948
6925
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
@@ -8849,7 +9826,6 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
8849
9826
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
8850
9827
|
/* harmony export */ DEFAULT_AGENT_PROMPTS: () => (/* binding */ DEFAULT_AGENT_PROMPTS),
|
|
8851
9828
|
/* harmony export */ DEFAULT_ATHENA_QUERY_PROMPT: () => (/* binding */ DEFAULT_ATHENA_QUERY_PROMPT),
|
|
8852
|
-
/* harmony export */ DEFAULT_LANGCHAIN_SYSTEM_PROMPT: () => (/* binding */ DEFAULT_LANGCHAIN_SYSTEM_PROMPT),
|
|
8853
9829
|
/* harmony export */ DEFAULT_PLANNER_PROMPT: () => (/* binding */ DEFAULT_PLANNER_PROMPT),
|
|
8854
9830
|
/* harmony export */ DEFAULT_PYTHON_DEVELOPER_PROMPT: () => (/* binding */ DEFAULT_PYTHON_DEVELOPER_PROMPT),
|
|
8855
9831
|
/* harmony export */ DEFAULT_RESEARCHER_PROMPT: () => (/* binding */ DEFAULT_RESEARCHER_PROMPT),
|
|
@@ -8919,7 +9895,6 @@ async function fetchDefaultPrompts() {
|
|
|
8919
9895
|
console.error('[ApiKeyManager] Failed to fetch default prompts:', error);
|
|
8920
9896
|
// Return fallback
|
|
8921
9897
|
return {
|
|
8922
|
-
single: FALLBACK_PROMPT,
|
|
8923
9898
|
planner: FALLBACK_PROMPT,
|
|
8924
9899
|
python_developer: FALLBACK_PROMPT,
|
|
8925
9900
|
researcher: FALLBACK_PROMPT,
|
|
@@ -8940,7 +9915,6 @@ function clearCachedPrompts() {
|
|
|
8940
9915
|
cachedPrompts = null;
|
|
8941
9916
|
}
|
|
8942
9917
|
// Legacy exports for backward compatibility (will be populated after fetch)
|
|
8943
|
-
let DEFAULT_LANGCHAIN_SYSTEM_PROMPT = FALLBACK_PROMPT;
|
|
8944
9918
|
let DEFAULT_PLANNER_PROMPT = FALLBACK_PROMPT;
|
|
8945
9919
|
let DEFAULT_PYTHON_DEVELOPER_PROMPT = FALLBACK_PROMPT;
|
|
8946
9920
|
let DEFAULT_RESEARCHER_PROMPT = FALLBACK_PROMPT;
|
|
@@ -8957,7 +9931,6 @@ let DEFAULT_AGENT_PROMPTS = {
|
|
|
8957
9931
|
*/
|
|
8958
9932
|
async function initializePrompts() {
|
|
8959
9933
|
const prompts = await fetchDefaultPrompts();
|
|
8960
|
-
DEFAULT_LANGCHAIN_SYSTEM_PROMPT = prompts.single;
|
|
8961
9934
|
DEFAULT_PLANNER_PROMPT = prompts.planner;
|
|
8962
9935
|
DEFAULT_PYTHON_DEVELOPER_PROMPT = prompts.python_developer;
|
|
8963
9936
|
DEFAULT_RESEARCHER_PROMPT = prompts.researcher;
|
|
@@ -9057,8 +10030,6 @@ function getDefaultLLMConfig() {
|
|
|
9057
10030
|
endpoint: 'http://localhost:8000/v1',
|
|
9058
10031
|
model: 'default'
|
|
9059
10032
|
},
|
|
9060
|
-
systemPrompt: prompts?.single || DEFAULT_LANGCHAIN_SYSTEM_PROMPT,
|
|
9061
|
-
agentMode: 'single',
|
|
9062
10033
|
agentPrompts: prompts ? {
|
|
9063
10034
|
planner: prompts.planner,
|
|
9064
10035
|
python_developer: prompts.python_developer,
|
|
@@ -9474,7 +10445,7 @@ class ApiService {
|
|
|
9474
10445
|
* 단순 Chat용 스트리밍 - /chat/stream 사용
|
|
9475
10446
|
* 원래 main 브랜치의 구현 복원 - LangChain 에이전트 없이 단순 Q&A
|
|
9476
10447
|
*/
|
|
9477
|
-
async sendChatStream(request, onChunk, onMetadata, abortSignal) {
|
|
10448
|
+
async sendChatStream(request, onChunk, onMetadata, abortSignal, onDebug) {
|
|
9478
10449
|
const MAX_RETRIES = 10;
|
|
9479
10450
|
let currentConfig = request.llmConfig;
|
|
9480
10451
|
let lastError = null;
|
|
@@ -9483,7 +10454,7 @@ class ApiService {
|
|
|
9483
10454
|
? { ...request, llmConfig: (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.buildSingleKeyConfig)(currentConfig) }
|
|
9484
10455
|
: request;
|
|
9485
10456
|
try {
|
|
9486
|
-
await this.sendChatStreamInternal(requestToSend, onChunk, onMetadata, abortSignal);
|
|
10457
|
+
await this.sendChatStreamInternal(requestToSend, onChunk, onMetadata, abortSignal, onDebug);
|
|
9487
10458
|
(0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.resetKeyRotation)();
|
|
9488
10459
|
return;
|
|
9489
10460
|
}
|
|
@@ -9514,7 +10485,7 @@ class ApiService {
|
|
|
9514
10485
|
/**
|
|
9515
10486
|
* 단순 Chat 스트리밍 내부 구현 - /chat/stream 엔드포인트 사용
|
|
9516
10487
|
*/
|
|
9517
|
-
async sendChatStreamInternal(request, onChunk, onMetadata, abortSignal) {
|
|
10488
|
+
async sendChatStreamInternal(request, onChunk, onMetadata, abortSignal, onDebug) {
|
|
9518
10489
|
const response = await fetch(`${this.baseUrl}/chat/stream`, {
|
|
9519
10490
|
method: 'POST',
|
|
9520
10491
|
headers: this.getHeaders(),
|
|
@@ -9547,6 +10518,15 @@ class ApiService {
|
|
|
9547
10518
|
if (data.error) {
|
|
9548
10519
|
throw new Error(data.error);
|
|
9549
10520
|
}
|
|
10521
|
+
// Handle status events (auto-compact, etc.)
|
|
10522
|
+
if (data.status && onDebug) {
|
|
10523
|
+
if (data.icon) {
|
|
10524
|
+
onDebug({ status: data.status, icon: data.icon });
|
|
10525
|
+
}
|
|
10526
|
+
else {
|
|
10527
|
+
onDebug(data.status);
|
|
10528
|
+
}
|
|
10529
|
+
}
|
|
9550
10530
|
if (data.content) {
|
|
9551
10531
|
onChunk(data.content);
|
|
9552
10532
|
}
|
|
@@ -9660,9 +10640,7 @@ class ApiService {
|
|
|
9660
10640
|
recent_cells: request.context.selectedCells?.map(cell => ({ source: cell })) || []
|
|
9661
10641
|
} : undefined,
|
|
9662
10642
|
llmConfig: requestConfig,
|
|
9663
|
-
workspaceRoot: request.llmConfig?.workspaceRoot || '.'
|
|
9664
|
-
// Agent mode: single (default) or multi (planner + subagents)
|
|
9665
|
-
agentMode: request.llmConfig?.agentMode || 'single'
|
|
10643
|
+
workspaceRoot: request.llmConfig?.workspaceRoot || '.'
|
|
9666
10644
|
};
|
|
9667
10645
|
// Debug: log request size
|
|
9668
10646
|
const requestBody = JSON.stringify(langchainRequest);
|
|
@@ -9720,6 +10698,15 @@ class ApiService {
|
|
|
9720
10698
|
currentEventType = '';
|
|
9721
10699
|
continue;
|
|
9722
10700
|
}
|
|
10701
|
+
// Handle summary event (final_summary_tool result)
|
|
10702
|
+
if (currentEventType === 'summary' && data.summary !== undefined) {
|
|
10703
|
+
if (onChunk) {
|
|
10704
|
+
// Convert to JSON string so StreamingMessage can detect and render as summary card
|
|
10705
|
+
onChunk(JSON.stringify(data));
|
|
10706
|
+
}
|
|
10707
|
+
currentEventType = '';
|
|
10708
|
+
continue;
|
|
10709
|
+
}
|
|
9723
10710
|
if (currentEventType === 'complete' || currentEventType === 'done') {
|
|
9724
10711
|
properlyTerminated = true;
|
|
9725
10712
|
if (onDebugClear) {
|
|
@@ -9875,9 +10862,7 @@ class ApiService {
|
|
|
9875
10862
|
feedback
|
|
9876
10863
|
}],
|
|
9877
10864
|
llmConfig: requestConfig,
|
|
9878
|
-
workspaceRoot: llmConfig?.workspaceRoot || '.'
|
|
9879
|
-
// Agent mode: single (default) or multi (planner + subagents)
|
|
9880
|
-
agentMode: llmConfig?.agentMode || 'single'
|
|
10865
|
+
workspaceRoot: llmConfig?.workspaceRoot || '.'
|
|
9881
10866
|
};
|
|
9882
10867
|
const response = await fetch(`${this.baseUrl}/agent/langchain/resume`, {
|
|
9883
10868
|
method: 'POST',
|
|
@@ -9933,6 +10918,15 @@ class ApiService {
|
|
|
9933
10918
|
currentEventType = '';
|
|
9934
10919
|
continue;
|
|
9935
10920
|
}
|
|
10921
|
+
// Handle summary event (final_summary_tool result)
|
|
10922
|
+
if (currentEventType === 'summary' && data.summary !== undefined) {
|
|
10923
|
+
if (onChunk) {
|
|
10924
|
+
// Convert to JSON string so StreamingMessage can detect and render as summary card
|
|
10925
|
+
onChunk(JSON.stringify(data));
|
|
10926
|
+
}
|
|
10927
|
+
currentEventType = '';
|
|
10928
|
+
continue;
|
|
10929
|
+
}
|
|
9936
10930
|
if (currentEventType === 'complete' || currentEventType === 'done') {
|
|
9937
10931
|
properlyTerminated = true;
|
|
9938
10932
|
if (onDebugClear) {
|
|
@@ -10304,6 +11298,81 @@ class ApiService {
|
|
|
10304
11298
|
}
|
|
10305
11299
|
return response.json();
|
|
10306
11300
|
}
|
|
11301
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
11302
|
+
// Admin/User Config APIs (Settings Split)
|
|
11303
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
11304
|
+
/**
|
|
11305
|
+
* Check if current environment is in admin mode
|
|
11306
|
+
*/
|
|
11307
|
+
async checkIsAdmin() {
|
|
11308
|
+
const response = await fetch(`${this.baseUrl}/config/is-admin`, {
|
|
11309
|
+
method: 'GET',
|
|
11310
|
+
credentials: 'include'
|
|
11311
|
+
});
|
|
11312
|
+
if (!response.ok) {
|
|
11313
|
+
throw new Error('Failed to check admin mode');
|
|
11314
|
+
}
|
|
11315
|
+
return response.json();
|
|
11316
|
+
}
|
|
11317
|
+
/**
|
|
11318
|
+
* Get admin configuration (LLM settings, prompts, server URLs)
|
|
11319
|
+
* API keys are masked for security
|
|
11320
|
+
*/
|
|
11321
|
+
async getAdminConfig() {
|
|
11322
|
+
const response = await fetch(`${this.baseUrl}/config/admin`, {
|
|
11323
|
+
method: 'GET',
|
|
11324
|
+
credentials: 'include'
|
|
11325
|
+
});
|
|
11326
|
+
if (!response.ok) {
|
|
11327
|
+
throw new Error('Failed to load admin configuration');
|
|
11328
|
+
}
|
|
11329
|
+
return response.json();
|
|
11330
|
+
}
|
|
11331
|
+
/**
|
|
11332
|
+
* Update admin configuration
|
|
11333
|
+
*/
|
|
11334
|
+
async updateAdminConfig(updates) {
|
|
11335
|
+
const response = await fetch(`${this.baseUrl}/config/admin`, {
|
|
11336
|
+
method: 'POST',
|
|
11337
|
+
headers: this.getHeaders(),
|
|
11338
|
+
credentials: 'include',
|
|
11339
|
+
body: JSON.stringify(updates)
|
|
11340
|
+
});
|
|
11341
|
+
if (!response.ok) {
|
|
11342
|
+
const error = await response.text();
|
|
11343
|
+
throw new Error(`Failed to update admin configuration: ${error}`);
|
|
11344
|
+
}
|
|
11345
|
+
return response.json();
|
|
11346
|
+
}
|
|
11347
|
+
/**
|
|
11348
|
+
* Get user configuration (preferences)
|
|
11349
|
+
*/
|
|
11350
|
+
async getUserConfig() {
|
|
11351
|
+
const response = await fetch(`${this.baseUrl}/config/user`, {
|
|
11352
|
+
method: 'GET',
|
|
11353
|
+
credentials: 'include'
|
|
11354
|
+
});
|
|
11355
|
+
if (!response.ok) {
|
|
11356
|
+
throw new Error('Failed to load user configuration');
|
|
11357
|
+
}
|
|
11358
|
+
return response.json();
|
|
11359
|
+
}
|
|
11360
|
+
/**
|
|
11361
|
+
* Update user configuration
|
|
11362
|
+
*/
|
|
11363
|
+
async updateUserConfig(updates) {
|
|
11364
|
+
const response = await fetch(`${this.baseUrl}/config/user`, {
|
|
11365
|
+
method: 'POST',
|
|
11366
|
+
headers: this.getHeaders(),
|
|
11367
|
+
credentials: 'include',
|
|
11368
|
+
body: JSON.stringify(updates)
|
|
11369
|
+
});
|
|
11370
|
+
if (!response.ok) {
|
|
11371
|
+
const error = await response.text();
|
|
11372
|
+
throw new Error(`Failed to update user configuration: ${error}`);
|
|
11373
|
+
}
|
|
11374
|
+
return response.json();
|
|
11375
|
+
}
|
|
10307
11376
|
/**
|
|
10308
11377
|
* Get health status
|
|
10309
11378
|
*/
|
|
@@ -10481,6 +11550,34 @@ class ApiService {
|
|
|
10481
11550
|
return response.json();
|
|
10482
11551
|
}
|
|
10483
11552
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
11553
|
+
// Chat Compact API (Conversation Summarization)
|
|
11554
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
11555
|
+
/**
|
|
11556
|
+
* Compact conversation history by summarizing older messages.
|
|
11557
|
+
*
|
|
11558
|
+
* Strategy (Claude Code benchmark):
|
|
11559
|
+
* - Keep the last 3 messages intact
|
|
11560
|
+
* - Summarize all older messages using LLM
|
|
11561
|
+
* - Replace history with [summary] + [last 3 messages]
|
|
11562
|
+
*/
|
|
11563
|
+
async compactConversation(conversationId, llmConfig) {
|
|
11564
|
+
console.log('[ApiService] compactConversation:', conversationId);
|
|
11565
|
+
const response = await fetch(`${this.baseUrl}/chat/compact`, {
|
|
11566
|
+
method: 'POST',
|
|
11567
|
+
headers: this.getHeaders(),
|
|
11568
|
+
credentials: 'include',
|
|
11569
|
+
body: JSON.stringify({
|
|
11570
|
+
conversationId,
|
|
11571
|
+
llmConfig
|
|
11572
|
+
})
|
|
11573
|
+
});
|
|
11574
|
+
if (!response.ok) {
|
|
11575
|
+
const error = await response.text();
|
|
11576
|
+
throw new Error(`Failed to compact conversation: ${error}`);
|
|
11577
|
+
}
|
|
11578
|
+
return response.json();
|
|
11579
|
+
}
|
|
11580
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
10484
11581
|
// Context Provider APIs (@file autocomplete)
|
|
10485
11582
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
10486
11583
|
/**
|
|
@@ -12422,4 +13519,4 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
12422
13519
|
/***/ }
|
|
12423
13520
|
|
|
12424
13521
|
}]);
|
|
12425
|
-
//# sourceMappingURL=lib_index_js.
|
|
13522
|
+
//# sourceMappingURL=lib_index_js.df05d90f366bfd5fa023.js.map
|