vidbyte-cli 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.
- vidbyte_cli/__init__.py +11 -0
- vidbyte_cli/__main__.py +6 -0
- vidbyte_cli/cli.py +18 -0
- vidbyte_cli/commands/__init__.py +86 -0
- vidbyte_cli/commands/agent_options.py +53 -0
- vidbyte_cli/commands/agents/__init__.py +31 -0
- vidbyte_cli/commands/agents/suggestion/__init__.py +38 -0
- vidbyte_cli/commands/agents/suggestion/prompts/__init__.py +3 -0
- vidbyte_cli/commands/agents/suggestion/prompts/all_categories.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/approaches.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/avoid.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/blockers.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/capability.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/categories.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/category.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/completed.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/constraint.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/context.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/count.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/critic_model.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/decision.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/dry_run.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/extra_compute.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/feedback_accept.md +7 -0
- vidbyte_cli/commands/agents/suggestion/prompts/feedback_group.md +7 -0
- vidbyte_cli/commands/agents/suggestion/prompts/feedback_project.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/feedback_reason.md +98 -0
- vidbyte_cli/commands/agents/suggestion/prompts/feedback_reject.md +7 -0
- vidbyte_cli/commands/agents/suggestion/prompts/feedback_suggestion.md +101 -0
- vidbyte_cli/commands/agents/suggestion/prompts/files.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/forbidden.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/goal.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/handoff.md +7 -0
- vidbyte_cli/commands/agents/suggestion/prompts/horizon.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/hypotheses.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/idea.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/in_progress.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/input.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/library.py +29 -0
- vidbyte_cli/commands/agents/suggestion/prompts/max_messages.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/max_output_tokens.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/max_total_tokens.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/mistakes.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/outcomes.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/project.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/project_create.md +7 -0
- vidbyte_cli/commands/agents/suggestion/prompts/project_description.md +98 -0
- vidbyte_cli/commands/agents/suggestion/prompts/project_group.md +7 -0
- vidbyte_cli/commands/agents/suggestion/prompts/project_key.md +96 -0
- vidbyte_cli/commands/agents/suggestion/prompts/project_list.md +7 -0
- vidbyte_cli/commands/agents/suggestion/prompts/project_title.md +99 -0
- vidbyte_cli/commands/agents/suggestion/prompts/provider.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/question.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/risks.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/rounds.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/run.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/success.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/timeout.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/trajectory.md +100 -0
- vidbyte_cli/commands/agents/suggestion/prompts/view.md +5 -0
- vidbyte_cli/commands/agents/suggestion/prompts/view_all.md +5 -0
- vidbyte_cli/commands/agents/suggestion/render.py +77 -0
- vidbyte_cli/commands/agents/suggestion/request_builder.py +240 -0
- vidbyte_cli/commands/agents/suggestion/suggest.py +154 -0
- vidbyte_cli/commands/agents/suggestion/suggestion_categories.py +77 -0
- vidbyte_cli/commands/agents/suggestion/suggestion_feedback.py +85 -0
- vidbyte_cli/commands/agents/suggestion/suggestion_handoff.py +67 -0
- vidbyte_cli/commands/agents/suggestion/suggestion_projects.py +95 -0
- vidbyte_cli/commands/auth/__init__.py +0 -0
- vidbyte_cli/commands/auth/login.py +100 -0
- vidbyte_cli/commands/auth/logout.py +41 -0
- vidbyte_cli/commands/auth/whoami.py +51 -0
- vidbyte_cli/commands/billing/__init__.py +20 -0
- vidbyte_cli/commands/billing/prompts/__init__.py +1 -0
- vidbyte_cli/commands/billing/prompts/confirm.md +16 -0
- vidbyte_cli/commands/billing/prompts/group.md +13 -0
- vidbyte_cli/commands/billing/prompts/idempotency_key.md +16 -0
- vidbyte_cli/commands/billing/prompts/library.py +18 -0
- vidbyte_cli/commands/billing/prompts/top_up.md +16 -0
- vidbyte_cli/commands/billing/top_up.py +65 -0
- vidbyte_cli/commands/config/__init__.py +0 -0
- vidbyte_cli/commands/config/get.py +45 -0
- vidbyte_cli/commands/config/set.py +39 -0
- vidbyte_cli/commands/provider/__init__.py +9 -0
- vidbyte_cli/commands/provider/login.py +102 -0
- vidbyte_cli/commands/provider/logout.py +45 -0
- vidbyte_cli/commands/provider/whoami.py +49 -0
- vidbyte_cli/commands/research/__init__.py +5 -0
- vidbyte_cli/commands/research/add.py +41 -0
- vidbyte_cli/commands/research/options.py +134 -0
- vidbyte_cli/commands/research/render.py +157 -0
- vidbyte_cli/commands/research/resume.py +37 -0
- vidbyte_cli/commands/research/start.py +37 -0
- vidbyte_cli/commands/research/status.py +32 -0
- vidbyte_cli/commands/research/thread.py +30 -0
- vidbyte_cli/commands/research/threads.py +35 -0
- vidbyte_cli/commands/research/watch.py +93 -0
- vidbyte_cli/commands/runtime/__init__.py +23 -0
- vidbyte_cli/commands/runtime/adversarial_team.py +41 -0
- vidbyte_cli/commands/runtime/doctor.py +44 -0
- vidbyte_cli/commands/runtime/list.py +47 -0
- vidbyte_cli/commands/runtime/persistence.py +141 -0
- vidbyte_cli/commands/runtime/same_host_ensemble.py +221 -0
- vidbyte_cli/commands/runtime/stages.py +553 -0
- vidbyte_cli/commands/runtime/task_board.py +1342 -0
- vidbyte_cli/commands/setup/__init__.py +0 -0
- vidbyte_cli/commands/setup/doctor.py +64 -0
- vidbyte_cli/commands/setup/prompts/__init__.py +1 -0
- vidbyte_cli/commands/setup/prompts/browser.md +7 -0
- vidbyte_cli/commands/setup/prompts/library.py +18 -0
- vidbyte_cli/commands/setup/prompts/star.md +13 -0
- vidbyte_cli/commands/setup/star.py +132 -0
- vidbyte_cli/lib/__init__.py +0 -0
- vidbyte_cli/lib/api/__init__.py +0 -0
- vidbyte_cli/lib/api/client.py +236 -0
- vidbyte_cli/lib/api/endpoints/__init__.py +0 -0
- vidbyte_cli/lib/api/endpoints/auth.py +24 -0
- vidbyte_cli/lib/api/endpoints/billing.py +36 -0
- vidbyte_cli/lib/api/endpoints/research.py +103 -0
- vidbyte_cli/lib/api/endpoints/runtime.py +113 -0
- vidbyte_cli/lib/api/problem.py +86 -0
- vidbyte_cli/lib/api/response.py +101 -0
- vidbyte_cli/lib/api/retry.py +116 -0
- vidbyte_cli/lib/api/runtime_payment.py +89 -0
- vidbyte_cli/lib/auth/__init__.py +16 -0
- vidbyte_cli/lib/auth/credentials.py +223 -0
- vidbyte_cli/lib/auth/input.py +59 -0
- vidbyte_cli/lib/auth/keyring_store.py +126 -0
- vidbyte_cli/lib/auth/provider_credentials.py +157 -0
- vidbyte_cli/lib/auth/provider_input.py +54 -0
- vidbyte_cli/lib/auth/provider_resolver.py +89 -0
- vidbyte_cli/lib/auth/provider_store.py +149 -0
- vidbyte_cli/lib/auth/provider_verifier.py +219 -0
- vidbyte_cli/lib/auth/resolver.py +63 -0
- vidbyte_cli/lib/auth/verifier.py +38 -0
- vidbyte_cli/lib/config/__init__.py +25 -0
- vidbyte_cli/lib/config/atomic.py +47 -0
- vidbyte_cli/lib/config/config.py +141 -0
- vidbyte_cli/lib/config/migration.py +81 -0
- vidbyte_cli/lib/config/models.py +145 -0
- vidbyte_cli/lib/config/paths.py +66 -0
- vidbyte_cli/lib/config/resolver.py +137 -0
- vidbyte_cli/lib/constants/__init__.py +1 -0
- vidbyte_cli/lib/constants/runtime.py +186 -0
- vidbyte_cli/lib/errors/__init__.py +12 -0
- vidbyte_cli/lib/errors/cli_error.py +86 -0
- vidbyte_cli/lib/errors/codes.py +43 -0
- vidbyte_cli/lib/errors/failures.py +2910 -0
- vidbyte_cli/lib/errors/handler.py +72 -0
- vidbyte_cli/lib/files/__init__.py +6 -0
- vidbyte_cli/lib/files/documents.py +94 -0
- vidbyte_cli/lib/files/store.py +156 -0
- vidbyte_cli/lib/io/__init__.py +16 -0
- vidbyte_cli/lib/io/attachments.py +129 -0
- vidbyte_cli/lib/io/codex_attachments.py +47 -0
- vidbyte_cli/lib/io/prompt.py +71 -0
- vidbyte_cli/lib/io/streams.py +37 -0
- vidbyte_cli/lib/io/terminal.py +66 -0
- vidbyte_cli/lib/output/__init__.py +7 -0
- vidbyte_cli/lib/output/formats.py +29 -0
- vidbyte_cli/lib/output/manager.py +95 -0
- vidbyte_cli/lib/output/models.py +49 -0
- vidbyte_cli/lib/runtime/__init__.py +9 -0
- vidbyte_cli/lib/runtime/application.py +134 -0
- vidbyte_cli/lib/runtime/context.py +250 -0
- vidbyte_cli/lib/runtime/options.py +159 -0
- vidbyte_cli/lib/runtime/version.py +30 -0
- vidbyte_cli/lib/runtime_primitives/__init__.py +28 -0
- vidbyte_cli/lib/runtime_primitives/executor.py +54 -0
- vidbyte_cli/lib/runtime_primitives/gate.py +105 -0
- vidbyte_cli/lib/runtime_primitives/hosts.py +45 -0
- vidbyte_cli/lib/runtime_primitives/planner.py +79 -0
- vidbyte_cli/lib/runtime_primitives/stages.py +338 -0
- vidbyte_cli/lib/runtime_primitives/task_board.py +982 -0
- vidbyte_cli/lib/runtime_primitives/task_board_checkpoints.py +473 -0
- vidbyte_cli/lib/runtime_primitives/task_board_system.md +33 -0
- vidbyte_cli/lib/runtime_primitives/task_board_tasks.py +111 -0
- vidbyte_cli/lib/runtime_primitives/verification.py +68 -0
- vidbyte_cli/services/__init__.py +5 -0
- vidbyte_cli/services/ensemble/__init__.py +7 -0
- vidbyte_cli/services/ensemble/prompts/__init__.py +6 -0
- vidbyte_cli/services/ensemble/prompts/implementer_system.md +546 -0
- vidbyte_cli/services/ensemble/prompts/implementer_turn.md +49 -0
- vidbyte_cli/services/ensemble/prompts/library.py +188 -0
- vidbyte_cli/services/ensemble/prompts/planner_system.md +728 -0
- vidbyte_cli/services/ensemble/prompts/planner_turn.md +6 -0
- vidbyte_cli/services/ensemble/prompts/role_system.md +104 -0
- vidbyte_cli/services/ensemble/prompts/role_turn.md +19 -0
- vidbyte_cli/services/ensemble/prompts/selector_final_turn.md +19 -0
- vidbyte_cli/services/ensemble/prompts/selector_round_turn.md +17 -0
- vidbyte_cli/services/ensemble/prompts/selector_system.md +89 -0
- vidbyte_cli/services/ensemble/runner.py +30 -0
- vidbyte_cli/services/ensemble/sdk.py +134 -0
- vidbyte_cli/services/ensemble/service.py +233 -0
- vidbyte_cli/services/ensemble/settings.py +60 -0
- vidbyte_cli/services/persistence/__init__.py +6 -0
- vidbyte_cli/services/persistence/prompts/__init__.py +6 -0
- vidbyte_cli/services/persistence/prompts/library.py +46 -0
- vidbyte_cli/services/persistence/prompts/persistence_system.md +1 -0
- vidbyte_cli/services/persistence/prompts/persistence_turn.md +3 -0
- vidbyte_cli/services/persistence/runner.py +35 -0
- vidbyte_cli/services/persistence/session.py +116 -0
- vidbyte_cli/services/suggestions/__init__.py +7 -0
- vidbyte_cli/services/suggestions/categories.py +270 -0
- vidbyte_cli/services/suggestions/context.py +222 -0
- vidbyte_cli/services/suggestions/extra_compute.py +53 -0
- vidbyte_cli/services/suggestions/handoff.py +173 -0
- vidbyte_cli/services/suggestions/message_tools.py +47 -0
- vidbyte_cli/services/suggestions/project.py +253 -0
- vidbyte_cli/services/suggestions/prompts/__init__.py +1 -0
- vidbyte_cli/services/suggestions/prompts/categories/__init__.py +3 -0
- vidbyte_cli/services/suggestions/prompts/categories/adjacent_opportunity.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/alternative.md +38 -0
- vidbyte_cli/services/suggestions/prompts/categories/big_bets.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/bottleneck.md +38 -0
- vidbyte_cli/services/suggestions/prompts/categories/business_growth.md +38 -0
- vidbyte_cli/services/suggestions/prompts/categories/completion.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/continuation.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/coordination.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/creative_exploration.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/cross_domain.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/delegation.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/experiment.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/feedback.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/goal_clarification.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/immediate_next_steps.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/investigation.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/learning.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/leverage.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/long_term_directions.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/preparation.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/prerequisite.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/prioritization.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/product_experience.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/quick_wins.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/reframing.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/risk_prevention.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/simplification.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/stop_or_defer.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/strategy.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/technical_possibilities.md +37 -0
- vidbyte_cli/services/suggestions/prompts/categories/verification.md +37 -0
- vidbyte_cli/services/suggestions/prompts/critic.md +209 -0
- vidbyte_cli/services/suggestions/prompts/critic_message.md +8 -0
- vidbyte_cli/services/suggestions/prompts/generator.md +64 -0
- vidbyte_cli/services/suggestions/prompts/library.py +72 -0
- vidbyte_cli/services/suggestions/prompts/message_generator_argument.md +1 -0
- vidbyte_cli/services/suggestions/prompts/message_generator_tool.md +1 -0
- vidbyte_cli/services/suggestions/prompts/message_parent_argument.md +1 -0
- vidbyte_cli/services/suggestions/prompts/message_parent_tool.md +1 -0
- vidbyte_cli/services/suggestions/prompts/message_receipt.md +1 -0
- vidbyte_cli/services/suggestions/prompts/revision.md +8 -0
- vidbyte_cli/services/suggestions/sdk.py +228 -0
- vidbyte_cli/services/suggestions/selection.py +78 -0
- vidbyte_cli/services/suggestions/service.py +540 -0
- vidbyte_cli/types/__init__.py +0 -0
- vidbyte_cli/types/api.py +101 -0
- vidbyte_cli/types/attachments.py +79 -0
- vidbyte_cli/types/ensemble.py +189 -0
- vidbyte_cli/types/provider.py +62 -0
- vidbyte_cli/types/research.py +260 -0
- vidbyte_cli/types/runtime.py +1098 -0
- vidbyte_cli/types/suggestions.py +518 -0
- vidbyte_cli-0.1.0.dist-info/METADATA +335 -0
- vidbyte_cli-0.1.0.dist-info/RECORD +269 -0
- vidbyte_cli-0.1.0.dist-info/WHEEL +5 -0
- vidbyte_cli-0.1.0.dist-info/entry_points.txt +2 -0
- vidbyte_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- vidbyte_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"""Runs caller-defined stages with one fresh Codex agent per stage attempt.
|
|
2
|
+
|
|
3
|
+
The SDK owns transport and thread identity. This adapter owns stage fan-out, sequential
|
|
4
|
+
handoff through the previous reply, and rejection of incomplete results. Every stage
|
|
5
|
+
setting arrives here already validated as a CLI enum or a settings-checked value, so this
|
|
6
|
+
module converts rather than parses: the closed value sets are enforced at the command
|
|
7
|
+
layer where Click can reject a bad word before the wallet is ever touched.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import json
|
|
14
|
+
import random
|
|
15
|
+
import time
|
|
16
|
+
from collections.abc import Callable, Mapping
|
|
17
|
+
from typing import TYPE_CHECKING, Any
|
|
18
|
+
|
|
19
|
+
from ...types.runtime import RuntimeLaunchPlan as Plan
|
|
20
|
+
from ...types.runtime import (
|
|
21
|
+
StageSpec,
|
|
22
|
+
StagesResult,
|
|
23
|
+
StagesSettings,
|
|
24
|
+
StageStepResult,
|
|
25
|
+
)
|
|
26
|
+
from ..constants.runtime import StagesCodexConfig as CodexConfig
|
|
27
|
+
from ..constants.runtime import StagesLimit, StagesProgress
|
|
28
|
+
from ..errors.failures import StagesHostFailed, StagesSettingsInvalid
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from vidbyte.agents.codex import CodexHarnessAgent
|
|
32
|
+
from vidbyte.agents.types import AgentMessage
|
|
33
|
+
from vidbyte.lib.dataclasses.codex import CodexHarnessAgentSettings
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class StagesCodexSession:
|
|
37
|
+
"""One invocation's environment and lazy per-stage SDK agents."""
|
|
38
|
+
|
|
39
|
+
def __init__(self, environment: Mapping[str, str], progress: Callable[[str], None]) -> None:
|
|
40
|
+
# Construction stays free of SDK imports so help never needs Codex.
|
|
41
|
+
self._environment = dict(environment)
|
|
42
|
+
self._progress = progress
|
|
43
|
+
self._executable = ""
|
|
44
|
+
self._working_directory = ""
|
|
45
|
+
self._prepared = False
|
|
46
|
+
|
|
47
|
+
def prepare(self, plan: Plan, settings: StagesSettings) -> None:
|
|
48
|
+
# Records launch paths without starting any model turn, so every failure the
|
|
49
|
+
# caller can cause is still ahead of payment. Agents are built lazily per
|
|
50
|
+
# attempt because a failed attempt may hold broken transport state.
|
|
51
|
+
from vidbyte.agents.codex import CodexHarnessAgent # noqa: F401
|
|
52
|
+
|
|
53
|
+
_ = settings
|
|
54
|
+
self._executable = str(plan.executable)
|
|
55
|
+
self._working_directory = str(plan.working_directory)
|
|
56
|
+
self._prepared = True
|
|
57
|
+
|
|
58
|
+
def run(self, plan: Plan, settings: StagesSettings, admission_id: str) -> StagesResult:
|
|
59
|
+
# Dispatches to the chosen topology behind one guarded entry.
|
|
60
|
+
from vidbyte.lib.errors import CodexAgentError
|
|
61
|
+
|
|
62
|
+
if not self._prepared:
|
|
63
|
+
raise StagesHostFailed()
|
|
64
|
+
try:
|
|
65
|
+
return asyncio.run(self._run(plan, settings, admission_id))
|
|
66
|
+
except StagesHostFailed:
|
|
67
|
+
raise
|
|
68
|
+
except StagesSettingsInvalid:
|
|
69
|
+
raise
|
|
70
|
+
except (CodexAgentError, TimeoutError) as error:
|
|
71
|
+
raise StagesHostFailed() from error
|
|
72
|
+
|
|
73
|
+
async def _run(self, plan: Plan, settings: StagesSettings, admission_id: str) -> StagesResult:
|
|
74
|
+
# Sequential threads prior output; parallel fans out independently.
|
|
75
|
+
if settings.parallel:
|
|
76
|
+
return await self._run_parallel(plan, settings, admission_id)
|
|
77
|
+
return await self._run_sequential(plan, settings, admission_id)
|
|
78
|
+
|
|
79
|
+
async def _run_sequential(
|
|
80
|
+
self, plan: Plan, settings: StagesSettings, admission_id: str
|
|
81
|
+
) -> StagesResult:
|
|
82
|
+
# Awaits each stage before starting the next one, keeping the completed prefix
|
|
83
|
+
# when stop_on_error halts the run at the first exhausted stage.
|
|
84
|
+
texts: list[str] = []
|
|
85
|
+
steps: list[StageStepResult] = []
|
|
86
|
+
completed = 0
|
|
87
|
+
failed = 0
|
|
88
|
+
previous = plan.task
|
|
89
|
+
self._progress(StagesProgress.STAGE_STARTING)
|
|
90
|
+
for index, spec in enumerate(settings.stages):
|
|
91
|
+
prompt = spec.prompt.replace("{{previous}}", previous)
|
|
92
|
+
outcome = await self._run_stage(plan, spec, index, prompt, settings)
|
|
93
|
+
if outcome is None:
|
|
94
|
+
failed += 1
|
|
95
|
+
steps.append(self._failed_step(spec, index, settings))
|
|
96
|
+
texts.append(f"Stage {index + 1} failed.")
|
|
97
|
+
if settings.stop_on_error:
|
|
98
|
+
break
|
|
99
|
+
continue
|
|
100
|
+
completed += 1
|
|
101
|
+
texts.append(outcome[0])
|
|
102
|
+
steps.append(self._completed_step(spec, index, outcome[1], outcome[2], outcome[3]))
|
|
103
|
+
previous = outcome[0]
|
|
104
|
+
self._progress(StagesProgress.STAGE_COMPLETE)
|
|
105
|
+
self._progress(StagesProgress.COMPLETE)
|
|
106
|
+
return self._result(admission_id, completed, failed, steps, texts)
|
|
107
|
+
|
|
108
|
+
async def _run_parallel(
|
|
109
|
+
self, plan: Plan, settings: StagesSettings, admission_id: str
|
|
110
|
+
) -> StagesResult:
|
|
111
|
+
# Starts every stage at once; stages must not depend on each other. Each stage
|
|
112
|
+
# still retries on its own, and failures are recorded with placeholders so
|
|
113
|
+
# indices stay aligned with the requested order.
|
|
114
|
+
prompts = [spec.prompt.replace("{{previous}}", plan.task) for spec in settings.stages]
|
|
115
|
+
outcomes = await asyncio.gather(
|
|
116
|
+
*[
|
|
117
|
+
self._run_stage(plan, spec, index, prompt, settings)
|
|
118
|
+
for index, (spec, prompt) in enumerate(zip(settings.stages, prompts, strict=True))
|
|
119
|
+
]
|
|
120
|
+
)
|
|
121
|
+
texts: list[str] = []
|
|
122
|
+
steps: list[StageStepResult] = []
|
|
123
|
+
completed = 0
|
|
124
|
+
failed = 0
|
|
125
|
+
for index, (spec, outcome) in enumerate(zip(settings.stages, outcomes, strict=True)):
|
|
126
|
+
if outcome is None:
|
|
127
|
+
failed += 1
|
|
128
|
+
steps.append(self._failed_step(spec, index, settings))
|
|
129
|
+
texts.append(f"Stage {index + 1} failed.")
|
|
130
|
+
continue
|
|
131
|
+
completed += 1
|
|
132
|
+
texts.append(outcome[0])
|
|
133
|
+
steps.append(self._completed_step(spec, index, outcome[1], outcome[2], outcome[3]))
|
|
134
|
+
self._progress(StagesProgress.COMPLETE)
|
|
135
|
+
return self._result(admission_id, completed, failed, steps, texts)
|
|
136
|
+
|
|
137
|
+
async def _run_stage(
|
|
138
|
+
self, plan: Plan, spec: StageSpec, index: int, prompt: str, settings: StagesSettings
|
|
139
|
+
) -> tuple[str, str, int, int] | None:
|
|
140
|
+
# One stage start to finish. Every attempt builds its own agent and sends the
|
|
141
|
+
# same prompt, so a retry recovers from a dead host rather than from a stage
|
|
142
|
+
# the agent understood but could not do. Returns text, thread id, attempts,
|
|
143
|
+
# and duration, or None when every attempt is exhausted.
|
|
144
|
+
from vidbyte.lib.errors import CodexAgentError
|
|
145
|
+
|
|
146
|
+
_ = index
|
|
147
|
+
started = time.perf_counter()
|
|
148
|
+
attempts = 0
|
|
149
|
+
for attempt in range(settings.max_retries_per_stage + 1):
|
|
150
|
+
attempts = attempt + 1
|
|
151
|
+
if attempt > 0:
|
|
152
|
+
self._progress(StagesProgress.STAGE_RETRYING)
|
|
153
|
+
await asyncio.sleep(self._backoff_seconds(attempt - 1))
|
|
154
|
+
try:
|
|
155
|
+
agent = self._build_agent(plan, spec)
|
|
156
|
+
reply = await self._turn(agent, spec, prompt)
|
|
157
|
+
thread = self._thread_id(reply)
|
|
158
|
+
text = self._completed_text(reply)
|
|
159
|
+
elapsed_ms = int((time.perf_counter() - started) * 1000)
|
|
160
|
+
return (text, thread, attempts, elapsed_ms)
|
|
161
|
+
except (asyncio.CancelledError, KeyboardInterrupt):
|
|
162
|
+
# Cancellation is never a host failure and never retries.
|
|
163
|
+
raise
|
|
164
|
+
except (StagesSettingsInvalid, ValueError):
|
|
165
|
+
# Fatal caller content fails fast instead of burning retries.
|
|
166
|
+
raise
|
|
167
|
+
except (TimeoutError, ConnectionError, StagesHostFailed, CodexAgentError):
|
|
168
|
+
# Transport and host failures retry with backoff at the loop top.
|
|
169
|
+
continue
|
|
170
|
+
return None
|
|
171
|
+
|
|
172
|
+
def _backoff_seconds(self, retry_index: int) -> float:
|
|
173
|
+
# Gives a recovering host room: 2s, 8s, 32s plus up to 1s of jitter so
|
|
174
|
+
# concurrent stages do not retry at the same instant.
|
|
175
|
+
return 2.0 * (4.0**retry_index) + random.uniform(0.0, 1.0)
|
|
176
|
+
|
|
177
|
+
def _build_agent(self, plan: Plan, spec: StageSpec) -> CodexHarnessAgent:
|
|
178
|
+
# Constructs a fresh agent so threads never leak across stages or attempts.
|
|
179
|
+
from vidbyte.agents.codex import CodexHarnessAgent
|
|
180
|
+
|
|
181
|
+
return CodexHarnessAgent(self._to_settings(plan, spec))
|
|
182
|
+
|
|
183
|
+
async def _turn(self, agent: CodexHarnessAgent, spec: StageSpec, prompt: str) -> AgentMessage:
|
|
184
|
+
# Timeout cancels arun so the SDK client unwinds before continuing.
|
|
185
|
+
from vidbyte.lib.dataclasses.codex import (
|
|
186
|
+
CodexMentionInput,
|
|
187
|
+
CodexRunInput,
|
|
188
|
+
CodexSkillInput,
|
|
189
|
+
CodexTextInput,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
items: list[Any] = [CodexTextInput(prompt)]
|
|
193
|
+
if spec.image.strip():
|
|
194
|
+
items.append(self._image_item(spec.image.strip()))
|
|
195
|
+
if spec.skill.strip():
|
|
196
|
+
name, _, path = spec.skill.partition("=")
|
|
197
|
+
items.append(CodexSkillInput(name.strip(), path.strip()))
|
|
198
|
+
if spec.mention.strip():
|
|
199
|
+
name, _, path = spec.mention.partition("=")
|
|
200
|
+
items.append(CodexMentionInput(name.strip(), path.strip()))
|
|
201
|
+
run_input = CodexRunInput(items=tuple(items))
|
|
202
|
+
async with asyncio.timeout(StagesLimit.TURN_TIMEOUT_SECONDS):
|
|
203
|
+
return await agent.arun(run_input)
|
|
204
|
+
|
|
205
|
+
def _image_item(self, image: str) -> Any:
|
|
206
|
+
# Remote URLs and local paths are different SDK items with no shared shape.
|
|
207
|
+
from vidbyte.lib.dataclasses.codex import CodexImageInput, CodexLocalImageInput
|
|
208
|
+
|
|
209
|
+
if image.lower().startswith(("https://", "http://", "data:")):
|
|
210
|
+
return CodexImageInput(image)
|
|
211
|
+
return CodexLocalImageInput(image)
|
|
212
|
+
|
|
213
|
+
def _completed_text(self, reply: AgentMessage) -> str:
|
|
214
|
+
# Rejects incomplete replies without exposing model content in errors.
|
|
215
|
+
data = reply.codex
|
|
216
|
+
if data is None or data.status != "completed" or not data.final_response:
|
|
217
|
+
raise StagesHostFailed()
|
|
218
|
+
return str(reply.content)
|
|
219
|
+
|
|
220
|
+
def _thread_id(self, reply: AgentMessage) -> str:
|
|
221
|
+
# Requires a stable thread identity for per-step accounting.
|
|
222
|
+
data = reply.codex
|
|
223
|
+
thread = str(data.thread_id) if data is not None else ""
|
|
224
|
+
if not thread.strip():
|
|
225
|
+
raise StagesHostFailed()
|
|
226
|
+
return thread
|
|
227
|
+
|
|
228
|
+
def _completed_step(
|
|
229
|
+
self, spec: StageSpec, index: int, thread: str, attempts: int, duration_ms: int
|
|
230
|
+
) -> StageStepResult:
|
|
231
|
+
# Records one successful stage with its identity and attempt accounting.
|
|
232
|
+
return StageStepResult(
|
|
233
|
+
index=index,
|
|
234
|
+
name=spec.name,
|
|
235
|
+
status="completed",
|
|
236
|
+
thread_id=thread,
|
|
237
|
+
attempts=attempts,
|
|
238
|
+
duration_ms=duration_ms,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
def _failed_step(
|
|
242
|
+
self, spec: StageSpec, index: int, settings: StagesSettings
|
|
243
|
+
) -> StageStepResult:
|
|
244
|
+
# Records one exhausted stage with a static placeholder thread identity.
|
|
245
|
+
return StageStepResult(
|
|
246
|
+
index=index,
|
|
247
|
+
name=spec.name,
|
|
248
|
+
status="failed",
|
|
249
|
+
thread_id=f"stage-{index + 1}-failed",
|
|
250
|
+
attempts=settings.max_retries_per_stage + 1,
|
|
251
|
+
duration_ms=0,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
def _result(
|
|
255
|
+
self,
|
|
256
|
+
admission_id: str,
|
|
257
|
+
completed: int,
|
|
258
|
+
failed: int,
|
|
259
|
+
steps: list[StageStepResult],
|
|
260
|
+
texts: list[str],
|
|
261
|
+
) -> StagesResult:
|
|
262
|
+
# Joins per-stage texts into the run-level text, keeping indices aligned even
|
|
263
|
+
# across failures so a later stage never reads the wrong predecessor.
|
|
264
|
+
text = texts[-1] if texts else ""
|
|
265
|
+
if any(step.status == "failed" for step in steps):
|
|
266
|
+
text = "\n\n".join(texts)
|
|
267
|
+
return StagesResult(
|
|
268
|
+
admission_id=admission_id,
|
|
269
|
+
completed=completed,
|
|
270
|
+
failed=failed,
|
|
271
|
+
steps=tuple(steps),
|
|
272
|
+
stage_texts=tuple(texts),
|
|
273
|
+
text=text,
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
def _to_settings(self, plan: Plan, spec: StageSpec) -> CodexHarnessAgentSettings:
|
|
277
|
+
# Translates one stage spec to validated harness agent settings. Every closed-set value
|
|
278
|
+
# is already a CLI enum whose members are a subset of the SDK's, so each one converts by
|
|
279
|
+
# value with no lookup table and no alias handling: review of PR #36 asked for the legal
|
|
280
|
+
# words to be defined and enforced as enums in the command layer rather than re-parsed
|
|
281
|
+
# from free strings here. A ValueError can still surface if the pinned SDK drops a
|
|
282
|
+
# member, and it is reported as invalid settings rather than as a host failure.
|
|
283
|
+
from vidbyte.lib.dataclasses.codex import (
|
|
284
|
+
CodexAgentSettings,
|
|
285
|
+
CodexClientSettings,
|
|
286
|
+
CodexHarnessAgentSettings,
|
|
287
|
+
CodexThreadSettings,
|
|
288
|
+
CodexTurnSettings,
|
|
289
|
+
)
|
|
290
|
+
from vidbyte.lib.enums.codex import (
|
|
291
|
+
CodexApprovalMode,
|
|
292
|
+
CodexPersonality,
|
|
293
|
+
CodexReasoningEffort,
|
|
294
|
+
CodexReasoningSummary,
|
|
295
|
+
CodexSandbox,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
try:
|
|
299
|
+
sandbox = CodexSandbox(spec.sandbox.value)
|
|
300
|
+
approval = CodexApprovalMode(spec.approval.value)
|
|
301
|
+
personality = CodexPersonality(spec.personality.value)
|
|
302
|
+
client = CodexClientSettings(
|
|
303
|
+
codex_bin=str(plan.executable),
|
|
304
|
+
cwd=str(plan.working_directory),
|
|
305
|
+
env=self._environment,
|
|
306
|
+
config_overrides=tuple(setting.value for setting in CodexConfig),
|
|
307
|
+
)
|
|
308
|
+
thread = CodexThreadSettings(
|
|
309
|
+
model=spec.model,
|
|
310
|
+
sandbox=sandbox,
|
|
311
|
+
approval_mode=approval,
|
|
312
|
+
personality=personality,
|
|
313
|
+
)
|
|
314
|
+
turn = CodexTurnSettings(
|
|
315
|
+
model=spec.model,
|
|
316
|
+
effort=CodexReasoningEffort(spec.effort.value),
|
|
317
|
+
summary=CodexReasoningSummary(spec.summary.value),
|
|
318
|
+
sandbox=sandbox,
|
|
319
|
+
approval_mode=approval,
|
|
320
|
+
personality=personality,
|
|
321
|
+
)
|
|
322
|
+
schema: dict[str, Any] | None = None
|
|
323
|
+
if spec.output_schema.strip():
|
|
324
|
+
parsed: Any = json.loads(spec.output_schema)
|
|
325
|
+
if not isinstance(parsed, dict):
|
|
326
|
+
raise StagesSettingsInvalid()
|
|
327
|
+
schema = parsed
|
|
328
|
+
return CodexHarnessAgentSettings(
|
|
329
|
+
name=spec.name,
|
|
330
|
+
system_prompt=spec.system_prompt,
|
|
331
|
+
additional_context=spec.additional_context,
|
|
332
|
+
output_schema=schema,
|
|
333
|
+
codex=CodexAgentSettings(client=client, thread=thread, turn=turn),
|
|
334
|
+
)
|
|
335
|
+
except StagesSettingsInvalid:
|
|
336
|
+
raise
|
|
337
|
+
except ValueError as error:
|
|
338
|
+
raise StagesSettingsInvalid() from error
|