yee88 0.3.0__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.
- yee88/__init__.py +1 -0
- yee88/api.py +116 -0
- yee88/backends.py +25 -0
- yee88/backends_helpers.py +14 -0
- yee88/cli/__init__.py +228 -0
- yee88/cli/config.py +320 -0
- yee88/cli/doctor.py +173 -0
- yee88/cli/init.py +113 -0
- yee88/cli/onboarding_cmd.py +126 -0
- yee88/cli/plugins.py +196 -0
- yee88/cli/run.py +419 -0
- yee88/cli/topic.py +355 -0
- yee88/commands.py +134 -0
- yee88/config.py +142 -0
- yee88/config_migrations.py +124 -0
- yee88/config_watch.py +146 -0
- yee88/context.py +9 -0
- yee88/directives.py +146 -0
- yee88/engines.py +53 -0
- yee88/events.py +170 -0
- yee88/ids.py +17 -0
- yee88/lockfile.py +158 -0
- yee88/logging.py +283 -0
- yee88/markdown.py +298 -0
- yee88/model.py +77 -0
- yee88/plugins.py +312 -0
- yee88/presenter.py +25 -0
- yee88/progress.py +99 -0
- yee88/router.py +113 -0
- yee88/runner.py +712 -0
- yee88/runner_bridge.py +619 -0
- yee88/runners/__init__.py +1 -0
- yee88/runners/claude.py +483 -0
- yee88/runners/codex.py +656 -0
- yee88/runners/mock.py +221 -0
- yee88/runners/opencode.py +505 -0
- yee88/runners/pi.py +523 -0
- yee88/runners/run_options.py +39 -0
- yee88/runners/tool_actions.py +90 -0
- yee88/runtime_loader.py +207 -0
- yee88/scheduler.py +159 -0
- yee88/schemas/__init__.py +1 -0
- yee88/schemas/claude.py +238 -0
- yee88/schemas/codex.py +169 -0
- yee88/schemas/opencode.py +51 -0
- yee88/schemas/pi.py +117 -0
- yee88/settings.py +360 -0
- yee88/telegram/__init__.py +20 -0
- yee88/telegram/api_models.py +37 -0
- yee88/telegram/api_schemas.py +152 -0
- yee88/telegram/backend.py +163 -0
- yee88/telegram/bridge.py +425 -0
- yee88/telegram/chat_prefs.py +242 -0
- yee88/telegram/chat_sessions.py +112 -0
- yee88/telegram/client.py +409 -0
- yee88/telegram/client_api.py +539 -0
- yee88/telegram/commands/__init__.py +12 -0
- yee88/telegram/commands/agent.py +196 -0
- yee88/telegram/commands/cancel.py +116 -0
- yee88/telegram/commands/dispatch.py +111 -0
- yee88/telegram/commands/executor.py +449 -0
- yee88/telegram/commands/file_transfer.py +586 -0
- yee88/telegram/commands/handlers.py +45 -0
- yee88/telegram/commands/media.py +143 -0
- yee88/telegram/commands/menu.py +139 -0
- yee88/telegram/commands/model.py +215 -0
- yee88/telegram/commands/overrides.py +159 -0
- yee88/telegram/commands/parse.py +30 -0
- yee88/telegram/commands/plan.py +16 -0
- yee88/telegram/commands/reasoning.py +234 -0
- yee88/telegram/commands/reply.py +23 -0
- yee88/telegram/commands/topics.py +332 -0
- yee88/telegram/commands/trigger.py +143 -0
- yee88/telegram/context.py +140 -0
- yee88/telegram/engine_defaults.py +86 -0
- yee88/telegram/engine_overrides.py +105 -0
- yee88/telegram/files.py +178 -0
- yee88/telegram/loop.py +1822 -0
- yee88/telegram/onboarding.py +1088 -0
- yee88/telegram/outbox.py +177 -0
- yee88/telegram/parsing.py +239 -0
- yee88/telegram/render.py +198 -0
- yee88/telegram/state_store.py +88 -0
- yee88/telegram/topic_state.py +334 -0
- yee88/telegram/topics.py +256 -0
- yee88/telegram/trigger_mode.py +68 -0
- yee88/telegram/types.py +63 -0
- yee88/telegram/voice.py +110 -0
- yee88/transport.py +53 -0
- yee88/transport_runtime.py +323 -0
- yee88/transports.py +76 -0
- yee88/utils/__init__.py +1 -0
- yee88/utils/git.py +87 -0
- yee88/utils/json_state.py +21 -0
- yee88/utils/paths.py +47 -0
- yee88/utils/streams.py +44 -0
- yee88/utils/subprocess.py +86 -0
- yee88/worktrees.py +135 -0
- yee88-0.3.0.dist-info/METADATA +116 -0
- yee88-0.3.0.dist-info/RECORD +103 -0
- yee88-0.3.0.dist-info/WHEEL +4 -0
- yee88-0.3.0.dist-info/entry_points.txt +11 -0
- yee88-0.3.0.dist-info/licenses/LICENSE +21 -0
yee88/runners/mock.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import uuid
|
|
5
|
+
from collections.abc import AsyncIterator, Awaitable, Callable, Iterable
|
|
6
|
+
from dataclasses import dataclass, replace
|
|
7
|
+
|
|
8
|
+
import anyio
|
|
9
|
+
|
|
10
|
+
from ..model import (
|
|
11
|
+
ActionEvent,
|
|
12
|
+
CompletedEvent,
|
|
13
|
+
EngineId,
|
|
14
|
+
ResumeToken,
|
|
15
|
+
StartedEvent,
|
|
16
|
+
TakopiEvent,
|
|
17
|
+
)
|
|
18
|
+
from ..runner import ResumeTokenMixin, Runner, SessionLockMixin
|
|
19
|
+
|
|
20
|
+
ENGINE: EngineId = "mock"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True, slots=True)
|
|
24
|
+
class Emit:
|
|
25
|
+
event: TakopiEvent
|
|
26
|
+
at: float | None = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True, slots=True)
|
|
30
|
+
class Advance:
|
|
31
|
+
now: float
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True, slots=True)
|
|
35
|
+
class Sleep:
|
|
36
|
+
seconds: float
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True, slots=True)
|
|
40
|
+
class Wait:
|
|
41
|
+
event: anyio.Event
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True, slots=True)
|
|
45
|
+
class Return:
|
|
46
|
+
answer: str
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True, slots=True)
|
|
50
|
+
class Raise:
|
|
51
|
+
error: Exception
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
type ScriptStep = Emit | Advance | Sleep | Wait | Return | Raise
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _resume_token(engine: EngineId, value: str | None) -> ResumeToken:
|
|
58
|
+
return ResumeToken(engine=engine, value=value or uuid.uuid4().hex)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class MockRunner(SessionLockMixin, ResumeTokenMixin, Runner):
|
|
62
|
+
engine: EngineId
|
|
63
|
+
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
*,
|
|
67
|
+
events: Iterable[TakopiEvent] | None = None,
|
|
68
|
+
answer: str = "",
|
|
69
|
+
engine: EngineId = ENGINE,
|
|
70
|
+
resume_value: str | None = None,
|
|
71
|
+
title: str | None = None,
|
|
72
|
+
) -> None:
|
|
73
|
+
self.engine = engine
|
|
74
|
+
self._events = list(events or [])
|
|
75
|
+
self._answer = answer
|
|
76
|
+
self._resume_value = resume_value
|
|
77
|
+
self.title = title or str(engine).title()
|
|
78
|
+
engine_name = re.escape(str(engine))
|
|
79
|
+
self.resume_re = re.compile(
|
|
80
|
+
rf"(?im)^\s*`?{engine_name}\s+resume\s+(?P<token>[^`\s]+)`?\s*$"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
async def run(
|
|
84
|
+
self, prompt: str, resume: ResumeToken | None
|
|
85
|
+
) -> AsyncIterator[TakopiEvent]:
|
|
86
|
+
token_value = None
|
|
87
|
+
if resume is not None:
|
|
88
|
+
if resume.engine != self.engine:
|
|
89
|
+
raise RuntimeError(
|
|
90
|
+
f"resume token is for engine {resume.engine!r}, not {self.engine!r}"
|
|
91
|
+
)
|
|
92
|
+
token_value = resume.value
|
|
93
|
+
if token_value is None:
|
|
94
|
+
token_value = self._resume_value
|
|
95
|
+
token = _resume_token(self.engine, token_value)
|
|
96
|
+
session_evt = StartedEvent(
|
|
97
|
+
engine=self.engine,
|
|
98
|
+
resume=token,
|
|
99
|
+
title=self.title,
|
|
100
|
+
)
|
|
101
|
+
lock = self.lock_for(token)
|
|
102
|
+
async with lock:
|
|
103
|
+
yield session_evt
|
|
104
|
+
|
|
105
|
+
for event in self._events:
|
|
106
|
+
event_out: TakopiEvent = event
|
|
107
|
+
if (
|
|
108
|
+
isinstance(event_out, ActionEvent)
|
|
109
|
+
and event_out.phase == "completed"
|
|
110
|
+
and event_out.ok is None
|
|
111
|
+
):
|
|
112
|
+
event_out = replace(event_out, ok=True)
|
|
113
|
+
yield event_out
|
|
114
|
+
await anyio.sleep(0)
|
|
115
|
+
|
|
116
|
+
yield CompletedEvent(
|
|
117
|
+
engine=self.engine,
|
|
118
|
+
resume=token,
|
|
119
|
+
ok=True,
|
|
120
|
+
answer=self._answer,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class ScriptRunner(MockRunner):
|
|
125
|
+
def __init__(
|
|
126
|
+
self,
|
|
127
|
+
script: Iterable[ScriptStep],
|
|
128
|
+
*,
|
|
129
|
+
engine: EngineId = ENGINE,
|
|
130
|
+
resume_value: str | None = None,
|
|
131
|
+
emit_session_start: bool = True,
|
|
132
|
+
sleep: Callable[[float], Awaitable[None]] = anyio.sleep,
|
|
133
|
+
advance: Callable[[float], None] | None = None,
|
|
134
|
+
default_answer: str = "",
|
|
135
|
+
title: str | None = None,
|
|
136
|
+
) -> None:
|
|
137
|
+
super().__init__(
|
|
138
|
+
events=[],
|
|
139
|
+
answer=default_answer,
|
|
140
|
+
engine=engine,
|
|
141
|
+
resume_value=resume_value,
|
|
142
|
+
title=title,
|
|
143
|
+
)
|
|
144
|
+
self.calls: list[tuple[str, ResumeToken | None]] = []
|
|
145
|
+
self._script = list(script)
|
|
146
|
+
self._emit_session_start = emit_session_start
|
|
147
|
+
self._sleep = sleep
|
|
148
|
+
self._advance = advance
|
|
149
|
+
|
|
150
|
+
def _advance_to(self, now: float) -> None:
|
|
151
|
+
if self._advance is None:
|
|
152
|
+
raise RuntimeError("ScriptRunner advance callback is not configured.")
|
|
153
|
+
self._advance(now)
|
|
154
|
+
|
|
155
|
+
async def run(
|
|
156
|
+
self, prompt: str, resume: ResumeToken | None
|
|
157
|
+
) -> AsyncIterator[TakopiEvent]:
|
|
158
|
+
self.calls.append((prompt, resume))
|
|
159
|
+
token_value = None
|
|
160
|
+
if resume is not None:
|
|
161
|
+
if resume.engine != self.engine:
|
|
162
|
+
raise RuntimeError(
|
|
163
|
+
f"resume token is for engine {resume.engine!r}, not {self.engine!r}"
|
|
164
|
+
)
|
|
165
|
+
token_value = resume.value
|
|
166
|
+
if token_value is None:
|
|
167
|
+
token_value = self._resume_value
|
|
168
|
+
token = _resume_token(self.engine, token_value)
|
|
169
|
+
session_evt = StartedEvent(
|
|
170
|
+
engine=self.engine,
|
|
171
|
+
resume=token,
|
|
172
|
+
title=self.title,
|
|
173
|
+
)
|
|
174
|
+
lock = self.lock_for(token)
|
|
175
|
+
|
|
176
|
+
async with lock:
|
|
177
|
+
if self._emit_session_start:
|
|
178
|
+
yield session_evt
|
|
179
|
+
await anyio.sleep(0)
|
|
180
|
+
|
|
181
|
+
for step in self._script:
|
|
182
|
+
if isinstance(step, Emit):
|
|
183
|
+
if step.at is not None:
|
|
184
|
+
self._advance_to(step.at)
|
|
185
|
+
event_out: TakopiEvent = step.event
|
|
186
|
+
if (
|
|
187
|
+
isinstance(event_out, ActionEvent)
|
|
188
|
+
and event_out.phase == "completed"
|
|
189
|
+
and event_out.ok is None
|
|
190
|
+
):
|
|
191
|
+
event_out = replace(event_out, ok=True)
|
|
192
|
+
yield event_out
|
|
193
|
+
await anyio.sleep(0)
|
|
194
|
+
continue
|
|
195
|
+
if isinstance(step, Advance):
|
|
196
|
+
self._advance_to(step.now)
|
|
197
|
+
continue
|
|
198
|
+
if isinstance(step, Sleep):
|
|
199
|
+
await self._sleep(step.seconds)
|
|
200
|
+
continue
|
|
201
|
+
if isinstance(step, Wait):
|
|
202
|
+
await step.event.wait()
|
|
203
|
+
continue
|
|
204
|
+
if isinstance(step, Raise):
|
|
205
|
+
raise step.error
|
|
206
|
+
if isinstance(step, Return):
|
|
207
|
+
yield CompletedEvent(
|
|
208
|
+
engine=self.engine,
|
|
209
|
+
resume=token,
|
|
210
|
+
ok=True,
|
|
211
|
+
answer=step.answer,
|
|
212
|
+
)
|
|
213
|
+
return
|
|
214
|
+
raise RuntimeError(f"Unhandled script step: {step!r}")
|
|
215
|
+
|
|
216
|
+
yield CompletedEvent(
|
|
217
|
+
engine=self.engine,
|
|
218
|
+
resume=token,
|
|
219
|
+
ok=True,
|
|
220
|
+
answer=self._answer,
|
|
221
|
+
)
|