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.
Files changed (35) hide show
  1. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/PKG-INFO +5 -4
  2. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/README.md +4 -3
  3. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/__init__.py +1 -1
  4. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/cli.py +15 -17
  5. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla_cli.egg-info/PKG-INFO +5 -4
  6. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/pyproject.toml +1 -1
  7. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_cli_ux.py +68 -69
  8. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/__main__.py +0 -0
  9. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/branding.py +0 -0
  10. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/config.py +0 -0
  11. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/contract.py +0 -0
  12. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/induce.py +0 -0
  13. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/profile.py +0 -0
  14. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/prompt_recon.py +0 -0
  15. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/recorder.py +0 -0
  16. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/samplegen.py +0 -0
  17. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/scanner.py +0 -0
  18. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/schema_resolver.py +0 -0
  19. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/schemas/site_profile.schema.json +0 -0
  20. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/term.py +0 -0
  21. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla/ui.py +0 -0
  22. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla_cli.egg-info/SOURCES.txt +0 -0
  23. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla_cli.egg-info/dependency_links.txt +0 -0
  24. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla_cli.egg-info/entry_points.txt +0 -0
  25. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla_cli.egg-info/requires.txt +0 -0
  26. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/nabla_cli.egg-info/top_level.txt +0 -0
  27. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/setup.cfg +0 -0
  28. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_config.py +0 -0
  29. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_e2e_profile.py +0 -0
  30. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_induce.py +0 -0
  31. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_prompt_recon.py +0 -0
  32. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_recorder.py +0 -0
  33. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_samplegen.py +0 -0
  34. {nabla_cli-0.7.0 → nabla_cli-0.7.2}/tests/test_scanner.py +0 -0
  35. {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.0
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": "You are generating new problems to test and eventually fine-tune a small student model on the following task: ...",
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
- The goal text is composed deterministically from the same inferred goal
180
- and constraints that `build` uses; the examples are the captured pairs.
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": "You are generating new problems to test and eventually fine-tune a small student model on the following task: ...",
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
- The goal text is composed deterministically from the same inferred goal
166
- and constraints that `build` uses; the examples are the captured pairs.
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
 
@@ -1,3 +1,3 @@
1
1
  """nabla — call-site harvester CLI (pre-Phase-0)."""
2
2
 
3
- __version__ = "0.7.0"
3
+ __version__ = "0.7.2"
@@ -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 <site>.jeremy.json —\n"
141
- "a generator-framed goal, the site's system prompt, and the captured\n"
142
- "pairs as {problem, solution} reference examples.\n\n"
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
- """Generator-framed goal text for the seed spec, composed in pure Python
848
- from the induced GoalBlock no extra LLM call, no invented facts."""
849
- parts = [
850
- "You are generating new problems to test and eventually fine-tune a "
851
- "small student model on the following task: " + goal.description.rstrip(".") + "."
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.0
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": "You are generating new problems to test and eventually fine-tune a small student model on the following task: ...",
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
- The goal text is composed deterministically from the same inferred goal
180
- and constraints that `build` uses; the examples are the captured pairs.
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
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nabla-cli"
3
- version = "0.7.0"
3
+ version = "0.7.2"
4
4
  description = "Call-site harvester: scan OpenAI call sites, record real traffic, emit site profiles"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -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("You are generating new problems")
552
- assert "grade-school word problem" in seed["goal"]
553
- assert "final answer is a bare number" in seed["goal"]
554
- assert "Extract the receipt fields" in seed["system_prompt"]
555
- assert seed["examples"] == [
556
- {"problem": "A farmer has 6 crates...", "solution": "The answer is 57."}
557
- ]
558
- assert "extract_receipt.jeremy.json" in capsys.readouterr().out
559
-
560
-
561
- def test_jeremy_multi_site(tmp_path, monkeypatch, capsys):
562
- repo = _make_two_site_repo(tmp_path)
563
- monkeypatch.chdir(tmp_path)
564
- pair = {"input_slots": {}, "prompt": "p", "completion": "s",
565
- "tokens": {"in": 1, "out": 1}, "latency_ms": 1, "_source_kind": "samples"}
566
- for symbol in ("extract_receipt", "extract_menu"):
567
- (tmp_path / f"{symbol}.captured.jsonl").write_text(
568
- json.dumps(pair) + "\n", encoding="utf-8")
569
- monkeypatch.setattr("nabla.induce.induce", _fake_goal)
570
-
571
- rc = main(["jeremy", str(repo)])
572
-
573
- assert rc == 0
574
- assert (tmp_path / "extract_receipt.jeremy.json").exists()
575
- assert (tmp_path / "extract_menu.jeremy.json").exists()
576
- assert "built 2 seed spec(s)" in capsys.readouterr().out
577
-
578
-
579
- def test_jeremy_without_captures_points_at_capture(tmp_path, monkeypatch, capsys):
580
- repo = _make_single_site_repo(tmp_path)
581
- monkeypatch.chdir(tmp_path)
582
-
583
- rc = main(["jeremy", str(repo)])
584
-
585
- assert rc == 2
586
- err = capsys.readouterr().err
587
- assert "nabla jeremy" in err
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