fable-engine 1.3.1__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.
- fable_compressor.py +356 -0
- fable_engine/__init__.py +1 -0
- fable_engine/actions/__init__.py +291 -0
- fable_engine/actions/cas.py +182 -0
- fable_engine/actions/deliberation.py +523 -0
- fable_engine/actions/fleet.py +807 -0
- fable_engine/actions/lifecycle.py +298 -0
- fable_engine/actions/scrapers.py +116 -0
- fable_engine/actions/system3.py +815 -0
- fable_engine/browser.py +824 -0
- fable_engine/cas.py +974 -0
- fable_engine/fable_session.json +510 -0
- fable_engine/guards.py +283 -0
- fable_engine/schema.py +714 -0
- fable_engine/scrapers/__init__.py +32 -0
- fable_engine/scrapers/arxiv.py +115 -0
- fable_engine/scrapers/base.py +386 -0
- fable_engine/scrapers/github.py +129 -0
- fable_engine/scrapers/reddit.py +154 -0
- fable_engine/scrapers/web.py +120 -0
- fable_engine/scrapers/x.py +125 -0
- fable_engine/scrapers/youtube.py +132 -0
- fable_engine/server.py +414 -0
- fable_engine/session.py +1819 -0
- fable_engine/test_server.py +1362 -0
- fable_engine/updater.py +541 -0
- fable_engine-1.3.1.dist-info/LICENSE +22 -0
- fable_engine-1.3.1.dist-info/METADATA +173 -0
- fable_engine-1.3.1.dist-info/RECORD +104 -0
- fable_engine-1.3.1.dist-info/WHEEL +5 -0
- fable_engine-1.3.1.dist-info/entry_points.txt +5 -0
- fable_engine-1.3.1.dist-info/top_level.txt +6 -0
- fable_mode/__init__.py +3 -0
- fable_mode/__main__.py +4 -0
- fable_mode/adapters.py +1014 -0
- fable_mode/installer.py +553 -0
- fable_mode/launcher.py +437 -0
- fable_mode/manifest.py +142 -0
- fable_mode/resources.json +114 -0
- fable_mode/safety.py +103 -0
- fable_mode_entry.py +10 -0
- fable_v2/__init__.py +146 -0
- fable_v2/adapters.py +151 -0
- fable_v2/coder_fleet/__init__.py +100 -0
- fable_v2/coder_fleet/ast_tools.py +158 -0
- fable_v2/coder_fleet/compute.py +199 -0
- fable_v2/coder_fleet/design_engine.py +1316 -0
- fable_v2/coder_fleet/diagnostics.py +293 -0
- fable_v2/coder_fleet/fleet_dispatcher.py +214 -0
- fable_v2/coder_fleet/mock_auditor.py +306 -0
- fable_v2/coder_fleet/mutation.py +216 -0
- fable_v2/coder_fleet/property_oracle.py +260 -0
- fable_v2/coder_fleet/receipt_attestor.py +122 -0
- fable_v2/coder_fleet/red_team_swarm.py +908 -0
- fable_v2/coder_fleet/test_harness.py +198 -0
- fable_v2/coder_fleet/vector_engine.py +1287 -0
- fable_v2/coder_fleet/visual.py +357 -0
- fable_v2/coder_fleet/workspace.py +153 -0
- fable_v2/cortical/__init__.py +20 -0
- fable_v2/cortical/plasticity_engine.py +992 -0
- fable_v2/execution_broker.py +811 -0
- fable_v2/proof_engine.py +1141 -0
- fable_v2/protocol.py +485 -0
- fable_v2/runtime.py +1010 -0
- fable_v2/system3/__init__.py +204 -0
- fable_v2/system3/causal.py +558 -0
- fable_v2/system3/dialectical.py +577 -0
- fable_v2/system3/evolution.py +503 -0
- fable_v2/system3/executive.py +338 -0
- fable_v2/system3/free_energy.py +479 -0
- fable_v2/system3/hyperbolic.py +555 -0
- fable_v2/system3/induction.py +336 -0
- fable_v2/system3/kripke.py +548 -0
- fable_v2/system3/oracle.py +745 -0
- fable_v2/verifiers.py +72 -0
- tests/__init__.py +1 -0
- tests/test_anti_loop_circuit_breaker.py +64 -0
- tests/test_auto_updater.py +407 -0
- tests/test_coder_fleet.py +535 -0
- tests/test_delegation_compiler.py +54 -0
- tests/test_descriptor_boundaries.py +126 -0
- tests/test_design_engine.py +603 -0
- tests/test_epistemic_evidence_validator.py +66 -0
- tests/test_execution_broker.py +233 -0
- tests/test_fable_v2.py +406 -0
- tests/test_fleet_transitions.py +116 -0
- tests/test_fsm_redteam_evolution.py +406 -0
- tests/test_goal_rubric_and_pipeline.py +367 -0
- tests/test_hebbian_plasticity.py +585 -0
- tests/test_packaging_runtime.py +194 -0
- tests/test_proof_engine.py +259 -0
- tests/test_red_team_swarm.py +645 -0
- tests/test_redteam_remediation.py +169 -0
- tests/test_registration_transaction.py +375 -0
- tests/test_requested_regressions.py +467 -0
- tests/test_scrapers.py +370 -0
- tests/test_server_actions.py +93 -0
- tests/test_server_frontier_actions.py +269 -0
- tests/test_server_protocol.py +88 -0
- tests/test_stealth_browser.py +970 -0
- tests/test_system3.py +381 -0
- tests/test_system3_deep_integration.py +385 -0
- tests/test_system3_frontier.py +436 -0
- tests/test_vector_engine.py +608 -0
fable_v2/protocol.py
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
"""Portable, host-neutral objects used by the Fable V2 runtime.
|
|
2
|
+
|
|
3
|
+
The protocol deliberately stores structured facts and tool receipts instead of
|
|
4
|
+
asking a model to self-report that it performed work. Hosts can bind their
|
|
5
|
+
native tools to these objects through an adapter.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import asdict, dataclass, field
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
import copy
|
|
13
|
+
import hashlib
|
|
14
|
+
import json
|
|
15
|
+
from typing import Any, Mapping
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def utc_now() -> str:
|
|
19
|
+
return datetime.now(timezone.utc).isoformat()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def canonical_hash(value: Any) -> str:
|
|
23
|
+
"""Return a stable SHA-256 hash for a JSON-compatible value."""
|
|
24
|
+
payload = json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
|
|
25
|
+
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _required_text(value: str, field_name: str) -> str:
|
|
29
|
+
if not isinstance(value, str) or not value.strip():
|
|
30
|
+
raise ValueError(f"{field_name} must be a non-empty string")
|
|
31
|
+
return value.strip()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _parse_timestamp(value: str, field_name: str) -> datetime:
|
|
35
|
+
text = _required_text(value, field_name)
|
|
36
|
+
normalized = text[:-1] + "+00:00" if text.endswith("Z") else text
|
|
37
|
+
try:
|
|
38
|
+
parsed = datetime.fromisoformat(normalized)
|
|
39
|
+
except ValueError as exc:
|
|
40
|
+
raise ValueError(f"{field_name} must be an ISO-8601 timestamp") from exc
|
|
41
|
+
if parsed.tzinfo is None or parsed.utcoffset() is None:
|
|
42
|
+
raise ValueError(f"{field_name} must include a timezone offset")
|
|
43
|
+
return parsed
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(frozen=True)
|
|
47
|
+
class VerificationPolicy:
|
|
48
|
+
"""Acceptance policy declared by the task, not by the model."""
|
|
49
|
+
|
|
50
|
+
# The default policy requires both an objective/deterministic check and a
|
|
51
|
+
# separately registered independent check. Tasks may explicitly choose a
|
|
52
|
+
# narrower policy when no independent check is meaningful.
|
|
53
|
+
required_verifier_classes: tuple[str, ...] = ("deterministic", "independent")
|
|
54
|
+
minimum_passing_verifiers: int = 2
|
|
55
|
+
require_independent: bool = True
|
|
56
|
+
# ``in_process`` is an application convention, not a security boundary.
|
|
57
|
+
# Production deployments should require ``process_attested`` and supply
|
|
58
|
+
# results from an isolated broker.
|
|
59
|
+
minimum_trust_boundary: str = "in_process"
|
|
60
|
+
|
|
61
|
+
TRUST_BOUNDARY_RANK = {"in_process": 0, "process_attested": 1}
|
|
62
|
+
|
|
63
|
+
def __post_init__(self) -> None:
|
|
64
|
+
if self.minimum_trust_boundary not in self.TRUST_BOUNDARY_RANK:
|
|
65
|
+
raise ValueError("unsupported minimum_trust_boundary")
|
|
66
|
+
if not self.required_verifier_classes:
|
|
67
|
+
raise ValueError("verification policy must require at least one verifier class")
|
|
68
|
+
if self.minimum_passing_verifiers < 1:
|
|
69
|
+
raise ValueError("minimum_passing_verifiers must be at least 1")
|
|
70
|
+
for item in self.required_verifier_classes:
|
|
71
|
+
_required_text(item, "verifier class")
|
|
72
|
+
|
|
73
|
+
def to_dict(self) -> dict[str, Any]:
|
|
74
|
+
return asdict(self)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass(frozen=True)
|
|
78
|
+
class TaskSpec:
|
|
79
|
+
"""The contract that defines what a run must accomplish."""
|
|
80
|
+
|
|
81
|
+
task_id: str
|
|
82
|
+
objective: str
|
|
83
|
+
constraints: tuple[str, ...] = ()
|
|
84
|
+
definition_of_done: tuple[str, ...] = ()
|
|
85
|
+
required_capabilities: tuple[str, ...] = ()
|
|
86
|
+
required_evidence: tuple[str, ...] = ()
|
|
87
|
+
verification_policy: VerificationPolicy = field(default_factory=VerificationPolicy)
|
|
88
|
+
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
89
|
+
|
|
90
|
+
def __post_init__(self) -> None:
|
|
91
|
+
_required_text(self.task_id, "task_id")
|
|
92
|
+
_required_text(self.objective, "objective")
|
|
93
|
+
if not self.definition_of_done:
|
|
94
|
+
raise ValueError("definition_of_done must contain at least one condition")
|
|
95
|
+
for item in (*self.constraints, *self.definition_of_done,
|
|
96
|
+
*self.required_capabilities, *self.required_evidence):
|
|
97
|
+
_required_text(item, "task contract item")
|
|
98
|
+
|
|
99
|
+
def to_dict(self) -> dict[str, Any]:
|
|
100
|
+
return asdict(self)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass(frozen=True)
|
|
104
|
+
class ToolReceipt:
|
|
105
|
+
"""A host-produced receipt proving invocation and captured output.
|
|
106
|
+
|
|
107
|
+
``success`` means the tool call completed successfully. It is not a claim
|
|
108
|
+
that the output is correct or that the candidate is correct.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
receipt_id: str
|
|
112
|
+
session_id: str
|
|
113
|
+
capability: str
|
|
114
|
+
tool_name: str
|
|
115
|
+
input_hash: str
|
|
116
|
+
output_hash: str
|
|
117
|
+
success: bool
|
|
118
|
+
started_at: str
|
|
119
|
+
finished_at: str
|
|
120
|
+
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
121
|
+
output: Any = None
|
|
122
|
+
|
|
123
|
+
def __post_init__(self) -> None:
|
|
124
|
+
for name in ("receipt_id", "session_id", "capability", "tool_name",
|
|
125
|
+
"input_hash", "output_hash", "started_at", "finished_at"):
|
|
126
|
+
_required_text(getattr(self, name), name)
|
|
127
|
+
started = _parse_timestamp(self.started_at, "started_at")
|
|
128
|
+
finished = _parse_timestamp(self.finished_at, "finished_at")
|
|
129
|
+
if finished < started:
|
|
130
|
+
raise ValueError("finished_at cannot be earlier than started_at")
|
|
131
|
+
# Snapshot mutable host payloads so later caller mutation cannot change
|
|
132
|
+
# what the receipt hashes or what evidence derives from it.
|
|
133
|
+
object.__setattr__(self, "output", copy.deepcopy(self.output))
|
|
134
|
+
object.__setattr__(self, "metadata", copy.deepcopy(dict(self.metadata)))
|
|
135
|
+
if canonical_hash(self.output) != self.output_hash:
|
|
136
|
+
raise ValueError("output_hash does not match the actual receipt output")
|
|
137
|
+
|
|
138
|
+
@classmethod
|
|
139
|
+
def from_result(
|
|
140
|
+
cls,
|
|
141
|
+
*,
|
|
142
|
+
receipt_id: str,
|
|
143
|
+
session_id: str,
|
|
144
|
+
capability: str,
|
|
145
|
+
tool_name: str,
|
|
146
|
+
tool_input: Any,
|
|
147
|
+
tool_output: Any,
|
|
148
|
+
success: bool,
|
|
149
|
+
started_at: str | None = None,
|
|
150
|
+
finished_at: str | None = None,
|
|
151
|
+
metadata: Mapping[str, Any] | None = None,
|
|
152
|
+
) -> "ToolReceipt":
|
|
153
|
+
start = started_at or utc_now()
|
|
154
|
+
finish = finished_at or start
|
|
155
|
+
return cls(
|
|
156
|
+
receipt_id=receipt_id,
|
|
157
|
+
session_id=session_id,
|
|
158
|
+
capability=capability,
|
|
159
|
+
tool_name=tool_name,
|
|
160
|
+
input_hash=canonical_hash(tool_input),
|
|
161
|
+
output_hash=canonical_hash(tool_output),
|
|
162
|
+
success=bool(success),
|
|
163
|
+
started_at=start,
|
|
164
|
+
finished_at=finish,
|
|
165
|
+
metadata=metadata or {},
|
|
166
|
+
output=tool_output,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
def to_dict(self) -> dict[str, Any]:
|
|
170
|
+
return asdict(self)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@dataclass(frozen=True)
|
|
174
|
+
class Evidence:
|
|
175
|
+
"""Captured evidence integrity-bound to one successful receipt.
|
|
176
|
+
|
|
177
|
+
Integrity binding proves provenance and content consistency only. It does
|
|
178
|
+
not prove that the claim is true; a verifier must establish that.
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
evidence_id: str
|
|
182
|
+
session_id: str
|
|
183
|
+
claim: str
|
|
184
|
+
kind: str
|
|
185
|
+
source: str
|
|
186
|
+
receipt_id: str
|
|
187
|
+
content_hash: str
|
|
188
|
+
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
189
|
+
content: Any = None
|
|
190
|
+
source_output_hash: str = ""
|
|
191
|
+
|
|
192
|
+
def __post_init__(self) -> None:
|
|
193
|
+
for name in ("evidence_id", "session_id", "claim", "kind", "source",
|
|
194
|
+
"receipt_id", "content_hash", "source_output_hash"):
|
|
195
|
+
_required_text(getattr(self, name), name)
|
|
196
|
+
object.__setattr__(self, "content", copy.deepcopy(self.content))
|
|
197
|
+
object.__setattr__(self, "metadata", copy.deepcopy(dict(self.metadata)))
|
|
198
|
+
if canonical_hash(self.content) != self.content_hash:
|
|
199
|
+
raise ValueError("content_hash does not match the evidence content")
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def integrity_bound(self) -> bool:
|
|
203
|
+
"""Whether provenance and content hashes agree; not claim truth."""
|
|
204
|
+
return (
|
|
205
|
+
canonical_hash(self.content) == self.content_hash
|
|
206
|
+
and self.content_hash == self.source_output_hash
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
@classmethod
|
|
210
|
+
def from_receipt(
|
|
211
|
+
cls,
|
|
212
|
+
receipt: ToolReceipt,
|
|
213
|
+
*,
|
|
214
|
+
evidence_id: str,
|
|
215
|
+
claim: str,
|
|
216
|
+
kind: str,
|
|
217
|
+
source: str,
|
|
218
|
+
metadata: Mapping[str, Any] | None = None,
|
|
219
|
+
) -> "Evidence":
|
|
220
|
+
"""Create full-output evidence directly from a host receipt.
|
|
221
|
+
|
|
222
|
+
Keeping the receipt output in the content-addressed evidence object
|
|
223
|
+
makes the integrity relationship checkable. Large-output adapters can
|
|
224
|
+
replace ``content`` with a content-addressed blob in a later version.
|
|
225
|
+
"""
|
|
226
|
+
if not receipt.success:
|
|
227
|
+
raise ValueError("evidence must be derived from a successful receipt")
|
|
228
|
+
return cls(
|
|
229
|
+
evidence_id=evidence_id,
|
|
230
|
+
session_id=receipt.session_id,
|
|
231
|
+
claim=claim,
|
|
232
|
+
kind=kind,
|
|
233
|
+
source=source,
|
|
234
|
+
receipt_id=receipt.receipt_id,
|
|
235
|
+
content_hash=receipt.output_hash,
|
|
236
|
+
metadata=metadata or {},
|
|
237
|
+
content=receipt.output,
|
|
238
|
+
source_output_hash=receipt.output_hash,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
def to_dict(self) -> dict[str, Any]:
|
|
242
|
+
return asdict(self)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@dataclass(frozen=True)
|
|
246
|
+
class Candidate:
|
|
247
|
+
"""One independently generated solution or trajectory."""
|
|
248
|
+
|
|
249
|
+
candidate_id: str
|
|
250
|
+
session_id: str
|
|
251
|
+
approach: str
|
|
252
|
+
artifact: Any
|
|
253
|
+
receipt_ids: tuple[str, ...] = ()
|
|
254
|
+
evidence_ids: tuple[str, ...] = ()
|
|
255
|
+
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
256
|
+
|
|
257
|
+
def __post_init__(self) -> None:
|
|
258
|
+
for name in ("candidate_id", "session_id", "approach"):
|
|
259
|
+
_required_text(getattr(self, name), name)
|
|
260
|
+
|
|
261
|
+
def to_dict(self) -> dict[str, Any]:
|
|
262
|
+
return asdict(self)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@dataclass(frozen=True)
|
|
266
|
+
class VerificationResult:
|
|
267
|
+
"""A runtime-attested verifier result.
|
|
268
|
+
|
|
269
|
+
The attestation and candidate hash are populated by ``FableRun`` after a
|
|
270
|
+
registered verifier is executed. Callers must not construct a passing
|
|
271
|
+
result and submit it directly to the run.
|
|
272
|
+
"""
|
|
273
|
+
|
|
274
|
+
verification_id: str
|
|
275
|
+
session_id: str
|
|
276
|
+
candidate_id: str
|
|
277
|
+
verifier: str
|
|
278
|
+
passed: bool
|
|
279
|
+
reasons: tuple[str, ...] = ()
|
|
280
|
+
evidence_ids: tuple[str, ...] = ()
|
|
281
|
+
score: float | None = None
|
|
282
|
+
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
283
|
+
verifier_class: str = ""
|
|
284
|
+
candidate_hash: str = ""
|
|
285
|
+
inspected_candidate: bool = False
|
|
286
|
+
independent: bool = False
|
|
287
|
+
# ``in_process`` describes where the result came from; it is not proof of
|
|
288
|
+
# trust. Only an external broker may issue ``process_attested`` results.
|
|
289
|
+
trust_boundary: str = ""
|
|
290
|
+
# Commitment to the candidate plus every referenced receipt/evidence object.
|
|
291
|
+
# The runtime populates this before computing ``runtime_attestation``.
|
|
292
|
+
candidate_graph_hash: str = ""
|
|
293
|
+
runtime_attestation: str = ""
|
|
294
|
+
|
|
295
|
+
def __post_init__(self) -> None:
|
|
296
|
+
for name in ("verification_id", "session_id", "candidate_id", "verifier"):
|
|
297
|
+
_required_text(getattr(self, name), name)
|
|
298
|
+
if self.score is not None and not 0 <= self.score <= 1:
|
|
299
|
+
raise ValueError("score must be between 0 and 1")
|
|
300
|
+
|
|
301
|
+
def to_dict(self) -> dict[str, Any]:
|
|
302
|
+
return asdict(self)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
@dataclass(frozen=True)
|
|
306
|
+
class ProofReceipt:
|
|
307
|
+
"""A deterministic proof receipt verifying an invariant or grounded claim."""
|
|
308
|
+
|
|
309
|
+
receipt_id: str
|
|
310
|
+
claim: str
|
|
311
|
+
proof_type: str
|
|
312
|
+
target_resource: str
|
|
313
|
+
sha256_digest: str
|
|
314
|
+
verified_at: str
|
|
315
|
+
verifier_details: Mapping[str, Any] = field(default_factory=dict)
|
|
316
|
+
|
|
317
|
+
def __post_init__(self) -> None:
|
|
318
|
+
for name in ("receipt_id", "claim", "proof_type", "target_resource",
|
|
319
|
+
"sha256_digest", "verified_at"):
|
|
320
|
+
_required_text(getattr(self, name), name)
|
|
321
|
+
object.__setattr__(self, "verifier_details", copy.deepcopy(dict(self.verifier_details)))
|
|
322
|
+
|
|
323
|
+
def to_dict(self) -> dict[str, Any]:
|
|
324
|
+
return asdict(self)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
@dataclass(frozen=True)
|
|
328
|
+
class FileChangeRecord:
|
|
329
|
+
"""Record of a file modification, creation, or deletion with hash tracking."""
|
|
330
|
+
|
|
331
|
+
file_path: str
|
|
332
|
+
change_type: str
|
|
333
|
+
before_hash: str | None = None
|
|
334
|
+
after_hash: str | None = None
|
|
335
|
+
diff_summary: str = ""
|
|
336
|
+
rationale: str = ""
|
|
337
|
+
affected_invariants: tuple[str, ...] = ()
|
|
338
|
+
timestamp: str = field(default_factory=utc_now)
|
|
339
|
+
|
|
340
|
+
def __post_init__(self) -> None:
|
|
341
|
+
_required_text(self.file_path, "file_path")
|
|
342
|
+
_required_text(self.change_type, "change_type")
|
|
343
|
+
for inv in self.affected_invariants:
|
|
344
|
+
_required_text(inv, "affected_invariant")
|
|
345
|
+
|
|
346
|
+
def to_dict(self) -> dict[str, Any]:
|
|
347
|
+
return asdict(self)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
@dataclass(frozen=True)
|
|
351
|
+
class VisualMockupSpec:
|
|
352
|
+
"""Specification for visual design mockups, coordinate bounds, and UI typography."""
|
|
353
|
+
|
|
354
|
+
mockup_id: str
|
|
355
|
+
concept_name: str
|
|
356
|
+
aesthetic_archetype: str
|
|
357
|
+
prompt: str
|
|
358
|
+
image_url: str | None = None
|
|
359
|
+
coordinates_data: Mapping[str, Any] = field(default_factory=dict)
|
|
360
|
+
palette: tuple[str, ...] = ()
|
|
361
|
+
typography: Mapping[str, str] = field(default_factory=dict)
|
|
362
|
+
status: str = "draft"
|
|
363
|
+
selected_by_user: bool = False
|
|
364
|
+
created_at: str = field(default_factory=utc_now)
|
|
365
|
+
|
|
366
|
+
def __post_init__(self) -> None:
|
|
367
|
+
for name in ("mockup_id", "concept_name", "aesthetic_archetype", "prompt"):
|
|
368
|
+
_required_text(getattr(self, name), name)
|
|
369
|
+
object.__setattr__(self, "coordinates_data", copy.deepcopy(dict(self.coordinates_data)))
|
|
370
|
+
object.__setattr__(self, "typography", copy.deepcopy(dict(self.typography)))
|
|
371
|
+
|
|
372
|
+
def to_dict(self) -> dict[str, Any]:
|
|
373
|
+
return asdict(self)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
@dataclass(frozen=True)
|
|
377
|
+
class ModelVelocityProfile:
|
|
378
|
+
"""Telemetry profile tracking token throughput, tool latency, and exploration multiplier."""
|
|
379
|
+
|
|
380
|
+
model_tier: str
|
|
381
|
+
tokens_per_sec: float = 0.0
|
|
382
|
+
avg_tool_latency_sec: float = 0.0
|
|
383
|
+
sample_count: int = 0
|
|
384
|
+
high_throughput_mode: bool = False
|
|
385
|
+
exploration_multiplier: float = 1.0
|
|
386
|
+
last_updated: str = field(default_factory=utc_now)
|
|
387
|
+
|
|
388
|
+
def __post_init__(self) -> None:
|
|
389
|
+
_required_text(self.model_tier, "model_tier")
|
|
390
|
+
if self.tokens_per_sec < 0:
|
|
391
|
+
raise ValueError("tokens_per_sec cannot be negative")
|
|
392
|
+
if self.avg_tool_latency_sec < 0:
|
|
393
|
+
raise ValueError("avg_tool_latency_sec cannot be negative")
|
|
394
|
+
if self.sample_count < 0:
|
|
395
|
+
raise ValueError("sample_count cannot be negative")
|
|
396
|
+
if self.exploration_multiplier <= 0:
|
|
397
|
+
raise ValueError("exploration_multiplier must be positive")
|
|
398
|
+
|
|
399
|
+
def to_dict(self) -> dict[str, Any]:
|
|
400
|
+
return asdict(self)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
@dataclass(frozen=True)
|
|
404
|
+
class GoalRubricItem:
|
|
405
|
+
"""A granular rubric item or verification criteria pointer with target weight."""
|
|
406
|
+
|
|
407
|
+
pointer_id: str
|
|
408
|
+
description: str
|
|
409
|
+
weight: float = 1.0
|
|
410
|
+
verifier_command: str = ""
|
|
411
|
+
satisfied: bool = False
|
|
412
|
+
score: float = 0.0
|
|
413
|
+
evidence_receipt_id: str = ""
|
|
414
|
+
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
415
|
+
|
|
416
|
+
def __post_init__(self) -> None:
|
|
417
|
+
_required_text(self.pointer_id, "pointer_id")
|
|
418
|
+
_required_text(self.description, "description")
|
|
419
|
+
if self.weight < 0:
|
|
420
|
+
raise ValueError("weight cannot be negative")
|
|
421
|
+
if not 0.0 <= self.score <= 1.0:
|
|
422
|
+
raise ValueError("score must be between 0.0 and 1.0")
|
|
423
|
+
object.__setattr__(self, "metadata", copy.deepcopy(dict(self.metadata)))
|
|
424
|
+
|
|
425
|
+
def to_dict(self) -> dict[str, Any]:
|
|
426
|
+
return asdict(self)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
@dataclass(frozen=True)
|
|
430
|
+
class GoalRubric:
|
|
431
|
+
"""Pre-flight goal scoring rubric contract with target threshold (default >= 0.95)."""
|
|
432
|
+
|
|
433
|
+
rubric_id: str
|
|
434
|
+
session_id: str
|
|
435
|
+
task_objective: str
|
|
436
|
+
target_score: float = 0.95
|
|
437
|
+
items: tuple[GoalRubricItem, ...] = ()
|
|
438
|
+
current_score: float = 0.0
|
|
439
|
+
status: str = "pending"
|
|
440
|
+
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
441
|
+
created_at: str = field(default_factory=utc_now)
|
|
442
|
+
|
|
443
|
+
def __post_init__(self) -> None:
|
|
444
|
+
_required_text(self.rubric_id, "rubric_id")
|
|
445
|
+
_required_text(self.session_id, "session_id")
|
|
446
|
+
_required_text(self.task_objective, "task_objective")
|
|
447
|
+
if not 0.0 <= self.target_score <= 1.0:
|
|
448
|
+
raise ValueError("target_score must be between 0.0 and 1.0")
|
|
449
|
+
if not 0.0 <= self.current_score <= 1.0:
|
|
450
|
+
raise ValueError("current_score must be between 0.0 and 1.0")
|
|
451
|
+
object.__setattr__(self, "metadata", copy.deepcopy(dict(self.metadata)))
|
|
452
|
+
|
|
453
|
+
def to_dict(self) -> dict[str, Any]:
|
|
454
|
+
return asdict(self)
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
@dataclass(frozen=True)
|
|
458
|
+
class AutomationPipelineSpec:
|
|
459
|
+
"""Specification for an autonomous generator-evaluator closed-loop pipeline."""
|
|
460
|
+
|
|
461
|
+
pipeline_id: str
|
|
462
|
+
session_id: str
|
|
463
|
+
name: str
|
|
464
|
+
pipeline_type: str = "closed_loop"
|
|
465
|
+
generator_command: str = ""
|
|
466
|
+
evaluator_command: str = ""
|
|
467
|
+
target_threshold: float = 0.95
|
|
468
|
+
max_iterations: int = 10
|
|
469
|
+
status: str = "active"
|
|
470
|
+
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
471
|
+
created_at: str = field(default_factory=utc_now)
|
|
472
|
+
|
|
473
|
+
def __post_init__(self) -> None:
|
|
474
|
+
_required_text(self.pipeline_id, "pipeline_id")
|
|
475
|
+
_required_text(self.session_id, "session_id")
|
|
476
|
+
_required_text(self.name, "name")
|
|
477
|
+
if not 0.0 <= self.target_threshold <= 1.0:
|
|
478
|
+
raise ValueError("target_threshold must be between 0.0 and 1.0")
|
|
479
|
+
if self.max_iterations < 1:
|
|
480
|
+
raise ValueError("max_iterations must be at least 1")
|
|
481
|
+
object.__setattr__(self, "metadata", copy.deepcopy(dict(self.metadata)))
|
|
482
|
+
|
|
483
|
+
def to_dict(self) -> dict[str, Any]:
|
|
484
|
+
return asdict(self)
|
|
485
|
+
|