rote-cli 0.8.0__tar.gz → 0.9.0__tar.gz
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.
- {rote_cli-0.8.0 → rote_cli-0.9.0}/PKG-INFO +7 -1
- {rote_cli-0.8.0 → rote_cli-0.9.0}/pyproject.toml +8 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/skills/rote-graduate/SKILL.md +13 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/__init__.py +1 -1
- rote_cli-0.9.0/src/rote/__main__.py +12 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/adapters/__init__.py +28 -11
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/adapters/_common.py +47 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/adapters/_ts_common.py +573 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/adapters/cloudflare.py +146 -15
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/adapters/dbos.py +29 -16
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/adapters/dbos_ts.py +27 -2
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/adapters/inngest.py +27 -2
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/cli.py +379 -66
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/eval/__init__.py +4 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/eval/empirical.py +31 -5
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/eval/scorecard.py +66 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/graduator/__init__.py +103 -4
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/graduator/drivers/__init__.py +22 -0
- rote_cli-0.9.0/src/rote/graduator/drivers/_fs_tools.py +280 -0
- rote_cli-0.9.0/src/rote/graduator/drivers/anthropic_api.py +466 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/graduator/drivers/claude.py +197 -41
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/graduator/drivers/codex.py +13 -2
- rote_cli-0.9.0/src/rote/graduator/drivers/openai_api.py +431 -0
- rote_cli-0.9.0/src/rote/graduator/events.py +255 -0
- rote_cli-0.9.0/src/rote/mcp/__init__.py +48 -0
- rote_cli-0.9.0/src/rote/mcp/_runtime_helper.py +247 -0
- rote_cli-0.9.0/src/rote/mcp/auth.py +166 -0
- rote_cli-0.9.0/src/rote/mcp/registry.py +124 -0
- rote_cli-0.9.0/src/rote/mcp/tokens.py +246 -0
- rote_cli-0.8.0/src/rote/graduator/drivers/anthropic_api.py +0 -423
- {rote_cli-0.8.0 → rote_cli-0.9.0}/.gitignore +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/LICENSE +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/README.md +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/skills/rote-graduate/references/crystallization-heuristics.md +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/skills/rote-graduate/references/eval-estimates.md +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/skills/rote-graduate/references/ir-schema.md +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/skills/rote-graduate/references/llm-judge-extraction.md +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/skills/rote-graduate/references/node-kinds.md +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/adapters/_py_common.py +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/adapters/python.py +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/adapters/temporal.py +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/eval/estimate.py +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/eval/pricing.py +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/eval/priors.py +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/eval/sidecar.py +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/eval/tokens.py +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/graduator/update.py +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/ir.py +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/serve/__init__.py +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/serve/backends.py +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/serve/registry.py +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/serve/server.py +0 -0
- {rote_cli-0.8.0 → rote_cli-0.9.0}/src/rote/skill_source.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rote-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Graduate fuzzy AI skills into deterministic, reliable workflows
|
|
5
5
|
Project-URL: Homepage, https://github.com/trevhud/rote
|
|
6
6
|
Project-URL: Repository, https://github.com/trevhud/rote
|
|
@@ -32,11 +32,17 @@ Requires-Dist: dbos>=2.26; extra == 'dev'
|
|
|
32
32
|
Requires-Dist: fastmcp>=3.4.2; extra == 'dev'
|
|
33
33
|
Requires-Dist: httpx>=0.28; extra == 'dev'
|
|
34
34
|
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
35
|
+
Requires-Dist: openai>=2.45; extra == 'dev'
|
|
35
36
|
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
36
37
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
37
38
|
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
38
39
|
Requires-Dist: temporalio>=1.7; extra == 'dev'
|
|
39
40
|
Requires-Dist: types-pyyaml; extra == 'dev'
|
|
41
|
+
Provides-Extra: mcp
|
|
42
|
+
Requires-Dist: fastmcp>=3.4.2; extra == 'mcp'
|
|
43
|
+
Requires-Dist: httpx>=0.28; extra == 'mcp'
|
|
44
|
+
Provides-Extra: openai-api
|
|
45
|
+
Requires-Dist: openai>=2.45; extra == 'openai-api'
|
|
40
46
|
Provides-Extra: serve
|
|
41
47
|
Requires-Dist: fastmcp>=3.4.2; extra == 'serve'
|
|
42
48
|
Requires-Dist: httpx>=0.28; extra == 'serve'
|
|
@@ -50,6 +50,9 @@ temporal = [
|
|
|
50
50
|
api = [
|
|
51
51
|
"anthropic>=0.89",
|
|
52
52
|
]
|
|
53
|
+
openai-api = [
|
|
54
|
+
"openai>=2.45",
|
|
55
|
+
]
|
|
53
56
|
dbos = [
|
|
54
57
|
"dbos>=2.26",
|
|
55
58
|
]
|
|
@@ -57,6 +60,10 @@ serve = [
|
|
|
57
60
|
"fastmcp>=3.4.2",
|
|
58
61
|
"httpx>=0.28",
|
|
59
62
|
]
|
|
63
|
+
mcp = [
|
|
64
|
+
"fastmcp>=3.4.2",
|
|
65
|
+
"httpx>=0.28",
|
|
66
|
+
]
|
|
60
67
|
dev = [
|
|
61
68
|
"pytest>=8.0",
|
|
62
69
|
"pytest-asyncio>=0.23",
|
|
@@ -65,6 +72,7 @@ dev = [
|
|
|
65
72
|
"types-pyyaml",
|
|
66
73
|
"temporalio>=1.7",
|
|
67
74
|
"anthropic>=0.89",
|
|
75
|
+
"openai>=2.45",
|
|
68
76
|
"dbos>=2.26",
|
|
69
77
|
"fastmcp>=3.4.2",
|
|
70
78
|
"httpx>=0.28",
|
|
@@ -46,6 +46,19 @@ re-deriving a known-fixed procedure at runtime is waste.
|
|
|
46
46
|
|
|
47
47
|
## Pipeline
|
|
48
48
|
|
|
49
|
+
**Progress markers.** At the START of each Phase N, rewrite
|
|
50
|
+
`progress.ndjson` in the work directory so it holds one JSON line per
|
|
51
|
+
phase entered so far, in order:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
{"phase": 1, "name": "Intake"}
|
|
55
|
+
{"phase": 2, "name": "Node Classification"}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Rewrite the whole file each time (append the new phase's line to the
|
|
59
|
+
ones already there). This file is machine-read for live progress
|
|
60
|
+
display; it is not a deliverable and never affects the IR.
|
|
61
|
+
|
|
49
62
|
### Phase 1: Intake
|
|
50
63
|
|
|
51
64
|
Read the target directory exhaustively:
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""``python -m rote`` — the same CLI as the ``rote`` console script.
|
|
2
|
+
|
|
3
|
+
Exists so generated configs can invoke rote through a known
|
|
4
|
+
interpreter (``sys.executable -m rote``) instead of guessing at PATH —
|
|
5
|
+
the eval harness's ``headersHelper`` wiring depends on this.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
from rote.cli import main
|
|
11
|
+
|
|
12
|
+
sys.exit(main())
|
|
@@ -31,7 +31,17 @@ class Adapter(Protocol):
|
|
|
31
31
|
#: Options a factory may receive (forwarded from ``get_adapter``). Factories
|
|
32
32
|
#: swallow unknown keys via ``**options`` — mirrors the driver-registry
|
|
33
33
|
#: convention — so a runtime that doesn't support an option just ignores it.
|
|
34
|
-
#: ``external_backend`` ("mcp" | "api") is understood by the DBOS
|
|
34
|
+
#: ``external_backend`` ("mcp" | "api") is understood by the DBOS, DBOS-TS,
|
|
35
|
+
#: and Inngest adapters.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _validated_backend(options: dict[str, Any]) -> Literal["mcp", "api"] | None:
|
|
39
|
+
backend = options.get("external_backend")
|
|
40
|
+
if backend is None:
|
|
41
|
+
return None
|
|
42
|
+
if backend not in ("mcp", "api"):
|
|
43
|
+
raise ValueError(f"external_backend must be 'mcp' or 'api', got {backend!r}")
|
|
44
|
+
return cast(Literal["mcp", "api"], backend)
|
|
35
45
|
|
|
36
46
|
|
|
37
47
|
def _temporal_adapter_factory(**options: Any) -> Adapter:
|
|
@@ -43,32 +53,39 @@ def _temporal_adapter_factory(**options: Any) -> Adapter:
|
|
|
43
53
|
|
|
44
54
|
|
|
45
55
|
def _cloudflare_adapter_factory(**options: Any) -> Adapter:
|
|
46
|
-
from rote.adapters.cloudflare import CloudflareAdapter
|
|
56
|
+
from rote.adapters.cloudflare import CloudflareAdapter, CloudflareAdapterConfig
|
|
47
57
|
|
|
48
|
-
|
|
58
|
+
backend = _validated_backend(options)
|
|
59
|
+
if backend is None:
|
|
60
|
+
return CloudflareAdapter()
|
|
61
|
+
return CloudflareAdapter(CloudflareAdapterConfig(external_backend=backend))
|
|
49
62
|
|
|
50
63
|
|
|
51
64
|
def _dbos_adapter_factory(**options: Any) -> Adapter:
|
|
52
65
|
from rote.adapters.dbos import DbosAdapter, DbosAdapterConfig
|
|
53
66
|
|
|
54
|
-
backend = options
|
|
67
|
+
backend = _validated_backend(options)
|
|
55
68
|
if backend is None:
|
|
56
69
|
return DbosAdapter()
|
|
57
|
-
|
|
58
|
-
raise ValueError(f"external_backend must be 'mcp' or 'api', got {backend!r}")
|
|
59
|
-
return DbosAdapter(DbosAdapterConfig(external_backend=cast(Literal["mcp", "api"], backend)))
|
|
70
|
+
return DbosAdapter(DbosAdapterConfig(external_backend=backend))
|
|
60
71
|
|
|
61
72
|
|
|
62
73
|
def _dbos_ts_adapter_factory(**options: Any) -> Adapter:
|
|
63
|
-
from rote.adapters.dbos_ts import DbosTsAdapter
|
|
74
|
+
from rote.adapters.dbos_ts import DbosTsAdapter, DbosTsAdapterConfig
|
|
64
75
|
|
|
65
|
-
|
|
76
|
+
backend = _validated_backend(options)
|
|
77
|
+
if backend is None:
|
|
78
|
+
return DbosTsAdapter()
|
|
79
|
+
return DbosTsAdapter(DbosTsAdapterConfig(external_backend=backend))
|
|
66
80
|
|
|
67
81
|
|
|
68
82
|
def _inngest_adapter_factory(**options: Any) -> Adapter:
|
|
69
|
-
from rote.adapters.inngest import InngestAdapter
|
|
83
|
+
from rote.adapters.inngest import InngestAdapter, InngestAdapterConfig
|
|
70
84
|
|
|
71
|
-
|
|
85
|
+
backend = _validated_backend(options)
|
|
86
|
+
if backend is None:
|
|
87
|
+
return InngestAdapter()
|
|
88
|
+
return InngestAdapter(InngestAdapterConfig(external_backend=backend))
|
|
72
89
|
|
|
73
90
|
|
|
74
91
|
def _python_adapter_factory(**options: Any) -> Adapter:
|
|
@@ -194,6 +194,27 @@ class EmitWriter:
|
|
|
194
194
|
)
|
|
195
195
|
|
|
196
196
|
|
|
197
|
+
class EmitResult(dict[str, Path]):
|
|
198
|
+
"""An adapter emit mapping that also carries preserved-conflict paths.
|
|
199
|
+
|
|
200
|
+
It *is* the ``label -> written path`` dict every ``emit()`` has always
|
|
201
|
+
returned (the path is the ``.new`` sibling when a user-edited file was
|
|
202
|
+
preserved), so existing consumers that iterate it keep working
|
|
203
|
+
unchanged. The extra :attr:`preserved` attribute exposes the
|
|
204
|
+
:class:`EmitWriter`'s ``rel -> .new path`` map directly, for callers
|
|
205
|
+
(the cloud runner) that want the conflict set without sniffing ``.new``
|
|
206
|
+
suffixes off the values.
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
def __init__(
|
|
210
|
+
self,
|
|
211
|
+
written: dict[str, Path] | None = None,
|
|
212
|
+
preserved: dict[str, Path] | None = None,
|
|
213
|
+
) -> None:
|
|
214
|
+
super().__init__(written or {})
|
|
215
|
+
self.preserved: dict[str, Path] = dict(preserved or {})
|
|
216
|
+
|
|
217
|
+
|
|
197
218
|
# ───────── Case conversion ─────────
|
|
198
219
|
|
|
199
220
|
|
|
@@ -238,6 +259,32 @@ def _pipeline_hash(pipeline: Pipeline) -> str:
|
|
|
238
259
|
return hashlib.sha256(payload.encode()).hexdigest()[:8]
|
|
239
260
|
|
|
240
261
|
|
|
262
|
+
def workflow_class_name(pipeline: Pipeline) -> str:
|
|
263
|
+
"""The generated workflow class name: ``<PascalName>Workflow``.
|
|
264
|
+
|
|
265
|
+
The Cloudflare and Temporal adapters both name their emitted
|
|
266
|
+
workflow class this way; centralizing it here keeps the manifest the
|
|
267
|
+
cloud consumes and the emitted TypeScript from drifting apart.
|
|
268
|
+
"""
|
|
269
|
+
return f"{_to_pascal_case(pipeline.name)}Workflow"
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def pipeline_identity(pipeline: Pipeline) -> dict[str, str]:
|
|
273
|
+
"""Stable identity fields for a pipeline, shared by emitters and manifests.
|
|
274
|
+
|
|
275
|
+
Returns ``{name, version, pipeline_hash, class_name}`` — the same four
|
|
276
|
+
values the Cloudflare adapter bakes into the workflow.ts header and
|
|
277
|
+
writes into ``manifest.json`` for the cloud runner. Single source of
|
|
278
|
+
truth so the two can't disagree.
|
|
279
|
+
"""
|
|
280
|
+
return {
|
|
281
|
+
"name": pipeline.name,
|
|
282
|
+
"version": pipeline.version,
|
|
283
|
+
"pipeline_hash": _pipeline_hash(pipeline),
|
|
284
|
+
"class_name": workflow_class_name(pipeline),
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
|
|
241
288
|
# ───────── Duration strings ─────────
|
|
242
289
|
|
|
243
290
|
_IR_DURATION_RE = re.compile(r"^(\d+(?:\.\d+)?)(ms|s|m|h|d)$")
|