yee88 0.1.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.
- takopi/__init__.py +1 -0
- takopi/api.py +116 -0
- takopi/backends.py +25 -0
- takopi/backends_helpers.py +14 -0
- takopi/cli/__init__.py +228 -0
- takopi/cli/config.py +320 -0
- takopi/cli/doctor.py +173 -0
- takopi/cli/init.py +113 -0
- takopi/cli/onboarding_cmd.py +126 -0
- takopi/cli/plugins.py +196 -0
- takopi/cli/run.py +419 -0
- takopi/cli/topic.py +355 -0
- takopi/commands.py +134 -0
- takopi/config.py +142 -0
- takopi/config_migrations.py +124 -0
- takopi/config_watch.py +146 -0
- takopi/context.py +9 -0
- takopi/directives.py +146 -0
- takopi/engines.py +53 -0
- takopi/events.py +170 -0
- takopi/ids.py +17 -0
- takopi/lockfile.py +158 -0
- takopi/logging.py +283 -0
- takopi/markdown.py +298 -0
- takopi/model.py +77 -0
- takopi/plugins.py +312 -0
- takopi/presenter.py +25 -0
- takopi/progress.py +99 -0
- takopi/router.py +113 -0
- takopi/runner.py +712 -0
- takopi/runner_bridge.py +619 -0
- takopi/runners/__init__.py +1 -0
- takopi/runners/claude.py +483 -0
- takopi/runners/codex.py +656 -0
- takopi/runners/mock.py +221 -0
- takopi/runners/opencode.py +505 -0
- takopi/runners/pi.py +523 -0
- takopi/runners/run_options.py +39 -0
- takopi/runners/tool_actions.py +90 -0
- takopi/runtime_loader.py +207 -0
- takopi/scheduler.py +159 -0
- takopi/schemas/__init__.py +1 -0
- takopi/schemas/claude.py +238 -0
- takopi/schemas/codex.py +169 -0
- takopi/schemas/opencode.py +51 -0
- takopi/schemas/pi.py +117 -0
- takopi/settings.py +360 -0
- takopi/telegram/__init__.py +20 -0
- takopi/telegram/api_models.py +37 -0
- takopi/telegram/api_schemas.py +152 -0
- takopi/telegram/backend.py +163 -0
- takopi/telegram/bridge.py +425 -0
- takopi/telegram/chat_prefs.py +242 -0
- takopi/telegram/chat_sessions.py +112 -0
- takopi/telegram/client.py +409 -0
- takopi/telegram/client_api.py +539 -0
- takopi/telegram/commands/__init__.py +12 -0
- takopi/telegram/commands/agent.py +196 -0
- takopi/telegram/commands/cancel.py +116 -0
- takopi/telegram/commands/dispatch.py +111 -0
- takopi/telegram/commands/executor.py +449 -0
- takopi/telegram/commands/file_transfer.py +586 -0
- takopi/telegram/commands/handlers.py +45 -0
- takopi/telegram/commands/media.py +143 -0
- takopi/telegram/commands/menu.py +139 -0
- takopi/telegram/commands/model.py +215 -0
- takopi/telegram/commands/overrides.py +159 -0
- takopi/telegram/commands/parse.py +30 -0
- takopi/telegram/commands/plan.py +16 -0
- takopi/telegram/commands/reasoning.py +234 -0
- takopi/telegram/commands/reply.py +23 -0
- takopi/telegram/commands/topics.py +332 -0
- takopi/telegram/commands/trigger.py +143 -0
- takopi/telegram/context.py +140 -0
- takopi/telegram/engine_defaults.py +86 -0
- takopi/telegram/engine_overrides.py +105 -0
- takopi/telegram/files.py +178 -0
- takopi/telegram/loop.py +1822 -0
- takopi/telegram/onboarding.py +1088 -0
- takopi/telegram/outbox.py +177 -0
- takopi/telegram/parsing.py +239 -0
- takopi/telegram/render.py +198 -0
- takopi/telegram/state_store.py +88 -0
- takopi/telegram/topic_state.py +334 -0
- takopi/telegram/topics.py +256 -0
- takopi/telegram/trigger_mode.py +68 -0
- takopi/telegram/types.py +63 -0
- takopi/telegram/voice.py +110 -0
- takopi/transport.py +53 -0
- takopi/transport_runtime.py +323 -0
- takopi/transports.py +76 -0
- takopi/utils/__init__.py +1 -0
- takopi/utils/git.py +87 -0
- takopi/utils/json_state.py +21 -0
- takopi/utils/paths.py +47 -0
- takopi/utils/streams.py +44 -0
- takopi/utils/subprocess.py +86 -0
- takopi/worktrees.py +135 -0
- yee88-0.1.0.dist-info/METADATA +116 -0
- yee88-0.1.0.dist-info/RECORD +103 -0
- yee88-0.1.0.dist-info/WHEEL +4 -0
- yee88-0.1.0.dist-info/entry_points.txt +11 -0
- yee88-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import AsyncIterator, Awaitable, Callable, Sequence
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from functools import partial
|
|
6
|
+
from typing import cast
|
|
7
|
+
|
|
8
|
+
import anyio
|
|
9
|
+
|
|
10
|
+
from ...commands import CommandExecutor, RunMode, RunRequest, RunResult
|
|
11
|
+
from ...config import ConfigError
|
|
12
|
+
from ...context import RunContext
|
|
13
|
+
from ...logging import bind_run_context, clear_context, get_logger
|
|
14
|
+
from ...model import Action, ActionEvent, EngineId, ResumeToken, TakopiEvent
|
|
15
|
+
from ...progress import ProgressTracker
|
|
16
|
+
from ...router import RunnerUnavailableError
|
|
17
|
+
from ...runner import Runner
|
|
18
|
+
from ...runners.run_options import EngineRunOptions, apply_run_options
|
|
19
|
+
from ...runner_bridge import (
|
|
20
|
+
ExecBridgeConfig,
|
|
21
|
+
IncomingMessage as RunnerIncomingMessage,
|
|
22
|
+
RunningTasks,
|
|
23
|
+
handle_message,
|
|
24
|
+
)
|
|
25
|
+
from ...scheduler import ThreadScheduler
|
|
26
|
+
from ...transport import MessageRef, RenderedMessage, SendOptions
|
|
27
|
+
from ...transport_runtime import TransportRuntime
|
|
28
|
+
from ...utils.paths import reset_run_base_dir, set_run_base_dir
|
|
29
|
+
from ..bridge import send_plain
|
|
30
|
+
from ..engine_overrides import supports_reasoning
|
|
31
|
+
|
|
32
|
+
logger = get_logger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(slots=True)
|
|
36
|
+
class _ResumeLineProxy:
|
|
37
|
+
runner: Runner
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def engine(self) -> str:
|
|
41
|
+
return self.runner.engine
|
|
42
|
+
|
|
43
|
+
def is_resume_line(self, line: str) -> bool:
|
|
44
|
+
return self.runner.is_resume_line(line)
|
|
45
|
+
|
|
46
|
+
def format_resume(self, _: ResumeToken) -> str:
|
|
47
|
+
return ""
|
|
48
|
+
|
|
49
|
+
def extract_resume(self, text: str | None) -> ResumeToken | None:
|
|
50
|
+
return self.runner.extract_resume(text)
|
|
51
|
+
|
|
52
|
+
def run(
|
|
53
|
+
self, prompt: str, resume: ResumeToken | None
|
|
54
|
+
) -> AsyncIterator[TakopiEvent]:
|
|
55
|
+
return self.runner.run(prompt, resume)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(slots=True)
|
|
59
|
+
class _PreludeRunner:
|
|
60
|
+
runner: Runner
|
|
61
|
+
prelude_events: Sequence[TakopiEvent]
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def engine(self) -> str:
|
|
65
|
+
return self.runner.engine
|
|
66
|
+
|
|
67
|
+
def is_resume_line(self, line: str) -> bool:
|
|
68
|
+
return self.runner.is_resume_line(line)
|
|
69
|
+
|
|
70
|
+
def format_resume(self, token: ResumeToken) -> str:
|
|
71
|
+
return self.runner.format_resume(token)
|
|
72
|
+
|
|
73
|
+
def extract_resume(self, text: str | None) -> ResumeToken | None:
|
|
74
|
+
return self.runner.extract_resume(text)
|
|
75
|
+
|
|
76
|
+
async def run(
|
|
77
|
+
self, prompt: str, resume: ResumeToken | None
|
|
78
|
+
) -> AsyncIterator[TakopiEvent]:
|
|
79
|
+
for event in self.prelude_events:
|
|
80
|
+
yield event
|
|
81
|
+
async for event in self.runner.run(prompt, resume):
|
|
82
|
+
yield event
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _reasoning_warning(
|
|
86
|
+
*, engine: str, run_options: EngineRunOptions | None
|
|
87
|
+
) -> ActionEvent | None:
|
|
88
|
+
if run_options is None or not run_options.reasoning:
|
|
89
|
+
return None
|
|
90
|
+
if supports_reasoning(engine):
|
|
91
|
+
return None
|
|
92
|
+
message = f"reasoning override is not supported for `{engine}`; ignoring."
|
|
93
|
+
return ActionEvent(
|
|
94
|
+
engine=engine,
|
|
95
|
+
action=Action(
|
|
96
|
+
id=f"{engine}.override.reasoning",
|
|
97
|
+
kind="note",
|
|
98
|
+
title=message,
|
|
99
|
+
detail={},
|
|
100
|
+
),
|
|
101
|
+
phase="completed",
|
|
102
|
+
ok=True,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _should_show_resume_line(
|
|
107
|
+
*,
|
|
108
|
+
show_resume_line: bool,
|
|
109
|
+
stateful_mode: bool,
|
|
110
|
+
context: RunContext | None,
|
|
111
|
+
) -> bool:
|
|
112
|
+
if show_resume_line:
|
|
113
|
+
return True
|
|
114
|
+
return not stateful_mode
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
async def _send_runner_unavailable(
|
|
118
|
+
exec_cfg: ExecBridgeConfig,
|
|
119
|
+
*,
|
|
120
|
+
chat_id: int,
|
|
121
|
+
user_msg_id: int,
|
|
122
|
+
resume_token: ResumeToken | None,
|
|
123
|
+
runner: Runner,
|
|
124
|
+
reason: str,
|
|
125
|
+
thread_id: int | None = None,
|
|
126
|
+
) -> None:
|
|
127
|
+
tracker = ProgressTracker(engine=runner.engine)
|
|
128
|
+
tracker.set_resume(resume_token)
|
|
129
|
+
state = tracker.snapshot(resume_formatter=runner.format_resume)
|
|
130
|
+
message = exec_cfg.presenter.render_final(
|
|
131
|
+
state,
|
|
132
|
+
elapsed_s=0.0,
|
|
133
|
+
status="error",
|
|
134
|
+
answer=f"error:\n{reason}",
|
|
135
|
+
)
|
|
136
|
+
reply_to = MessageRef(channel_id=chat_id, message_id=user_msg_id)
|
|
137
|
+
await exec_cfg.transport.send(
|
|
138
|
+
channel_id=chat_id,
|
|
139
|
+
message=message,
|
|
140
|
+
options=SendOptions(reply_to=reply_to, notify=True, thread_id=thread_id),
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
async def _run_engine(
|
|
145
|
+
*,
|
|
146
|
+
exec_cfg: ExecBridgeConfig,
|
|
147
|
+
runtime: TransportRuntime,
|
|
148
|
+
running_tasks: RunningTasks | None,
|
|
149
|
+
chat_id: int,
|
|
150
|
+
user_msg_id: int,
|
|
151
|
+
text: str,
|
|
152
|
+
resume_token: ResumeToken | None,
|
|
153
|
+
context: RunContext | None,
|
|
154
|
+
reply_ref: MessageRef | None = None,
|
|
155
|
+
on_thread_known: Callable[[ResumeToken, anyio.Event], Awaitable[None]]
|
|
156
|
+
| None = None,
|
|
157
|
+
engine_override: EngineId | None = None,
|
|
158
|
+
thread_id: int | None = None,
|
|
159
|
+
show_resume_line: bool = True,
|
|
160
|
+
progress_ref: MessageRef | None = None,
|
|
161
|
+
run_options: EngineRunOptions | None = None,
|
|
162
|
+
) -> None:
|
|
163
|
+
reply = partial(
|
|
164
|
+
send_plain,
|
|
165
|
+
exec_cfg.transport,
|
|
166
|
+
chat_id=chat_id,
|
|
167
|
+
user_msg_id=user_msg_id,
|
|
168
|
+
thread_id=thread_id,
|
|
169
|
+
)
|
|
170
|
+
try:
|
|
171
|
+
try:
|
|
172
|
+
entry = runtime.resolve_runner(
|
|
173
|
+
resume_token=resume_token,
|
|
174
|
+
engine_override=engine_override,
|
|
175
|
+
)
|
|
176
|
+
except RunnerUnavailableError as exc:
|
|
177
|
+
await reply(text=f"error:\n{exc}")
|
|
178
|
+
return
|
|
179
|
+
runner: Runner = entry.runner
|
|
180
|
+
if not show_resume_line:
|
|
181
|
+
runner = cast(Runner, _ResumeLineProxy(runner))
|
|
182
|
+
warning = _reasoning_warning(engine=runner.engine, run_options=run_options)
|
|
183
|
+
if warning is not None:
|
|
184
|
+
runner = cast(Runner, _PreludeRunner(runner, [warning]))
|
|
185
|
+
if not entry.available:
|
|
186
|
+
reason = entry.issue or "engine unavailable"
|
|
187
|
+
await _send_runner_unavailable(
|
|
188
|
+
exec_cfg,
|
|
189
|
+
chat_id=chat_id,
|
|
190
|
+
user_msg_id=user_msg_id,
|
|
191
|
+
resume_token=resume_token,
|
|
192
|
+
runner=runner,
|
|
193
|
+
reason=reason,
|
|
194
|
+
thread_id=thread_id,
|
|
195
|
+
)
|
|
196
|
+
return
|
|
197
|
+
try:
|
|
198
|
+
cwd = runtime.resolve_run_cwd(context)
|
|
199
|
+
except ConfigError as exc:
|
|
200
|
+
await reply(text=f"error:\n{exc}")
|
|
201
|
+
return
|
|
202
|
+
run_base_token = set_run_base_dir(cwd)
|
|
203
|
+
try:
|
|
204
|
+
run_fields = {
|
|
205
|
+
"chat_id": chat_id,
|
|
206
|
+
"user_msg_id": user_msg_id,
|
|
207
|
+
"engine": runner.engine,
|
|
208
|
+
"resume": resume_token.value if resume_token else None,
|
|
209
|
+
}
|
|
210
|
+
if context is not None:
|
|
211
|
+
run_fields["project"] = context.project
|
|
212
|
+
run_fields["branch"] = context.branch
|
|
213
|
+
if cwd is not None:
|
|
214
|
+
run_fields["cwd"] = str(cwd)
|
|
215
|
+
bind_run_context(**run_fields)
|
|
216
|
+
context_line = runtime.format_context_line(context)
|
|
217
|
+
incoming = RunnerIncomingMessage(
|
|
218
|
+
channel_id=chat_id,
|
|
219
|
+
message_id=user_msg_id,
|
|
220
|
+
text=text,
|
|
221
|
+
reply_to=reply_ref,
|
|
222
|
+
thread_id=thread_id,
|
|
223
|
+
)
|
|
224
|
+
with apply_run_options(run_options):
|
|
225
|
+
await handle_message(
|
|
226
|
+
exec_cfg,
|
|
227
|
+
runner=runner,
|
|
228
|
+
incoming=incoming,
|
|
229
|
+
resume_token=resume_token,
|
|
230
|
+
context=context,
|
|
231
|
+
context_line=context_line,
|
|
232
|
+
strip_resume_line=runtime.is_resume_line,
|
|
233
|
+
running_tasks=running_tasks,
|
|
234
|
+
on_thread_known=on_thread_known,
|
|
235
|
+
progress_ref=progress_ref,
|
|
236
|
+
)
|
|
237
|
+
finally:
|
|
238
|
+
reset_run_base_dir(run_base_token)
|
|
239
|
+
except Exception as exc:
|
|
240
|
+
logger.exception(
|
|
241
|
+
"handle.worker_failed",
|
|
242
|
+
error=str(exc),
|
|
243
|
+
error_type=exc.__class__.__name__,
|
|
244
|
+
)
|
|
245
|
+
finally:
|
|
246
|
+
clear_context()
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class _CaptureTransport:
|
|
250
|
+
def __init__(self) -> None:
|
|
251
|
+
self._next_id = 1
|
|
252
|
+
self.last_message: RenderedMessage | None = None
|
|
253
|
+
|
|
254
|
+
async def send(
|
|
255
|
+
self,
|
|
256
|
+
*,
|
|
257
|
+
channel_id: int | str,
|
|
258
|
+
message: RenderedMessage,
|
|
259
|
+
options: SendOptions | None = None,
|
|
260
|
+
) -> MessageRef:
|
|
261
|
+
thread_id = options.thread_id if options is not None else None
|
|
262
|
+
ref = MessageRef(channel_id=channel_id, message_id=self._next_id)
|
|
263
|
+
self._next_id += 1
|
|
264
|
+
self.last_message = message
|
|
265
|
+
return MessageRef(
|
|
266
|
+
channel_id=ref.channel_id,
|
|
267
|
+
message_id=ref.message_id,
|
|
268
|
+
thread_id=thread_id,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
async def edit(
|
|
272
|
+
self, *, ref: MessageRef, message: RenderedMessage, wait: bool = True
|
|
273
|
+
) -> MessageRef:
|
|
274
|
+
self.last_message = message
|
|
275
|
+
return ref
|
|
276
|
+
|
|
277
|
+
async def delete(self, *, ref: MessageRef) -> bool:
|
|
278
|
+
return True
|
|
279
|
+
|
|
280
|
+
async def close(self) -> None:
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
class _TelegramCommandExecutor(CommandExecutor):
|
|
285
|
+
def __init__(
|
|
286
|
+
self,
|
|
287
|
+
*,
|
|
288
|
+
exec_cfg: ExecBridgeConfig,
|
|
289
|
+
runtime: TransportRuntime,
|
|
290
|
+
running_tasks: RunningTasks,
|
|
291
|
+
scheduler: ThreadScheduler,
|
|
292
|
+
on_thread_known: Callable[[ResumeToken, anyio.Event], Awaitable[None]] | None,
|
|
293
|
+
engine_overrides_resolver: Callable[
|
|
294
|
+
[EngineId], Awaitable[EngineRunOptions | None]
|
|
295
|
+
]
|
|
296
|
+
| None,
|
|
297
|
+
chat_id: int,
|
|
298
|
+
user_msg_id: int,
|
|
299
|
+
thread_id: int | None,
|
|
300
|
+
show_resume_line: bool,
|
|
301
|
+
stateful_mode: bool,
|
|
302
|
+
default_engine_override: EngineId | None,
|
|
303
|
+
) -> None:
|
|
304
|
+
self._exec_cfg = exec_cfg
|
|
305
|
+
self._runtime = runtime
|
|
306
|
+
self._running_tasks = running_tasks
|
|
307
|
+
self._scheduler = scheduler
|
|
308
|
+
self._on_thread_known = on_thread_known
|
|
309
|
+
self._engine_overrides_resolver = engine_overrides_resolver
|
|
310
|
+
self._chat_id = chat_id
|
|
311
|
+
self._user_msg_id = user_msg_id
|
|
312
|
+
self._thread_id = thread_id
|
|
313
|
+
self._show_resume_line = show_resume_line
|
|
314
|
+
self._stateful_mode = stateful_mode
|
|
315
|
+
self._default_engine_override = default_engine_override
|
|
316
|
+
self._reply_ref = MessageRef(
|
|
317
|
+
channel_id=chat_id,
|
|
318
|
+
message_id=user_msg_id,
|
|
319
|
+
thread_id=thread_id,
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
def _apply_default_context(self, request: RunRequest) -> RunRequest:
|
|
323
|
+
if request.context is not None:
|
|
324
|
+
return request
|
|
325
|
+
context = self._runtime.default_context_for_chat(self._chat_id)
|
|
326
|
+
if context is None:
|
|
327
|
+
return request
|
|
328
|
+
return RunRequest(
|
|
329
|
+
prompt=request.prompt,
|
|
330
|
+
engine=request.engine,
|
|
331
|
+
context=context,
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
def _apply_default_engine(self, request: RunRequest) -> RunRequest:
|
|
335
|
+
if request.engine is not None or self._default_engine_override is None:
|
|
336
|
+
return request
|
|
337
|
+
return RunRequest(
|
|
338
|
+
prompt=request.prompt,
|
|
339
|
+
engine=self._default_engine_override,
|
|
340
|
+
context=request.context,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
async def send(
|
|
344
|
+
self,
|
|
345
|
+
message: RenderedMessage | str,
|
|
346
|
+
*,
|
|
347
|
+
reply_to: MessageRef | None = None,
|
|
348
|
+
notify: bool = True,
|
|
349
|
+
) -> MessageRef | None:
|
|
350
|
+
rendered = (
|
|
351
|
+
message
|
|
352
|
+
if isinstance(message, RenderedMessage)
|
|
353
|
+
else RenderedMessage(text=message)
|
|
354
|
+
)
|
|
355
|
+
reply_ref = self._reply_ref if reply_to is None else reply_to
|
|
356
|
+
return await self._exec_cfg.transport.send(
|
|
357
|
+
channel_id=self._chat_id,
|
|
358
|
+
message=rendered,
|
|
359
|
+
options=SendOptions(
|
|
360
|
+
reply_to=reply_ref,
|
|
361
|
+
notify=notify,
|
|
362
|
+
thread_id=self._thread_id,
|
|
363
|
+
),
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
async def run_one(
|
|
367
|
+
self, request: RunRequest, *, mode: RunMode = "emit"
|
|
368
|
+
) -> RunResult:
|
|
369
|
+
request = self._apply_default_context(request)
|
|
370
|
+
request = self._apply_default_engine(request)
|
|
371
|
+
effective_show_resume_line = _should_show_resume_line(
|
|
372
|
+
show_resume_line=self._show_resume_line,
|
|
373
|
+
stateful_mode=self._stateful_mode,
|
|
374
|
+
context=request.context,
|
|
375
|
+
)
|
|
376
|
+
engine = self._runtime.resolve_engine(
|
|
377
|
+
engine_override=request.engine,
|
|
378
|
+
context=request.context,
|
|
379
|
+
)
|
|
380
|
+
run_options = None
|
|
381
|
+
if self._engine_overrides_resolver is not None:
|
|
382
|
+
run_options = await self._engine_overrides_resolver(engine)
|
|
383
|
+
on_thread_known = (
|
|
384
|
+
self._scheduler.note_thread_known
|
|
385
|
+
if self._on_thread_known is None
|
|
386
|
+
else self._on_thread_known
|
|
387
|
+
)
|
|
388
|
+
if mode == "capture":
|
|
389
|
+
capture = _CaptureTransport()
|
|
390
|
+
exec_cfg = ExecBridgeConfig(
|
|
391
|
+
transport=capture,
|
|
392
|
+
presenter=self._exec_cfg.presenter,
|
|
393
|
+
final_notify=False,
|
|
394
|
+
)
|
|
395
|
+
await _run_engine(
|
|
396
|
+
exec_cfg=exec_cfg,
|
|
397
|
+
runtime=self._runtime,
|
|
398
|
+
running_tasks={},
|
|
399
|
+
chat_id=self._chat_id,
|
|
400
|
+
user_msg_id=self._user_msg_id,
|
|
401
|
+
text=request.prompt,
|
|
402
|
+
resume_token=None,
|
|
403
|
+
context=request.context,
|
|
404
|
+
reply_ref=self._reply_ref,
|
|
405
|
+
on_thread_known=on_thread_known,
|
|
406
|
+
engine_override=engine,
|
|
407
|
+
thread_id=self._thread_id,
|
|
408
|
+
show_resume_line=effective_show_resume_line,
|
|
409
|
+
run_options=run_options,
|
|
410
|
+
)
|
|
411
|
+
return RunResult(engine=engine, message=capture.last_message)
|
|
412
|
+
await _run_engine(
|
|
413
|
+
exec_cfg=self._exec_cfg,
|
|
414
|
+
runtime=self._runtime,
|
|
415
|
+
running_tasks=self._running_tasks,
|
|
416
|
+
chat_id=self._chat_id,
|
|
417
|
+
user_msg_id=self._user_msg_id,
|
|
418
|
+
text=request.prompt,
|
|
419
|
+
resume_token=None,
|
|
420
|
+
context=request.context,
|
|
421
|
+
reply_ref=self._reply_ref,
|
|
422
|
+
on_thread_known=on_thread_known,
|
|
423
|
+
engine_override=engine,
|
|
424
|
+
thread_id=self._thread_id,
|
|
425
|
+
show_resume_line=effective_show_resume_line,
|
|
426
|
+
run_options=run_options,
|
|
427
|
+
)
|
|
428
|
+
return RunResult(engine=engine, message=None)
|
|
429
|
+
|
|
430
|
+
async def run_many(
|
|
431
|
+
self,
|
|
432
|
+
requests: Sequence[RunRequest],
|
|
433
|
+
*,
|
|
434
|
+
mode: RunMode = "emit",
|
|
435
|
+
parallel: bool = False,
|
|
436
|
+
) -> list[RunResult]:
|
|
437
|
+
if not parallel:
|
|
438
|
+
return [await self.run_one(request, mode=mode) for request in requests]
|
|
439
|
+
results: list[RunResult | None] = [None] * len(requests)
|
|
440
|
+
|
|
441
|
+
async with anyio.create_task_group() as tg:
|
|
442
|
+
|
|
443
|
+
async def run_idx(idx: int, request: RunRequest) -> None:
|
|
444
|
+
results[idx] = await self.run_one(request, mode=mode)
|
|
445
|
+
|
|
446
|
+
for idx, request in enumerate(requests):
|
|
447
|
+
tg.start_soon(run_idx, idx, request)
|
|
448
|
+
|
|
449
|
+
return [result for result in results if result is not None]
|