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.
Files changed (37) hide show
  1. agent_server/routers/langchain_agent.py +100 -341
  2. {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
  3. {hdsp_jupyter_extension-2.0.21.data → hdsp_jupyter_extension-2.0.22.data}/data/share/jupyter/labextensions/hdsp-agent/package.json +2 -2
  4. 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
  5. 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
  6. {hdsp_jupyter_extension-2.0.21.dist-info → hdsp_jupyter_extension-2.0.22.dist-info}/METADATA +1 -1
  7. {hdsp_jupyter_extension-2.0.21.dist-info → hdsp_jupyter_extension-2.0.22.dist-info}/RECORD +37 -37
  8. jupyter_ext/_version.py +1 -1
  9. jupyter_ext/labextension/build_log.json +1 -1
  10. jupyter_ext/labextension/package.json +2 -2
  11. jupyter_ext/labextension/static/{remoteEntry.93b1c499786ecd47b837.js → remoteEntry.8496e8475f1bd164669b.js} +2 -2
  12. 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
  13. {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
  14. {hdsp_jupyter_extension-2.0.21.data → hdsp_jupyter_extension-2.0.22.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
  15. {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
  16. {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
  17. {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
  18. {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
  19. {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
  20. {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
  21. {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
  22. {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
  23. {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
  24. {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
  25. {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
  26. {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
  27. {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
  28. {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
  29. {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
  30. {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
  31. {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
  32. {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
  33. {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
  34. {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
  35. {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
  36. {hdsp_jupyter_extension-2.0.21.dist-info → hdsp_jupyter_extension-2.0.22.dist-info}/WHEEL +0 -0
  37. {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
- "event": "debug_clear",
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
- "event": "token",
1302
- "data": json.dumps({"content": repaired_content}),
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
- "thread_id": thread_id,
1530
- "action": action.get("name", "unknown"),
1531
- "args": action.get("arguments", {}),
1532
- "description": action.get("description", ""),
1533
- "action_index": idx,
1534
- "total_actions": total_actions,
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
- "event": "debug",
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
- "event": "debug",
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
- "event": "debug",
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
- "tool": "jupyter_cell",
1766
- "code": code,
1767
- "description": f"Converted from {tool_name}",
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
- "content": f"알 수 없는 도구 '{tool_name}'입니다. jupyter_cell_tool을 사용해주세요."
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
- "event": "debug_clear",
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
- "event": "token",
2408
- "data": json.dumps({"content": repaired_content}),
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
- "thread_id": request.threadId,
2551
- "action": action.get("name", "unknown"),
2552
- "args": action.get("arguments", {}),
2553
- "description": action.get("description", ""),
2554
- "action_index": idx,
2555
- "total_actions": total_actions,
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