nabla-cli 0.7.0__tar.gz → 0.7.2__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.
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/PKG-INFO +5 -4
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/README.md +4 -3
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/__init__.py +1 -1
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/cli.py +15 -17
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla_cli.egg-info/PKG-INFO +5 -4
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/pyproject.toml +1 -1
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_cli_ux.py +68 -69
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/__main__.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/branding.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/config.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/contract.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/induce.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/profile.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/prompt_recon.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/recorder.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/samplegen.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/scanner.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/schema_resolver.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/schemas/site_profile.schema.json +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/term.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/ui.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla_cli.egg-info/SOURCES.txt +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla_cli.egg-info/dependency_links.txt +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla_cli.egg-info/entry_points.txt +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla_cli.egg-info/requires.txt +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla_cli.egg-info/top_level.txt +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/setup.cfg +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_config.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_e2e_profile.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_induce.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_prompt_recon.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_recorder.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_samplegen.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_scanner.py +0 -0
- {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_schema_resolver.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nabla-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Call-site harvester: scan OpenAI call sites, record real traffic, emit site profiles
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -170,14 +170,15 @@ a problem generator:
|
|
|
170
170
|
|
|
171
171
|
```json
|
|
172
172
|
{
|
|
173
|
-
"goal": "
|
|
173
|
+
"goal": "<build's inferred goal description and constraints, joined into one string>",
|
|
174
174
|
"system_prompt": "<the site's own system prompt>",
|
|
175
175
|
"examples": [ {"problem": "...", "solution": "..."}, ... ]
|
|
176
176
|
}
|
|
177
177
|
```
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
Same pipeline as `build` (captured pairs in, one goal-inference call) —
|
|
180
|
+
only the output shape differs; nothing is added that `build`'s own
|
|
181
|
+
output doesn't contain.
|
|
181
182
|
|
|
182
183
|
## Providers
|
|
183
184
|
|
|
@@ -156,14 +156,15 @@ a problem generator:
|
|
|
156
156
|
|
|
157
157
|
```json
|
|
158
158
|
{
|
|
159
|
-
"goal": "
|
|
159
|
+
"goal": "<build's inferred goal description and constraints, joined into one string>",
|
|
160
160
|
"system_prompt": "<the site's own system prompt>",
|
|
161
161
|
"examples": [ {"problem": "...", "solution": "..."}, ... ]
|
|
162
162
|
}
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
Same pipeline as `build` (captured pairs in, one goal-inference call) —
|
|
166
|
+
only the output shape differs; nothing is added that `build`'s own
|
|
167
|
+
output doesn't contain.
|
|
167
168
|
|
|
168
169
|
## Providers
|
|
169
170
|
|
|
@@ -137,9 +137,10 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
137
137
|
help="assemble a seed spec (goal + system_prompt + problem/solution examples)",
|
|
138
138
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
139
139
|
epilog=(
|
|
140
|
-
"an alternative to `build`: same inputs, but emits
|
|
141
|
-
"
|
|
142
|
-
"
|
|
140
|
+
"an alternative to `build`: same pipeline and inputs, but emits\n"
|
|
141
|
+
"<site>.jeremy.json — build's goal rearranged into one string, the\n"
|
|
142
|
+
"site's system prompt as its own field, and the captured pairs as\n"
|
|
143
|
+
"{problem, solution} reference examples.\n\n"
|
|
143
144
|
"examples:\n"
|
|
144
145
|
" nabla jeremy auto: pick site, find <site>.captured.jsonl\n"
|
|
145
146
|
" nabla jeremy path/to/repo --site extract_invoice --captured captured.jsonl\n"
|
|
@@ -568,9 +569,11 @@ def _cmd_record(args) -> int:
|
|
|
568
569
|
"data and rerun for a higher-fidelity harvest", style="yellow")
|
|
569
570
|
rail(_out)
|
|
570
571
|
if args.out is None and args.site is None:
|
|
571
|
-
rail_end(_out, f"next: nabla build{'' if args.repo == '.' else ' ' + args.repo}"
|
|
572
|
+
rail_end(_out, f"next: nabla build{'' if args.repo == '.' else ' ' + args.repo}"
|
|
573
|
+
" (site profile) · or: nabla jeremy (seed spec)")
|
|
572
574
|
else:
|
|
573
|
-
rail_end(_out, f"next: nabla build {args.repo} --site {site.symbol} --captured {out}"
|
|
575
|
+
rail_end(_out, f"next: nabla build {args.repo} --site {site.symbol} --captured {out}"
|
|
576
|
+
" · or: nabla jeremy (same flags)")
|
|
574
577
|
return 0
|
|
575
578
|
|
|
576
579
|
|
|
@@ -762,7 +765,8 @@ def _capture_many(args, supported) -> int:
|
|
|
762
765
|
for r in failed):
|
|
763
766
|
_warn("the configured provider rejected your API key — rerun `nabla init`")
|
|
764
767
|
rail(_out)
|
|
765
|
-
rail_end(_out, f"next: nabla build{'' if args.repo == '.' else ' ' + args.repo}"
|
|
768
|
+
rail_end(_out, f"next: nabla build{'' if args.repo == '.' else ' ' + args.repo}"
|
|
769
|
+
" (site profile) · or: nabla jeremy (seed spec)")
|
|
766
770
|
return 1 if len(failed) == len(results) else 0
|
|
767
771
|
|
|
768
772
|
|
|
@@ -844,17 +848,11 @@ def _cmd_profile(args, mode: str = "profile") -> int:
|
|
|
844
848
|
|
|
845
849
|
|
|
846
850
|
def _compose_seed_goal(goal) -> str:
|
|
847
|
-
"""
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
]
|
|
853
|
-
if goal.constraints:
|
|
854
|
-
parts.append("Every generated problem's solution must satisfy: "
|
|
855
|
-
+ " ".join(c.rstrip(".") + "." for c in goal.constraints))
|
|
856
|
-
parts.append("Match the style, difficulty range, and output format of the "
|
|
857
|
-
"reference examples below.")
|
|
851
|
+
"""The seed spec's goal string: build's induced goal, rearranged into
|
|
852
|
+
prose — description followed by the constraints. Nothing is added that
|
|
853
|
+
build's own output doesn't contain."""
|
|
854
|
+
parts = [goal.description.rstrip(".") + "."]
|
|
855
|
+
parts += [c.rstrip(".") + "." for c in goal.constraints]
|
|
858
856
|
return " ".join(parts)
|
|
859
857
|
|
|
860
858
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nabla-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Call-site harvester: scan OpenAI call sites, record real traffic, emit site profiles
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -170,14 +170,15 @@ a problem generator:
|
|
|
170
170
|
|
|
171
171
|
```json
|
|
172
172
|
{
|
|
173
|
-
"goal": "
|
|
173
|
+
"goal": "<build's inferred goal description and constraints, joined into one string>",
|
|
174
174
|
"system_prompt": "<the site's own system prompt>",
|
|
175
175
|
"examples": [ {"problem": "...", "solution": "..."}, ... ]
|
|
176
176
|
}
|
|
177
177
|
```
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
Same pipeline as `build` (captured pairs in, one goal-inference call) —
|
|
180
|
+
only the output shape differs; nothing is added that `build`'s own
|
|
181
|
+
output doesn't contain.
|
|
181
182
|
|
|
182
183
|
## Providers
|
|
183
184
|
|
|
@@ -517,72 +517,71 @@ def test_scan_skips_build_and_dist_dirs(tmp_path, monkeypatch, capsys):
|
|
|
517
517
|
out = capsys.readouterr().out
|
|
518
518
|
assert "1 call site found" in out
|
|
519
519
|
assert "copy_of_site" not in out
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
# ---------------------------------------------------------------------------
|
|
523
|
-
# nabla jeremy — seed-spec sibling of build
|
|
524
|
-
# ---------------------------------------------------------------------------
|
|
525
|
-
|
|
526
|
-
def _fake_goal(*a, **k):
|
|
527
|
-
from nabla.contract import GoalBlock
|
|
528
|
-
|
|
529
|
-
return GoalBlock(
|
|
530
|
-
description="Solve a grade-school word problem step by step.",
|
|
531
|
-
constraints=["final answer is a bare number", "no invented constants"],
|
|
532
|
-
difficulty_dimensions=[{"name": "d", "levels": ["a", "b"], "evidence": "e"}],
|
|
533
|
-
)
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
def test_jeremy_single_site_writes_seed_spec(tmp_path, monkeypatch, capsys):
|
|
537
|
-
repo = _make_single_site_repo(tmp_path)
|
|
538
|
-
monkeypatch.chdir(tmp_path)
|
|
539
|
-
pair = {"input_slots": {"ocr_text": "r"}, "prompt": "A farmer has 6 crates...",
|
|
540
|
-
"completion": "The answer is 57.",
|
|
541
|
-
"tokens": {"in": 5, "out": 5}, "latency_ms": 10, "_source_kind": "samples"}
|
|
542
|
-
(tmp_path / "extract_receipt.captured.jsonl").write_text(
|
|
543
|
-
json.dumps(pair) + "\n", encoding="utf-8")
|
|
544
|
-
monkeypatch.setattr("nabla.induce.induce", _fake_goal)
|
|
545
|
-
|
|
546
|
-
rc = main(["jeremy", str(repo)])
|
|
547
|
-
|
|
548
|
-
assert rc == 0
|
|
549
|
-
seed = json.loads((tmp_path / "extract_receipt.jeremy.json").read_text(encoding="utf-8"))
|
|
550
|
-
assert set(seed) == {"goal", "system_prompt", "examples"}
|
|
551
|
-
assert seed["goal"].startswith("
|
|
552
|
-
assert "
|
|
553
|
-
assert "
|
|
554
|
-
assert "
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
assert
|
|
574
|
-
assert (tmp_path / "
|
|
575
|
-
assert (
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
assert "nabla
|
|
588
|
-
assert "nabla capture" in err
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
# ---------------------------------------------------------------------------
|
|
523
|
+
# nabla jeremy — seed-spec sibling of build
|
|
524
|
+
# ---------------------------------------------------------------------------
|
|
525
|
+
|
|
526
|
+
def _fake_goal(*a, **k):
|
|
527
|
+
from nabla.contract import GoalBlock
|
|
528
|
+
|
|
529
|
+
return GoalBlock(
|
|
530
|
+
description="Solve a grade-school word problem step by step.",
|
|
531
|
+
constraints=["final answer is a bare number", "no invented constants"],
|
|
532
|
+
difficulty_dimensions=[{"name": "d", "levels": ["a", "b"], "evidence": "e"}],
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
def test_jeremy_single_site_writes_seed_spec(tmp_path, monkeypatch, capsys):
|
|
537
|
+
repo = _make_single_site_repo(tmp_path)
|
|
538
|
+
monkeypatch.chdir(tmp_path)
|
|
539
|
+
pair = {"input_slots": {"ocr_text": "r"}, "prompt": "A farmer has 6 crates...",
|
|
540
|
+
"completion": "The answer is 57.",
|
|
541
|
+
"tokens": {"in": 5, "out": 5}, "latency_ms": 10, "_source_kind": "samples"}
|
|
542
|
+
(tmp_path / "extract_receipt.captured.jsonl").write_text(
|
|
543
|
+
json.dumps(pair) + "\n", encoding="utf-8")
|
|
544
|
+
monkeypatch.setattr("nabla.induce.induce", _fake_goal)
|
|
545
|
+
|
|
546
|
+
rc = main(["jeremy", str(repo)])
|
|
547
|
+
|
|
548
|
+
assert rc == 0
|
|
549
|
+
seed = json.loads((tmp_path / "extract_receipt.jeremy.json").read_text(encoding="utf-8"))
|
|
550
|
+
assert set(seed) == {"goal", "system_prompt", "examples"}
|
|
551
|
+
assert seed["goal"].startswith("Solve a grade-school word problem")
|
|
552
|
+
assert "final answer is a bare number" in seed["goal"]
|
|
553
|
+
assert "Extract the receipt fields" in seed["system_prompt"]
|
|
554
|
+
assert seed["examples"] == [
|
|
555
|
+
{"problem": "A farmer has 6 crates...", "solution": "The answer is 57."}
|
|
556
|
+
]
|
|
557
|
+
assert "extract_receipt.jeremy.json" in capsys.readouterr().out
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
def test_jeremy_multi_site(tmp_path, monkeypatch, capsys):
|
|
561
|
+
repo = _make_two_site_repo(tmp_path)
|
|
562
|
+
monkeypatch.chdir(tmp_path)
|
|
563
|
+
pair = {"input_slots": {}, "prompt": "p", "completion": "s",
|
|
564
|
+
"tokens": {"in": 1, "out": 1}, "latency_ms": 1, "_source_kind": "samples"}
|
|
565
|
+
for symbol in ("extract_receipt", "extract_menu"):
|
|
566
|
+
(tmp_path / f"{symbol}.captured.jsonl").write_text(
|
|
567
|
+
json.dumps(pair) + "\n", encoding="utf-8")
|
|
568
|
+
monkeypatch.setattr("nabla.induce.induce", _fake_goal)
|
|
569
|
+
|
|
570
|
+
rc = main(["jeremy", str(repo)])
|
|
571
|
+
|
|
572
|
+
assert rc == 0
|
|
573
|
+
assert (tmp_path / "extract_receipt.jeremy.json").exists()
|
|
574
|
+
assert (tmp_path / "extract_menu.jeremy.json").exists()
|
|
575
|
+
assert "built 2 seed spec(s)" in capsys.readouterr().out
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def test_jeremy_without_captures_points_at_capture(tmp_path, monkeypatch, capsys):
|
|
579
|
+
repo = _make_single_site_repo(tmp_path)
|
|
580
|
+
monkeypatch.chdir(tmp_path)
|
|
581
|
+
|
|
582
|
+
rc = main(["jeremy", str(repo)])
|
|
583
|
+
|
|
584
|
+
assert rc == 2
|
|
585
|
+
err = capsys.readouterr().err
|
|
586
|
+
assert "nabla jeremy" in err
|
|
587
|
+
assert "nabla capture" in err
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|