nabla-cli 0.5.2__tar.gz → 0.6.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.
Files changed (35) hide show
  1. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/PKG-INFO +10 -6
  2. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/README.md +227 -223
  3. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/__init__.py +1 -1
  4. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/__main__.py +8 -8
  5. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/branding.py +94 -94
  6. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/cli.py +864 -679
  7. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/config.py +113 -113
  8. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/contract.py +132 -132
  9. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/induce.py +176 -176
  10. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/profile.py +138 -138
  11. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/prompt_recon.py +542 -542
  12. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/recorder.py +621 -621
  13. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/samplegen.py +118 -118
  14. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/scanner.py +453 -453
  15. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/schema_resolver.py +309 -309
  16. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/schemas/site_profile.schema.json +136 -136
  17. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/term.py +53 -53
  18. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla/ui.py +269 -171
  19. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla_cli.egg-info/PKG-INFO +10 -6
  20. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/pyproject.toml +1 -1
  21. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/tests/test_cli_ux.py +406 -297
  22. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/tests/test_config.py +179 -179
  23. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/tests/test_e2e_profile.py +99 -99
  24. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/tests/test_induce.py +79 -79
  25. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/tests/test_prompt_recon.py +178 -178
  26. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/tests/test_recorder.py +279 -279
  27. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/tests/test_samplegen.py +81 -81
  28. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/tests/test_scanner.py +96 -96
  29. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/tests/test_schema_resolver.py +214 -214
  30. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla_cli.egg-info/SOURCES.txt +0 -0
  31. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla_cli.egg-info/dependency_links.txt +0 -0
  32. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla_cli.egg-info/entry_points.txt +0 -0
  33. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla_cli.egg-info/requires.txt +0 -0
  34. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/nabla_cli.egg-info/top_level.txt +0 -0
  35. {nabla_cli-0.5.2 → nabla_cli-0.6.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nabla-cli
3
- Version: 0.5.2
3
+ Version: 0.6.0
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
@@ -68,11 +68,15 @@ nabla build
68
68
  ```
69
69
 
70
70
  No flags needed on the happy path: the repo defaults to the current
71
- directory, `--site` is auto-picked when the repo has exactly one
72
- supported call site (with more than one, pass `--site <symbol>` — the
73
- names come from `scan`'s output), `capture` writes
74
- `<site>.captured.jsonl`, and `build` finds that file automatically by
75
- the same convention.
71
+ directory, `capture` writes `<site>.captured.jsonl` per site, and
72
+ `build` finds those files automatically by the same convention.
73
+
74
+ With one supported call site, `capture` and `build` pick it
75
+ automatically. With several, `capture` opens a multi-select (space
76
+ toggles a site, `a` selects all, enter runs) and captures the selected
77
+ sites **in parallel**, one progress bar each; `build` then produces a
78
+ profile for every site that has captured pairs. `--site <symbol>`
79
+ narrows any command to one site.
76
80
 
77
81
  If you don't pass `--inputs`, `capture` looks for
78
82
  `<site>.inputs.jsonl` and otherwise generates a handful of synthetic
@@ -1,223 +1,227 @@
1
- # nabla — OpenAI call-site harvester
2
-
3
- `nabla` finds the OpenAI call sites in a Python repo, records real
4
- (prompt, completion) traffic for one of them, and emits a single
5
- self-contained **site profile** (`site_profile.json`) describing that
6
- call site: prompt template + slots, resolved output JSON Schema,
7
- sampling parameters, recorded examples, an inferred goal with difficulty
8
- dimensions, and a cost/latency baseline.
9
-
10
- The profile is the handoff artifact for training a small, cheap
11
- replacement model for that one call site — everything downstream codes
12
- against the profile, never against your repo.
13
-
14
- ## Install
15
-
16
- ```bash
17
- pip install nabla-cli
18
- # or, isolated:
19
- pipx install nabla-cli
20
- ```
21
-
22
- Requires Python ≥ 3.11.
23
-
24
- ### `nabla` not found after `pip install`?
25
-
26
- That means pip's script directory isn't on your PATH (pip prints a
27
- yellow warning about this during install). Two fixes, either works:
28
-
29
- - Run it as a module instead — always works, no PATH needed:
30
-
31
- ```bash
32
- python -m nabla scan path/to/repo
33
- ```
34
-
35
- - Or add pip's script directory to PATH. Find it with
36
- `python -c "import sysconfig; print(sysconfig.get_path('scripts'))"`
37
- and add that folder to your PATH (on Windows: Settings → search
38
- "environment variables" → edit `Path` → add the folder, then open a
39
- new terminal).
40
-
41
- `pipx install nabla-cli` avoids the problem entirely if you have pipx.
42
-
43
- ## Quickstart
44
-
45
- Run from inside the target repo:
46
-
47
- ```bash
48
- pip install nabla-cli
49
-
50
- nabla init # one-time setup, once per machine
51
- nabla scan
52
- nabla capture
53
- nabla build
54
- ```
55
-
56
- No flags needed on the happy path: the repo defaults to the current
57
- directory, `--site` is auto-picked when the repo has exactly one
58
- supported call site (with more than one, pass `--site <symbol>` — the
59
- names come from `scan`'s output), `capture` writes
60
- `<site>.captured.jsonl`, and `build` finds that file automatically by
61
- the same convention.
62
-
63
- If you don't pass `--inputs`, `capture` looks for
64
- `<site>.inputs.jsonl` and otherwise generates a handful of synthetic
65
- inputs from the site's prompt template (saving them to that file so you
66
- can edit them and rerun with realistic data). Profiles built on
67
- generated inputs are marked `generated_samples` in their provenance —
68
- fine for a first look, but prefer real sample data for a profile you
69
- intend to hand off.
70
-
71
- ### `nabla init`
72
-
73
- Interactive, run once per machine:
74
-
75
- ```
76
- $ nabla init
77
- Pick a provider:
78
- 1) openai
79
- 2) groq
80
- 3) gemini
81
- > 2
82
- Paste your GROQ_API_KEY: ****************
83
- Saved to ~/.nabla/config.json
84
- ```
85
-
86
- That's the only credential nabla needs. Base URLs, models, and request
87
- pacing all come from a built-in preset for whichever provider you picked
88
- — see [Providers](#providers).
89
-
90
- ### `nabla scan`
91
-
92
- ```
93
- SYMBOL FILE KIND VERIFIABILITY FLAGS
94
- extract_invoice app/invoice_extract.py create json_schema -
95
-
96
- 1 call site found.
97
- ```
98
-
99
- ### `nabla capture`
100
-
101
- One progress line per sample, then a summary:
102
-
103
- ```
104
- nabla capture: sample 1/6 -> 1 capture(s)
105
- nabla capture: sample 2/6 -> 1 capture(s)
106
- ...
107
- captured 6 (prompt, completion) pairs from site extract_invoice -> extract_invoice.captured.jsonl
108
- ```
109
-
110
- `--out` defaults to `<site>.captured.jsonl` in the current directory (so
111
- `build` can find it by the same convention afterward) — pass `--out`
112
- yourself only to pick a different name or location.
113
-
114
- To record your own inputs instead of generated ones, pass `--inputs`
115
- (or create `<site>.inputs.jsonl`, which is picked up automatically): a
116
- JSONL file, one line per call to record. Each line is
117
- `{"input_slots": {...}}`, where the keys match the site function's
118
- keyword arguments (as reported by `scan`):
119
-
120
- ```json
121
- {"input_slots": {"document_text": "Invoice INV-1001 total $250.00 due 2026-08-01", "locale_hint": "en-US"}}
122
- {"input_slots": {"document_text": "Facture FR-552 montant 99,50 EUR", "locale_hint": "fr-FR"}}
123
- ```
124
-
125
- nabla runs the site's function once per line, in a subprocess, with its
126
- OpenAI client rerouted through a local recording proxy.
127
-
128
- ### `nabla build`
129
-
130
- ```
131
- nabla build: goal — "Extract structured invoice fields (total, currency,
132
- due date) from free-form invoice or receipt text, normalizing
133
- locale-specific number and date formats."
134
- nabla build: wrote extract_invoice.profile.json (site 4f2a9b1c3d8e…, supported=True, 6 examples)
135
- ```
136
-
137
- `--out` defaults to `<site>.profile.json` in the current directory.
138
- `--captured` is optional: `build` looks for `<site>.captured.jsonl` in
139
- the current directory — the file `capture` just wrote — and you only need
140
- `--captured <path>` to point it at a different recording. The one LLM
141
- call in this step is goal inference: it reads the reconstructed prompt
142
- template, the resolved output schema, and the recorded examples, and
143
- returns a goal description, implicit constraints, and difficulty
144
- dimensions with cited evidence. That's the goal line printed above, and
145
- it's what lands in the profile's `goal` block.
146
-
147
- ## Providers
148
-
149
- `nabla init` supports three providers. Pick whichever you have a key
150
- for — it doesn't need to be the provider your target repo already calls.
151
-
152
- | Provider | Default model | Role |
153
- |---|---|---|
154
- | `openai` | your repo's own model (goal inference uses `gpt-4o-mini`) | Real upstream — `capture` calls OpenAI directly with your key, auth passthrough. |
155
- | `groq` | `openai/gpt-oss-120b` | Stand-in oracle — `capture` calls Groq instead of OpenAI. |
156
- | `gemini` | `gemini-3.5-flash` | Stand-in oracle — `capture` calls Gemini instead of OpenAI. |
157
-
158
- When you pick `groq` or `gemini`, they only stand in for *generating* the
159
- completions during `capture`. The profile's cost/latency baseline always
160
- reports numbers for the target repo's own model the one your code
161
- actually calls in production — never the stand-in's.
162
-
163
- Gemini's free tier has a very low daily quota: expect `capture` to pace
164
- requests with multi-second delays and to still hit rate limits past a
165
- handful of samples. `groq` or `openai` handle larger sample sets more
166
- comfortably.
167
-
168
- ## What gets detected
169
-
170
- `scan` classifies every `chat.completions.create` / `beta...parse` call
171
- by verifiability: `json_schema` and `pydantic_parse` sites are supported
172
- harvest targets; `tool_call`, `json_object_no_schema`, and `free_text`
173
- sites are detected and reported but not harvested. Streaming,
174
- multi-turn, vision, and `n>1` sites are flagged unsupported. Generic
175
- wrapper functions are expanded to their callers; functions that own
176
- their prompt template are kept as the site.
177
-
178
- ## Advanced: overrides
179
-
180
- ### Flags
181
-
182
- Auto-detection is a default, not a requirement — the explicit flags still
183
- work and take priority over it:
184
-
185
- - `--site <symbol>` — skip site auto-pick; required once a repo has more
186
- than one supported call site.
187
- - `--out <path>` (`capture`) write recorded examples somewhere other than
188
- `<site>.captured.jsonl`.
189
- - `--captured <path>` (`build`) read recorded examples from somewhere
190
- other than `<site>.captured.jsonl`.
191
- - `--out <path>` (`build`) — write the profile somewhere other than
192
- `<site>.profile.json`.
193
-
194
- ### Environment variables
195
-
196
- Everything `nabla init` sets up can also be overridden by hand. Precedence,
197
- highest first:
198
-
199
- 1. An env var you set yourself
200
- 2. `~/.nabla/config.json` (written by `nabla init`)
201
- 3. The provider preset's built-in defaults
202
-
203
- | Variable | Purpose |
204
- |---|---|
205
- | `OPENAI_API_KEY` | Default upstream for `capture` (auth passthrough). |
206
- | `GROQ_API_KEY` / `GEMINI_API_KEY` | That provider's conventional key env, read if `~/.nabla/config.json` has no stored key. |
207
- | `NABLA_PROVIDER` | Overrides the provider `nabla init` selected. |
208
- | `NABLA_UPSTREAM_BASE_URL` | Record through any OpenAI-compatible stand-in oracle instead. |
209
- | `NABLA_UPSTREAM_API_KEY` | Key for the stand-in oracle. |
210
- | `NABLA_UPSTREAM_MODEL` | Rewrite the model for the stand-in only (the target repo's own model still lands in the profile). |
211
- | `NABLA_RECORD_DELAY_MS` | Pacing between samples for rate-limited upstreams (default 0). |
212
- | `NABLA_SAMPLE_TIMEOUT` | Per-sample subprocess timeout in seconds (default 300). |
213
- | `NABLA_INDUCE_BASE_URL` / `NABLA_INDUCE_API_KEY_ENV` / `NABLA_INDUCE_MODEL` | Swap the goal-inference provider/model. |
214
-
215
- ## Notes
216
-
217
- - `capture` runs the site's enclosing function in a subprocess per sample,
218
- with `OPENAI_BASE_URL` pointed at a local recording proxy. If the
219
- target's tests mock the SDK, the proxy path reports zero traffic
220
- explicitly instead of writing an empty profile.
221
- - Recorded prompts/completions are real data from your repo and leave
222
- the machine only via the goal-inference call. Review before sharing
223
- profiles.
1
+ # nabla — OpenAI call-site harvester
2
+
3
+ `nabla` finds the OpenAI call sites in a Python repo, records real
4
+ (prompt, completion) traffic for one of them, and emits a single
5
+ self-contained **site profile** (`site_profile.json`) describing that
6
+ call site: prompt template + slots, resolved output JSON Schema,
7
+ sampling parameters, recorded examples, an inferred goal with difficulty
8
+ dimensions, and a cost/latency baseline.
9
+
10
+ The profile is the handoff artifact for training a small, cheap
11
+ replacement model for that one call site — everything downstream codes
12
+ against the profile, never against your repo.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pip install nabla-cli
18
+ # or, isolated:
19
+ pipx install nabla-cli
20
+ ```
21
+
22
+ Requires Python ≥ 3.11.
23
+
24
+ ### `nabla` not found after `pip install`?
25
+
26
+ That means pip's script directory isn't on your PATH (pip prints a
27
+ yellow warning about this during install). Two fixes, either works:
28
+
29
+ - Run it as a module instead — always works, no PATH needed:
30
+
31
+ ```bash
32
+ python -m nabla scan path/to/repo
33
+ ```
34
+
35
+ - Or add pip's script directory to PATH. Find it with
36
+ `python -c "import sysconfig; print(sysconfig.get_path('scripts'))"`
37
+ and add that folder to your PATH (on Windows: Settings → search
38
+ "environment variables" → edit `Path` → add the folder, then open a
39
+ new terminal).
40
+
41
+ `pipx install nabla-cli` avoids the problem entirely if you have pipx.
42
+
43
+ ## Quickstart
44
+
45
+ Run from inside the target repo:
46
+
47
+ ```bash
48
+ pip install nabla-cli
49
+
50
+ nabla init # one-time setup, once per machine
51
+ nabla scan
52
+ nabla capture
53
+ nabla build
54
+ ```
55
+
56
+ No flags needed on the happy path: the repo defaults to the current
57
+ directory, `capture` writes `<site>.captured.jsonl` per site, and
58
+ `build` finds those files automatically by the same convention.
59
+
60
+ With one supported call site, `capture` and `build` pick it
61
+ automatically. With several, `capture` opens a multi-select (space
62
+ toggles a site, `a` selects all, enter runs) and captures the selected
63
+ sites **in parallel**, one progress bar each; `build` then produces a
64
+ profile for every site that has captured pairs. `--site <symbol>`
65
+ narrows any command to one site.
66
+
67
+ If you don't pass `--inputs`, `capture` looks for
68
+ `<site>.inputs.jsonl` and otherwise generates a handful of synthetic
69
+ inputs from the site's prompt template (saving them to that file so you
70
+ can edit them and rerun with realistic data). Profiles built on
71
+ generated inputs are marked `generated_samples` in their provenance —
72
+ fine for a first look, but prefer real sample data for a profile you
73
+ intend to hand off.
74
+
75
+ ### `nabla init`
76
+
77
+ Interactive, run once per machine:
78
+
79
+ ```
80
+ $ nabla init
81
+ Pick a provider:
82
+ 1) openai
83
+ 2) groq
84
+ 3) gemini
85
+ > 2
86
+ Paste your GROQ_API_KEY: ****************
87
+ Saved to ~/.nabla/config.json
88
+ ```
89
+
90
+ That's the only credential nabla needs. Base URLs, models, and request
91
+ pacing all come from a built-in preset for whichever provider you picked
92
+ — see [Providers](#providers).
93
+
94
+ ### `nabla scan`
95
+
96
+ ```
97
+ SYMBOL FILE KIND VERIFIABILITY FLAGS
98
+ extract_invoice app/invoice_extract.py create json_schema -
99
+
100
+ 1 call site found.
101
+ ```
102
+
103
+ ### `nabla capture`
104
+
105
+ One progress line per sample, then a summary:
106
+
107
+ ```
108
+ nabla capture: sample 1/6 -> 1 capture(s)
109
+ nabla capture: sample 2/6 -> 1 capture(s)
110
+ ...
111
+ captured 6 (prompt, completion) pairs from site extract_invoice -> extract_invoice.captured.jsonl
112
+ ```
113
+
114
+ `--out` defaults to `<site>.captured.jsonl` in the current directory (so
115
+ `build` can find it by the same convention afterward) — pass `--out`
116
+ yourself only to pick a different name or location.
117
+
118
+ To record your own inputs instead of generated ones, pass `--inputs`
119
+ (or create `<site>.inputs.jsonl`, which is picked up automatically): a
120
+ JSONL file, one line per call to record. Each line is
121
+ `{"input_slots": {...}}`, where the keys match the site function's
122
+ keyword arguments (as reported by `scan`):
123
+
124
+ ```json
125
+ {"input_slots": {"document_text": "Invoice INV-1001 total $250.00 due 2026-08-01", "locale_hint": "en-US"}}
126
+ {"input_slots": {"document_text": "Facture FR-552 montant 99,50 EUR", "locale_hint": "fr-FR"}}
127
+ ```
128
+
129
+ nabla runs the site's function once per line, in a subprocess, with its
130
+ OpenAI client rerouted through a local recording proxy.
131
+
132
+ ### `nabla build`
133
+
134
+ ```
135
+ nabla build: goal — "Extract structured invoice fields (total, currency,
136
+ due date) from free-form invoice or receipt text, normalizing
137
+ locale-specific number and date formats."
138
+ nabla build: wrote extract_invoice.profile.json (site 4f2a9b1c3d8e…, supported=True, 6 examples)
139
+ ```
140
+
141
+ `--out` defaults to `<site>.profile.json` in the current directory.
142
+ `--captured` is optional: `build` looks for `<site>.captured.jsonl` in
143
+ the current directory the file `capture` just wrote — and you only need
144
+ `--captured <path>` to point it at a different recording. The one LLM
145
+ call in this step is goal inference: it reads the reconstructed prompt
146
+ template, the resolved output schema, and the recorded examples, and
147
+ returns a goal description, implicit constraints, and difficulty
148
+ dimensions with cited evidence. That's the goal line printed above, and
149
+ it's what lands in the profile's `goal` block.
150
+
151
+ ## Providers
152
+
153
+ `nabla init` supports three providers. Pick whichever you have a key
154
+ for it doesn't need to be the provider your target repo already calls.
155
+
156
+ | Provider | Default model | Role |
157
+ |---|---|---|
158
+ | `openai` | your repo's own model (goal inference uses `gpt-4o-mini`) | Real upstream — `capture` calls OpenAI directly with your key, auth passthrough. |
159
+ | `groq` | `openai/gpt-oss-120b` | Stand-in oracle — `capture` calls Groq instead of OpenAI. |
160
+ | `gemini` | `gemini-3.5-flash` | Stand-in oracle`capture` calls Gemini instead of OpenAI. |
161
+
162
+ When you pick `groq` or `gemini`, they only stand in for *generating* the
163
+ completions during `capture`. The profile's cost/latency baseline always
164
+ reports numbers for the target repo's own model the one your code
165
+ actually calls in production never the stand-in's.
166
+
167
+ Gemini's free tier has a very low daily quota: expect `capture` to pace
168
+ requests with multi-second delays and to still hit rate limits past a
169
+ handful of samples. `groq` or `openai` handle larger sample sets more
170
+ comfortably.
171
+
172
+ ## What gets detected
173
+
174
+ `scan` classifies every `chat.completions.create` / `beta...parse` call
175
+ by verifiability: `json_schema` and `pydantic_parse` sites are supported
176
+ harvest targets; `tool_call`, `json_object_no_schema`, and `free_text`
177
+ sites are detected and reported but not harvested. Streaming,
178
+ multi-turn, vision, and `n>1` sites are flagged unsupported. Generic
179
+ wrapper functions are expanded to their callers; functions that own
180
+ their prompt template are kept as the site.
181
+
182
+ ## Advanced: overrides
183
+
184
+ ### Flags
185
+
186
+ Auto-detection is a default, not a requirement — the explicit flags still
187
+ work and take priority over it:
188
+
189
+ - `--site <symbol>` — skip site auto-pick; required once a repo has more
190
+ than one supported call site.
191
+ - `--out <path>` (`capture`) — write recorded examples somewhere other than
192
+ `<site>.captured.jsonl`.
193
+ - `--captured <path>` (`build`) — read recorded examples from somewhere
194
+ other than `<site>.captured.jsonl`.
195
+ - `--out <path>` (`build`) — write the profile somewhere other than
196
+ `<site>.profile.json`.
197
+
198
+ ### Environment variables
199
+
200
+ Everything `nabla init` sets up can also be overridden by hand. Precedence,
201
+ highest first:
202
+
203
+ 1. An env var you set yourself
204
+ 2. `~/.nabla/config.json` (written by `nabla init`)
205
+ 3. The provider preset's built-in defaults
206
+
207
+ | Variable | Purpose |
208
+ |---|---|
209
+ | `OPENAI_API_KEY` | Default upstream for `capture` (auth passthrough). |
210
+ | `GROQ_API_KEY` / `GEMINI_API_KEY` | That provider's conventional key env, read if `~/.nabla/config.json` has no stored key. |
211
+ | `NABLA_PROVIDER` | Overrides the provider `nabla init` selected. |
212
+ | `NABLA_UPSTREAM_BASE_URL` | Record through any OpenAI-compatible stand-in oracle instead. |
213
+ | `NABLA_UPSTREAM_API_KEY` | Key for the stand-in oracle. |
214
+ | `NABLA_UPSTREAM_MODEL` | Rewrite the model for the stand-in only (the target repo's own model still lands in the profile). |
215
+ | `NABLA_RECORD_DELAY_MS` | Pacing between samples for rate-limited upstreams (default 0). |
216
+ | `NABLA_SAMPLE_TIMEOUT` | Per-sample subprocess timeout in seconds (default 300). |
217
+ | `NABLA_INDUCE_BASE_URL` / `NABLA_INDUCE_API_KEY_ENV` / `NABLA_INDUCE_MODEL` | Swap the goal-inference provider/model. |
218
+
219
+ ## Notes
220
+
221
+ - `capture` runs the site's enclosing function in a subprocess per sample,
222
+ with `OPENAI_BASE_URL` pointed at a local recording proxy. If the
223
+ target's tests mock the SDK, the proxy path reports zero traffic
224
+ explicitly instead of writing an empty profile.
225
+ - Recorded prompts/completions are real data from your repo and leave
226
+ the machine only via the goal-inference call. Review before sharing
227
+ profiles.
@@ -1,3 +1,3 @@
1
1
  """nabla — call-site harvester CLI (pre-Phase-0)."""
2
2
 
3
- __version__ = "0.5.2"
3
+ __version__ = "0.6.0"
@@ -1,8 +1,8 @@
1
- """Allow `python -m nabla` — works even when pip's Scripts dir isn't on PATH."""
2
-
3
- import sys
4
-
5
- from .cli import main
6
-
7
- if __name__ == "__main__":
8
- sys.exit(main())
1
+ """Allow `python -m nabla` — works even when pip's Scripts dir isn't on PATH."""
2
+
3
+ import sys
4
+
5
+ from .cli import main
6
+
7
+ if __name__ == "__main__":
8
+ sys.exit(main())