route66 0.1.1__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.
- route66-0.1.1/.gitignore +58 -0
- route66-0.1.1/PKG-INFO +104 -0
- route66-0.1.1/README.md +83 -0
- route66-0.1.1/bench/README.md +115 -0
- route66-0.1.1/pyproject.toml +66 -0
- route66-0.1.1/release/CHANGELOG.md +23 -0
- route66-0.1.1/release/README.md +152 -0
- route66-0.1.1/release/versions/generic.json +15 -0
- route66-0.1.1/release/versions/gitlab.json +16 -0
- route66-0.1.1/release/versions/pypi.json +16 -0
- route66-0.1.1/router-testkit/README.md +137 -0
- route66-0.1.1/specs/README.md +110 -0
- route66-0.1.1/src/route66/__init__.py +24 -0
- route66-0.1.1/src/route66/__main__.py +13 -0
- route66-0.1.1/src/route66/_version.py +3 -0
- route66-0.1.1/src/route66/py.typed +1 -0
route66-0.1.1/.gitignore
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
*.pyc
|
|
7
|
+
*.pdb
|
|
8
|
+
|
|
9
|
+
# Virtual environment (uv)
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
|
|
14
|
+
# Distribution / packaging
|
|
15
|
+
dist/
|
|
16
|
+
build/
|
|
17
|
+
*.egg-info/
|
|
18
|
+
*.egg
|
|
19
|
+
MANIFEST
|
|
20
|
+
|
|
21
|
+
# Testing
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.coverage
|
|
24
|
+
coverage.xml
|
|
25
|
+
htmlcov/
|
|
26
|
+
.tox/
|
|
27
|
+
|
|
28
|
+
# Linting (ruff)
|
|
29
|
+
.ruff_cache/
|
|
30
|
+
|
|
31
|
+
# Type checking
|
|
32
|
+
.mypy_cache/
|
|
33
|
+
.pytype/
|
|
34
|
+
|
|
35
|
+
# Jupyter
|
|
36
|
+
.ipynb_checkpoints/
|
|
37
|
+
*.ipynb
|
|
38
|
+
|
|
39
|
+
# Environment variables
|
|
40
|
+
.env
|
|
41
|
+
.env.*
|
|
42
|
+
!.env.example
|
|
43
|
+
|
|
44
|
+
# OS
|
|
45
|
+
.DS_Store
|
|
46
|
+
Thumbs.db
|
|
47
|
+
desktop.ini
|
|
48
|
+
|
|
49
|
+
# Editors
|
|
50
|
+
.vscode/
|
|
51
|
+
.idea/
|
|
52
|
+
*.swp
|
|
53
|
+
*.swo
|
|
54
|
+
*~
|
|
55
|
+
|
|
56
|
+
# Logs
|
|
57
|
+
*.log
|
|
58
|
+
logs/
|
route66-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: route66
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: route66 Python SDK
|
|
5
|
+
Author: route66 maintainers
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Keywords: route66,sdk
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
11
|
+
Classifier: Typing :: Typed
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
16
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
17
|
+
Requires-Dist: twine>=5.1; extra == 'dev'
|
|
18
|
+
Provides-Extra: semantic
|
|
19
|
+
Requires-Dist: sentence-transformers>=3.0; extra == 'semantic'
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# route66
|
|
23
|
+
|
|
24
|
+
**Intelligent Tool Router for LLM Agents** — a catalog-in library that,
|
|
25
|
+
given a query and a large tool catalog, returns only the relevant tools,
|
|
26
|
+
deduplicated, in an ordered execution plan, with policy-aware defaults.
|
|
27
|
+
|
|
28
|
+
Built against the **`router-testkit`** hackathon grading contract
|
|
29
|
+
(single-tool routing, ordered multi-tool plans, near-duplicate
|
|
30
|
+
resolution, capability-based fallback widening, deprecation policy,
|
|
31
|
+
out-of-scope refusal, and clarify-on-vague-destructive).
|
|
32
|
+
|
|
33
|
+
## Layout
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
route66/
|
|
37
|
+
├── tool_router/ # The POC library (facade + retrievers + dedup)
|
|
38
|
+
├── testkit_adapter.py # Bridges tool_router to the harness contract
|
|
39
|
+
├── demo.py # Toy 60-tool catalog demo of the POC
|
|
40
|
+
├── metrics.py # Recall/precision/token-savings eval on the toy set
|
|
41
|
+
├── router-testkit/ # Hackathon input kit — DO NOT MODIFY
|
|
42
|
+
│ ├── catalog/ # 64 mock tools · 13 clusters · dup/version metadata
|
|
43
|
+
│ ├── test_cases/ # 29 scored cases across 11 categories
|
|
44
|
+
│ ├── registry_guardrail/# Intake validator fixtures
|
|
45
|
+
│ └── harness/ # run_benchmark.py (scorer) + baseline_router.py
|
|
46
|
+
├── bench/ # Additional benchmarks (baseline, mutations, guardrail)
|
|
47
|
+
├── specs/ # Spec-driven-development plan (SPEC-001..011)
|
|
48
|
+
├── docs/ # PRDs and the hackathon problem statement
|
|
49
|
+
└── tests/ # Unit tests (populated as specs are executed)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Baseline
|
|
53
|
+
|
|
54
|
+
| Router | Cases passed | Token savings |
|
|
55
|
+
|---------------------------------------|--------------|----------------|
|
|
56
|
+
| Testkit reference `baseline_router` | 7 / 29 (24%) | 94% |
|
|
57
|
+
| `tool_router` (this repo, current) | 5 / 29 (17%) | 90% |
|
|
58
|
+
|
|
59
|
+
The gap is deliberate and mapped in `specs/`. See
|
|
60
|
+
[`specs/README.md`](./specs/README.md) for the priority-ordered plan
|
|
61
|
+
(P0 → P1 → P2) with acceptance criteria pinned to specific `TC-*`
|
|
62
|
+
cases. Target after P0: ≥ 20/29. After P1: ≥ 24/29.
|
|
63
|
+
|
|
64
|
+
## Quick start
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Install (uv-managed)
|
|
68
|
+
uv sync
|
|
69
|
+
|
|
70
|
+
# Score the reference baseline
|
|
71
|
+
cd router-testkit/harness
|
|
72
|
+
python run_benchmark.py --verbose
|
|
73
|
+
|
|
74
|
+
# Score this project's router
|
|
75
|
+
python run_benchmark.py --router testkit_adapter:MyRouter --verbose
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The harness has no third-party dependencies; standard-library only.
|
|
79
|
+
The router itself can run on `HashingEmbedder` (zero deps) or opt-in
|
|
80
|
+
`SentenceTransformerEmbedder` via `uv sync --extra semantic`.
|
|
81
|
+
|
|
82
|
+
## Where to read next
|
|
83
|
+
|
|
84
|
+
1. **`specs/README.md`** — the executable plan. Every spec is pickable
|
|
85
|
+
independently and has acceptance criteria tied to testkit case IDs.
|
|
86
|
+
2. **`router-testkit/README.md`** — the grading contract (authoritative).
|
|
87
|
+
3. **`tool_router/README.md`** (below `tool_router/`) — the current POC
|
|
88
|
+
library docs. Will be reframed in SPEC-011.
|
|
89
|
+
4. **`docs/hackathon-definition.md`** — the problem statement.
|
|
90
|
+
5. **`docs/PRD.md`** — the initial PRD.
|
|
91
|
+
|
|
92
|
+
## Toolchain
|
|
93
|
+
|
|
94
|
+
- Python ≥ 3.14 (pinned in `.python-version`, managed by uv)
|
|
95
|
+
- Ruff for lint + format
|
|
96
|
+
- Pytest for unit tests (see `pyproject.toml`'s `[dev]` extra)
|
|
97
|
+
|
|
98
|
+
## Dev
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
uv run ruff check .
|
|
102
|
+
uv run ruff format .
|
|
103
|
+
uv run pytest
|
|
104
|
+
```
|
route66-0.1.1/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# route66
|
|
2
|
+
|
|
3
|
+
**Intelligent Tool Router for LLM Agents** — a catalog-in library that,
|
|
4
|
+
given a query and a large tool catalog, returns only the relevant tools,
|
|
5
|
+
deduplicated, in an ordered execution plan, with policy-aware defaults.
|
|
6
|
+
|
|
7
|
+
Built against the **`router-testkit`** hackathon grading contract
|
|
8
|
+
(single-tool routing, ordered multi-tool plans, near-duplicate
|
|
9
|
+
resolution, capability-based fallback widening, deprecation policy,
|
|
10
|
+
out-of-scope refusal, and clarify-on-vague-destructive).
|
|
11
|
+
|
|
12
|
+
## Layout
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
route66/
|
|
16
|
+
├── tool_router/ # The POC library (facade + retrievers + dedup)
|
|
17
|
+
├── testkit_adapter.py # Bridges tool_router to the harness contract
|
|
18
|
+
├── demo.py # Toy 60-tool catalog demo of the POC
|
|
19
|
+
├── metrics.py # Recall/precision/token-savings eval on the toy set
|
|
20
|
+
├── router-testkit/ # Hackathon input kit — DO NOT MODIFY
|
|
21
|
+
│ ├── catalog/ # 64 mock tools · 13 clusters · dup/version metadata
|
|
22
|
+
│ ├── test_cases/ # 29 scored cases across 11 categories
|
|
23
|
+
│ ├── registry_guardrail/# Intake validator fixtures
|
|
24
|
+
│ └── harness/ # run_benchmark.py (scorer) + baseline_router.py
|
|
25
|
+
├── bench/ # Additional benchmarks (baseline, mutations, guardrail)
|
|
26
|
+
├── specs/ # Spec-driven-development plan (SPEC-001..011)
|
|
27
|
+
├── docs/ # PRDs and the hackathon problem statement
|
|
28
|
+
└── tests/ # Unit tests (populated as specs are executed)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Baseline
|
|
32
|
+
|
|
33
|
+
| Router | Cases passed | Token savings |
|
|
34
|
+
|---------------------------------------|--------------|----------------|
|
|
35
|
+
| Testkit reference `baseline_router` | 7 / 29 (24%) | 94% |
|
|
36
|
+
| `tool_router` (this repo, current) | 5 / 29 (17%) | 90% |
|
|
37
|
+
|
|
38
|
+
The gap is deliberate and mapped in `specs/`. See
|
|
39
|
+
[`specs/README.md`](./specs/README.md) for the priority-ordered plan
|
|
40
|
+
(P0 → P1 → P2) with acceptance criteria pinned to specific `TC-*`
|
|
41
|
+
cases. Target after P0: ≥ 20/29. After P1: ≥ 24/29.
|
|
42
|
+
|
|
43
|
+
## Quick start
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Install (uv-managed)
|
|
47
|
+
uv sync
|
|
48
|
+
|
|
49
|
+
# Score the reference baseline
|
|
50
|
+
cd router-testkit/harness
|
|
51
|
+
python run_benchmark.py --verbose
|
|
52
|
+
|
|
53
|
+
# Score this project's router
|
|
54
|
+
python run_benchmark.py --router testkit_adapter:MyRouter --verbose
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The harness has no third-party dependencies; standard-library only.
|
|
58
|
+
The router itself can run on `HashingEmbedder` (zero deps) or opt-in
|
|
59
|
+
`SentenceTransformerEmbedder` via `uv sync --extra semantic`.
|
|
60
|
+
|
|
61
|
+
## Where to read next
|
|
62
|
+
|
|
63
|
+
1. **`specs/README.md`** — the executable plan. Every spec is pickable
|
|
64
|
+
independently and has acceptance criteria tied to testkit case IDs.
|
|
65
|
+
2. **`router-testkit/README.md`** — the grading contract (authoritative).
|
|
66
|
+
3. **`tool_router/README.md`** (below `tool_router/`) — the current POC
|
|
67
|
+
library docs. Will be reframed in SPEC-011.
|
|
68
|
+
4. **`docs/hackathon-definition.md`** — the problem statement.
|
|
69
|
+
5. **`docs/PRD.md`** — the initial PRD.
|
|
70
|
+
|
|
71
|
+
## Toolchain
|
|
72
|
+
|
|
73
|
+
- Python ≥ 3.14 (pinned in `.python-version`, managed by uv)
|
|
74
|
+
- Ruff for lint + format
|
|
75
|
+
- Pytest for unit tests (see `pyproject.toml`'s `[dev]` extra)
|
|
76
|
+
|
|
77
|
+
## Dev
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
uv run ruff check .
|
|
81
|
+
uv run ruff format .
|
|
82
|
+
uv run pytest
|
|
83
|
+
```
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# bench/ — Conduit Evaluation Suite
|
|
2
|
+
|
|
3
|
+
This directory holds the benchmark that makes Conduit's claims falsifiable. Nothing in the PRD is defensible without the numbers this suite produces.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
| File | Purpose |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `tools.jsonl` | The 500-tool catalog. One tool per line. Ground truth for what the router picks *from*. |
|
|
10
|
+
| `queries.jsonl` | The 200-query labeled eval set. Each query has a `gold_tools` list — the ground-truth tool(s) that should be selected. |
|
|
11
|
+
| `queries_ood.jsonl` | 20-query out-of-distribution slice — sampled/authored from real oncall vernacular, held out from all tuning. This is the honest test. |
|
|
12
|
+
| `run_baseline.py` | Long-context baseline runner (Sonnet 4.5 + prompt caching + all 500 tools in context). Produces `baseline_longcontext.md`. See §2.5 of PRD. |
|
|
13
|
+
| `run_conduit.py` | Conduit routed pipeline runner. Produces `report.md`. |
|
|
14
|
+
| `metrics.py` | Shared metric computations (precision@k, recall@k, token counting, cost). |
|
|
15
|
+
| `baseline_longcontext.md` | Auto-generated report from `run_baseline.py`. **This file drives the §2.5 gate decision.** |
|
|
16
|
+
| `report.md` | Auto-generated final report comparing all baselines. |
|
|
17
|
+
|
|
18
|
+
## Schemas
|
|
19
|
+
|
|
20
|
+
### `tools.jsonl` — one tool per line
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"id": "datadog.query_service_map",
|
|
25
|
+
"server": "datadog",
|
|
26
|
+
"name": "query_service_map",
|
|
27
|
+
"description": "Query the Datadog service map to identify upstream/downstream dependencies of a service. Useful for diagnosing latency propagation and identifying the source of a cascading failure.",
|
|
28
|
+
"params_summary": "service_name (str), time_window_minutes (int, default 15), env (str, optional)",
|
|
29
|
+
"returns_summary": "list of connected services with edge metadata (latency_p95, error_rate)",
|
|
30
|
+
"tags": ["datadog", "observability", "dependency-graph", "read"],
|
|
31
|
+
"policy_tags": ["read-only"],
|
|
32
|
+
"signature": "query_service_map(service_name: str, time_window_minutes: int = 15, env: str | None = None) -> ServiceMap"
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- `id` — globally-unique, dotted namespace `{server}.{name}`.
|
|
37
|
+
- `signature` — the compressed form injected into prompts (see PRD §3.6).
|
|
38
|
+
- `policy_tags` — used by §3.7 policy filter (P1).
|
|
39
|
+
- `tags` — semantic tags; used for BM25 keyword match.
|
|
40
|
+
|
|
41
|
+
### `queries.jsonl` — one labeled query per line
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"id": "q_001",
|
|
46
|
+
"archetype": "bad_deploy",
|
|
47
|
+
"difficulty": "easy",
|
|
48
|
+
"query": "Our checkout service p99 latency spiked after the 2:14pm deploy — can you find which commit and roll it back?",
|
|
49
|
+
"gold_tools": ["datadog.query_latency", "github.get_recent_deploys", "argocd.rollback"],
|
|
50
|
+
"gold_k": 3,
|
|
51
|
+
"notes": "Multi-step SRE flow. Router should return the diagnostic tool plus the deploy-history tool plus the mitigation tool."
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- `archetype` — one of 10 SRE incident archetypes (see below).
|
|
56
|
+
- `difficulty` — `easy | medium | hard`. Hard queries use colloquial language, abbreviations, or ambiguous phrasing.
|
|
57
|
+
- `gold_tools` — ordered list of tools that constitute the correct chain. Precision@k evaluates whether these appear in the top-k selection.
|
|
58
|
+
- `gold_k` — expected number of tools for this query. Used to evaluate the dynamic-K gate (§3.5).
|
|
59
|
+
|
|
60
|
+
### `queries_ood.jsonl` — same schema as `queries.jsonl`
|
|
61
|
+
|
|
62
|
+
Sampled from real oncall vernacular. Held out from all tuning. If Conduit hits precision@3 ≥ 80% here, the "94/8" claim is real. If it doesn't, we don't ship the number.
|
|
63
|
+
|
|
64
|
+
## Archetypes (10 SRE incident categories)
|
|
65
|
+
|
|
66
|
+
| Archetype | Description |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `bad_deploy` | Regression introduced by a recent deploy |
|
|
69
|
+
| `memory_leak` | Slow-growing memory that eventually OOMs |
|
|
70
|
+
| `dns_failure` | DNS resolution issues (misconfig, expired records, propagation) |
|
|
71
|
+
| `cert_expiry` | TLS certificate expired or about to expire |
|
|
72
|
+
| `db_failover` | Database primary crashed or is being failed over |
|
|
73
|
+
| `cache_stampede` | Thundering-herd behavior after cache invalidation |
|
|
74
|
+
| `quota_exhaustion` | API rate limit or cloud quota exceeded |
|
|
75
|
+
| `secret_rotation` | Credentials rotated, breaking downstream services |
|
|
76
|
+
| `capacity_spike` | Sudden traffic surge exceeding capacity |
|
|
77
|
+
| `dependency_outage` | A third-party or upstream dependency is down |
|
|
78
|
+
|
|
79
|
+
Each archetype targets 20 queries in `queries.jsonl` — 6 easy, 8 medium, 6 hard.
|
|
80
|
+
|
|
81
|
+
## Metrics
|
|
82
|
+
|
|
83
|
+
- **precision@k** — fraction of queries where any gold tool appears in the top-k selection.
|
|
84
|
+
- **recall@k** — fraction of gold tools present in the top-k selection, averaged over queries.
|
|
85
|
+
- **exact_match@k** — fraction of queries where *all* gold tools appear in the top-k.
|
|
86
|
+
- **prompt_tokens** — median + p95 tokens sent to the LLM per query (before prompt-cache credit).
|
|
87
|
+
- **effective_tokens** — median + p95 tokens billed after prompt caching.
|
|
88
|
+
- **cost_per_query** — USD, using published Sonnet 4.5 pricing.
|
|
89
|
+
- **latency_p50 / latency_p95** — end-to-end latency including LLM response.
|
|
90
|
+
- **hallucination_rate** — fraction of tool calls where the LLM invented a tool name not in the catalog.
|
|
91
|
+
- **end_to_end_success** — human-graded: did the response answer the user's question correctly?
|
|
92
|
+
|
|
93
|
+
## Running
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# 1. Long-context baseline (the §2.5 gate)
|
|
97
|
+
python bench/run_baseline.py --tools bench/tools.jsonl --queries bench/queries.jsonl --out bench/baseline_longcontext.md
|
|
98
|
+
|
|
99
|
+
# 2. Conduit routed pipeline (once §2.5 gate says "build")
|
|
100
|
+
python bench/run_conduit.py --tools bench/tools.jsonl --queries bench/queries.jsonl --out bench/report.md
|
|
101
|
+
|
|
102
|
+
# 3. Out-of-distribution slice (both pipelines)
|
|
103
|
+
python bench/run_baseline.py --queries bench/queries_ood.jsonl --out bench/baseline_ood.md
|
|
104
|
+
python bench/run_conduit.py --queries bench/queries_ood.jsonl --out bench/report_ood.md
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## The §2.5 gate
|
|
108
|
+
|
|
109
|
+
After `run_baseline.py` completes, read `baseline_longcontext.md`. Follow the decision table in PRD §2.5:
|
|
110
|
+
|
|
111
|
+
- Baseline precision@3 ≥ 88% → **strip retrieval, ship the long-context path.**
|
|
112
|
+
- Baseline precision@3 80–88% → build retrieval, demote §3.7/§3.8 to P1.
|
|
113
|
+
- Baseline precision@3 < 80% → build retrieval, mandatory validation gates.
|
|
114
|
+
|
|
115
|
+
Do this by **Day 2 EOD**. Everything downstream depends on it.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "route66"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "route66 Python SDK"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "Proprietary" }
|
|
12
|
+
authors = [{ name = "route66 maintainers" }]
|
|
13
|
+
keywords = ["route66", "sdk"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Typing :: Typed",
|
|
19
|
+
]
|
|
20
|
+
dependencies = []
|
|
21
|
+
|
|
22
|
+
[project.optional-dependencies]
|
|
23
|
+
semantic = ["sentence-transformers>=3.0"]
|
|
24
|
+
dev = [
|
|
25
|
+
"ruff>=0.5",
|
|
26
|
+
"pytest>=8.0",
|
|
27
|
+
"build>=1.2",
|
|
28
|
+
"twine>=5.1",
|
|
29
|
+
"pytest>=8.0",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
route66 = "route66.__main__:main"
|
|
34
|
+
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
# Build backend (hatchling) — src layout
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
[tool.hatch.version]
|
|
39
|
+
# The version is stamped into src/route66/_version.py by the release pipeline
|
|
40
|
+
# just before build. Keeping it in a dedicated file (instead of pyproject.toml)
|
|
41
|
+
# lets each provider release its own version without touching this file.
|
|
42
|
+
source = "regex"
|
|
43
|
+
path = "src/route66/_version.py"
|
|
44
|
+
pattern = "^__version__ = \"(?P<version>[^\"]+)\""
|
|
45
|
+
|
|
46
|
+
[tool.hatch.build.targets.wheel]
|
|
47
|
+
packages = ["src/route66"]
|
|
48
|
+
|
|
49
|
+
[tool.hatch.build.targets.sdist]
|
|
50
|
+
include = [
|
|
51
|
+
"src/route66",
|
|
52
|
+
"README.md",
|
|
53
|
+
"pyproject.toml",
|
|
54
|
+
"release/",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
# ---------------------------------------------------------------------------
|
|
58
|
+
# Tooling
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
line-length = 100
|
|
62
|
+
target-version = "py310"
|
|
63
|
+
|
|
64
|
+
[tool.pytest.ini_options]
|
|
65
|
+
testpaths = ["tests"]
|
|
66
|
+
addopts = "-ra"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# route66 — release changelog
|
|
2
|
+
|
|
3
|
+
Each provider tracks its own version line. Entries are appended by the release
|
|
4
|
+
pipeline (`ci/scripts/record_release.py`) so the file below is the source of
|
|
5
|
+
truth for what shipped where.
|
|
6
|
+
|
|
7
|
+
## generic
|
|
8
|
+
|
|
9
|
+
<!-- generic:begin -->
|
|
10
|
+
- _no releases yet_
|
|
11
|
+
<!-- generic:end -->
|
|
12
|
+
|
|
13
|
+
## gitlab (GitLab Package Registry)
|
|
14
|
+
|
|
15
|
+
<!-- gitlab:begin -->
|
|
16
|
+
- _no releases yet_
|
|
17
|
+
<!-- gitlab:end -->
|
|
18
|
+
|
|
19
|
+
## pypi (public PyPI)
|
|
20
|
+
|
|
21
|
+
<!-- pypi:begin -->
|
|
22
|
+
- _no releases yet_
|
|
23
|
+
<!-- pypi:end -->
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# route66 — release infrastructure
|
|
2
|
+
|
|
3
|
+
route66 ships as a Python package to two provider registries, each with its
|
|
4
|
+
own independently-managed version line, plus a `generic` line that fans out to
|
|
5
|
+
both.
|
|
6
|
+
|
|
7
|
+
## Providers
|
|
8
|
+
|
|
9
|
+
| Provider | Registry | Version file | Auth in CI |
|
|
10
|
+
| --------- | -------------------------------------------- | ----------------------------------------- | --------------------------- |
|
|
11
|
+
| `gitlab` | Private GitLab project Package Registry | `release/versions/gitlab.json` | `CI_JOB_TOKEN` (built-in) |
|
|
12
|
+
| `pypi` | Public PyPI (`pypi.org`) | `release/versions/pypi.json` | `PYPI_API_TOKEN` (variable) |
|
|
13
|
+
| `generic` | Fans out to **both** providers with a shared | `release/versions/generic.json` (+ writes | uses provider tokens above |
|
|
14
|
+
| | version number | into each provider file as well) | |
|
|
15
|
+
|
|
16
|
+
Each provider's file keeps a `current` version and an append-only `history`
|
|
17
|
+
list. The release pipeline never rewrites a past entry — it only updates the
|
|
18
|
+
one for the version that was just published (idempotent re-runs) or prepends a
|
|
19
|
+
new entry.
|
|
20
|
+
|
|
21
|
+
## Layout
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
route66/
|
|
25
|
+
├── src/route66/ # SDK source (src-layout)
|
|
26
|
+
│ ├── __init__.py
|
|
27
|
+
│ ├── __main__.py
|
|
28
|
+
│ └── _version.py # stamped by ci/scripts/bump_version.py
|
|
29
|
+
├── tests/
|
|
30
|
+
├── ci/
|
|
31
|
+
│ ├── release.gitlab-ci.yml # included by .gitlab-ci.yml
|
|
32
|
+
│ └── scripts/
|
|
33
|
+
│ ├── _common.py
|
|
34
|
+
│ ├── bump_version.py
|
|
35
|
+
│ ├── publish.py
|
|
36
|
+
│ └── record_release.py
|
|
37
|
+
├── release/
|
|
38
|
+
│ ├── CHANGELOG.md # per-provider sections, auto-updated
|
|
39
|
+
│ └── versions/
|
|
40
|
+
│ ├── generic.json
|
|
41
|
+
│ ├── gitlab.json
|
|
42
|
+
│ └── pypi.json
|
|
43
|
+
├── .gitlab-ci.yml
|
|
44
|
+
└── pyproject.toml
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Continuous pipeline
|
|
48
|
+
|
|
49
|
+
Runs on every push / MR:
|
|
50
|
+
|
|
51
|
+
1. **lint** — `ruff check`.
|
|
52
|
+
2. **test** — `pytest`.
|
|
53
|
+
3. **build** — `python -m build` → uploads `dist/` as a job artifact.
|
|
54
|
+
|
|
55
|
+
## On-demand release pipelines
|
|
56
|
+
|
|
57
|
+
All three release jobs are `when: manual` and only appear on the default
|
|
58
|
+
(protected) branch. Trigger from **CI/CD → Pipelines → Run pipeline**, choose
|
|
59
|
+
the job, and (optionally) set variables:
|
|
60
|
+
|
|
61
|
+
| Variable | Default | Meaning |
|
|
62
|
+
| --------------- | ------- | ---------------------------------------------------- |
|
|
63
|
+
| `BUMP` | `patch` | One of `patch` / `minor` / `major`. |
|
|
64
|
+
| `VERSION` | *empty* | Explicit `x.y.z` — overrides `BUMP` when set. |
|
|
65
|
+
| `RELEASE_NOTES` | *empty* | Free-form text appended to the changelog entry. |
|
|
66
|
+
|
|
67
|
+
### `release:gitlab`
|
|
68
|
+
|
|
69
|
+
Publishes to the private GitLab Package Registry using `CI_JOB_TOKEN`. No
|
|
70
|
+
extra project variable required. Endpoint is derived from `CI_API_V4_URL` and
|
|
71
|
+
`CI_PROJECT_ID` at runtime, so the same job works on any GitLab (SaaS or
|
|
72
|
+
self-hosted).
|
|
73
|
+
|
|
74
|
+
Install a released version:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install route66 \
|
|
78
|
+
--index-url https://__token__:<deploy-token>@<gitlab-host>/api/v4/projects/<project-id>/packages/pypi/simple
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### `release:pypi`
|
|
82
|
+
|
|
83
|
+
Publishes to public PyPI. Requires a **project-scoped** API token added as a
|
|
84
|
+
masked + protected CI variable named `PYPI_API_TOKEN`.
|
|
85
|
+
|
|
86
|
+
Install:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pip install route66
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### `release:generic`
|
|
93
|
+
|
|
94
|
+
Bumps the shared version in `release/versions/generic.json`, then fans out and
|
|
95
|
+
publishes to `gitlab` **and** `pypi` with the same version number. Each
|
|
96
|
+
provider's own history file gets an entry for that version too, so any
|
|
97
|
+
provider can be inspected independently.
|
|
98
|
+
|
|
99
|
+
## Version history model
|
|
100
|
+
|
|
101
|
+
Every publish produces exactly one entry in the affected provider file(s):
|
|
102
|
+
|
|
103
|
+
```jsonc
|
|
104
|
+
{
|
|
105
|
+
"version": "1.4.2",
|
|
106
|
+
"released_at": "2026-07-14T12:34:56+00:00",
|
|
107
|
+
"commit": "<full sha>",
|
|
108
|
+
"pipeline": "<pipeline url>",
|
|
109
|
+
"artifacts": ["route66-1.4.2-py3-none-any.whl", "route66-1.4.2.tar.gz"],
|
|
110
|
+
"notes": "..."
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Because provider version files are independent, the `pypi` line can lag or
|
|
115
|
+
lead the `gitlab` line freely (e.g. hotfix a private release without touching
|
|
116
|
+
public PyPI). The `generic` line acts as a "canonical" release marker that
|
|
117
|
+
guarantees both providers agree.
|
|
118
|
+
|
|
119
|
+
## Required CI/CD variables
|
|
120
|
+
|
|
121
|
+
Set these under **Settings → CI/CD → Variables** on the route66 project:
|
|
122
|
+
|
|
123
|
+
| Name | Scope | Notes |
|
|
124
|
+
| ------------------- | -------------------- | ------------------------------------------------- |
|
|
125
|
+
| `GITLAB_PUSH_TOKEN` | Protected + Masked | Project access token with `write_repository`. Used to push the version-bump commit + tag back to the default branch. |
|
|
126
|
+
| `PYPI_API_TOKEN` | Protected + Masked | Only needed for `release:pypi` and `release:generic`. |
|
|
127
|
+
|
|
128
|
+
`CI_JOB_TOKEN` is provided automatically by GitLab and is already scoped to
|
|
129
|
+
the project's Package Registry.
|
|
130
|
+
|
|
131
|
+
## Tagging convention
|
|
132
|
+
|
|
133
|
+
Each release creates an annotated tag of the form `{provider}/v{X.Y.Z}`, e.g.
|
|
134
|
+
`gitlab/v1.4.2`, `pypi/v1.4.2`, `generic/v2.0.0`. That way `git tag --list`
|
|
135
|
+
tells you every artifact that ever shipped for a given provider, and provider
|
|
136
|
+
lines never collide.
|
|
137
|
+
|
|
138
|
+
## Local dry-run
|
|
139
|
+
|
|
140
|
+
To verify a bump without publishing:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
python ci/scripts/bump_version.py --provider gitlab --kind patch
|
|
144
|
+
cat release/versions/gitlab.json
|
|
145
|
+
python -m build
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Then discard the local changes:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
git checkout -- src/route66/_version.py release/versions/gitlab.json
|
|
152
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"provider": "generic",
|
|
3
|
+
"description": "Shared/coordinated release version. Bumped by the generic release pipeline and published to every provider.",
|
|
4
|
+
"current": "0.1.0",
|
|
5
|
+
"history": [
|
|
6
|
+
{
|
|
7
|
+
"version": "0.1.0",
|
|
8
|
+
"released_at": null,
|
|
9
|
+
"commit": null,
|
|
10
|
+
"pipeline": null,
|
|
11
|
+
"artifacts": [],
|
|
12
|
+
"notes": "Initial scaffold. No published artifact yet."
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"provider": "gitlab",
|
|
3
|
+
"description": "GitLab Package Registry (private, project-scoped PyPI index).",
|
|
4
|
+
"index_url_template": "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi",
|
|
5
|
+
"current": "0.1.0",
|
|
6
|
+
"history": [
|
|
7
|
+
{
|
|
8
|
+
"version": "0.1.0",
|
|
9
|
+
"released_at": null,
|
|
10
|
+
"commit": null,
|
|
11
|
+
"pipeline": null,
|
|
12
|
+
"artifacts": [],
|
|
13
|
+
"notes": "Initial scaffold. No published artifact yet."
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"provider": "pypi",
|
|
3
|
+
"description": "Public PyPI (pypi.org).",
|
|
4
|
+
"index_url": "https://upload.pypi.org/legacy/",
|
|
5
|
+
"current": "0.1.0",
|
|
6
|
+
"history": [
|
|
7
|
+
{
|
|
8
|
+
"version": "0.1.0",
|
|
9
|
+
"released_at": null,
|
|
10
|
+
"commit": null,
|
|
11
|
+
"pipeline": null,
|
|
12
|
+
"artifacts": [],
|
|
13
|
+
"notes": "Initial scaffold. No published artifact yet."
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# AI Tool-Routing Hackathon — Input Test Kit
|
|
2
|
+
|
|
3
|
+
A ready-to-run input set for building and grading an **intelligent tool-routing
|
|
4
|
+
layer** that filters a large enterprise tool catalog down to a small,
|
|
5
|
+
high-precision subset per query.
|
|
6
|
+
|
|
7
|
+
Everything here is *input* for participants: a mock catalog, a scored test
|
|
8
|
+
suite, registry-guardrail fixtures, catalog-churn scenarios, and a benchmark
|
|
9
|
+
harness with a reference baseline router to beat.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
router-testkit/
|
|
13
|
+
├── catalog/
|
|
14
|
+
│ └── tools_catalog.json 64 mock tools · 13 clusters · dup/version metadata
|
|
15
|
+
├── registry_guardrail/
|
|
16
|
+
│ └── submissions.json valid + malformed intake fixtures (accept/reject)
|
|
17
|
+
├── test_cases/
|
|
18
|
+
│ ├── 01_single_tool.json ── 29 scored query packets across 11 categories
|
|
19
|
+
│ ├── 02_multi_tool_ordered.json
|
|
20
|
+
│ ├── 03_deduplication.json
|
|
21
|
+
│ ├── 04_repeat_count.json
|
|
22
|
+
│ ├── 05_clarification.json
|
|
23
|
+
│ ├── 06_fallback_widening.json
|
|
24
|
+
│ ├── 07_backward_compat.json
|
|
25
|
+
│ ├── 08_negative_out_of_scope.json
|
|
26
|
+
│ ├── 09_cross_cluster.json
|
|
27
|
+
│ ├── 10_positional_bias.json
|
|
28
|
+
│ ├── 11_scope_overlap.json
|
|
29
|
+
│ ├── catalog_mutations.json add / remove / replace / upgrade resilience
|
|
30
|
+
│ └── _index.json
|
|
31
|
+
└── harness/
|
|
32
|
+
├── run_benchmark.py scorer (prints metrics; no dependencies)
|
|
33
|
+
└── baseline_router.py naive reference router (REPLACE THIS)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick start
|
|
37
|
+
|
|
38
|
+
Standard-library Python only — nothing to install.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
cd harness
|
|
42
|
+
python run_benchmark.py # runs the baseline (~24% accuracy)
|
|
43
|
+
python run_benchmark.py --router mymod:MyRouter # run your own
|
|
44
|
+
python run_benchmark.py --verbose # also print why each case failed
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Your router is any class with:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
class MyRouter:
|
|
51
|
+
def __init__(self, catalog): ...
|
|
52
|
+
def route(self, query, context=None) -> dict:
|
|
53
|
+
return {
|
|
54
|
+
"selected_tools": ["fin.get_revenue_report", ...], # the K schemas you'd inject
|
|
55
|
+
"plan": ["fin.get_revenue_report", ...], # ordered call plan (ids may repeat)
|
|
56
|
+
"clarify": False, # True => you asked instead of acting
|
|
57
|
+
"clarify_question": None,
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## The catalog (`catalog/tools_catalog.json`)
|
|
62
|
+
|
|
63
|
+
64 tools over 13 clusters (finance, communication, analytics, hr, identity,
|
|
64
|
+
it_devops, crm, calendar, documents, cloud_storage, marketing, legal,
|
|
65
|
+
data_export). It carries the metadata your router is expected to exploit:
|
|
66
|
+
|
|
67
|
+
- `near_duplicate_groups` — deliberately overlapping tools. E.g. the classic
|
|
68
|
+
`get_user` / `fetch_profile` / `lookup_member` / `get_user_by_email` quartet,
|
|
69
|
+
two revenue fetchers, two chart makers, `create_ticket` vs `open_incident`.
|
|
70
|
+
- `version_families` — `create_invoice` (deprecated v1) coexists with
|
|
71
|
+
`create_invoice_v2` (current). Each tool has `deprecated`, `replaces`,
|
|
72
|
+
`replaced_by`, `sunset_date`.
|
|
73
|
+
- `cross_cluster_traps` — tools whose **name** points at the wrong cluster:
|
|
74
|
+
`email_report` lives in data_export (not communication); `send_campaign` is
|
|
75
|
+
a marketing blast, not a 1:1 email.
|
|
76
|
+
- Per-tool `side_effects` (`read` / `write` / `destructive`) so you can be
|
|
77
|
+
conservative about routing destructive actions on vague queries.
|
|
78
|
+
|
|
79
|
+
## The scored suite (`test_cases/*.json`)
|
|
80
|
+
|
|
81
|
+
Each case has a machine-checkable `expected` block:
|
|
82
|
+
|
|
83
|
+
| field | meaning |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `tools_required` | `{tool_id, min_calls, max_calls}` — must be selected and called within the count band |
|
|
86
|
+
| `order` | ordered list that must appear as a **subsequence** of your `plan` (or `null`) |
|
|
87
|
+
| `forbidden` | tool ids that must NOT be selected. `"*"` means *no tool at all* |
|
|
88
|
+
| `should_clarify` | router should ask, not act |
|
|
89
|
+
| `allow_any_of` | dedup groups: pick **exactly one** id per group |
|
|
90
|
+
|
|
91
|
+
Categories map 1:1 to the problem's pillars:
|
|
92
|
+
|
|
93
|
+
- **single_tool / cross_cluster** — basic and multi-cluster routing accuracy.
|
|
94
|
+
- **multi_tool_ordered** — the canonical *"pull revenue → make chart → email to
|
|
95
|
+
finance"* chain, with ordering enforced.
|
|
96
|
+
- **deduplication** — the near-dup groups; injecting all three user lookups
|
|
97
|
+
fails, injecting one passes.
|
|
98
|
+
- **repeat_count** — same tool invoked N times (e.g. restart 3 services →
|
|
99
|
+
`restart_service` ×3).
|
|
100
|
+
- **clarification / negative_out_of_scope** — must *not* fabricate a tool call.
|
|
101
|
+
- **fallback_widening** — the initially-obvious tool lacks a needed field/scope
|
|
102
|
+
(e.g. `send_email` can't attach; identity lookup has no SSN); the case encodes
|
|
103
|
+
the correct widen target.
|
|
104
|
+
- **backward_compat** — unversioned request → current tool; explicit "legacy v1"
|
|
105
|
+
→ deprecated tool; genuinely ambiguous → clarify.
|
|
106
|
+
- **positional_bias / scope_overlap** — set `context.shuffle_catalog=true` and
|
|
107
|
+
present near-dups in random order; correct pick must be signal-driven, not
|
|
108
|
+
position-driven (DMS vs raw S3, etc.).
|
|
109
|
+
|
|
110
|
+
## Registry guardrail (`registry_guardrail/submissions.json`)
|
|
111
|
+
|
|
112
|
+
Feed each `invalid_submissions` entry to your intake validator; it must reject
|
|
113
|
+
with the given `expected_reason` (vague/too-long description, bad id/name,
|
|
114
|
+
missing field, duplicate id, bad param type, too many params, bad semver,
|
|
115
|
+
dangling `replaced_by`, malformed JSON). The two `valid_submissions` must pass.
|
|
116
|
+
|
|
117
|
+
## Catalog churn (`test_cases/catalog_mutations.json`)
|
|
118
|
+
|
|
119
|
+
Directly answers the "what if tools change?" evaluation questions. Apply each
|
|
120
|
+
`delta` to the catalog, re-run the probe, and assert the invariant:
|
|
121
|
+
|
|
122
|
+
- **add** — new tool routable immediately, no regressions elsewhere.
|
|
123
|
+
- **remove** — depended-on tool gone → graceful degradation, no crash.
|
|
124
|
+
- **replace** — renamed tool routes by capability, not hardcoded id.
|
|
125
|
+
- **upgrade (both alive)** — `PREFER_CURRENT` unless an explicit version signal;
|
|
126
|
+
never inject both versions at once.
|
|
127
|
+
- **scale stress** — auto-grow to ~104 tools; accuracy and token savings must hold.
|
|
128
|
+
|
|
129
|
+
## Scoring & metrics
|
|
130
|
+
|
|
131
|
+
`run_benchmark.py` checks each case and prints, in plain text: a PASS/FAIL line
|
|
132
|
+
per case (with token cost), a per-category tally, and a summary with overall
|
|
133
|
+
accuracy and token savings (average routed tokens vs. the full-catalog dump).
|
|
134
|
+
Run with `--verbose` to see the reason each failed case failed. No charts, no
|
|
135
|
+
dependencies — just numbers you can read or pipe.
|
|
136
|
+
|
|
137
|
+
Baseline reference: ~24% case accuracy, ~94% token savings. Beat both.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Route66 — Spec Index
|
|
2
|
+
|
|
3
|
+
Spec-driven development for the hackathon build-out of the `tool_router`
|
|
4
|
+
library (this repo) against the `router-testkit` grading contract.
|
|
5
|
+
|
|
6
|
+
## How to use this directory
|
|
7
|
+
|
|
8
|
+
- Each spec is **pickable independently**. If the "Dependencies" block is
|
|
9
|
+
empty or its dependencies are marked ✅, you can start.
|
|
10
|
+
- Every spec ships with **concrete acceptance criteria tied to specific
|
|
11
|
+
testkit case IDs** (`TC-XYZ`). "Done" means: those cases flip PASS on the
|
|
12
|
+
harness *and* the unit tests defined in the spec pass.
|
|
13
|
+
- Read the spec top-to-bottom before writing code — the Design section
|
|
14
|
+
locks names, signatures, and file paths that later specs depend on.
|
|
15
|
+
|
|
16
|
+
## Ground-truth baseline (measured 2026-07-13)
|
|
17
|
+
|
|
18
|
+
| Router | Cases | Token savings |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| Reference `baseline_router` | 7/29 (24%) | 94% |
|
|
21
|
+
| POC via adapter (today) | 5/29 (17%) | 90% |
|
|
22
|
+
|
|
23
|
+
Target after P0: **≥20/29 (69%)**. Target with P1: **≥24/29 (83%)**.
|
|
24
|
+
|
|
25
|
+
## Priority tiers
|
|
26
|
+
|
|
27
|
+
- **P0 — Ship-critical.** Without this the submission is below baseline.
|
|
28
|
+
- **P1 — Category-unlock.** Turns a 0/N category into a passing one.
|
|
29
|
+
- **P2 — Pitch / polish.** Doesn't move the score much, but sells the story.
|
|
30
|
+
|
|
31
|
+
## Specs
|
|
32
|
+
|
|
33
|
+
### P0 — Foundation cleanup (Day 1, ~half a day total)
|
|
34
|
+
|
|
35
|
+
| ID | Title | Unblocks |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| [SPEC-001](./SPEC-001-structured-tool-metadata.md) | Structured `Tool` metadata (id, cluster, tags, deprecated, side_effects, parameters) | Every P0/P1 spec below |
|
|
38
|
+
| [SPEC-002](./SPEC-002-runtime-dedup.md) | Runtime near-duplicate resolution (replace index-time collapse) | Deduplication, positional_bias |
|
|
39
|
+
| [SPEC-003](./SPEC-003-route-context-and-plan-return.md) | `RouteContext` passthrough + return full plan (not top-1) | Everything downstream — every case scored on `plan` |
|
|
40
|
+
|
|
41
|
+
### P0 — Core build (Days 2–4)
|
|
42
|
+
|
|
43
|
+
| ID | Title | Unblocks (testkit categories) |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| [SPEC-004](./SPEC-004-chain-planner.md) | `ChainPlanner`: multi-step ordered plans | `multi_tool_ordered`, `cross_cluster`, `fallback_widening` |
|
|
46
|
+
| [SPEC-005](./SPEC-005-policy-layer.md) | Policy layer: `PREFER_CURRENT`, `NO_TOOL_ON_OOS`, `CLARIFY_ON_DESTRUCTIVE_VAGUE` | `backward_compat`, `clarification`, `negative_out_of_scope` |
|
|
47
|
+
| [SPEC-006](./SPEC-006-capability-filter.md) | Capability-based fallback widening | `fallback_widening` |
|
|
48
|
+
| [SPEC-010](./SPEC-010-testkit-adapter-hardening.md) | Harden the `MyRouter` testkit adapter | Passes the actual `run_benchmark.py` scoring |
|
|
49
|
+
|
|
50
|
+
### P1 — Category unlocks (Day 5)
|
|
51
|
+
|
|
52
|
+
| ID | Title | Unblocks |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| [SPEC-007](./SPEC-007-entity-count-extractor.md) | Entity/count extractor for repeat-call plans | `repeat_count` |
|
|
55
|
+
| [SPEC-008](./SPEC-008-schema-intake-validator.md) | `SchemaIntakeValidator` for registry guardrail | `registry_guardrail/submissions.json` |
|
|
56
|
+
|
|
57
|
+
### P2 — Completeness & pitch (Days 6–7)
|
|
58
|
+
|
|
59
|
+
| ID | Title | Value |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| [SPEC-009](./SPEC-009-catalog-registry.md) | `CatalogRegistry` with add/remove/replace/upgrade deltas | `catalog_mutations.json`; the "scales to 104 tools" pitch slide |
|
|
62
|
+
| [SPEC-011](./SPEC-011-poc-reframing.md) | Reframe POC README + demo around what the testkit actually grades | Judge-facing story |
|
|
63
|
+
|
|
64
|
+
## Dependency graph
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
SPEC-001 (Tool metadata)
|
|
68
|
+
│
|
|
69
|
+
├── SPEC-002 (runtime dedup)
|
|
70
|
+
├── SPEC-003 (context + plan)
|
|
71
|
+
│ │
|
|
72
|
+
│ ├── SPEC-004 (planner) ──┬── SPEC-006 (capability filter)
|
|
73
|
+
│ ├── SPEC-005 (policy) │
|
|
74
|
+
│ └── SPEC-007 (entities) ──┘
|
|
75
|
+
│
|
|
76
|
+
├── SPEC-008 (intake validator, independent)
|
|
77
|
+
└── SPEC-009 (catalog registry, depends on SPEC-001)
|
|
78
|
+
|
|
79
|
+
SPEC-010 (adapter) — depends on all P0 specs shipping
|
|
80
|
+
SPEC-011 (framing) — independent, do last
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Suggested pickup order (single-owner)
|
|
84
|
+
|
|
85
|
+
**Day 1:** SPEC-001 → SPEC-002 → SPEC-003 (~5 hrs, cleanup)
|
|
86
|
+
**Day 2:** SPEC-005 (policy layer — highest per-hour ROI)
|
|
87
|
+
**Day 3:** SPEC-004 (planner)
|
|
88
|
+
**Day 4:** SPEC-006 (capability filter) → SPEC-010 (adapter hardening)
|
|
89
|
+
**Day 5:** SPEC-007 → SPEC-008 (parallelizable)
|
|
90
|
+
**Day 6:** SPEC-009 (catalog churn)
|
|
91
|
+
**Day 7:** SPEC-011 (reframing) + bulletproofing
|
|
92
|
+
|
|
93
|
+
## What is *out of scope* for these specs
|
|
94
|
+
|
|
95
|
+
Per the decision to keep-but-bypass POC assets that don't score against
|
|
96
|
+
this testkit, the following files are **frozen**: `session.py`,
|
|
97
|
+
`storage.py`, `providers.py`, `adapters.py`, and the
|
|
98
|
+
`AssociationRetriever` in `retrieval.py`. Do not modify them; do not
|
|
99
|
+
delete them. The specs below explicitly bypass them via the `RouteContext`
|
|
100
|
+
+ adapter path.
|
|
101
|
+
|
|
102
|
+
## Test commands (reference)
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# 1. From router-testkit/harness/:
|
|
106
|
+
python run_benchmark.py --router testkit_adapter:MyRouter --verbose
|
|
107
|
+
|
|
108
|
+
# 2. From :
|
|
109
|
+
python3 -m pytest tests/ # spec-defined unit tests
|
|
110
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""route66 - Python SDK.
|
|
2
|
+
|
|
3
|
+
Public entry point for the route66 package. Version is sourced from the
|
|
4
|
+
installed distribution metadata so that provider-specific builds report
|
|
5
|
+
the version that was actually published.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from importlib import metadata as _metadata
|
|
11
|
+
|
|
12
|
+
from ._version import __version__ as _stamped_version
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
__version__ = _metadata.version("route66")
|
|
16
|
+
except _metadata.PackageNotFoundError: # pragma: no cover - local checkout
|
|
17
|
+
__version__ = _stamped_version
|
|
18
|
+
|
|
19
|
+
__all__ = ["__version__", "hello"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def hello(name: str = "world") -> str:
|
|
23
|
+
"""Return a friendly greeting. Placeholder SDK surface."""
|
|
24
|
+
return f"Hello from route66, {name}!"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Marker file so type checkers (mypy, pyright) treat route66 as a typed package.
|