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,228 @@
|
|
|
1
|
+
"""Lazily binds the suggestion service to Vidbyte SDK Codex agents.
|
|
2
|
+
|
|
3
|
+
This is the only module that imports SDK symbols. It constructs read-only,
|
|
4
|
+
structured-output agents, places one validated stage context primitive in
|
|
5
|
+
each fresh context manager, and wraps each CLI message tool as an SDK tool.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections.abc import Mapping
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from types import MappingProxyType
|
|
13
|
+
from typing import Any, Literal, Protocol, cast
|
|
14
|
+
|
|
15
|
+
from ...lib.errors.failures import SuggestionSdkUnavailable
|
|
16
|
+
from ...lib.io.codex_attachments import CodexAttachmentInputBuilder
|
|
17
|
+
from ...types.attachments import AttachmentBundle
|
|
18
|
+
from ...types.suggestions import SuggestionAgentContext
|
|
19
|
+
from .message_tools import SuggestionMessageTool
|
|
20
|
+
|
|
21
|
+
_CODEX_MODULE = "vidbyte.agents.codex"
|
|
22
|
+
_CONTEXT_MODULE = "vidbyte.context"
|
|
23
|
+
_ERRORS_MODULE = "vidbyte.lib.errors"
|
|
24
|
+
_TOOLS_MODULE = "vidbyte.tools"
|
|
25
|
+
_REQUIRED = (
|
|
26
|
+
"CodexAgentSettings",
|
|
27
|
+
"CodexApprovalMode",
|
|
28
|
+
"CodexHarnessAgentSettings",
|
|
29
|
+
"CodexRunInput",
|
|
30
|
+
"CodexSandbox",
|
|
31
|
+
"CodexThreadSettings",
|
|
32
|
+
"CodexTurnSettings",
|
|
33
|
+
)
|
|
34
|
+
_TOOL_SYMBOLS = ("BaseTool", "ToolParameter", "ToolResult", "ToolSpec")
|
|
35
|
+
_PROVIDER_MAP = {"openai": "openai"}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True, slots=True)
|
|
39
|
+
class SuggestionSdkBindings:
|
|
40
|
+
"""Validated SDK classes and errors resolved together by the lazy loader."""
|
|
41
|
+
|
|
42
|
+
symbols: Mapping[str, Any]
|
|
43
|
+
agent_type: type
|
|
44
|
+
provider_error_type: type[Exception]
|
|
45
|
+
schema_error_type: type[Exception]
|
|
46
|
+
|
|
47
|
+
def __post_init__(self) -> None:
|
|
48
|
+
expected = frozenset((*_REQUIRED, *_TOOL_SYMBOLS, "ContextManager"))
|
|
49
|
+
if frozenset(self.symbols) != expected:
|
|
50
|
+
raise ValueError("Suggestion SDK bindings do not match the required surface.")
|
|
51
|
+
if any(not callable(symbol) for symbol in self.symbols.values()):
|
|
52
|
+
raise TypeError("Every suggestion SDK binding must be callable.")
|
|
53
|
+
if not isinstance(self.agent_type, type):
|
|
54
|
+
raise TypeError("Suggestion SDK agent_type must be a class.")
|
|
55
|
+
for field_name in ("provider_error_type", "schema_error_type"):
|
|
56
|
+
value = getattr(self, field_name)
|
|
57
|
+
if not isinstance(value, type) or not issubclass(value, Exception):
|
|
58
|
+
raise TypeError(f"Suggestion SDK {field_name} must be an Exception class.")
|
|
59
|
+
object.__setattr__(self, "symbols", MappingProxyType(dict(self.symbols)))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass(frozen=True, slots=True)
|
|
63
|
+
class SuggestionAgentSettingsInput:
|
|
64
|
+
"""Strict local input for one generator or critic Codex configuration."""
|
|
65
|
+
|
|
66
|
+
role: Literal["generator", "critic"]
|
|
67
|
+
system_prompt: str
|
|
68
|
+
context: SuggestionAgentContext
|
|
69
|
+
output_schema: type | Mapping[str, Any]
|
|
70
|
+
provider: str | None = None
|
|
71
|
+
model: str | None = None
|
|
72
|
+
tools: tuple[SuggestionMessageTool, ...] = ()
|
|
73
|
+
|
|
74
|
+
def __post_init__(self) -> None:
|
|
75
|
+
if self.role not in ("generator", "critic"):
|
|
76
|
+
raise ValueError("Suggestion agent role must be generator or critic.")
|
|
77
|
+
if type(self.system_prompt) is not str or not self.system_prompt.strip():
|
|
78
|
+
raise ValueError("Suggestion agent system_prompt must be non-empty.")
|
|
79
|
+
if not isinstance(self.context, SuggestionAgentContext):
|
|
80
|
+
raise TypeError("Suggestion agent context must be SuggestionAgentContext.")
|
|
81
|
+
if not isinstance(self.output_schema, (type, Mapping)):
|
|
82
|
+
raise TypeError("Suggestion agent output_schema must be a class or mapping.")
|
|
83
|
+
if not isinstance(self.tools, tuple) or any(
|
|
84
|
+
not isinstance(tool, SuggestionMessageTool) for tool in self.tools
|
|
85
|
+
):
|
|
86
|
+
raise TypeError("Suggestion agent tools must be a tuple of SuggestionMessageTool.")
|
|
87
|
+
for name, value in (("provider", self.provider), ("model", self.model)):
|
|
88
|
+
if value is not None and (type(value) is not str or not value.strip()):
|
|
89
|
+
raise ValueError(f"Suggestion agent {name} must be None or non-empty.")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass(frozen=True, slots=True)
|
|
93
|
+
class SuggestionTextInput:
|
|
94
|
+
"""Strict local input for the single text turn used by this workflow."""
|
|
95
|
+
|
|
96
|
+
prompt: str
|
|
97
|
+
attachments: AttachmentBundle = field(default_factory=AttachmentBundle)
|
|
98
|
+
|
|
99
|
+
def __post_init__(self) -> None:
|
|
100
|
+
if type(self.prompt) is not str or not self.prompt.strip():
|
|
101
|
+
raise ValueError("Suggestion text prompt must be non-empty.")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class SuggestionAgent(Protocol):
|
|
105
|
+
"""Subset of CodexHarnessAgent driven by the suggestion workflow."""
|
|
106
|
+
|
|
107
|
+
thread_id: str
|
|
108
|
+
|
|
109
|
+
async def arun(self, request: Any) -> Any: ...
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class SuggestionSdk:
|
|
113
|
+
"""Translates strict local values into fresh, read-only Codex agents."""
|
|
114
|
+
|
|
115
|
+
def __init__(self, bindings: SuggestionSdkBindings) -> None:
|
|
116
|
+
self._bindings = bindings
|
|
117
|
+
|
|
118
|
+
@classmethod
|
|
119
|
+
def load(cls) -> SuggestionSdk:
|
|
120
|
+
"""Resolve the pinned SDK only when a model-backed operation needs it."""
|
|
121
|
+
try:
|
|
122
|
+
codex = __import__(_CODEX_MODULE, fromlist=["*"])
|
|
123
|
+
context = __import__(_CONTEXT_MODULE, fromlist=["ContextManager"])
|
|
124
|
+
errors = __import__(_ERRORS_MODULE, fromlist=["*"])
|
|
125
|
+
tools = __import__(_TOOLS_MODULE, fromlist=["*"])
|
|
126
|
+
symbols = {name: getattr(codex, name) for name in _REQUIRED}
|
|
127
|
+
symbols.update({name: getattr(tools, name) for name in _TOOL_SYMBOLS})
|
|
128
|
+
symbols["ContextManager"] = context.ContextManager
|
|
129
|
+
bindings = SuggestionSdkBindings(
|
|
130
|
+
symbols=symbols,
|
|
131
|
+
agent_type=codex.CodexHarnessAgent,
|
|
132
|
+
provider_error_type=errors.CodexAgentError,
|
|
133
|
+
schema_error_type=errors.OutputSchemaViolationError,
|
|
134
|
+
)
|
|
135
|
+
except (ImportError, AttributeError, TypeError, ValueError) as error:
|
|
136
|
+
raise SuggestionSdkUnavailable(error) from error
|
|
137
|
+
return cls(bindings)
|
|
138
|
+
|
|
139
|
+
def agent(self, settings: Any) -> SuggestionAgent:
|
|
140
|
+
"""Construct one new SDK agent from already validated settings."""
|
|
141
|
+
return cast(SuggestionAgent, self._bindings.agent_type(settings))
|
|
142
|
+
|
|
143
|
+
def run_input(self, request: SuggestionTextInput) -> Any:
|
|
144
|
+
"""Translate one local text request into the SDK's typed input."""
|
|
145
|
+
if not isinstance(request, SuggestionTextInput):
|
|
146
|
+
raise TypeError("run_input requires SuggestionTextInput.")
|
|
147
|
+
return CodexAttachmentInputBuilder().build(request.prompt, request.attachments)
|
|
148
|
+
|
|
149
|
+
def is_provider_error(self, error: Exception) -> bool:
|
|
150
|
+
"""Identify SDK transport/host failures without importing SDK types elsewhere."""
|
|
151
|
+
return isinstance(error, self._bindings.provider_error_type)
|
|
152
|
+
|
|
153
|
+
def is_schema_error(self, error: Exception) -> bool:
|
|
154
|
+
"""Identify a provider reply that violated the declared output schema."""
|
|
155
|
+
return isinstance(error, self._bindings.schema_error_type)
|
|
156
|
+
|
|
157
|
+
def agent_settings(self, request: SuggestionAgentSettingsInput) -> Any:
|
|
158
|
+
"""Build the exact read-only SDK settings for one independent context window."""
|
|
159
|
+
if not isinstance(request, SuggestionAgentSettingsInput):
|
|
160
|
+
raise TypeError("agent_settings requires SuggestionAgentSettingsInput.")
|
|
161
|
+
symbols = self._bindings.symbols
|
|
162
|
+
provider = "" if request.provider is None else _PROVIDER_MAP.get(request.provider)
|
|
163
|
+
if request.provider is not None and provider is None:
|
|
164
|
+
raise ValueError(f"Unsupported suggestion provider: {request.provider}")
|
|
165
|
+
sandbox = symbols["CodexSandbox"].READ_ONLY
|
|
166
|
+
deny = symbols["CodexApprovalMode"].DENY_ALL
|
|
167
|
+
codex = symbols["CodexAgentSettings"](
|
|
168
|
+
thread=symbols["CodexThreadSettings"](
|
|
169
|
+
model=request.model or "",
|
|
170
|
+
model_provider=provider or "",
|
|
171
|
+
sandbox=sandbox,
|
|
172
|
+
approval_mode=deny,
|
|
173
|
+
),
|
|
174
|
+
turn=symbols["CodexTurnSettings"](
|
|
175
|
+
model=request.model or "",
|
|
176
|
+
sandbox=sandbox,
|
|
177
|
+
approval_mode=deny,
|
|
178
|
+
),
|
|
179
|
+
)
|
|
180
|
+
manager = symbols["ContextManager"]()
|
|
181
|
+
manager.place_after_system_prompt(request.context)
|
|
182
|
+
return symbols["CodexHarnessAgentSettings"](
|
|
183
|
+
name=f"suggestion-{request.role}",
|
|
184
|
+
system_prompt=request.system_prompt,
|
|
185
|
+
codex=codex,
|
|
186
|
+
context_manager=manager,
|
|
187
|
+
output_schema=request.output_schema,
|
|
188
|
+
tools=tuple(self._message_tool(tool) for tool in request.tools),
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
def _message_tool(self, tool: SuggestionMessageTool) -> Any:
|
|
192
|
+
# Codex runs custom tools as SDK BaseTools, so each CLI tool gets a thin BaseTool
|
|
193
|
+
# whose spec is its authored text and whose execute delivers the model's message.
|
|
194
|
+
symbols = self._bindings.symbols
|
|
195
|
+
spec = symbols["ToolSpec"](
|
|
196
|
+
name=tool.name,
|
|
197
|
+
description=tool.description,
|
|
198
|
+
parameters=(
|
|
199
|
+
symbols["ToolParameter"](
|
|
200
|
+
name="message", type="string", description=tool.message_description
|
|
201
|
+
),
|
|
202
|
+
),
|
|
203
|
+
)
|
|
204
|
+
result = symbols["ToolResult"]
|
|
205
|
+
base: Any = symbols["BaseTool"]
|
|
206
|
+
|
|
207
|
+
# The base class is only known after the lazy SDK import, so mypy sees it as Any.
|
|
208
|
+
class _MessageTool(base): # type: ignore[misc]
|
|
209
|
+
def spec(self) -> Any:
|
|
210
|
+
return spec
|
|
211
|
+
|
|
212
|
+
async def execute(self, call: Any) -> Any:
|
|
213
|
+
# An invalid message is returned to the model so it can retry the call.
|
|
214
|
+
try:
|
|
215
|
+
return result.success(tool.name, tool.deliver(call.arguments.get("message")))
|
|
216
|
+
except ValueError as error:
|
|
217
|
+
return result.error(tool.name, str(error))
|
|
218
|
+
|
|
219
|
+
return _MessageTool()
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
__all__ = [
|
|
223
|
+
"SuggestionAgent",
|
|
224
|
+
"SuggestionAgentSettingsInput",
|
|
225
|
+
"SuggestionSdk",
|
|
226
|
+
"SuggestionSdkBindings",
|
|
227
|
+
"SuggestionTextInput",
|
|
228
|
+
]
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Validates eligibility, ranking, and redundancy for candidate ideas.
|
|
2
|
+
|
|
3
|
+
Exact duplicates are removed in code by normalized title; semantic overlap is
|
|
4
|
+
left to the generator's revision, guided by the critic's review. Selection
|
|
5
|
+
never invents evidence: every evidence ref must already exist in the run's
|
|
6
|
+
context manifest.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from ...types.suggestions import SuggestionIdea
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SuggestionSelection:
|
|
15
|
+
"""Applies deterministic selection rules over reviewed candidates."""
|
|
16
|
+
|
|
17
|
+
def __init__(self) -> None:
|
|
18
|
+
# Stateless across runs; all inputs arrive per call.
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
def validate_evidence(
|
|
22
|
+
self, ideas: tuple[SuggestionIdea, ...], allowed: set[str]
|
|
23
|
+
) -> tuple[SuggestionIdea, ...]:
|
|
24
|
+
# Drops ideas that cite refs the manifest never issued.
|
|
25
|
+
return tuple(idea for idea in ideas if set(idea.evidence_refs) <= allowed)
|
|
26
|
+
|
|
27
|
+
def validate_categories(
|
|
28
|
+
self, ideas: tuple[SuggestionIdea, ...], allowed: set[str]
|
|
29
|
+
) -> tuple[SuggestionIdea, ...]:
|
|
30
|
+
# Category ids are checked after model output so a provider cannot invent a lens.
|
|
31
|
+
return tuple(
|
|
32
|
+
idea
|
|
33
|
+
for idea in ideas
|
|
34
|
+
if idea.primary_category in allowed and set(idea.secondary_categories) <= allowed
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def deduplicate(self, ideas: tuple[SuggestionIdea, ...]) -> tuple[SuggestionIdea, ...]:
|
|
38
|
+
# Keeps the first idea per normalized title so ordering stays stable.
|
|
39
|
+
seen: set[str] = set()
|
|
40
|
+
kept: list[SuggestionIdea] = []
|
|
41
|
+
for idea in ideas:
|
|
42
|
+
key = " ".join(idea.title.lower().split())
|
|
43
|
+
if key in seen:
|
|
44
|
+
continue
|
|
45
|
+
seen.add(key)
|
|
46
|
+
kept.append(idea)
|
|
47
|
+
return tuple(kept)
|
|
48
|
+
|
|
49
|
+
def suppress_rejected(
|
|
50
|
+
self, ideas: tuple[SuggestionIdea, ...], rejected: tuple[str, ...]
|
|
51
|
+
) -> tuple[SuggestionIdea, ...]:
|
|
52
|
+
# Removes ideas whose title or summary echoes caller-rejected directions.
|
|
53
|
+
needles = [item.lower() for item in rejected if item.strip()]
|
|
54
|
+
if not needles:
|
|
55
|
+
return ideas
|
|
56
|
+
kept: list[SuggestionIdea] = []
|
|
57
|
+
for idea in ideas:
|
|
58
|
+
haystack = (
|
|
59
|
+
f"{idea.title} {idea.summary} {idea.first_action} "
|
|
60
|
+
f"{' '.join(idea.suggested_actions)}"
|
|
61
|
+
).lower()
|
|
62
|
+
if any(needle and needle in haystack for needle in needles):
|
|
63
|
+
continue
|
|
64
|
+
kept.append(idea)
|
|
65
|
+
return tuple(kept)
|
|
66
|
+
|
|
67
|
+
def rank(self, ideas: tuple[SuggestionIdea, ...], limit: int) -> tuple[SuggestionIdea, ...]:
|
|
68
|
+
# Reassigns rank sequentially after trimming to the requested maximum.
|
|
69
|
+
return tuple(
|
|
70
|
+
idea.model_copy(update={"rank": index}) for index, idea in enumerate(ideas[:limit], 1)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def coverage(self, ideas: tuple[SuggestionIdea, ...]) -> dict[str, int]:
|
|
74
|
+
# Counts primary categories represented in the final slate.
|
|
75
|
+
counts: dict[str, int] = {}
|
|
76
|
+
for idea in ideas:
|
|
77
|
+
counts[idea.primary_category] = counts.get(idea.primary_category, 0) + 1
|
|
78
|
+
return counts
|