deepy-cli 0.2.4__tar.gz → 0.2.5__tar.gz
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.
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/PKG-INFO +1 -1
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/pyproject.toml +1 -1
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/__init__.py +1 -1
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/runner.py +149 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/README.md +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/__main__.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/cli.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/config/__init__.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/config/settings.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/__init__.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/skills/skill-creator/SKILL.md +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/skills/skill-installer/SKILL.md +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/AskUserQuestion.md +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/WebFetch.md +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/WebSearch.md +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/__init__.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/edit.md +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/modify.md +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/read.md +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/shell.md +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/todo_write.md +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/write.md +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/errors.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/__init__.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/agent.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/compaction.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/context.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/events.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/model_capabilities.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/provider.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/replay.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/thinking.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/mcp.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/prompts/__init__.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/prompts/compact.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/prompts/init_agents.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/prompts/rules.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/prompts/runtime_context.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/prompts/system.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/prompts/tool_docs.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/sessions/__init__.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/sessions/jsonl.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/sessions/manager.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/skill_market.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/skills.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/status.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/todos.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/tools/__init__.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/tools/agents.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/tools/builtin.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/tools/file_state.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/tools/result.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/tools/shell_output.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/tools/shell_utils.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/types/__init__.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/types/sdk.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/types/tool_payloads.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/__init__.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/app.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/ask_user_question.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/exit_summary.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/file_mentions.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/loading_text.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/local_command.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/markdown.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/message_view.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/model_picker.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/prompt_buffer.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/prompt_input.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/session_list.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/session_picker.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/skill_picker.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/slash_commands.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/status_footer.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/styles.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/terminal.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/theme_picker.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/thinking_state.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/welcome.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/update_check.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/usage.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/utils/__init__.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/utils/debug_logger.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/utils/error_logger.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/utils/json.py +0 -0
- {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/utils/notify.py +0 -0
|
@@ -25,6 +25,11 @@ from .events import DeepyStreamEvent, normalize_stream_event
|
|
|
25
25
|
from .provider import ProviderBundle, build_provider_bundle
|
|
26
26
|
|
|
27
27
|
DEFAULT_MAX_TURNS = 100
|
|
28
|
+
INTERRUPTED_MARKER_TEXT = (
|
|
29
|
+
"Interrupted by user with Esc. This turn was stopped before completion. "
|
|
30
|
+
"Do not continue the interrupted request unless the user explicitly asks to continue."
|
|
31
|
+
)
|
|
32
|
+
INTERRUPTED_TOOL_OUTPUT_TEXT = "Tool execution interrupted by user with Esc."
|
|
28
33
|
|
|
29
34
|
|
|
30
35
|
@dataclass(frozen=True)
|
|
@@ -128,6 +133,7 @@ async def run_prompt_once(
|
|
|
128
133
|
pending_questions: list[dict[str, Any]] = []
|
|
129
134
|
usage = TokenUsage()
|
|
130
135
|
interrupt_task: asyncio.Task[bool] | None = None
|
|
136
|
+
session_baseline_count = len(await session.get_items())
|
|
131
137
|
try:
|
|
132
138
|
result = Runner.run_streamed(
|
|
133
139
|
agent,
|
|
@@ -272,6 +278,12 @@ async def run_prompt_once(
|
|
|
272
278
|
raise
|
|
273
279
|
|
|
274
280
|
interrupted = interrupted or await _finish_interrupt_task(interrupt_task)
|
|
281
|
+
if interrupted:
|
|
282
|
+
await _reconcile_interrupted_session_tail(
|
|
283
|
+
session,
|
|
284
|
+
baseline_count=session_baseline_count,
|
|
285
|
+
prompt=prompt,
|
|
286
|
+
)
|
|
275
287
|
|
|
276
288
|
final_output = getattr(result, "final_output", None)
|
|
277
289
|
output = final_output if isinstance(final_output, str) else "".join(chunks)
|
|
@@ -512,6 +524,143 @@ async def _finish_interrupt_task(task: asyncio.Task[bool] | None) -> bool:
|
|
|
512
524
|
return False
|
|
513
525
|
|
|
514
526
|
|
|
527
|
+
async def _reconcile_interrupted_session_tail(
|
|
528
|
+
session: DeepyJsonlSession,
|
|
529
|
+
*,
|
|
530
|
+
baseline_count: int,
|
|
531
|
+
prompt: str,
|
|
532
|
+
) -> None:
|
|
533
|
+
items = await session.get_items()
|
|
534
|
+
if baseline_count < 0 or baseline_count > len(items):
|
|
535
|
+
return
|
|
536
|
+
suffix = items[baseline_count:]
|
|
537
|
+
if not suffix:
|
|
538
|
+
return
|
|
539
|
+
|
|
540
|
+
if len(suffix) == 1 and _is_user_prompt_item(suffix[0], prompt):
|
|
541
|
+
await session.pop_item()
|
|
542
|
+
return
|
|
543
|
+
|
|
544
|
+
additions = _interrupted_tool_output_items(suffix)
|
|
545
|
+
if not _is_interrupt_marker_item(suffix[-1]):
|
|
546
|
+
additions.append(_interrupted_marker_item())
|
|
547
|
+
if additions:
|
|
548
|
+
await session.add_items(additions)
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def _is_user_prompt_item(item: dict[str, Any], prompt: str) -> bool:
|
|
552
|
+
if item.get("role") != "user":
|
|
553
|
+
return False
|
|
554
|
+
return _item_text_content(item) == prompt
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
def _item_text_content(item: dict[str, Any]) -> str:
|
|
558
|
+
content = item.get("content")
|
|
559
|
+
if isinstance(content, str):
|
|
560
|
+
return content
|
|
561
|
+
if not isinstance(content, list):
|
|
562
|
+
return ""
|
|
563
|
+
parts: list[str] = []
|
|
564
|
+
for part in content:
|
|
565
|
+
if isinstance(part, dict):
|
|
566
|
+
text = part.get("text")
|
|
567
|
+
if text is None:
|
|
568
|
+
text = part.get("input_text")
|
|
569
|
+
if isinstance(text, str):
|
|
570
|
+
parts.append(text)
|
|
571
|
+
return "".join(parts)
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def _interrupted_tool_output_items(items: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
575
|
+
output_call_ids = {
|
|
576
|
+
call_id
|
|
577
|
+
for item in items
|
|
578
|
+
if (call_id := _function_call_output_id(item))
|
|
579
|
+
}
|
|
580
|
+
additions: list[dict[str, Any]] = []
|
|
581
|
+
added_call_ids: set[str] = set()
|
|
582
|
+
for item in items:
|
|
583
|
+
for call_id, output_item in _missing_output_items_for_call(item, output_call_ids):
|
|
584
|
+
if call_id in added_call_ids:
|
|
585
|
+
continue
|
|
586
|
+
additions.append(output_item)
|
|
587
|
+
added_call_ids.add(call_id)
|
|
588
|
+
return additions
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
def _missing_output_items_for_call(
|
|
592
|
+
item: dict[str, Any],
|
|
593
|
+
output_call_ids: set[str],
|
|
594
|
+
) -> list[tuple[str, dict[str, Any]]]:
|
|
595
|
+
call_id = _function_call_id(item)
|
|
596
|
+
if call_id:
|
|
597
|
+
return (
|
|
598
|
+
[]
|
|
599
|
+
if call_id in output_call_ids
|
|
600
|
+
else [
|
|
601
|
+
(
|
|
602
|
+
call_id,
|
|
603
|
+
{
|
|
604
|
+
"type": "function_call_output",
|
|
605
|
+
"call_id": call_id,
|
|
606
|
+
"output": INTERRUPTED_TOOL_OUTPUT_TEXT,
|
|
607
|
+
},
|
|
608
|
+
)
|
|
609
|
+
]
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
missing: list[tuple[str, dict[str, Any]]] = []
|
|
613
|
+
if item.get("role") != "assistant":
|
|
614
|
+
return missing
|
|
615
|
+
tool_calls = item.get("tool_calls")
|
|
616
|
+
if not isinstance(tool_calls, list):
|
|
617
|
+
return missing
|
|
618
|
+
for tool_call in tool_calls:
|
|
619
|
+
if not isinstance(tool_call, dict):
|
|
620
|
+
continue
|
|
621
|
+
chat_call_id = tool_call.get("id")
|
|
622
|
+
if not isinstance(chat_call_id, str) or not chat_call_id or chat_call_id in output_call_ids:
|
|
623
|
+
continue
|
|
624
|
+
missing.append(
|
|
625
|
+
(
|
|
626
|
+
chat_call_id,
|
|
627
|
+
{
|
|
628
|
+
"role": "tool",
|
|
629
|
+
"tool_call_id": chat_call_id,
|
|
630
|
+
"content": INTERRUPTED_TOOL_OUTPUT_TEXT,
|
|
631
|
+
},
|
|
632
|
+
)
|
|
633
|
+
)
|
|
634
|
+
return missing
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def _function_call_id(item: dict[str, Any]) -> str:
|
|
638
|
+
if item.get("type") != "function_call":
|
|
639
|
+
return ""
|
|
640
|
+
call_id = item.get("call_id")
|
|
641
|
+
if call_id is None:
|
|
642
|
+
call_id = item.get("id")
|
|
643
|
+
return call_id if isinstance(call_id, str) else ""
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
def _function_call_output_id(item: dict[str, Any]) -> str:
|
|
647
|
+
if item.get("type") == "function_call_output":
|
|
648
|
+
call_id = item.get("call_id")
|
|
649
|
+
return call_id if isinstance(call_id, str) else ""
|
|
650
|
+
if item.get("role") == "tool":
|
|
651
|
+
tool_call_id = item.get("tool_call_id")
|
|
652
|
+
return tool_call_id if isinstance(tool_call_id, str) else ""
|
|
653
|
+
return ""
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
def _interrupted_marker_item() -> dict[str, Any]:
|
|
657
|
+
return {"role": "assistant", "content": INTERRUPTED_MARKER_TEXT}
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
def _is_interrupt_marker_item(item: dict[str, Any]) -> bool:
|
|
661
|
+
return item.get("role") == "assistant" and item.get("content") == INTERRUPTED_MARKER_TEXT
|
|
662
|
+
|
|
663
|
+
|
|
515
664
|
def _pending_questions_from_tool_output(output: str) -> list[dict[str, Any]]:
|
|
516
665
|
if not output.strip():
|
|
517
666
|
return []
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|