hdsp-jupyter-extension 2.0.1__py3-none-any.whl → 2.0.3__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/langchain/__init__.py +18 -0
- agent_server/langchain/agent.py +694 -0
- agent_server/langchain/executors/__init__.py +15 -0
- agent_server/langchain/executors/jupyter_executor.py +429 -0
- agent_server/langchain/executors/notebook_searcher.py +477 -0
- agent_server/langchain/middleware/__init__.py +36 -0
- agent_server/langchain/middleware/code_search_middleware.py +278 -0
- agent_server/langchain/middleware/error_handling_middleware.py +338 -0
- agent_server/langchain/middleware/jupyter_execution_middleware.py +301 -0
- agent_server/langchain/middleware/rag_middleware.py +227 -0
- agent_server/langchain/middleware/validation_middleware.py +240 -0
- agent_server/langchain/state.py +159 -0
- agent_server/langchain/tools/__init__.py +39 -0
- agent_server/langchain/tools/file_tools.py +279 -0
- agent_server/langchain/tools/jupyter_tools.py +143 -0
- agent_server/langchain/tools/search_tools.py +309 -0
- agent_server/main.py +13 -0
- agent_server/routers/health.py +14 -0
- agent_server/routers/langchain_agent.py +1368 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.3.data}/data/share/jupyter/labextensions/hdsp-agent/build_log.json +1 -1
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.3.data}/data/share/jupyter/labextensions/hdsp-agent/package.json +2 -2
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2607ff74c74acfa83158.js → hdsp_jupyter_extension-2.0.3.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.634cf0ae0f3592d0882f.js +408 -4
- hdsp_jupyter_extension-2.0.3.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.634cf0ae0f3592d0882f.js.map +1 -0
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.622c1a5918b3aafb2315.js → hdsp_jupyter_extension-2.0.3.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.1366019c413f1d68467f.js +753 -65
- hdsp_jupyter_extension-2.0.3.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.1366019c413f1d68467f.js.map +1 -0
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.729f933de01ad5620730.js → hdsp_jupyter_extension-2.0.3.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.b6d91b150c0800bddfa4.js +8 -8
- hdsp_jupyter_extension-2.0.3.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.b6d91b150c0800bddfa4.js.map +1 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js → hdsp_jupyter_extension-2.0.3.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js +2 -209
- hdsp_jupyter_extension-2.0.3.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js.map +1 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js → hdsp_jupyter_extension-2.0.3.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js +209 -2
- hdsp_jupyter_extension-2.0.3.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js.map +1 -0
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js → hdsp_jupyter_extension-2.0.3.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js +212 -3
- hdsp_jupyter_extension-2.0.3.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js.map +1 -0
- {hdsp_jupyter_extension-2.0.1.dist-info → hdsp_jupyter_extension-2.0.3.dist-info}/METADATA +6 -1
- {hdsp_jupyter_extension-2.0.1.dist-info → hdsp_jupyter_extension-2.0.3.dist-info}/RECORD +66 -49
- jupyter_ext/_version.py +1 -1
- jupyter_ext/handlers.py +126 -1
- jupyter_ext/labextension/build_log.json +1 -1
- jupyter_ext/labextension/package.json +2 -2
- jupyter_ext/labextension/static/{frontend_styles_index_js.2607ff74c74acfa83158.js → frontend_styles_index_js.634cf0ae0f3592d0882f.js} +408 -4
- jupyter_ext/labextension/static/frontend_styles_index_js.634cf0ae0f3592d0882f.js.map +1 -0
- jupyter_ext/labextension/static/{lib_index_js.622c1a5918b3aafb2315.js → lib_index_js.1366019c413f1d68467f.js} +753 -65
- jupyter_ext/labextension/static/lib_index_js.1366019c413f1d68467f.js.map +1 -0
- jupyter_ext/labextension/static/{remoteEntry.729f933de01ad5620730.js → remoteEntry.b6d91b150c0800bddfa4.js} +8 -8
- jupyter_ext/labextension/static/remoteEntry.b6d91b150c0800bddfa4.js.map +1 -0
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js → jupyter_ext/labextension/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js +2 -209
- jupyter_ext/labextension/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js.map +1 -0
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js → jupyter_ext/labextension/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js +209 -2
- jupyter_ext/labextension/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js.map +1 -0
- jupyter_ext/labextension/static/{vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js → vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js} +212 -3
- jupyter_ext/labextension/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js.map +1 -0
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2607ff74c74acfa83158.js.map +0 -1
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.622c1a5918b3aafb2315.js.map +0 -1
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.729f933de01ad5620730.js.map +0 -1
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js.map +0 -1
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js.map +0 -1
- hdsp_jupyter_extension-2.0.1.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js.map +0 -1
- jupyter_ext/labextension/static/frontend_styles_index_js.2607ff74c74acfa83158.js.map +0 -1
- jupyter_ext/labextension/static/lib_index_js.622c1a5918b3aafb2315.js.map +0 -1
- jupyter_ext/labextension/static/remoteEntry.729f933de01ad5620730.js.map +0 -1
- jupyter_ext/labextension/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js.map +0 -1
- jupyter_ext/labextension/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js.map +0 -1
- jupyter_ext/labextension/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js.map +0 -1
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.3.data}/data/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.3.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.3.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.1.data → hdsp_jupyter_extension-2.0.3.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.1.data → hdsp_jupyter_extension-2.0.3.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.1.data → hdsp_jupyter_extension-2.0.3.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.1.data → hdsp_jupyter_extension-2.0.3.data}/data/share/jupyter/labextensions/hdsp-agent/static/style.js +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.3.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.1.data → hdsp_jupyter_extension-2.0.3.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.1.data → hdsp_jupyter_extension-2.0.3.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.1.data → hdsp_jupyter_extension-2.0.3.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.1.data → hdsp_jupyter_extension-2.0.3.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +0 -0
- {hdsp_jupyter_extension-2.0.1.data → hdsp_jupyter_extension-2.0.3.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.1.dist-info → hdsp_jupyter_extension-2.0.3.dist-info}/WHEEL +0 -0
- {hdsp_jupyter_extension-2.0.1.dist-info → hdsp_jupyter_extension-2.0.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -15,13 +15,15 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
15
15
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
16
16
|
/* harmony import */ var _jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @jupyterlab/ui-components */ "webpack/sharing/consume/default/@jupyterlab/ui-components");
|
|
17
17
|
/* harmony import */ var _jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_1__);
|
|
18
|
-
/* harmony import */ var
|
|
19
|
-
/* harmony import */ var
|
|
20
|
-
/* harmony import */ var
|
|
21
|
-
/* harmony import */ var
|
|
22
|
-
/* harmony import */ var
|
|
23
|
-
/* harmony import */ var
|
|
24
|
-
/* harmony import */ var
|
|
18
|
+
/* harmony import */ var _jupyterlab_notebook__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @jupyterlab/notebook */ "webpack/sharing/consume/default/@jupyterlab/notebook");
|
|
19
|
+
/* harmony import */ var _jupyterlab_notebook__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_jupyterlab_notebook__WEBPACK_IMPORTED_MODULE_2__);
|
|
20
|
+
/* harmony import */ var _SettingsPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./SettingsPanel */ "./lib/components/SettingsPanel.js");
|
|
21
|
+
/* harmony import */ var _services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../services/ApiKeyManager */ "./lib/services/ApiKeyManager.js");
|
|
22
|
+
/* harmony import */ var _services_AgentOrchestrator__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../services/AgentOrchestrator */ "./lib/services/AgentOrchestrator.js");
|
|
23
|
+
/* harmony import */ var _types_auto_agent__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../types/auto-agent */ "./lib/types/auto-agent.js");
|
|
24
|
+
/* harmony import */ var _utils_markdownRenderer__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../utils/markdownRenderer */ "./lib/utils/markdownRenderer.js");
|
|
25
|
+
/* harmony import */ var _FileSelectionDialog__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./FileSelectionDialog */ "./lib/components/FileSelectionDialog.js");
|
|
26
|
+
/* harmony import */ var _logoSvg__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../logoSvg */ "./lib/logoSvg.js");
|
|
25
27
|
/**
|
|
26
28
|
* Agent Panel - Main sidebar panel for Jupyter Agent
|
|
27
29
|
* Cursor AI Style: Unified Chat + Agent Interface
|
|
@@ -34,12 +36,13 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
|
|
39
|
+
|
|
37
40
|
// 로고 이미지 (SVG) - TypeScript 모듈에서 인라인 문자열로 import
|
|
38
41
|
|
|
39
42
|
// 탭바 아이콘 생성
|
|
40
43
|
const hdspTabIcon = new _jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_1__.LabIcon({
|
|
41
44
|
name: 'hdsp-agent:tab-icon',
|
|
42
|
-
svgstr:
|
|
45
|
+
svgstr: _logoSvg__WEBPACK_IMPORTED_MODULE_9__.tabbarLogoSvg
|
|
43
46
|
});
|
|
44
47
|
// Agent 명령어 감지 함수
|
|
45
48
|
const isAgentCommand = (input) => {
|
|
@@ -196,6 +199,194 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
196
199
|
// File selection state
|
|
197
200
|
const [fileSelectionMetadata, setFileSelectionMetadata] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
198
201
|
const [pendingAgentRequest, setPendingAgentRequest] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
202
|
+
// Human-in-the-Loop state
|
|
203
|
+
const [debugStatus, setDebugStatus] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
204
|
+
const [interruptData, setInterruptData] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
|
205
|
+
// Todo list state (from TodoListMiddleware)
|
|
206
|
+
const [todos, setTodos] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);
|
|
207
|
+
const [isTodoExpanded, setIsTodoExpanded] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
|
|
208
|
+
const interruptMessageIdRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
|
|
209
|
+
const approvalPendingRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false);
|
|
210
|
+
const pendingToolCallsRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)([]);
|
|
211
|
+
const handledToolCallKeysRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(new Set());
|
|
212
|
+
const getActiveNotebookPanel = () => {
|
|
213
|
+
const app = window.jupyterapp;
|
|
214
|
+
if (app?.shell?.currentWidget) {
|
|
215
|
+
const currentWidget = app.shell.currentWidget;
|
|
216
|
+
if (currentWidget instanceof _jupyterlab_notebook__WEBPACK_IMPORTED_MODULE_2__.NotebookPanel) {
|
|
217
|
+
return currentWidget;
|
|
218
|
+
}
|
|
219
|
+
if ('content' in currentWidget && currentWidget.content?.model) {
|
|
220
|
+
return currentWidget;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return notebookTracker?.currentWidget || null;
|
|
224
|
+
};
|
|
225
|
+
const insertCell = (notebook, cellType, source) => {
|
|
226
|
+
const model = notebook.content?.model;
|
|
227
|
+
if (!model?.sharedModel) {
|
|
228
|
+
console.warn('[AgentPanel] Notebook model not ready for insert');
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
const insertIndex = model.cells.length;
|
|
232
|
+
model.sharedModel.insertCell(insertIndex, {
|
|
233
|
+
cell_type: cellType,
|
|
234
|
+
source
|
|
235
|
+
});
|
|
236
|
+
notebook.content.activeCellIndex = insertIndex;
|
|
237
|
+
return insertIndex;
|
|
238
|
+
};
|
|
239
|
+
const executeCell = async (notebook, cellIndex) => {
|
|
240
|
+
try {
|
|
241
|
+
await notebook.sessionContext.ready;
|
|
242
|
+
notebook.content.activeCellIndex = cellIndex;
|
|
243
|
+
await _jupyterlab_notebook__WEBPACK_IMPORTED_MODULE_2__.NotebookActions.run(notebook.content, notebook.sessionContext);
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
console.error('[AgentPanel] Cell execution failed:', error);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
const captureExecutionResult = (notebook, cellIndex) => {
|
|
250
|
+
const cell = notebook.content.widgets[cellIndex];
|
|
251
|
+
const model = cell?.model;
|
|
252
|
+
const outputs = model?.outputs;
|
|
253
|
+
const rawState = model?.executionState;
|
|
254
|
+
const executionState = typeof rawState === 'string'
|
|
255
|
+
? rawState
|
|
256
|
+
: rawState?.get?.() ?? rawState?.value ?? null;
|
|
257
|
+
const result = {
|
|
258
|
+
success: true,
|
|
259
|
+
output: '',
|
|
260
|
+
error: undefined,
|
|
261
|
+
error_type: undefined,
|
|
262
|
+
traceback: undefined,
|
|
263
|
+
execution_count: model?.executionCount ?? null,
|
|
264
|
+
execution_state: executionState,
|
|
265
|
+
kernel_status: notebook.sessionContext?.session?.kernel?.status ?? null,
|
|
266
|
+
cell_type: model?.type ?? null,
|
|
267
|
+
outputs: [],
|
|
268
|
+
};
|
|
269
|
+
if (!outputs || outputs.length === 0) {
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
const stripImageData = (data) => {
|
|
273
|
+
const filtered = {};
|
|
274
|
+
Object.keys(data || {}).forEach((key) => {
|
|
275
|
+
if (!key.toLowerCase().startsWith('image/')) {
|
|
276
|
+
filtered[key] = data[key];
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
return filtered;
|
|
280
|
+
};
|
|
281
|
+
const errorSignatures = [
|
|
282
|
+
'command not found',
|
|
283
|
+
'ModuleNotFoundError',
|
|
284
|
+
'No module named',
|
|
285
|
+
'Traceback (most recent call last)',
|
|
286
|
+
'Error:',
|
|
287
|
+
];
|
|
288
|
+
for (let i = 0; i < outputs.length; i++) {
|
|
289
|
+
const output = outputs.get(i);
|
|
290
|
+
const json = output.toJSON?.() || output;
|
|
291
|
+
let sanitizedOutput = json;
|
|
292
|
+
if (output.type === 'error') {
|
|
293
|
+
result.outputs.push(sanitizedOutput);
|
|
294
|
+
result.success = false;
|
|
295
|
+
result.error_type = json.ename;
|
|
296
|
+
result.error = json.evalue;
|
|
297
|
+
if (Array.isArray(json.traceback)) {
|
|
298
|
+
result.traceback = json.traceback;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
else if (output.type === 'stream' && json.text) {
|
|
302
|
+
result.outputs.push(sanitizedOutput);
|
|
303
|
+
result.output += json.text;
|
|
304
|
+
const lowerText = json.text.toLowerCase();
|
|
305
|
+
if (errorSignatures.some(signature => lowerText.includes(signature.toLowerCase()))) {
|
|
306
|
+
result.success = false;
|
|
307
|
+
result.error_type = 'runtime_error';
|
|
308
|
+
result.error = json.text.trim();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
else if ((output.type === 'execute_result' || output.type === 'display_data') && json.data) {
|
|
312
|
+
const filteredData = stripImageData(json.data);
|
|
313
|
+
if (Object.keys(filteredData).length > 0) {
|
|
314
|
+
sanitizedOutput = { ...json, data: filteredData };
|
|
315
|
+
result.outputs.push(sanitizedOutput);
|
|
316
|
+
result.output += JSON.stringify(filteredData);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return result;
|
|
321
|
+
};
|
|
322
|
+
const executePendingApproval = async () => {
|
|
323
|
+
const notebook = getActiveNotebookPanel();
|
|
324
|
+
if (!notebook) {
|
|
325
|
+
console.warn('[AgentPanel] No active notebook to execute cell');
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
const pending = pendingToolCallsRef.current;
|
|
329
|
+
if (pending.length === 0) {
|
|
330
|
+
approvalPendingRef.current = false;
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
const next = pending.shift();
|
|
334
|
+
if (!next) {
|
|
335
|
+
approvalPendingRef.current = false;
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
if (next.tool === 'jupyter_cell' && next.code && typeof next.cellIndex === 'number') {
|
|
339
|
+
await executeCell(notebook, next.cellIndex);
|
|
340
|
+
const execResult = captureExecutionResult(notebook, next.cellIndex);
|
|
341
|
+
execResult.code = next.code;
|
|
342
|
+
next.execution_result = execResult;
|
|
343
|
+
console.log('[AgentPanel] Executed approved code cell from tool call');
|
|
344
|
+
approvalPendingRef.current = pendingToolCallsRef.current.length > 0;
|
|
345
|
+
return next;
|
|
346
|
+
}
|
|
347
|
+
approvalPendingRef.current = pendingToolCallsRef.current.length > 0;
|
|
348
|
+
return next;
|
|
349
|
+
};
|
|
350
|
+
const queueApprovalCell = (code) => {
|
|
351
|
+
const notebook = getActiveNotebookPanel();
|
|
352
|
+
if (!notebook) {
|
|
353
|
+
console.warn('[AgentPanel] No active notebook to add approval cell');
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const key = `jupyter_cell:${code}`;
|
|
357
|
+
if (handledToolCallKeysRef.current.has(key)) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const index = insertCell(notebook, 'code', code);
|
|
361
|
+
if (index === null) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
handledToolCallKeysRef.current.add(key);
|
|
365
|
+
approvalPendingRef.current = true;
|
|
366
|
+
pendingToolCallsRef.current.push({ tool: 'jupyter_cell', code, cellIndex: index });
|
|
367
|
+
console.log('[AgentPanel] Added code cell pending approval via interrupt');
|
|
368
|
+
};
|
|
369
|
+
const handleToolCall = (toolCall) => {
|
|
370
|
+
const key = `${toolCall.tool}:${toolCall.code || toolCall.content || ''}`;
|
|
371
|
+
if (handledToolCallKeysRef.current.has(key)) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (toolCall.tool === 'jupyter_cell') {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
if (toolCall.tool === 'markdown' && toolCall.content) {
|
|
378
|
+
const notebook = getActiveNotebookPanel();
|
|
379
|
+
if (!notebook) {
|
|
380
|
+
console.warn('[AgentPanel] No active notebook to add markdown');
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const index = insertCell(notebook, 'markdown', toolCall.content);
|
|
384
|
+
if (index !== null) {
|
|
385
|
+
handledToolCallKeysRef.current.add(key);
|
|
386
|
+
console.log('[AgentPanel] Added markdown cell from tool call');
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
};
|
|
199
390
|
// 모드 변경 시 로컬 스토리지에 저장
|
|
200
391
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
201
392
|
try {
|
|
@@ -211,6 +402,10 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
211
402
|
const currentCellIdRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
|
|
212
403
|
const currentCellIndexRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
|
|
213
404
|
const orchestratorRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
|
|
405
|
+
const makeMessageId = (suffix) => {
|
|
406
|
+
const base = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
407
|
+
return suffix ? `${base}-${suffix}` : base;
|
|
408
|
+
};
|
|
214
409
|
// Expose handleSendMessage via ref
|
|
215
410
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useImperativeHandle)(ref, () => ({
|
|
216
411
|
handleSendMessage: async () => {
|
|
@@ -342,7 +537,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
342
537
|
// CRITICAL: Prevent concurrent agent executions
|
|
343
538
|
if (isAgentRunning) {
|
|
344
539
|
const errorMessage = {
|
|
345
|
-
id:
|
|
540
|
+
id: makeMessageId(),
|
|
346
541
|
role: 'assistant',
|
|
347
542
|
content: '⚠️ 이전 작업이 아직 실행 중입니다. 완료될 때까지 기다려주세요.',
|
|
348
543
|
timestamp: Date.now(),
|
|
@@ -373,7 +568,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
373
568
|
if (!notebook || !sessionContext) {
|
|
374
569
|
// 노트북이 없으면 에러 메시지 표시
|
|
375
570
|
const errorMessage = {
|
|
376
|
-
id:
|
|
571
|
+
id: makeMessageId(),
|
|
377
572
|
role: 'assistant',
|
|
378
573
|
content: '노트북을 먼저 열어주세요. Agent 실행은 활성 노트북이 필요합니다.',
|
|
379
574
|
timestamp: Date.now(),
|
|
@@ -382,8 +577,8 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
382
577
|
return;
|
|
383
578
|
}
|
|
384
579
|
// ★ 현재 활성화된 노트북으로 AgentOrchestrator 재생성 (탭 전환 대응)
|
|
385
|
-
const config = { ...
|
|
386
|
-
const orchestrator = new
|
|
580
|
+
const config = { ..._types_auto_agent__WEBPACK_IMPORTED_MODULE_6__.DEFAULT_AUTO_AGENT_CONFIG, executionSpeed };
|
|
581
|
+
const orchestrator = new _services_AgentOrchestrator__WEBPACK_IMPORTED_MODULE_5__.AgentOrchestrator(notebook, sessionContext, apiService, config);
|
|
387
582
|
// Agent 실행 메시지 생성
|
|
388
583
|
const agentMessageId = `agent-${Date.now()}`;
|
|
389
584
|
const agentMessage = {
|
|
@@ -402,7 +597,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
402
597
|
setIsAgentRunning(true);
|
|
403
598
|
try {
|
|
404
599
|
// Get current llmConfig for the agent execution
|
|
405
|
-
const currentLlmConfig = llmConfig || (0,
|
|
600
|
+
const currentLlmConfig = llmConfig || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_4__.getLLMConfig)() || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_4__.getDefaultLLMConfig)();
|
|
406
601
|
const result = await orchestrator.executeTask(request, notebook, (newStatus) => {
|
|
407
602
|
// 실시간 상태 업데이트
|
|
408
603
|
setMessages(prev => prev.map(msg => msg.id === agentMessageId && 'type' in msg && msg.type === 'agent_execution'
|
|
@@ -470,10 +665,10 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
470
665
|
}, [notebookTracker, apiService]);
|
|
471
666
|
const loadConfig = () => {
|
|
472
667
|
// Load from localStorage using ApiKeyManager
|
|
473
|
-
const config = (0,
|
|
668
|
+
const config = (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_4__.getLLMConfig)();
|
|
474
669
|
if (!config) {
|
|
475
670
|
console.log('[AgentPanel] No config in localStorage, using default');
|
|
476
|
-
const defaultConfig = (0,
|
|
671
|
+
const defaultConfig = (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_4__.getDefaultLLMConfig)();
|
|
477
672
|
setLlmConfig(defaultConfig);
|
|
478
673
|
return;
|
|
479
674
|
}
|
|
@@ -639,7 +834,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
639
834
|
console.log('[AgentPanel] File fix response received, fixed files:', fixedFiles.length);
|
|
640
835
|
// Assistant 메시지로 응답 표시
|
|
641
836
|
const assistantMessage = {
|
|
642
|
-
id:
|
|
837
|
+
id: makeMessageId('file-fix'),
|
|
643
838
|
role: 'assistant',
|
|
644
839
|
content: response,
|
|
645
840
|
timestamp: Date.now(),
|
|
@@ -658,7 +853,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
658
853
|
if (success) {
|
|
659
854
|
// 성공 메시지
|
|
660
855
|
const successMessage = {
|
|
661
|
-
id:
|
|
856
|
+
id: makeMessageId('apply-success'),
|
|
662
857
|
role: 'assistant',
|
|
663
858
|
content: `✅ **${fix.path}** 파일이 수정되었습니다.\n\n파일 에디터에서 변경사항을 확인하세요.`,
|
|
664
859
|
timestamp: Date.now(),
|
|
@@ -674,7 +869,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
674
869
|
// 에러 메시지 추가 헬퍼
|
|
675
870
|
const addErrorMessage = (message) => {
|
|
676
871
|
const errorMessage = {
|
|
677
|
-
id:
|
|
872
|
+
id: makeMessageId('error'),
|
|
678
873
|
role: 'assistant',
|
|
679
874
|
content: `⚠️ ${message}`,
|
|
680
875
|
timestamp: Date.now(),
|
|
@@ -684,9 +879,9 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
684
879
|
const handleSaveConfig = (config) => {
|
|
685
880
|
console.log('[AgentPanel] Saving config to localStorage');
|
|
686
881
|
console.log('Provider:', config.provider);
|
|
687
|
-
console.log('API Key configured:', (0,
|
|
882
|
+
console.log('API Key configured:', (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_4__.hasValidApiKey)(config) ? '✓ Yes' : '✗ No');
|
|
688
883
|
// Save to localStorage using ApiKeyManager
|
|
689
|
-
(0,
|
|
884
|
+
(0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_4__.saveLLMConfig)(config);
|
|
690
885
|
// Update state
|
|
691
886
|
setLlmConfig(config);
|
|
692
887
|
console.log('[AgentPanel] Config saved successfully');
|
|
@@ -766,10 +961,17 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
766
961
|
setIsAgentRunning(false);
|
|
767
962
|
setCurrentAgentMessageId(null);
|
|
768
963
|
};
|
|
769
|
-
// Auto-scroll to bottom when messages change
|
|
964
|
+
// Auto-scroll to bottom when messages change or streaming
|
|
770
965
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
771
|
-
|
|
772
|
-
|
|
966
|
+
if (isStreaming) {
|
|
967
|
+
// 스트리밍 중에는 즉시 스크롤
|
|
968
|
+
messagesEndRef.current?.scrollIntoView({ behavior: 'auto' });
|
|
969
|
+
}
|
|
970
|
+
else {
|
|
971
|
+
// 일반 메시지 추가 시 부드러운 스크롤
|
|
972
|
+
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
973
|
+
}
|
|
974
|
+
}, [messages, isStreaming]);
|
|
773
975
|
// Extract and store code blocks from messages, setup button listeners
|
|
774
976
|
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
|
775
977
|
// Use a small delay to ensure DOM is updated after message rendering
|
|
@@ -1360,6 +1562,171 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
1360
1562
|
div.textContent = text;
|
|
1361
1563
|
return div.innerHTML;
|
|
1362
1564
|
};
|
|
1565
|
+
const isAutoApprovedCode = (code) => {
|
|
1566
|
+
const lines = code
|
|
1567
|
+
.split('\n')
|
|
1568
|
+
.map(line => line.trim())
|
|
1569
|
+
.filter(line => line.length > 0 && !line.startsWith('#'));
|
|
1570
|
+
if (lines.length === 0) {
|
|
1571
|
+
return true;
|
|
1572
|
+
}
|
|
1573
|
+
const disallowedPatterns = [
|
|
1574
|
+
/(^|[^=!<>])=([^=]|$)/,
|
|
1575
|
+
/\.read_[a-zA-Z0-9_]*\s*\(/,
|
|
1576
|
+
/\bread_[a-zA-Z0-9_]*\s*\(/,
|
|
1577
|
+
/\.to_[a-zA-Z0-9_]*\s*\(/,
|
|
1578
|
+
];
|
|
1579
|
+
if (lines.some(line => disallowedPatterns.some(pattern => pattern.test(line)))) {
|
|
1580
|
+
return false;
|
|
1581
|
+
}
|
|
1582
|
+
const allowedPatterns = [
|
|
1583
|
+
/^print\(.+\)$/,
|
|
1584
|
+
/^display\(.+\)$/,
|
|
1585
|
+
/^df\.(head|info|describe)\s*\(.*\)$/,
|
|
1586
|
+
/^display\(df\.(head|info|describe)\s*\(.*\)\)$/,
|
|
1587
|
+
/^df\.(tail|sample)\s*\(.*\)$/,
|
|
1588
|
+
/^display\(df\.(tail|sample)\s*\(.*\)\)$/,
|
|
1589
|
+
/^df\.(shape|columns|dtypes)$/,
|
|
1590
|
+
/^import\s+pandas\s+as\s+pd$/,
|
|
1591
|
+
];
|
|
1592
|
+
return lines.every(line => allowedPatterns.some(pattern => pattern.test(line)));
|
|
1593
|
+
};
|
|
1594
|
+
const upsertInterruptMessage = (interrupt) => {
|
|
1595
|
+
const interruptMessageId = interruptMessageIdRef.current || makeMessageId('interrupt');
|
|
1596
|
+
interruptMessageIdRef.current = interruptMessageId;
|
|
1597
|
+
const interruptMessage = {
|
|
1598
|
+
id: interruptMessageId,
|
|
1599
|
+
role: 'system',
|
|
1600
|
+
content: interrupt.description || '코드 실행 승인이 필요합니다.',
|
|
1601
|
+
timestamp: Date.now(),
|
|
1602
|
+
metadata: { interrupt }
|
|
1603
|
+
};
|
|
1604
|
+
setMessages(prev => {
|
|
1605
|
+
const hasExisting = prev.some(msg => msg.id === interruptMessageId);
|
|
1606
|
+
if (hasExisting) {
|
|
1607
|
+
return prev.map(msg => msg.id === interruptMessageId ? interruptMessage : msg);
|
|
1608
|
+
}
|
|
1609
|
+
return [...prev, interruptMessage];
|
|
1610
|
+
});
|
|
1611
|
+
};
|
|
1612
|
+
const clearInterruptMessage = () => {
|
|
1613
|
+
if (!interruptMessageIdRef.current)
|
|
1614
|
+
return;
|
|
1615
|
+
const messageId = interruptMessageIdRef.current;
|
|
1616
|
+
interruptMessageIdRef.current = null;
|
|
1617
|
+
setMessages(prev => prev.map(msg => {
|
|
1618
|
+
// Only IChatMessage has metadata property (check via 'role' property)
|
|
1619
|
+
if (msg.id === messageId && 'role' in msg) {
|
|
1620
|
+
const chatMsg = msg;
|
|
1621
|
+
return {
|
|
1622
|
+
...chatMsg,
|
|
1623
|
+
metadata: {
|
|
1624
|
+
...chatMsg.metadata,
|
|
1625
|
+
interrupt: {
|
|
1626
|
+
...(chatMsg.metadata?.interrupt || {}),
|
|
1627
|
+
resolved: true
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
return msg;
|
|
1633
|
+
}));
|
|
1634
|
+
};
|
|
1635
|
+
const resumeFromInterrupt = async (interrupt, decision) => {
|
|
1636
|
+
const { threadId } = interrupt;
|
|
1637
|
+
setInterruptData(null);
|
|
1638
|
+
setDebugStatus(null);
|
|
1639
|
+
clearInterruptMessage();
|
|
1640
|
+
let resumeDecision = decision;
|
|
1641
|
+
let resumeArgs = undefined;
|
|
1642
|
+
if (decision === 'approve') {
|
|
1643
|
+
const executed = await executePendingApproval();
|
|
1644
|
+
if (executed && executed.tool === 'jupyter_cell') {
|
|
1645
|
+
resumeDecision = 'edit';
|
|
1646
|
+
resumeArgs = {
|
|
1647
|
+
code: executed.code,
|
|
1648
|
+
execution_result: executed.execution_result
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
else if (executed && executed.tool === 'markdown') {
|
|
1652
|
+
resumeDecision = 'edit';
|
|
1653
|
+
resumeArgs = {
|
|
1654
|
+
content: executed.content
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
else {
|
|
1659
|
+
pendingToolCallsRef.current.shift();
|
|
1660
|
+
approvalPendingRef.current = pendingToolCallsRef.current.length > 0;
|
|
1661
|
+
}
|
|
1662
|
+
setIsLoading(true);
|
|
1663
|
+
setIsStreaming(true);
|
|
1664
|
+
let interrupted = false;
|
|
1665
|
+
// 항상 새 메시지 생성 - 승인 UI 아래에 append되도록
|
|
1666
|
+
const assistantMessageId = makeMessageId('assistant');
|
|
1667
|
+
setStreamingMessageId(assistantMessageId);
|
|
1668
|
+
// 새 메시지 추가 (맨 아래에 append)
|
|
1669
|
+
setMessages(prev => [
|
|
1670
|
+
...prev,
|
|
1671
|
+
{
|
|
1672
|
+
id: assistantMessageId,
|
|
1673
|
+
role: 'assistant',
|
|
1674
|
+
content: '',
|
|
1675
|
+
timestamp: Date.now()
|
|
1676
|
+
}
|
|
1677
|
+
]);
|
|
1678
|
+
let streamedContent = '';
|
|
1679
|
+
try {
|
|
1680
|
+
await apiService.resumeAgent(threadId, resumeDecision, resumeArgs, resumeDecision === 'reject' ? 'User rejected this action' : undefined, llmConfig || undefined, (chunk) => {
|
|
1681
|
+
streamedContent += chunk;
|
|
1682
|
+
setMessages(prev => prev.map(msg => msg.id === assistantMessageId && isChatMessage(msg)
|
|
1683
|
+
? { ...msg, content: streamedContent }
|
|
1684
|
+
: msg));
|
|
1685
|
+
}, (status) => {
|
|
1686
|
+
setDebugStatus(status);
|
|
1687
|
+
}, (nextInterrupt) => {
|
|
1688
|
+
interrupted = true;
|
|
1689
|
+
approvalPendingRef.current = true;
|
|
1690
|
+
if (nextInterrupt.action === 'jupyter_cell_tool' && nextInterrupt.args?.code) {
|
|
1691
|
+
if (isAutoApprovedCode(nextInterrupt.args.code)) {
|
|
1692
|
+
queueApprovalCell(nextInterrupt.args.code);
|
|
1693
|
+
void resumeFromInterrupt(nextInterrupt, 'approve');
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
queueApprovalCell(nextInterrupt.args.code);
|
|
1697
|
+
}
|
|
1698
|
+
setInterruptData(nextInterrupt);
|
|
1699
|
+
upsertInterruptMessage(nextInterrupt);
|
|
1700
|
+
setIsLoading(false);
|
|
1701
|
+
setIsStreaming(false);
|
|
1702
|
+
}, (newTodos) => {
|
|
1703
|
+
setTodos(newTodos);
|
|
1704
|
+
}, () => {
|
|
1705
|
+
setDebugStatus(null);
|
|
1706
|
+
}, handleToolCall);
|
|
1707
|
+
}
|
|
1708
|
+
catch (error) {
|
|
1709
|
+
const message = error instanceof Error ? error.message : 'Failed to resume';
|
|
1710
|
+
setDebugStatus(`오류: ${message}`);
|
|
1711
|
+
console.error('Resume failed:', error);
|
|
1712
|
+
setMessages(prev => [
|
|
1713
|
+
...prev,
|
|
1714
|
+
{
|
|
1715
|
+
id: makeMessageId(),
|
|
1716
|
+
role: 'assistant',
|
|
1717
|
+
content: `Error: ${message}`,
|
|
1718
|
+
timestamp: Date.now()
|
|
1719
|
+
}
|
|
1720
|
+
]);
|
|
1721
|
+
}
|
|
1722
|
+
finally {
|
|
1723
|
+
setIsLoading(false);
|
|
1724
|
+
setIsStreaming(false);
|
|
1725
|
+
if (!interrupted) {
|
|
1726
|
+
approvalPendingRef.current = false;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
};
|
|
1363
1730
|
const handleSendMessage = async () => {
|
|
1364
1731
|
// Check if there's an LLM prompt stored (from cell action)
|
|
1365
1732
|
const textarea = messagesEndRef.current?.parentElement?.querySelector('.jp-agent-input');
|
|
@@ -1389,7 +1756,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
1389
1756
|
console.log('[AgentPanel] Agent mode: No notebook, but Python error detected - switching to file fix mode');
|
|
1390
1757
|
// User 메시지 추가
|
|
1391
1758
|
const userMessage = {
|
|
1392
|
-
id:
|
|
1759
|
+
id: makeMessageId(),
|
|
1393
1760
|
role: 'user',
|
|
1394
1761
|
content: currentInput,
|
|
1395
1762
|
timestamp: Date.now(),
|
|
@@ -1421,7 +1788,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
1421
1788
|
console.log('[AgentPanel] Agent mode: No notebook, file fix request detected - prompting for error details');
|
|
1422
1789
|
// User 메시지 추가
|
|
1423
1790
|
const userMessage = {
|
|
1424
|
-
id:
|
|
1791
|
+
id: makeMessageId(),
|
|
1425
1792
|
role: 'user',
|
|
1426
1793
|
content: currentInput,
|
|
1427
1794
|
timestamp: Date.now(),
|
|
@@ -1430,7 +1797,7 @@ const ChatPanel = (0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(({ apiServic
|
|
|
1430
1797
|
setInput('');
|
|
1431
1798
|
// 에러 메시지 요청 안내
|
|
1432
1799
|
const guideMessage = {
|
|
1433
|
-
id:
|
|
1800
|
+
id: makeMessageId('guide'),
|
|
1434
1801
|
role: 'assistant',
|
|
1435
1802
|
content: `파일 에러 수정을 도와드리겠습니다! 🔧
|
|
1436
1803
|
|
|
@@ -1462,7 +1829,7 @@ SyntaxError: '(' was never closed
|
|
|
1462
1829
|
// 노트북이 있으면 Agent 실행
|
|
1463
1830
|
// User 메시지 추가
|
|
1464
1831
|
const userMessage = {
|
|
1465
|
-
id:
|
|
1832
|
+
id: makeMessageId(),
|
|
1466
1833
|
role: 'user',
|
|
1467
1834
|
content: `@agent ${currentInput}`,
|
|
1468
1835
|
timestamp: Date.now(),
|
|
@@ -1480,7 +1847,7 @@ SyntaxError: '(' was never closed
|
|
|
1480
1847
|
if (agentRequest) {
|
|
1481
1848
|
// User 메시지 추가
|
|
1482
1849
|
const userMessage = {
|
|
1483
|
-
id:
|
|
1850
|
+
id: makeMessageId(),
|
|
1484
1851
|
role: 'user',
|
|
1485
1852
|
content: currentInput,
|
|
1486
1853
|
timestamp: Date.now(),
|
|
@@ -1497,7 +1864,7 @@ SyntaxError: '(' was never closed
|
|
|
1497
1864
|
console.log('[AgentPanel] Python error detected in message');
|
|
1498
1865
|
// User 메시지 추가
|
|
1499
1866
|
const userMessage = {
|
|
1500
|
-
id:
|
|
1867
|
+
id: makeMessageId(),
|
|
1501
1868
|
role: 'user',
|
|
1502
1869
|
content: currentInput,
|
|
1503
1870
|
timestamp: Date.now(),
|
|
@@ -1512,16 +1879,16 @@ SyntaxError: '(' was never closed
|
|
|
1512
1879
|
let currentConfig = llmConfig;
|
|
1513
1880
|
if (!currentConfig) {
|
|
1514
1881
|
// Config not loaded yet, try to load from localStorage
|
|
1515
|
-
currentConfig = (0,
|
|
1882
|
+
currentConfig = (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_4__.getLLMConfig)() || (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_4__.getDefaultLLMConfig)();
|
|
1516
1883
|
setLlmConfig(currentConfig);
|
|
1517
1884
|
}
|
|
1518
1885
|
// Check API key using ApiKeyManager
|
|
1519
|
-
const hasApiKey = (0,
|
|
1886
|
+
const hasApiKey = (0,_services_ApiKeyManager__WEBPACK_IMPORTED_MODULE_4__.hasValidApiKey)(currentConfig);
|
|
1520
1887
|
if (!hasApiKey) {
|
|
1521
1888
|
// Show error message and open settings
|
|
1522
1889
|
const providerName = llmConfig?.provider || 'LLM';
|
|
1523
1890
|
const errorMessage = {
|
|
1524
|
-
id:
|
|
1891
|
+
id: makeMessageId(),
|
|
1525
1892
|
role: 'assistant',
|
|
1526
1893
|
content: `API Key가 설정되지 않았습니다.\n\n${providerName === 'gemini' ? 'Gemini' : providerName === 'openai' ? 'OpenAI' : 'vLLM'} API Key를 먼저 설정해주세요.\n\n설정 버튼을 클릭하여 API Key를 입력하세요.`,
|
|
1527
1894
|
timestamp: Date.now()
|
|
@@ -1533,7 +1900,7 @@ SyntaxError: '(' was never closed
|
|
|
1533
1900
|
// Use the display prompt (input) for the user message, or use a fallback if input is empty
|
|
1534
1901
|
const displayContent = currentInput || (llmPrompt ? '셀 분석 요청' : '');
|
|
1535
1902
|
const userMessage = {
|
|
1536
|
-
id:
|
|
1903
|
+
id: makeMessageId(),
|
|
1537
1904
|
role: 'user',
|
|
1538
1905
|
content: displayContent,
|
|
1539
1906
|
timestamp: Date.now()
|
|
@@ -1551,7 +1918,7 @@ SyntaxError: '(' was never closed
|
|
|
1551
1918
|
pendingLlmPromptRef.current = null;
|
|
1552
1919
|
}
|
|
1553
1920
|
// Create assistant message ID for streaming updates
|
|
1554
|
-
const assistantMessageId =
|
|
1921
|
+
const assistantMessageId = makeMessageId('assistant');
|
|
1555
1922
|
let streamedContent = '';
|
|
1556
1923
|
setStreamingMessageId(assistantMessageId);
|
|
1557
1924
|
// Add empty assistant message that will be updated during streaming
|
|
@@ -1562,6 +1929,7 @@ SyntaxError: '(' was never closed
|
|
|
1562
1929
|
timestamp: Date.now()
|
|
1563
1930
|
};
|
|
1564
1931
|
setMessages(prev => [...prev, initialAssistantMessage]);
|
|
1932
|
+
let interrupted = false;
|
|
1565
1933
|
try {
|
|
1566
1934
|
// Use LLM prompt if available, otherwise use the display content
|
|
1567
1935
|
const messageToSend = llmPrompt || displayContent;
|
|
@@ -1594,14 +1962,47 @@ SyntaxError: '(' was never closed
|
|
|
1594
1962
|
}
|
|
1595
1963
|
: msg));
|
|
1596
1964
|
}
|
|
1597
|
-
}
|
|
1965
|
+
},
|
|
1966
|
+
// onDebug callback - show debug status in gray
|
|
1967
|
+
(status) => {
|
|
1968
|
+
setDebugStatus(status);
|
|
1969
|
+
},
|
|
1970
|
+
// onInterrupt callback - show approval dialog
|
|
1971
|
+
(interrupt) => {
|
|
1972
|
+
interrupted = true;
|
|
1973
|
+
approvalPendingRef.current = true;
|
|
1974
|
+
if (interrupt.action === 'jupyter_cell_tool' && interrupt.args?.code) {
|
|
1975
|
+
if (isAutoApprovedCode(interrupt.args.code)) {
|
|
1976
|
+
queueApprovalCell(interrupt.args.code);
|
|
1977
|
+
void resumeFromInterrupt(interrupt, 'approve');
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
queueApprovalCell(interrupt.args.code);
|
|
1981
|
+
}
|
|
1982
|
+
setInterruptData(interrupt);
|
|
1983
|
+
upsertInterruptMessage(interrupt);
|
|
1984
|
+
setIsLoading(false);
|
|
1985
|
+
setIsStreaming(false);
|
|
1986
|
+
},
|
|
1987
|
+
// onTodos callback - update todo list UI
|
|
1988
|
+
(newTodos) => {
|
|
1989
|
+
setTodos(newTodos);
|
|
1990
|
+
},
|
|
1991
|
+
// onDebugClear callback - clear debug status
|
|
1992
|
+
() => {
|
|
1993
|
+
setDebugStatus(null);
|
|
1994
|
+
},
|
|
1995
|
+
// onToolCall callback - add cells to notebook
|
|
1996
|
+
handleToolCall);
|
|
1598
1997
|
}
|
|
1599
1998
|
catch (error) {
|
|
1999
|
+
const message = error instanceof Error ? error.message : 'Failed to send message';
|
|
2000
|
+
setDebugStatus(`오류: ${message}`);
|
|
1600
2001
|
// Update the assistant message with error
|
|
1601
2002
|
setMessages(prev => prev.map(msg => msg.id === assistantMessageId && isChatMessage(msg)
|
|
1602
2003
|
? {
|
|
1603
2004
|
...msg,
|
|
1604
|
-
content: streamedContent + `\n\nError: ${
|
|
2005
|
+
content: streamedContent + `\n\nError: ${message}`
|
|
1605
2006
|
}
|
|
1606
2007
|
: msg));
|
|
1607
2008
|
}
|
|
@@ -1609,8 +2010,15 @@ SyntaxError: '(' was never closed
|
|
|
1609
2010
|
setIsLoading(false);
|
|
1610
2011
|
setIsStreaming(false);
|
|
1611
2012
|
setStreamingMessageId(null);
|
|
2013
|
+
// Keep completed todos visible after the run
|
|
1612
2014
|
}
|
|
1613
2015
|
};
|
|
2016
|
+
// Handle resume after user approval/rejection
|
|
2017
|
+
const handleResumeAgent = async (decision) => {
|
|
2018
|
+
if (!interruptData)
|
|
2019
|
+
return;
|
|
2020
|
+
await resumeFromInterrupt(interruptData, decision);
|
|
2021
|
+
};
|
|
1614
2022
|
const handleKeyDown = (e) => {
|
|
1615
2023
|
// Enter: 전송
|
|
1616
2024
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
@@ -1707,7 +2115,7 @@ SyntaxError: '(' was never closed
|
|
|
1707
2115
|
.map((tc, i) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { key: i, className: "jp-agent-execution-tool-tag" }, tc.tool)))))));
|
|
1708
2116
|
})))),
|
|
1709
2117
|
result && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `jp-agent-execution-result jp-agent-execution-result--${result.success ? 'success' : 'error'}` },
|
|
1710
|
-
result.finalAnswer && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-execution-result-message jp-RenderedHTMLCommon", dangerouslySetInnerHTML: { __html: (0,
|
|
2118
|
+
result.finalAnswer && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-execution-result-message jp-RenderedHTMLCommon", dangerouslySetInnerHTML: { __html: (0,_utils_markdownRenderer__WEBPACK_IMPORTED_MODULE_7__.formatMarkdownToHtml)(result.finalAnswer) } })),
|
|
1711
2119
|
result.error && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", { className: "jp-agent-execution-result-error" }, result.error)),
|
|
1712
2120
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-execution-result-stats" },
|
|
1713
2121
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null,
|
|
@@ -1724,10 +2132,40 @@ SyntaxError: '(' was never closed
|
|
|
1724
2132
|
const isChatMessage = (msg) => {
|
|
1725
2133
|
return !('type' in msg) || msg.type !== 'agent_execution';
|
|
1726
2134
|
};
|
|
2135
|
+
const mapStatusText = (raw) => {
|
|
2136
|
+
const normalized = raw.toLowerCase();
|
|
2137
|
+
if (normalized.includes('calling tool')) {
|
|
2138
|
+
return raw.replace(/Calling tool:/gi, '도구 호출 중:').trim();
|
|
2139
|
+
}
|
|
2140
|
+
if (normalized.includes('waiting for user approval')) {
|
|
2141
|
+
return '승인 대기 중...';
|
|
2142
|
+
}
|
|
2143
|
+
if (normalized.includes('resuming execution')) {
|
|
2144
|
+
return '승인 반영 후 실행 재개 중...';
|
|
2145
|
+
}
|
|
2146
|
+
if (normalized.includes('tool')) {
|
|
2147
|
+
return '도구 실행 중...';
|
|
2148
|
+
}
|
|
2149
|
+
return raw;
|
|
2150
|
+
};
|
|
2151
|
+
const getStatusText = () => {
|
|
2152
|
+
if (interruptData) {
|
|
2153
|
+
return `승인 대기: ${interruptData.action}`;
|
|
2154
|
+
}
|
|
2155
|
+
if (debugStatus) {
|
|
2156
|
+
return mapStatusText(debugStatus);
|
|
2157
|
+
}
|
|
2158
|
+
if (isLoading || isStreaming) {
|
|
2159
|
+
// 기본 상태 메시지 - todo 내용은 compact todo UI에서 표시
|
|
2160
|
+
return '요청 처리 중';
|
|
2161
|
+
}
|
|
2162
|
+
return null;
|
|
2163
|
+
};
|
|
2164
|
+
const statusText = getStatusText();
|
|
1727
2165
|
return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-panel" },
|
|
1728
|
-
showSettings && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
2166
|
+
showSettings && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_SettingsPanel__WEBPACK_IMPORTED_MODULE_3__.SettingsPanel, { onClose: () => setShowSettings(false), onSave: handleSaveConfig, currentConfig: llmConfig || undefined })),
|
|
1729
2167
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-header" },
|
|
1730
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-header-logo", dangerouslySetInnerHTML: { __html:
|
|
2168
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-header-logo", dangerouslySetInnerHTML: { __html: _logoSvg__WEBPACK_IMPORTED_MODULE_9__.headerLogoSvg } }),
|
|
1731
2169
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-header-buttons" },
|
|
1732
2170
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-clear-button", onClick: clearChat, title: "\uB300\uD654 \uCD08\uAE30\uD654" },
|
|
1733
2171
|
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" },
|
|
@@ -1754,13 +2192,46 @@ SyntaxError: '(' was never closed
|
|
|
1754
2192
|
: '메시지를 입력하거나 아래 버튼으로 Agent 모드를 선택하세요.'))) : (messages.map(msg => {
|
|
1755
2193
|
if (isChatMessage(msg)) {
|
|
1756
2194
|
// 일반 Chat 메시지
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className:
|
|
2195
|
+
const isAssistant = msg.role === 'assistant';
|
|
2196
|
+
return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { key: msg.id, className: isAssistant
|
|
2197
|
+
? 'jp-agent-message jp-agent-message-assistant-inline'
|
|
2198
|
+
: `jp-agent-message jp-agent-message-${msg.role}` },
|
|
2199
|
+
!isAssistant && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-message-header" },
|
|
2200
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-message-role" }, msg.role === 'user' ? '사용자' : msg.role === 'system' ? '승인 요청' : 'Agent'),
|
|
2201
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-message-time" }, new Date(msg.timestamp).toLocaleTimeString()))),
|
|
2202
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `jp-agent-message-content${streamingMessageId === msg.id ? ' streaming' : ''}` }, msg.role === 'system' && msg.metadata?.interrupt ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-interrupt-inline" },
|
|
2203
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-interrupt-description" }, msg.content),
|
|
2204
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-interrupt-action" },
|
|
2205
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-interrupt-action-args" }, (() => {
|
|
2206
|
+
const code = msg.metadata?.interrupt?.args?.code || msg.metadata?.interrupt?.args?.content || '';
|
|
2207
|
+
const lines = code.split('\n');
|
|
2208
|
+
const preview = lines.slice(0, 8).join('\n');
|
|
2209
|
+
const suffix = lines.length > 8 ? '\n...' : '';
|
|
2210
|
+
const resolved = msg.metadata?.interrupt?.resolved;
|
|
2211
|
+
const actionHtml = resolved
|
|
2212
|
+
? '<div class="jp-agent-interrupt-actions jp-agent-interrupt-actions--resolved">승인됨</div>'
|
|
2213
|
+
: `
|
|
2214
|
+
<div class="code-block-actions jp-agent-interrupt-actions">
|
|
2215
|
+
<button class="jp-agent-interrupt-approve-btn" data-action="approve">승인</button>
|
|
2216
|
+
<button class="jp-agent-interrupt-reject-btn" data-action="reject">거부</button>
|
|
2217
|
+
</div>
|
|
2218
|
+
`;
|
|
2219
|
+
return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-RenderedHTMLCommon", style: { padding: '0 4px' }, dangerouslySetInnerHTML: { __html: (0,_utils_markdownRenderer__WEBPACK_IMPORTED_MODULE_7__.formatMarkdownToHtml)(`\n\`\`\`python\n${preview}${suffix}\n\`\`\``).replace('</div>', `${actionHtml}</div>`) }, onClick: (event) => {
|
|
2220
|
+
const target = event.target;
|
|
2221
|
+
const action = target?.getAttribute?.('data-action');
|
|
2222
|
+
if (msg.metadata?.interrupt?.resolved) {
|
|
2223
|
+
return;
|
|
2224
|
+
}
|
|
2225
|
+
if (action === 'approve') {
|
|
2226
|
+
handleResumeAgent('approve');
|
|
2227
|
+
}
|
|
2228
|
+
else if (action === 'reject') {
|
|
2229
|
+
handleResumeAgent('reject');
|
|
2230
|
+
}
|
|
2231
|
+
} }));
|
|
2232
|
+
})())))) : msg.role === 'assistant' ? (
|
|
1762
2233
|
// Assistant(AI) 메시지: 마크다운 HTML 렌더링 + Jupyter 스타일 적용
|
|
1763
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-RenderedHTMLCommon", style: { padding: '0 5px' }, dangerouslySetInnerHTML: { __html: (0,
|
|
2234
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-RenderedHTMLCommon", style: { padding: '0 5px' }, dangerouslySetInnerHTML: { __html: (0,_utils_markdownRenderer__WEBPACK_IMPORTED_MODULE_7__.formatMarkdownToHtml)(msg.content) } })) : (
|
|
1764
2235
|
// User(사용자) 메시지: 텍스트 그대로 줄바꿈만 처리
|
|
1765
2236
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { style: { whiteSpace: 'pre-wrap' } }, msg.content)))));
|
|
1766
2237
|
}
|
|
@@ -1769,13 +2240,6 @@ SyntaxError: '(' was never closed
|
|
|
1769
2240
|
return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { key: msg.id, className: "jp-agent-message jp-agent-message-agent-execution" }, renderAgentExecutionMessage(msg)));
|
|
1770
2241
|
}
|
|
1771
2242
|
})),
|
|
1772
|
-
isLoading && !isStreaming && !isAgentRunning && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-message jp-agent-message-assistant" },
|
|
1773
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-message-header" },
|
|
1774
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-message-role" }, "Agent")),
|
|
1775
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-message-content jp-agent-loading" },
|
|
1776
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-loading-dot" }, "."),
|
|
1777
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-loading-dot" }, "."),
|
|
1778
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-loading-dot" }, ".")))),
|
|
1779
2243
|
showConsoleErrorNotification && lastConsoleError && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-console-error-notification" },
|
|
1780
2244
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-console-error-header" },
|
|
1781
2245
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("svg", { viewBox: "0 0 16 16", fill: "currentColor", width: "16", height: "16" },
|
|
@@ -1812,6 +2276,38 @@ SyntaxError: '(' was never closed
|
|
|
1812
2276
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-file-fix-apply", onClick: () => applyFileFix(fix), title: `${fix.path} 파일에 수정 적용` }, "\uC801\uC6A9\uD558\uAE30"))))),
|
|
1813
2277
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "jp-agent-file-fixes-dismiss", onClick: () => setPendingFileFixes([]), title: "\uC218\uC815 \uC81C\uC548 \uB2EB\uAE30" }, "\uB2EB\uAE30"))),
|
|
1814
2278
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { ref: messagesEndRef })),
|
|
2279
|
+
statusText && todos.length === 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `jp-agent-message jp-agent-message-debug${statusText.startsWith('오류:') ? ' jp-agent-message-debug-error' : ''}` },
|
|
2280
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-debug-content" },
|
|
2281
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-debug-branch", "aria-hidden": "true" }),
|
|
2282
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-debug-text" }, statusText),
|
|
2283
|
+
!statusText.startsWith('오류:') && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-debug-ellipsis", "aria-hidden": "true" }))))),
|
|
2284
|
+
todos.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-todo-compact" },
|
|
2285
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-todo-compact-header", onClick: () => setIsTodoExpanded(!isTodoExpanded) },
|
|
2286
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-todo-compact-left" },
|
|
2287
|
+
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" },
|
|
2288
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("path", { d: "M6 12l4-4-4-4" })),
|
|
2289
|
+
(() => {
|
|
2290
|
+
const currentTodo = todos.find(t => t.status === 'in_progress') || todos.find(t => t.status === 'pending');
|
|
2291
|
+
return currentTodo ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
|
|
2292
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-todo-compact-spinner" }),
|
|
2293
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-todo-compact-current" }, currentTodo.content))) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-todo-compact-current" }, "\u2713 \uBAA8\uB4E0 \uC791\uC5C5 \uC644\uB8CC"));
|
|
2294
|
+
})()),
|
|
2295
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-todo-compact-progress" },
|
|
2296
|
+
todos.filter(t => t.status === 'completed').length,
|
|
2297
|
+
"/",
|
|
2298
|
+
todos.length)),
|
|
2299
|
+
statusText && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: `jp-agent-message jp-agent-message-debug jp-agent-message-debug--inline${statusText.startsWith('오류:') ? ' jp-agent-message-debug-error' : ''}` },
|
|
2300
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-debug-content" },
|
|
2301
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-debug-branch", "aria-hidden": "true" }),
|
|
2302
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-debug-text" }, statusText),
|
|
2303
|
+
!statusText.startsWith('오류:') && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-debug-ellipsis", "aria-hidden": "true" }))))),
|
|
2304
|
+
isTodoExpanded && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-todo-expanded" }, todos.map((todo, index) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { key: index, className: `jp-agent-todo-item jp-agent-todo-item--${todo.status}` },
|
|
2305
|
+
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-todo-item-indicator" },
|
|
2306
|
+
todo.status === 'completed' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("svg", { viewBox: "0 0 16 16", fill: "currentColor", width: "12", height: "12" },
|
|
2307
|
+
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" }))),
|
|
2308
|
+
todo.status === 'in_progress' && react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-todo-item-spinner" }),
|
|
2309
|
+
todo.status === 'pending' && react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-todo-item-number" }, index + 1)),
|
|
2310
|
+
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)))))))),
|
|
1815
2311
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-input-container" },
|
|
1816
2312
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-input-wrapper" },
|
|
1817
2313
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("textarea", { className: `jp-agent-input ${inputMode === 'agent' ? 'jp-agent-input--agent-mode' : ''}`, value: input, onChange: (e) => setInput(e.target.value), onKeyDown: handleKeyDown, placeholder: inputMode === 'agent'
|
|
@@ -1842,7 +2338,7 @@ SyntaxError: '(' was never closed
|
|
|
1842
2338
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-mode-shortcut" }, "\uB178\uD2B8\uBD81 \uC790\uB3D9 \uC2E4\uD589"))))),
|
|
1843
2339
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "jp-agent-mode-hints" },
|
|
1844
2340
|
react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { className: "jp-agent-mode-hint" }, "\u21E7Tab \uBAA8\uB4DC \uC804\uD658")))),
|
|
1845
|
-
fileSelectionMetadata && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
2341
|
+
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 }))));
|
|
1846
2342
|
});
|
|
1847
2343
|
ChatPanel.displayName = 'ChatPanel';
|
|
1848
2344
|
/**
|
|
@@ -6857,7 +7353,7 @@ class ApiService {
|
|
|
6857
7353
|
* - Server receives ONLY ONE key per request
|
|
6858
7354
|
* - On 429 rate limit, frontend rotates key and retries with next key
|
|
6859
7355
|
*/
|
|
6860
|
-
async sendMessageStream(request, onChunk, onMetadata) {
|
|
7356
|
+
async sendMessageStream(request, onChunk, onMetadata, onDebug, onInterrupt, onTodos, onDebugClear, onToolCall) {
|
|
6861
7357
|
// Maximum retry attempts (should match number of keys)
|
|
6862
7358
|
const MAX_RETRIES = 10;
|
|
6863
7359
|
let currentConfig = request.llmConfig;
|
|
@@ -6868,7 +7364,7 @@ class ApiService {
|
|
|
6868
7364
|
? { ...request, llmConfig: (0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.buildSingleKeyConfig)(currentConfig) }
|
|
6869
7365
|
: request;
|
|
6870
7366
|
try {
|
|
6871
|
-
await this.sendMessageStreamInternal(requestToSend, onChunk, onMetadata);
|
|
7367
|
+
await this.sendMessageStreamInternal(requestToSend, onChunk, onMetadata, onDebug, onInterrupt, onTodos, onDebugClear, onToolCall);
|
|
6872
7368
|
// Success - reset key rotation state
|
|
6873
7369
|
(0,_ApiKeyManager__WEBPACK_IMPORTED_MODULE_0__.resetKeyRotation)();
|
|
6874
7370
|
return;
|
|
@@ -6916,13 +7412,29 @@ class ApiService {
|
|
|
6916
7412
|
}
|
|
6917
7413
|
/**
|
|
6918
7414
|
* Internal streaming implementation (without retry logic)
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
7415
|
+
* Uses LangChain agent endpoint for improved middleware support
|
|
7416
|
+
*/
|
|
7417
|
+
async sendMessageStreamInternal(request, onChunk, onMetadata, onDebug, onInterrupt, onTodos, onDebugClear, onToolCall) {
|
|
7418
|
+
// Convert IChatRequest to LangChain AgentRequest format
|
|
7419
|
+
// Frontend's context has limited fields, map what's available
|
|
7420
|
+
const langchainRequest = {
|
|
7421
|
+
request: request.message,
|
|
7422
|
+
notebookContext: request.context ? {
|
|
7423
|
+
notebook_path: request.context.notebookPath,
|
|
7424
|
+
cell_count: 0,
|
|
7425
|
+
imported_libraries: [],
|
|
7426
|
+
defined_variables: [],
|
|
7427
|
+
recent_cells: request.context.selectedCells?.map(cell => ({ source: cell })) || []
|
|
7428
|
+
} : undefined,
|
|
7429
|
+
llmConfig: request.llmConfig,
|
|
7430
|
+
workspaceRoot: '.'
|
|
7431
|
+
};
|
|
7432
|
+
// Use LangChain streaming endpoint
|
|
7433
|
+
const response = await fetch(`${this.baseUrl}/agent/langchain/stream`, {
|
|
6922
7434
|
method: 'POST',
|
|
6923
7435
|
headers: this.getHeaders(),
|
|
6924
7436
|
credentials: 'include',
|
|
6925
|
-
body: JSON.stringify(
|
|
7437
|
+
body: JSON.stringify(langchainRequest)
|
|
6926
7438
|
});
|
|
6927
7439
|
if (!response.ok) {
|
|
6928
7440
|
const error = await response.text();
|
|
@@ -6943,15 +7455,78 @@ class ApiService {
|
|
|
6943
7455
|
// Process complete SSE messages
|
|
6944
7456
|
const lines = buffer.split('\n');
|
|
6945
7457
|
buffer = lines.pop() || ''; // Keep incomplete line in buffer
|
|
7458
|
+
let currentEventType = '';
|
|
6946
7459
|
for (const line of lines) {
|
|
7460
|
+
// Handle SSE event type: "event: type"
|
|
7461
|
+
if (line.startsWith('event: ')) {
|
|
7462
|
+
currentEventType = line.slice(7).trim();
|
|
7463
|
+
continue;
|
|
7464
|
+
}
|
|
7465
|
+
// Handle SSE data: "data: json"
|
|
6947
7466
|
if (line.startsWith('data: ')) {
|
|
6948
7467
|
try {
|
|
6949
7468
|
const data = JSON.parse(line.slice(6));
|
|
7469
|
+
// Handle based on event type
|
|
7470
|
+
if (currentEventType === 'todos' && data.todos && onTodos) {
|
|
7471
|
+
onTodos(data.todos);
|
|
7472
|
+
currentEventType = '';
|
|
7473
|
+
continue;
|
|
7474
|
+
}
|
|
7475
|
+
if (currentEventType === 'debug_clear' && onDebugClear) {
|
|
7476
|
+
onDebugClear();
|
|
7477
|
+
currentEventType = '';
|
|
7478
|
+
continue;
|
|
7479
|
+
}
|
|
7480
|
+
if (currentEventType === 'complete') {
|
|
7481
|
+
if (onDebugClear) {
|
|
7482
|
+
onDebugClear();
|
|
7483
|
+
}
|
|
7484
|
+
return;
|
|
7485
|
+
}
|
|
6950
7486
|
// Handle errors
|
|
6951
7487
|
if (data.error) {
|
|
7488
|
+
if (onDebug) {
|
|
7489
|
+
onDebug(`오류: ${data.error}`);
|
|
7490
|
+
}
|
|
6952
7491
|
throw new Error(data.error);
|
|
6953
7492
|
}
|
|
6954
|
-
// Handle
|
|
7493
|
+
// Handle debug events (display in gray)
|
|
7494
|
+
if (data.status && onDebug) {
|
|
7495
|
+
onDebug(data.status);
|
|
7496
|
+
}
|
|
7497
|
+
// Handle interrupt events (Human-in-the-Loop)
|
|
7498
|
+
if (data.thread_id && data.action && onInterrupt) {
|
|
7499
|
+
onInterrupt({
|
|
7500
|
+
threadId: data.thread_id,
|
|
7501
|
+
action: data.action,
|
|
7502
|
+
args: data.args || {},
|
|
7503
|
+
description: data.description || ''
|
|
7504
|
+
});
|
|
7505
|
+
return; // Stop processing, wait for user decision
|
|
7506
|
+
}
|
|
7507
|
+
// Handle token events (streaming LLM response)
|
|
7508
|
+
if (data.content) {
|
|
7509
|
+
onChunk(data.content);
|
|
7510
|
+
}
|
|
7511
|
+
// Handle tool_call events - pass to handler for cell creation
|
|
7512
|
+
if (currentEventType === 'tool_call' && data.tool && onToolCall) {
|
|
7513
|
+
onToolCall({
|
|
7514
|
+
tool: data.tool,
|
|
7515
|
+
code: data.code,
|
|
7516
|
+
content: data.content
|
|
7517
|
+
});
|
|
7518
|
+
currentEventType = '';
|
|
7519
|
+
continue;
|
|
7520
|
+
}
|
|
7521
|
+
// Handle tool_result events - skip displaying raw output
|
|
7522
|
+
if (data.output) {
|
|
7523
|
+
// Don't add raw tool output to chat
|
|
7524
|
+
}
|
|
7525
|
+
// Handle completion
|
|
7526
|
+
if (data.final_answer) {
|
|
7527
|
+
onChunk(data.final_answer);
|
|
7528
|
+
}
|
|
7529
|
+
// Handle metadata
|
|
6955
7530
|
if (data.conversationId && onMetadata) {
|
|
6956
7531
|
onMetadata({
|
|
6957
7532
|
conversationId: data.conversationId,
|
|
@@ -6960,10 +7535,6 @@ class ApiService {
|
|
|
6960
7535
|
model: data.metadata?.model
|
|
6961
7536
|
});
|
|
6962
7537
|
}
|
|
6963
|
-
// Handle content chunks
|
|
6964
|
-
if (data.content) {
|
|
6965
|
-
onChunk(data.content);
|
|
6966
|
-
}
|
|
6967
7538
|
// Final metadata update
|
|
6968
7539
|
if (data.done && data.metadata && onMetadata) {
|
|
6969
7540
|
onMetadata({
|
|
@@ -6971,6 +7542,7 @@ class ApiService {
|
|
|
6971
7542
|
model: data.metadata.model
|
|
6972
7543
|
});
|
|
6973
7544
|
}
|
|
7545
|
+
currentEventType = '';
|
|
6974
7546
|
}
|
|
6975
7547
|
catch (e) {
|
|
6976
7548
|
if (e instanceof SyntaxError) {
|
|
@@ -6988,6 +7560,122 @@ class ApiService {
|
|
|
6988
7560
|
reader.releaseLock();
|
|
6989
7561
|
}
|
|
6990
7562
|
}
|
|
7563
|
+
/**
|
|
7564
|
+
* Resume interrupted agent execution with user decision
|
|
7565
|
+
*/
|
|
7566
|
+
async resumeAgent(threadId, decision, args, feedback, llmConfig, onChunk, onDebug, onInterrupt, onTodos, onDebugClear, onToolCall) {
|
|
7567
|
+
const resumeRequest = {
|
|
7568
|
+
threadId,
|
|
7569
|
+
decisions: [{
|
|
7570
|
+
type: decision,
|
|
7571
|
+
args,
|
|
7572
|
+
feedback
|
|
7573
|
+
}],
|
|
7574
|
+
llmConfig,
|
|
7575
|
+
workspaceRoot: '.'
|
|
7576
|
+
};
|
|
7577
|
+
const response = await fetch(`${this.baseUrl}/agent/langchain/resume`, {
|
|
7578
|
+
method: 'POST',
|
|
7579
|
+
headers: this.getHeaders(),
|
|
7580
|
+
credentials: 'include',
|
|
7581
|
+
body: JSON.stringify(resumeRequest)
|
|
7582
|
+
});
|
|
7583
|
+
if (!response.ok) {
|
|
7584
|
+
const error = await response.text();
|
|
7585
|
+
throw new Error(`Failed to resume agent: ${error}`);
|
|
7586
|
+
}
|
|
7587
|
+
// Process SSE stream (same as sendMessageStream)
|
|
7588
|
+
const reader = response.body?.getReader();
|
|
7589
|
+
if (!reader) {
|
|
7590
|
+
throw new Error('Response body is not readable');
|
|
7591
|
+
}
|
|
7592
|
+
const decoder = new TextDecoder();
|
|
7593
|
+
let buffer = '';
|
|
7594
|
+
try {
|
|
7595
|
+
while (true) {
|
|
7596
|
+
const { done, value } = await reader.read();
|
|
7597
|
+
if (done)
|
|
7598
|
+
break;
|
|
7599
|
+
buffer += decoder.decode(value, { stream: true });
|
|
7600
|
+
const lines = buffer.split('\n');
|
|
7601
|
+
buffer = lines.pop() || '';
|
|
7602
|
+
let currentEventType = '';
|
|
7603
|
+
for (const line of lines) {
|
|
7604
|
+
// Handle SSE event type
|
|
7605
|
+
if (line.startsWith('event: ')) {
|
|
7606
|
+
currentEventType = line.slice(7).trim();
|
|
7607
|
+
continue;
|
|
7608
|
+
}
|
|
7609
|
+
if (line.startsWith('data: ')) {
|
|
7610
|
+
try {
|
|
7611
|
+
const data = JSON.parse(line.slice(6));
|
|
7612
|
+
if (data.error) {
|
|
7613
|
+
if (onDebug) {
|
|
7614
|
+
onDebug(`오류: ${data.error}`);
|
|
7615
|
+
}
|
|
7616
|
+
throw new Error(data.error);
|
|
7617
|
+
}
|
|
7618
|
+
// Handle todos event
|
|
7619
|
+
if (currentEventType === 'todos' && data.todos && onTodos) {
|
|
7620
|
+
onTodos(data.todos);
|
|
7621
|
+
currentEventType = '';
|
|
7622
|
+
continue;
|
|
7623
|
+
}
|
|
7624
|
+
// Handle debug_clear event
|
|
7625
|
+
if (currentEventType === 'debug_clear' && onDebugClear) {
|
|
7626
|
+
onDebugClear();
|
|
7627
|
+
currentEventType = '';
|
|
7628
|
+
continue;
|
|
7629
|
+
}
|
|
7630
|
+
if (currentEventType === 'complete') {
|
|
7631
|
+
if (onDebugClear) {
|
|
7632
|
+
onDebugClear();
|
|
7633
|
+
}
|
|
7634
|
+
return;
|
|
7635
|
+
}
|
|
7636
|
+
// Debug events
|
|
7637
|
+
if (data.status && onDebug) {
|
|
7638
|
+
onDebug(data.status);
|
|
7639
|
+
}
|
|
7640
|
+
// Another interrupt
|
|
7641
|
+
if (data.thread_id && data.action && onInterrupt) {
|
|
7642
|
+
onInterrupt({
|
|
7643
|
+
threadId: data.thread_id,
|
|
7644
|
+
action: data.action,
|
|
7645
|
+
args: data.args || {},
|
|
7646
|
+
description: data.description || ''
|
|
7647
|
+
});
|
|
7648
|
+
return;
|
|
7649
|
+
}
|
|
7650
|
+
// Content chunks
|
|
7651
|
+
if (data.content && onChunk) {
|
|
7652
|
+
onChunk(data.content);
|
|
7653
|
+
}
|
|
7654
|
+
// Tool call events during resume
|
|
7655
|
+
if (currentEventType === 'tool_call' && data.tool && onToolCall) {
|
|
7656
|
+
onToolCall({
|
|
7657
|
+
tool: data.tool,
|
|
7658
|
+
code: data.code,
|
|
7659
|
+
content: data.content
|
|
7660
|
+
});
|
|
7661
|
+
currentEventType = '';
|
|
7662
|
+
continue;
|
|
7663
|
+
}
|
|
7664
|
+
currentEventType = '';
|
|
7665
|
+
}
|
|
7666
|
+
catch (e) {
|
|
7667
|
+
if (!(e instanceof SyntaxError)) {
|
|
7668
|
+
throw e;
|
|
7669
|
+
}
|
|
7670
|
+
}
|
|
7671
|
+
}
|
|
7672
|
+
}
|
|
7673
|
+
}
|
|
7674
|
+
}
|
|
7675
|
+
finally {
|
|
7676
|
+
reader.releaseLock();
|
|
7677
|
+
}
|
|
7678
|
+
}
|
|
6991
7679
|
/**
|
|
6992
7680
|
* Save configuration
|
|
6993
7681
|
*/
|
|
@@ -12493,4 +13181,4 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
12493
13181
|
/***/ }
|
|
12494
13182
|
|
|
12495
13183
|
}]);
|
|
12496
|
-
//# sourceMappingURL=lib_index_js.
|
|
13184
|
+
//# sourceMappingURL=lib_index_js.1366019c413f1d68467f.js.map
|