hdsp-jupyter-extension 2.0.21__py3-none-any.whl → 2.0.22__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/routers/langchain_agent.py +100 -341
- {hdsp_jupyter_extension-2.0.21.data → hdsp_jupyter_extension-2.0.22.data}/data/share/jupyter/labextensions/hdsp-agent/build_log.json +1 -1
- {hdsp_jupyter_extension-2.0.21.data → hdsp_jupyter_extension-2.0.22.data}/data/share/jupyter/labextensions/hdsp-agent/package.json +2 -2
- hdsp_jupyter_extension-2.0.21.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.93b1c499786ecd47b837.js → hdsp_jupyter_extension-2.0.22.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.8496e8475f1bd164669b.js +2 -2
- jupyter_ext/labextension/static/remoteEntry.93b1c499786ecd47b837.js.map → hdsp_jupyter_extension-2.0.22.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.8496e8475f1bd164669b.js.map +1 -1
- {hdsp_jupyter_extension-2.0.21.dist-info → hdsp_jupyter_extension-2.0.22.dist-info}/METADATA +1 -1
- {hdsp_jupyter_extension-2.0.21.dist-info → hdsp_jupyter_extension-2.0.22.dist-info}/RECORD +37 -37
- jupyter_ext/_version.py +1 -1
- jupyter_ext/labextension/build_log.json +1 -1
- jupyter_ext/labextension/package.json +2 -2
- jupyter_ext/labextension/static/{remoteEntry.93b1c499786ecd47b837.js → remoteEntry.8496e8475f1bd164669b.js} +2 -2
- hdsp_jupyter_extension-2.0.21.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.93b1c499786ecd47b837.js.map → jupyter_ext/labextension/static/remoteEntry.8496e8475f1bd164669b.js.map +1 -1
- {hdsp_jupyter_extension-2.0.21.data → hdsp_jupyter_extension-2.0.22.data}/data/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +0 -0
- {hdsp_jupyter_extension-2.0.21.data → hdsp_jupyter_extension-2.0.22.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
- {hdsp_jupyter_extension-2.0.21.data → hdsp_jupyter_extension-2.0.22.data}/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.96745acc14125453fba8.js +0 -0
- {hdsp_jupyter_extension-2.0.21.data → hdsp_jupyter_extension-2.0.22.data}/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.96745acc14125453fba8.js.map +0 -0
- {hdsp_jupyter_extension-2.0.21.data → hdsp_jupyter_extension-2.0.22.data}/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.90f80cb80187de8c5ae5.js +0 -0
- {hdsp_jupyter_extension-2.0.21.data → hdsp_jupyter_extension-2.0.22.data}/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.90f80cb80187de8c5ae5.js.map +0 -0
- {hdsp_jupyter_extension-2.0.21.data → hdsp_jupyter_extension-2.0.22.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.21.data → hdsp_jupyter_extension-2.0.22.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.21.data → hdsp_jupyter_extension-2.0.22.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.21.data → hdsp_jupyter_extension-2.0.22.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.21.data → hdsp_jupyter_extension-2.0.22.data}/data/share/jupyter/labextensions/hdsp-agent/static/style.js +0 -0
- {hdsp_jupyter_extension-2.0.21.data → hdsp_jupyter_extension-2.0.22.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.21.data → hdsp_jupyter_extension-2.0.22.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.21.data → hdsp_jupyter_extension-2.0.22.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.21.data → hdsp_jupyter_extension-2.0.22.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.21.data → hdsp_jupyter_extension-2.0.22.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.21.data → hdsp_jupyter_extension-2.0.22.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.21.data → hdsp_jupyter_extension-2.0.22.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.21.data → hdsp_jupyter_extension-2.0.22.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.21.data → hdsp_jupyter_extension-2.0.22.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +0 -0
- {hdsp_jupyter_extension-2.0.21.data → hdsp_jupyter_extension-2.0.22.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.21.data → hdsp_jupyter_extension-2.0.22.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.21.data → hdsp_jupyter_extension-2.0.22.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.21.dist-info → hdsp_jupyter_extension-2.0.22.dist-info}/WHEEL +0 -0
- {hdsp_jupyter_extension-2.0.21.dist-info → hdsp_jupyter_extension-2.0.22.dist-info}/licenses/LICENSE +0 -0
|
@@ -895,10 +895,7 @@ async def stream_agent(request: AgentRequest):
|
|
|
895
895
|
|
|
896
896
|
# Initial status: waiting for LLM
|
|
897
897
|
logger.info("SSE: Sending initial debug status 'LLM 응답 대기 중'")
|
|
898
|
-
yield {
|
|
899
|
-
"event": "debug",
|
|
900
|
-
"data": json.dumps({"status": "LLM 응답 대기 중", "icon": "thinking"}),
|
|
901
|
-
}
|
|
898
|
+
yield {"event": "debug", "data": json.dumps({"status": "LLM 응답 대기 중", "icon": "thinking"})}
|
|
902
899
|
|
|
903
900
|
# Main streaming loop
|
|
904
901
|
async for step in _async_stream_wrapper(
|
|
@@ -908,10 +905,7 @@ async def stream_agent(request: AgentRequest):
|
|
|
908
905
|
if is_thread_cancelled(thread_id):
|
|
909
906
|
logger.info(f"Thread {thread_id} cancelled by user, stopping stream")
|
|
910
907
|
clear_cancelled_thread(thread_id)
|
|
911
|
-
yield {
|
|
912
|
-
"event": "cancelled",
|
|
913
|
-
"data": json.dumps({"message": "작업이 사용자에 의해 중단되었습니다."}),
|
|
914
|
-
}
|
|
908
|
+
yield {"event": "cancelled", "data": json.dumps({"message": "작업이 사용자에 의해 중단되었습니다."})}
|
|
915
909
|
return
|
|
916
910
|
|
|
917
911
|
if isinstance(step, dict):
|
|
@@ -931,18 +925,12 @@ async def stream_agent(request: AgentRequest):
|
|
|
931
925
|
todos = step["todos"]
|
|
932
926
|
if todos:
|
|
933
927
|
latest_todos = todos
|
|
934
|
-
yield {
|
|
935
|
-
"event": "todos",
|
|
936
|
-
"data": json.dumps({"todos": todos}),
|
|
937
|
-
}
|
|
928
|
+
yield {"event": "todos", "data": json.dumps({"todos": todos})}
|
|
938
929
|
elif isinstance(step, dict):
|
|
939
930
|
todos = _extract_todos(step)
|
|
940
931
|
if todos:
|
|
941
932
|
latest_todos = todos
|
|
942
|
-
yield {
|
|
943
|
-
"event": "todos",
|
|
944
|
-
"data": json.dumps({"todos": todos}),
|
|
945
|
-
}
|
|
933
|
+
yield {"event": "todos", "data": json.dumps({"todos": todos})}
|
|
946
934
|
|
|
947
935
|
# Process messages (no continue statements to ensure interrupt check always runs)
|
|
948
936
|
if isinstance(step, dict) and "messages" in step:
|
|
@@ -992,10 +980,7 @@ async def stream_agent(request: AgentRequest):
|
|
|
992
980
|
todos = _extract_todos(last_message.content)
|
|
993
981
|
if todos:
|
|
994
982
|
latest_todos = todos
|
|
995
|
-
yield {
|
|
996
|
-
"event": "todos",
|
|
997
|
-
"data": json.dumps({"todos": todos}),
|
|
998
|
-
}
|
|
983
|
+
yield {"event": "todos", "data": json.dumps({"todos": todos})}
|
|
999
984
|
# Check if all todos are completed - auto terminate only if summary exists
|
|
1000
985
|
all_completed = all(
|
|
1001
986
|
t.get("status") == "completed" for t in todos
|
|
@@ -1039,21 +1024,10 @@ async def stream_agent(request: AgentRequest):
|
|
|
1039
1024
|
repaired_content[:100],
|
|
1040
1025
|
)
|
|
1041
1026
|
produced_output = True
|
|
1042
|
-
yield {
|
|
1043
|
-
"event": "token",
|
|
1044
|
-
"data": json.dumps({"content": repaired_content}),
|
|
1045
|
-
}
|
|
1027
|
+
yield {"event": "token", "data": json.dumps({"content": repaired_content})}
|
|
1046
1028
|
break
|
|
1047
|
-
yield {
|
|
1048
|
-
|
|
1049
|
-
"data": json.dumps({}),
|
|
1050
|
-
}
|
|
1051
|
-
yield {
|
|
1052
|
-
"event": "done",
|
|
1053
|
-
"data": json.dumps(
|
|
1054
|
-
{"reason": "all_todos_completed"}
|
|
1055
|
-
),
|
|
1056
|
-
}
|
|
1029
|
+
yield {"event": "debug_clear", "data": json.dumps({})}
|
|
1030
|
+
yield {"event": "done", "data": json.dumps({"reason": "all_todos_completed"})}
|
|
1057
1031
|
return # Exit the generator
|
|
1058
1032
|
else:
|
|
1059
1033
|
logger.warning(
|
|
@@ -1183,20 +1157,10 @@ async def stream_agent(request: AgentRequest):
|
|
|
1183
1157
|
logger.warning(
|
|
1184
1158
|
"MALFORMED_FUNCTION_CALL with empty response - sending error to client"
|
|
1185
1159
|
)
|
|
1186
|
-
yield {
|
|
1187
|
-
"event": "token",
|
|
1188
|
-
"data": json.dumps(
|
|
1189
|
-
{
|
|
1160
|
+
yield {"event": "token", "data": json.dumps({
|
|
1190
1161
|
"content": "\n\n[경고] LLM이 잘못된 응답을 반환했습니다. 다시 시도해주세요.\n"
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
}
|
|
1194
|
-
yield {
|
|
1195
|
-
"event": "debug",
|
|
1196
|
-
"data": json.dumps(
|
|
1197
|
-
{"status": "[경고] MALFORMED_FUNCTION_CALL 에러"}
|
|
1198
|
-
),
|
|
1199
|
-
}
|
|
1162
|
+
})}
|
|
1163
|
+
yield {"event": "debug", "data": json.dumps({"status": "[경고] MALFORMED_FUNCTION_CALL 에러"})}
|
|
1200
1164
|
produced_output = True
|
|
1201
1165
|
# Continue to let agent retry on next iteration
|
|
1202
1166
|
|
|
@@ -1226,10 +1190,7 @@ async def stream_agent(request: AgentRequest):
|
|
|
1226
1190
|
len(todos),
|
|
1227
1191
|
)
|
|
1228
1192
|
latest_todos = todos
|
|
1229
|
-
yield {
|
|
1230
|
-
"event": "todos",
|
|
1231
|
-
"data": json.dumps({"todos": todos}),
|
|
1232
|
-
}
|
|
1193
|
+
yield {"event": "todos", "data": json.dumps({"todos": todos})}
|
|
1233
1194
|
# Check if all todos are completed - terminate early
|
|
1234
1195
|
all_completed = all(
|
|
1235
1196
|
t.get("status") == "completed" for t in todos
|
|
@@ -1297,20 +1258,9 @@ async def stream_agent(request: AgentRequest):
|
|
|
1297
1258
|
repaired_content[:100],
|
|
1298
1259
|
)
|
|
1299
1260
|
produced_output = True
|
|
1300
|
-
yield {
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
}
|
|
1304
|
-
yield {
|
|
1305
|
-
"event": "debug_clear",
|
|
1306
|
-
"data": json.dumps({}),
|
|
1307
|
-
}
|
|
1308
|
-
yield {
|
|
1309
|
-
"event": "done",
|
|
1310
|
-
"data": json.dumps(
|
|
1311
|
-
{"reason": "all_todos_completed"}
|
|
1312
|
-
),
|
|
1313
|
-
}
|
|
1261
|
+
yield {"event": "token", "data": json.dumps({"content": repaired_content})}
|
|
1262
|
+
yield {"event": "debug_clear", "data": json.dumps({})}
|
|
1263
|
+
yield {"event": "done", "data": json.dumps({"reason": "all_todos_completed"})}
|
|
1314
1264
|
return # Exit before executing more tool calls
|
|
1315
1265
|
for tool_call in tool_calls:
|
|
1316
1266
|
tool_name = tool_call.get("name", "unknown")
|
|
@@ -1325,10 +1275,7 @@ async def stream_agent(request: AgentRequest):
|
|
|
1325
1275
|
"SSE: Emitting debug event for tool: %s",
|
|
1326
1276
|
tool_name,
|
|
1327
1277
|
)
|
|
1328
|
-
yield {
|
|
1329
|
-
"event": "debug",
|
|
1330
|
-
"data": json.dumps(status_msg),
|
|
1331
|
-
}
|
|
1278
|
+
yield {"event": "debug", "data": json.dumps(status_msg)}
|
|
1332
1279
|
|
|
1333
1280
|
# Send tool_call event with details for frontend to execute
|
|
1334
1281
|
if tool_name in (
|
|
@@ -1336,55 +1283,37 @@ async def stream_agent(request: AgentRequest):
|
|
|
1336
1283
|
"jupyter_cell",
|
|
1337
1284
|
):
|
|
1338
1285
|
produced_output = True
|
|
1339
|
-
yield {
|
|
1340
|
-
"event": "tool_call",
|
|
1341
|
-
"data": json.dumps(
|
|
1342
|
-
{
|
|
1286
|
+
yield {"event": "tool_call", "data": json.dumps({
|
|
1343
1287
|
"tool": "jupyter_cell",
|
|
1344
1288
|
"code": tool_args.get("code", ""),
|
|
1345
1289
|
"description": tool_args.get(
|
|
1346
1290
|
"description", ""
|
|
1347
1291
|
),
|
|
1348
|
-
}
|
|
1349
|
-
),
|
|
1350
|
-
}
|
|
1292
|
+
})}
|
|
1351
1293
|
elif tool_name in ("markdown_tool", "markdown"):
|
|
1352
1294
|
produced_output = True
|
|
1353
|
-
yield {
|
|
1354
|
-
"event": "tool_call",
|
|
1355
|
-
"data": json.dumps(
|
|
1356
|
-
{
|
|
1295
|
+
yield {"event": "tool_call", "data": json.dumps({
|
|
1357
1296
|
"tool": "markdown",
|
|
1358
1297
|
"content": tool_args.get(
|
|
1359
1298
|
"content", ""
|
|
1360
1299
|
),
|
|
1361
|
-
}
|
|
1362
|
-
),
|
|
1363
|
-
}
|
|
1300
|
+
})}
|
|
1364
1301
|
elif tool_name == "execute_command_tool":
|
|
1365
1302
|
produced_output = True
|
|
1366
|
-
yield {
|
|
1367
|
-
"event": "tool_call",
|
|
1368
|
-
"data": json.dumps(
|
|
1369
|
-
{
|
|
1303
|
+
yield {"event": "tool_call", "data": json.dumps({
|
|
1370
1304
|
"tool": "execute_command_tool",
|
|
1371
1305
|
"command": tool_args.get(
|
|
1372
1306
|
"command", ""
|
|
1373
1307
|
),
|
|
1374
1308
|
"timeout": tool_args.get("timeout"),
|
|
1375
|
-
}
|
|
1376
|
-
),
|
|
1377
|
-
}
|
|
1309
|
+
})}
|
|
1378
1310
|
elif tool_name in (
|
|
1379
1311
|
"search_notebook_cells_tool",
|
|
1380
1312
|
"search_notebook_cells",
|
|
1381
1313
|
):
|
|
1382
1314
|
# Search notebook cells - emit tool_call for client-side execution
|
|
1383
1315
|
produced_output = True
|
|
1384
|
-
yield {
|
|
1385
|
-
"event": "tool_call",
|
|
1386
|
-
"data": json.dumps(
|
|
1387
|
-
{
|
|
1316
|
+
yield {"event": "tool_call", "data": json.dumps({
|
|
1388
1317
|
"tool": "search_notebook_cells",
|
|
1389
1318
|
"pattern": tool_args.get(
|
|
1390
1319
|
"pattern", ""
|
|
@@ -1401,9 +1330,7 @@ async def stream_agent(request: AgentRequest):
|
|
|
1401
1330
|
"case_sensitive": tool_args.get(
|
|
1402
1331
|
"case_sensitive", False
|
|
1403
1332
|
),
|
|
1404
|
-
}
|
|
1405
|
-
),
|
|
1406
|
-
}
|
|
1333
|
+
})}
|
|
1407
1334
|
|
|
1408
1335
|
# Only display content if it's not empty and not a JSON tool response
|
|
1409
1336
|
if (
|
|
@@ -1472,12 +1399,7 @@ async def stream_agent(request: AgentRequest):
|
|
|
1472
1399
|
repaired_content[:100],
|
|
1473
1400
|
)
|
|
1474
1401
|
produced_output = True
|
|
1475
|
-
yield {
|
|
1476
|
-
"event": "token",
|
|
1477
|
-
"data": json.dumps(
|
|
1478
|
-
{"content": repaired_content}
|
|
1479
|
-
),
|
|
1480
|
-
}
|
|
1402
|
+
yield {"event": "token", "data": json.dumps({"content": repaired_content})}
|
|
1481
1403
|
|
|
1482
1404
|
# Drain and emit any subagent events (tool calls from subagents)
|
|
1483
1405
|
for subagent_event in get_subagent_debug_events():
|
|
@@ -1496,10 +1418,7 @@ async def stream_agent(request: AgentRequest):
|
|
|
1496
1418
|
else interrupt
|
|
1497
1419
|
)
|
|
1498
1420
|
|
|
1499
|
-
yield {
|
|
1500
|
-
"event": "debug",
|
|
1501
|
-
"data": json.dumps({"status": "사용자 승인 대기 중", "icon": "pause"}),
|
|
1502
|
-
}
|
|
1421
|
+
yield {"event": "debug", "data": json.dumps({"status": "사용자 승인 대기 중", "icon": "pause"})}
|
|
1503
1422
|
|
|
1504
1423
|
# Process regular HITL interrupts (non-subagent)
|
|
1505
1424
|
for interrupt in interrupts:
|
|
@@ -1524,16 +1443,14 @@ async def stream_agent(request: AgentRequest):
|
|
|
1524
1443
|
for idx, action in enumerate(normalized_actions):
|
|
1525
1444
|
yield {
|
|
1526
1445
|
"event": "interrupt",
|
|
1527
|
-
"data": json.dumps(
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
}
|
|
1536
|
-
),
|
|
1446
|
+
"data": json.dumps({
|
|
1447
|
+
"thread_id": thread_id,
|
|
1448
|
+
"action": action.get("name", "unknown"),
|
|
1449
|
+
"args": action.get("arguments", {}),
|
|
1450
|
+
"description": action.get("description", ""),
|
|
1451
|
+
"action_index": idx,
|
|
1452
|
+
"total_actions": total_actions,
|
|
1453
|
+
}),
|
|
1537
1454
|
}
|
|
1538
1455
|
|
|
1539
1456
|
# Save last signature for resume to avoid duplicate content
|
|
@@ -1624,14 +1541,9 @@ async def stream_agent(request: AgentRequest):
|
|
|
1624
1541
|
)
|
|
1625
1542
|
except asyncio.TimeoutError:
|
|
1626
1543
|
logger.error("SimpleAgent fallback timed out after 30s")
|
|
1627
|
-
yield {
|
|
1628
|
-
"event": "token",
|
|
1629
|
-
"data": json.dumps(
|
|
1630
|
-
{
|
|
1544
|
+
yield {"event": "token", "data": json.dumps({
|
|
1631
1545
|
"content": "모델이 도구 호출을 생성하지 못했습니다. 다시 시도해주세요."
|
|
1632
|
-
}
|
|
1633
|
-
),
|
|
1634
|
-
}
|
|
1546
|
+
})}
|
|
1635
1547
|
produced_output = True
|
|
1636
1548
|
fallback_response = None
|
|
1637
1549
|
except Exception as fallback_error:
|
|
@@ -1640,12 +1552,7 @@ async def stream_agent(request: AgentRequest):
|
|
|
1640
1552
|
fallback_error,
|
|
1641
1553
|
exc_info=True,
|
|
1642
1554
|
)
|
|
1643
|
-
yield {
|
|
1644
|
-
"event": "token",
|
|
1645
|
-
"data": json.dumps(
|
|
1646
|
-
{"content": f"오류가 발생했습니다: {str(fallback_error)}"}
|
|
1647
|
-
),
|
|
1648
|
-
}
|
|
1555
|
+
yield {"event": "token", "data": json.dumps({"content": f"오류가 발생했습니다: {str(fallback_error)}"})}
|
|
1649
1556
|
produced_output = True
|
|
1650
1557
|
fallback_response = None
|
|
1651
1558
|
if isinstance(fallback_response, AIMessage) and getattr(
|
|
@@ -1659,57 +1566,27 @@ async def stream_agent(request: AgentRequest):
|
|
|
1659
1566
|
|
|
1660
1567
|
if tool_name in ("jupyter_cell_tool", "jupyter_cell"):
|
|
1661
1568
|
produced_output = True
|
|
1662
|
-
yield {
|
|
1663
|
-
|
|
1664
|
-
"data": json.dumps(
|
|
1665
|
-
_get_tool_status_message(tool_name, tool_args)
|
|
1666
|
-
),
|
|
1667
|
-
}
|
|
1668
|
-
yield {
|
|
1669
|
-
"event": "tool_call",
|
|
1670
|
-
"data": json.dumps(
|
|
1671
|
-
{
|
|
1569
|
+
yield {"event": "debug", "data": json.dumps(_get_tool_status_message(tool_name, tool_args))}
|
|
1570
|
+
yield {"event": "tool_call", "data": json.dumps({
|
|
1672
1571
|
"tool": "jupyter_cell",
|
|
1673
1572
|
"code": tool_args.get("code", ""),
|
|
1674
1573
|
"description": tool_args.get("description", ""),
|
|
1675
|
-
}
|
|
1676
|
-
),
|
|
1677
|
-
}
|
|
1574
|
+
})}
|
|
1678
1575
|
elif tool_name in ("markdown_tool", "markdown"):
|
|
1679
1576
|
produced_output = True
|
|
1680
|
-
yield {
|
|
1681
|
-
|
|
1682
|
-
"data": json.dumps(
|
|
1683
|
-
_get_tool_status_message(tool_name, tool_args)
|
|
1684
|
-
),
|
|
1685
|
-
}
|
|
1686
|
-
yield {
|
|
1687
|
-
"event": "tool_call",
|
|
1688
|
-
"data": json.dumps(
|
|
1689
|
-
{
|
|
1577
|
+
yield {"event": "debug", "data": json.dumps(_get_tool_status_message(tool_name, tool_args))}
|
|
1578
|
+
yield {"event": "tool_call", "data": json.dumps({
|
|
1690
1579
|
"tool": "markdown",
|
|
1691
1580
|
"content": tool_args.get("content", ""),
|
|
1692
|
-
}
|
|
1693
|
-
),
|
|
1694
|
-
}
|
|
1581
|
+
})}
|
|
1695
1582
|
elif tool_name == "execute_command_tool":
|
|
1696
1583
|
produced_output = True
|
|
1697
|
-
yield {
|
|
1698
|
-
|
|
1699
|
-
"data": json.dumps(
|
|
1700
|
-
_get_tool_status_message(tool_name, tool_args)
|
|
1701
|
-
),
|
|
1702
|
-
}
|
|
1703
|
-
yield {
|
|
1704
|
-
"event": "tool_call",
|
|
1705
|
-
"data": json.dumps(
|
|
1706
|
-
{
|
|
1584
|
+
yield {"event": "debug", "data": json.dumps(_get_tool_status_message(tool_name, tool_args))}
|
|
1585
|
+
yield {"event": "tool_call", "data": json.dumps({
|
|
1707
1586
|
"tool": "execute_command_tool",
|
|
1708
1587
|
"command": tool_args.get("command", ""),
|
|
1709
1588
|
"timeout": tool_args.get("timeout"),
|
|
1710
|
-
}
|
|
1711
|
-
),
|
|
1712
|
-
}
|
|
1589
|
+
})}
|
|
1713
1590
|
elif tool_name == "read_file_tool":
|
|
1714
1591
|
# For file operations, generate code with the LLM
|
|
1715
1592
|
logger.info(
|
|
@@ -1741,32 +1618,20 @@ async def stream_agent(request: AgentRequest):
|
|
|
1741
1618
|
)
|
|
1742
1619
|
|
|
1743
1620
|
if not code:
|
|
1744
|
-
yield {
|
|
1745
|
-
"event": "token",
|
|
1746
|
-
"data": json.dumps(
|
|
1747
|
-
{
|
|
1621
|
+
yield {"event": "token", "data": json.dumps({
|
|
1748
1622
|
"content": "도구 실행을 위한 코드를 생성하지 못했습니다. 다시 시도해주세요."
|
|
1749
|
-
}
|
|
1750
|
-
),
|
|
1751
|
-
}
|
|
1623
|
+
})}
|
|
1752
1624
|
produced_output = True
|
|
1753
1625
|
continue
|
|
1754
1626
|
|
|
1755
|
-
yield {
|
|
1756
|
-
"event": "debug",
|
|
1757
|
-
"data": json.dumps(
|
|
1758
|
-
{"status": "[변환] Jupyter Cell로 변환 중"}
|
|
1759
|
-
),
|
|
1760
|
-
}
|
|
1627
|
+
yield {"event": "debug", "data": json.dumps({"status": "[변환] Jupyter Cell로 변환 중"})}
|
|
1761
1628
|
yield {
|
|
1762
1629
|
"event": "tool_call",
|
|
1763
|
-
"data": json.dumps(
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
}
|
|
1769
|
-
),
|
|
1630
|
+
"data": json.dumps({
|
|
1631
|
+
"tool": "jupyter_cell",
|
|
1632
|
+
"code": code,
|
|
1633
|
+
"description": f"Converted from {tool_name}",
|
|
1634
|
+
}),
|
|
1770
1635
|
}
|
|
1771
1636
|
else:
|
|
1772
1637
|
# Unknown tool - skip and show message
|
|
@@ -1775,11 +1640,9 @@ async def stream_agent(request: AgentRequest):
|
|
|
1775
1640
|
)
|
|
1776
1641
|
yield {
|
|
1777
1642
|
"event": "token",
|
|
1778
|
-
"data": json.dumps(
|
|
1779
|
-
{
|
|
1780
|
-
|
|
1781
|
-
}
|
|
1782
|
-
),
|
|
1643
|
+
"data": json.dumps({
|
|
1644
|
+
"content": f"알 수 없는 도구 '{tool_name}'입니다. jupyter_cell_tool을 사용해주세요."
|
|
1645
|
+
}),
|
|
1783
1646
|
}
|
|
1784
1647
|
produced_output = True
|
|
1785
1648
|
elif (
|
|
@@ -1791,41 +1654,25 @@ async def stream_agent(request: AgentRequest):
|
|
|
1791
1654
|
repaired_content = _repair_summary_json_content(
|
|
1792
1655
|
fallback_response.content
|
|
1793
1656
|
)
|
|
1794
|
-
yield {
|
|
1795
|
-
"event": "token",
|
|
1796
|
-
"data": json.dumps({"content": repaired_content}),
|
|
1797
|
-
}
|
|
1657
|
+
yield {"event": "token", "data": json.dumps({"content": repaired_content})}
|
|
1798
1658
|
elif fallback_response is not None and not produced_output:
|
|
1799
|
-
yield {
|
|
1800
|
-
"event": "token",
|
|
1801
|
-
"data": json.dumps(
|
|
1802
|
-
{
|
|
1659
|
+
yield {"event": "token", "data": json.dumps({
|
|
1803
1660
|
"content": "모델이 도구 호출을 생성하지 못했습니다. 다시 시도해주세요."
|
|
1804
|
-
}
|
|
1805
|
-
),
|
|
1806
|
-
}
|
|
1661
|
+
})}
|
|
1807
1662
|
produced_output = True
|
|
1808
1663
|
|
|
1809
1664
|
# Clear debug status before completion
|
|
1810
1665
|
yield {"event": "debug_clear", "data": json.dumps({})}
|
|
1811
1666
|
|
|
1812
1667
|
# No interrupt - execution completed
|
|
1813
|
-
yield {
|
|
1814
|
-
"event": "complete",
|
|
1815
|
-
"data": json.dumps({"success": True, "thread_id": thread_id}),
|
|
1816
|
-
}
|
|
1668
|
+
yield {"event": "complete", "data": json.dumps({"success": True, "thread_id": thread_id})}
|
|
1817
1669
|
|
|
1818
1670
|
except Exception as e:
|
|
1819
1671
|
logger.error(f"Stream error: {e}", exc_info=True)
|
|
1820
|
-
yield {
|
|
1821
|
-
"event": "error",
|
|
1822
|
-
"data": json.dumps(
|
|
1823
|
-
{
|
|
1672
|
+
yield {"event": "error", "data": json.dumps({
|
|
1824
1673
|
"error": str(e),
|
|
1825
1674
|
"error_type": type(e).__name__,
|
|
1826
|
-
}
|
|
1827
|
-
),
|
|
1828
|
-
}
|
|
1675
|
+
})}
|
|
1829
1676
|
|
|
1830
1677
|
return EventSourceResponse(event_generator())
|
|
1831
1678
|
|
|
@@ -1879,16 +1726,11 @@ async def resume_agent(request: ResumeRequest):
|
|
|
1879
1726
|
"Server may have restarted or session expired.",
|
|
1880
1727
|
request.threadId,
|
|
1881
1728
|
)
|
|
1882
|
-
yield {
|
|
1883
|
-
"event": "error",
|
|
1884
|
-
"data": json.dumps(
|
|
1885
|
-
{
|
|
1729
|
+
yield {"event": "error", "data": json.dumps({
|
|
1886
1730
|
"error": "Session expired or not found",
|
|
1887
1731
|
"code": "CHECKPOINT_NOT_FOUND",
|
|
1888
1732
|
"message": "이전 세션을 찾을 수 없습니다. 서버가 재시작되었거나 세션이 만료되었습니다. 새로운 대화를 시작해주세요.",
|
|
1889
|
-
}
|
|
1890
|
-
),
|
|
1891
|
-
}
|
|
1733
|
+
})}
|
|
1892
1734
|
return
|
|
1893
1735
|
|
|
1894
1736
|
checkpointer = _simple_agent_checkpointers.get(request.threadId)
|
|
@@ -1998,10 +1840,7 @@ async def resume_agent(request: ResumeRequest):
|
|
|
1998
1840
|
)
|
|
1999
1841
|
|
|
2000
1842
|
# Resume execution
|
|
2001
|
-
yield {
|
|
2002
|
-
"event": "debug",
|
|
2003
|
-
"data": json.dumps({"status": "실행 재개 중", "icon": "play"}),
|
|
2004
|
-
}
|
|
1843
|
+
yield {"event": "debug", "data": json.dumps({"status": "실행 재개 중", "icon": "play"})}
|
|
2005
1844
|
|
|
2006
1845
|
_simple_agent_pending_actions.pop(request.threadId, None)
|
|
2007
1846
|
|
|
@@ -2027,10 +1866,7 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2027
1866
|
)
|
|
2028
1867
|
|
|
2029
1868
|
# Status: waiting for LLM response
|
|
2030
|
-
yield {
|
|
2031
|
-
"event": "debug",
|
|
2032
|
-
"data": json.dumps({"status": "LLM 응답 대기 중", "icon": "thinking"}),
|
|
2033
|
-
}
|
|
1869
|
+
yield {"event": "debug", "data": json.dumps({"status": "LLM 응답 대기 중", "icon": "thinking"})}
|
|
2034
1870
|
|
|
2035
1871
|
step_count = 0
|
|
2036
1872
|
|
|
@@ -2044,10 +1880,7 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2044
1880
|
if is_thread_cancelled(request.threadId):
|
|
2045
1881
|
logger.info(f"Thread {request.threadId} cancelled by user, stopping resume stream")
|
|
2046
1882
|
clear_cancelled_thread(request.threadId)
|
|
2047
|
-
yield {
|
|
2048
|
-
"event": "cancelled",
|
|
2049
|
-
"data": json.dumps({"message": "작업이 사용자에 의해 중단되었습니다."}),
|
|
2050
|
-
}
|
|
1883
|
+
yield {"event": "cancelled", "data": json.dumps({"message": "작업이 사용자에 의해 중단되었습니다."})}
|
|
2051
1884
|
return
|
|
2052
1885
|
|
|
2053
1886
|
step_count += 1
|
|
@@ -2149,10 +1982,7 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2149
1982
|
todos = _extract_todos(last_message.content)
|
|
2150
1983
|
if todos:
|
|
2151
1984
|
latest_todos = todos
|
|
2152
|
-
yield {
|
|
2153
|
-
"event": "todos",
|
|
2154
|
-
"data": json.dumps({"todos": todos}),
|
|
2155
|
-
}
|
|
1985
|
+
yield {"event": "todos", "data": json.dumps({"todos": todos})}
|
|
2156
1986
|
# Check if all todos are completed - auto terminate only if summary exists
|
|
2157
1987
|
all_completed = all(
|
|
2158
1988
|
t.get("status") == "completed" for t in todos
|
|
@@ -2195,21 +2025,10 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2195
2025
|
len(repaired_content),
|
|
2196
2026
|
repaired_content[:100],
|
|
2197
2027
|
)
|
|
2198
|
-
yield {
|
|
2199
|
-
"event": "token",
|
|
2200
|
-
"data": json.dumps({"content": repaired_content}),
|
|
2201
|
-
}
|
|
2028
|
+
yield {"event": "token", "data": json.dumps({"content": repaired_content})}
|
|
2202
2029
|
break
|
|
2203
|
-
yield {
|
|
2204
|
-
|
|
2205
|
-
"data": json.dumps({}),
|
|
2206
|
-
}
|
|
2207
|
-
yield {
|
|
2208
|
-
"event": "done",
|
|
2209
|
-
"data": json.dumps(
|
|
2210
|
-
{"reason": "all_todos_completed"}
|
|
2211
|
-
),
|
|
2212
|
-
}
|
|
2030
|
+
yield {"event": "debug_clear", "data": json.dumps({})}
|
|
2031
|
+
yield {"event": "done", "data": json.dumps({"reason": "all_todos_completed"})}
|
|
2213
2032
|
return # Exit the generator
|
|
2214
2033
|
else:
|
|
2215
2034
|
logger.warning(
|
|
@@ -2301,12 +2120,7 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2301
2120
|
len(repaired_content),
|
|
2302
2121
|
log_preview,
|
|
2303
2122
|
)
|
|
2304
|
-
yield {
|
|
2305
|
-
"event": "token",
|
|
2306
|
-
"data": json.dumps(
|
|
2307
|
-
{"content": repaired_content}
|
|
2308
|
-
),
|
|
2309
|
-
}
|
|
2123
|
+
yield {"event": "token", "data": json.dumps({"content": repaired_content})}
|
|
2310
2124
|
|
|
2311
2125
|
if (
|
|
2312
2126
|
hasattr(last_message, "tool_calls")
|
|
@@ -2333,10 +2147,7 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2333
2147
|
todos = _emit_todos_from_tool_calls(new_tool_calls)
|
|
2334
2148
|
if todos:
|
|
2335
2149
|
latest_todos = todos
|
|
2336
|
-
yield {
|
|
2337
|
-
"event": "todos",
|
|
2338
|
-
"data": json.dumps({"todos": todos}),
|
|
2339
|
-
}
|
|
2150
|
+
yield {"event": "todos", "data": json.dumps({"todos": todos})}
|
|
2340
2151
|
# Check if all todos are completed - terminate early
|
|
2341
2152
|
all_completed = all(
|
|
2342
2153
|
t.get("status") == "completed" for t in todos
|
|
@@ -2403,20 +2214,9 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2403
2214
|
len(repaired_content),
|
|
2404
2215
|
repaired_content[:100],
|
|
2405
2216
|
)
|
|
2406
|
-
yield {
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
}
|
|
2410
|
-
yield {
|
|
2411
|
-
"event": "debug_clear",
|
|
2412
|
-
"data": json.dumps({}),
|
|
2413
|
-
}
|
|
2414
|
-
yield {
|
|
2415
|
-
"event": "done",
|
|
2416
|
-
"data": json.dumps(
|
|
2417
|
-
{"reason": "all_todos_completed"}
|
|
2418
|
-
),
|
|
2419
|
-
}
|
|
2217
|
+
yield {"event": "token", "data": json.dumps({"content": repaired_content})}
|
|
2218
|
+
yield {"event": "debug_clear", "data": json.dumps({})}
|
|
2219
|
+
yield {"event": "done", "data": json.dumps({"reason": "all_todos_completed"})}
|
|
2420
2220
|
return # Exit before executing more tool calls
|
|
2421
2221
|
|
|
2422
2222
|
# Process tool calls
|
|
@@ -2436,61 +2236,40 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2436
2236
|
tool_name, tool_args
|
|
2437
2237
|
)
|
|
2438
2238
|
|
|
2439
|
-
yield {
|
|
2440
|
-
"event": "debug",
|
|
2441
|
-
"data": json.dumps(status_msg),
|
|
2442
|
-
}
|
|
2239
|
+
yield {"event": "debug", "data": json.dumps(status_msg)}
|
|
2443
2240
|
|
|
2444
2241
|
if tool_name in (
|
|
2445
2242
|
"jupyter_cell_tool",
|
|
2446
2243
|
"jupyter_cell",
|
|
2447
2244
|
):
|
|
2448
|
-
yield {
|
|
2449
|
-
"event": "tool_call",
|
|
2450
|
-
"data": json.dumps(
|
|
2451
|
-
{
|
|
2245
|
+
yield {"event": "tool_call", "data": json.dumps({
|
|
2452
2246
|
"tool": "jupyter_cell",
|
|
2453
2247
|
"code": tool_args.get("code", ""),
|
|
2454
2248
|
"description": tool_args.get(
|
|
2455
2249
|
"description", ""
|
|
2456
2250
|
),
|
|
2457
|
-
}
|
|
2458
|
-
),
|
|
2459
|
-
}
|
|
2251
|
+
})}
|
|
2460
2252
|
elif tool_name in ("markdown_tool", "markdown"):
|
|
2461
|
-
yield {
|
|
2462
|
-
"event": "tool_call",
|
|
2463
|
-
"data": json.dumps(
|
|
2464
|
-
{
|
|
2253
|
+
yield {"event": "tool_call", "data": json.dumps({
|
|
2465
2254
|
"tool": "markdown",
|
|
2466
2255
|
"content": tool_args.get(
|
|
2467
2256
|
"content", ""
|
|
2468
2257
|
),
|
|
2469
|
-
}
|
|
2470
|
-
),
|
|
2471
|
-
}
|
|
2258
|
+
})}
|
|
2472
2259
|
elif tool_name == "execute_command_tool":
|
|
2473
|
-
yield {
|
|
2474
|
-
"event": "tool_call",
|
|
2475
|
-
"data": json.dumps(
|
|
2476
|
-
{
|
|
2260
|
+
yield {"event": "tool_call", "data": json.dumps({
|
|
2477
2261
|
"tool": "execute_command_tool",
|
|
2478
2262
|
"command": tool_args.get(
|
|
2479
2263
|
"command", ""
|
|
2480
2264
|
),
|
|
2481
2265
|
"timeout": tool_args.get("timeout"),
|
|
2482
|
-
}
|
|
2483
|
-
),
|
|
2484
|
-
}
|
|
2266
|
+
})}
|
|
2485
2267
|
elif tool_name in (
|
|
2486
2268
|
"search_notebook_cells_tool",
|
|
2487
2269
|
"search_notebook_cells",
|
|
2488
2270
|
):
|
|
2489
2271
|
# Search notebook cells - emit tool_call for client-side execution
|
|
2490
|
-
yield {
|
|
2491
|
-
"event": "tool_call",
|
|
2492
|
-
"data": json.dumps(
|
|
2493
|
-
{
|
|
2272
|
+
yield {"event": "tool_call", "data": json.dumps({
|
|
2494
2273
|
"tool": "search_notebook_cells",
|
|
2495
2274
|
"pattern": tool_args.get(
|
|
2496
2275
|
"pattern", ""
|
|
@@ -2507,9 +2286,7 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2507
2286
|
"case_sensitive": tool_args.get(
|
|
2508
2287
|
"case_sensitive", False
|
|
2509
2288
|
),
|
|
2510
|
-
}
|
|
2511
|
-
),
|
|
2512
|
-
}
|
|
2289
|
+
})}
|
|
2513
2290
|
|
|
2514
2291
|
# Drain and emit any subagent events (tool calls from subagents)
|
|
2515
2292
|
for subagent_event in get_subagent_debug_events():
|
|
@@ -2520,10 +2297,7 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2520
2297
|
if isinstance(step, dict) and "__interrupt__" in step:
|
|
2521
2298
|
interrupts = step["__interrupt__"]
|
|
2522
2299
|
|
|
2523
|
-
yield {
|
|
2524
|
-
"event": "debug",
|
|
2525
|
-
"data": json.dumps({"status": "사용자 승인 대기 중", "icon": "pause"}),
|
|
2526
|
-
}
|
|
2300
|
+
yield {"event": "debug", "data": json.dumps({"status": "사용자 승인 대기 중", "icon": "pause"})}
|
|
2527
2301
|
|
|
2528
2302
|
for interrupt in interrupts:
|
|
2529
2303
|
interrupt_value = (
|
|
@@ -2545,16 +2319,14 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2545
2319
|
for idx, action in enumerate(normalized_actions):
|
|
2546
2320
|
yield {
|
|
2547
2321
|
"event": "interrupt",
|
|
2548
|
-
"data": json.dumps(
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
}
|
|
2557
|
-
),
|
|
2322
|
+
"data": json.dumps({
|
|
2323
|
+
"thread_id": request.threadId,
|
|
2324
|
+
"action": action.get("name", "unknown"),
|
|
2325
|
+
"args": action.get("arguments", {}),
|
|
2326
|
+
"description": action.get("description", ""),
|
|
2327
|
+
"action_index": idx,
|
|
2328
|
+
"total_actions": total_actions,
|
|
2329
|
+
}),
|
|
2558
2330
|
}
|
|
2559
2331
|
|
|
2560
2332
|
# Save last signature for next resume to avoid duplicate content
|
|
@@ -2587,10 +2359,7 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2587
2359
|
last_signature,
|
|
2588
2360
|
latest_todos,
|
|
2589
2361
|
)
|
|
2590
|
-
yield {
|
|
2591
|
-
"event": "complete",
|
|
2592
|
-
"data": json.dumps({"success": True, "thread_id": request.threadId}),
|
|
2593
|
-
}
|
|
2362
|
+
yield {"event": "complete", "data": json.dumps({"success": True, "thread_id": request.threadId})}
|
|
2594
2363
|
|
|
2595
2364
|
except Exception as e:
|
|
2596
2365
|
error_msg = str(e)
|
|
@@ -2601,27 +2370,17 @@ async def resume_agent(request: ResumeRequest):
|
|
|
2601
2370
|
logger.warning(
|
|
2602
2371
|
"Detected 'contents is not specified' error - likely session state loss"
|
|
2603
2372
|
)
|
|
2604
|
-
yield {
|
|
2605
|
-
"event": "error",
|
|
2606
|
-
"data": json.dumps(
|
|
2607
|
-
{
|
|
2373
|
+
yield {"event": "error", "data": json.dumps({
|
|
2608
2374
|
"error": "Session state lost",
|
|
2609
2375
|
"code": "CONTENTS_NOT_SPECIFIED",
|
|
2610
2376
|
"error_type": type(e).__name__,
|
|
2611
2377
|
"message": "세션 상태가 손실되었습니다. 서버가 재시작되었거나 세션이 만료되었습니다. 새로운 대화를 시작해주세요.",
|
|
2612
|
-
}
|
|
2613
|
-
),
|
|
2614
|
-
}
|
|
2378
|
+
})}
|
|
2615
2379
|
else:
|
|
2616
|
-
yield {
|
|
2617
|
-
"event": "error",
|
|
2618
|
-
"data": json.dumps(
|
|
2619
|
-
{
|
|
2380
|
+
yield {"event": "error", "data": json.dumps({
|
|
2620
2381
|
"error": error_msg,
|
|
2621
2382
|
"error_type": type(e).__name__,
|
|
2622
|
-
}
|
|
2623
|
-
),
|
|
2624
|
-
}
|
|
2383
|
+
})}
|
|
2625
2384
|
|
|
2626
2385
|
return EventSourceResponse(event_generator())
|
|
2627
2386
|
|