parseforge 0.2.2__tar.gz → 0.2.3__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 (48) hide show
  1. parseforge-0.2.3/PKG-INFO +208 -0
  2. parseforge-0.2.3/README.md +167 -0
  3. {parseforge-0.2.2 → parseforge-0.2.3}/SPEC.md +53 -45
  4. parseforge-0.2.3/parseforge/__init__.py +1 -0
  5. parseforge-0.2.3/parseforge/cli/config.py +146 -0
  6. parseforge-0.2.3/parseforge/cli/main.py +787 -0
  7. parseforge-0.2.3/parseforge/drift.py +141 -0
  8. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/paths.py +9 -0
  9. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/pipeline.py +1 -0
  10. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/promotion.py +3 -10
  11. parseforge-0.2.3/parseforge.egg-info/PKG-INFO +208 -0
  12. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge.egg-info/SOURCES.txt +2 -0
  13. {parseforge-0.2.2 → parseforge-0.2.3}/pyproject.toml +1 -1
  14. parseforge-0.2.2/PKG-INFO +0 -83
  15. parseforge-0.2.2/README.md +0 -42
  16. parseforge-0.2.2/parseforge/__init__.py +0 -1
  17. parseforge-0.2.2/parseforge/cli/main.py +0 -202
  18. parseforge-0.2.2/parseforge.egg-info/PKG-INFO +0 -83
  19. {parseforge-0.2.2 → parseforge-0.2.3}/LICENSE +0 -0
  20. {parseforge-0.2.2 → parseforge-0.2.3}/MANIFEST.in +0 -0
  21. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/cli/__init__.py +0 -0
  22. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/generation.py +0 -0
  23. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/integration.py +0 -0
  24. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/__init__.py +0 -0
  25. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/assemble.py +0 -0
  26. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/cache.py +0 -0
  27. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/llm.py +0 -0
  28. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/prompts.py +0 -0
  29. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/prompts.yaml +0 -0
  30. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/providers/__init__.py +0 -0
  31. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/providers/anthropic.py +0 -0
  32. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/providers/cost.py +0 -0
  33. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/providers/deepseek.py +0 -0
  34. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/providers/errors.py +0 -0
  35. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/providers/models.py +0 -0
  36. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/providers/models.yaml +0 -0
  37. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/providers/text.py +0 -0
  38. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/naming/resolver.py +0 -0
  39. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/sampling/__init__.py +0 -0
  40. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/sampling/backends/__init__.py +0 -0
  41. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/sampling/backends/netmiko.py +0 -0
  42. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/sampling/core.py +0 -0
  43. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge/validation.py +0 -0
  44. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge.egg-info/dependency_links.txt +0 -0
  45. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge.egg-info/entry_points.txt +0 -0
  46. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge.egg-info/requires.txt +0 -0
  47. {parseforge-0.2.2 → parseforge-0.2.3}/parseforge.egg-info/top_level.txt +0 -0
  48. {parseforge-0.2.2 → parseforge-0.2.3}/setup.cfg +0 -0
@@ -0,0 +1,208 @@
1
+ Metadata-Version: 2.4
2
+ Name: parseforge
3
+ Version: 0.2.3
4
+ Summary: LLM-driven pipeline that forges, validates, and promotes TextFSM templates from network CLI output
5
+ Author-email: Tuyen Mathew Duong <tuyen@geekstrident.com>
6
+ Maintainer-email: Tuyen Mathew Duong <tuyen@geekstrident.com>
7
+ License: MIT
8
+ Keywords: textfsm,network automation,cli parsing,template generator,ai,llm,netmiko,device output parsing
9
+ Classifier: Development Status :: 2 - Pre-Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: Information Technology
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Libraries
19
+ Classifier: Topic :: Software Development :: Code Generators
20
+ Classifier: Topic :: Text Processing
21
+ Classifier: Topic :: Utilities
22
+ Classifier: Operating System :: OS Independent
23
+ Requires-Python: >=3.9
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: click>=8.1
27
+ Requires-Dist: PyYAML>=6.0
28
+ Requires-Dist: textfsm>=1.1.0
29
+ Requires-Dist: anthropic>=0.40.0
30
+ Requires-Dist: openai>=1.0.0
31
+ Requires-Dist: textfsm-ai>=0.5.1
32
+ Provides-Extra: sampling
33
+ Requires-Dist: netmiko>=4.0; extra == "sampling"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
36
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
37
+ Requires-Dist: ruff>=0.4.0; extra == "dev"
38
+ Requires-Dist: black>=24.0.0; extra == "dev"
39
+ Requires-Dist: mypy>=1.10.0; extra == "dev"
40
+ Dynamic: license-file
41
+
42
+ # ParseForge
43
+
44
+ LLM-driven pipeline that forges, cross-validates, and promotes [TextFSM](https://github.com/google/textfsm)
45
+ templates from network device CLI output.
46
+
47
+ Full design plan: [SPEC.md](SPEC.md).
48
+
49
+ ## Status
50
+
51
+ Early beta. The full pipeline is implemented and tested end to end — naming,
52
+ sampling, generation, self-validation, integration (output-schema group/variant
53
+ clustering), promotion (auto and human-reviewed), and drift monitoring — and
54
+ wired into the CLI below. A few things are intentionally not there yet:
55
+
56
+ - **`USER_REVIEWED` promotion has a library entry point but no CLI command**
57
+ (`promotion.promote_user_reviewed()` works today; there's no
58
+ `parseforge promotion --mode user-reviewed` yet). Deferred until real
59
+ human-reviewed cases exist to show what a CLI/config shape for a list of
60
+ case/suffix/gate requests should actually look like, rather than guessing
61
+ ahead of need.
62
+ - **Batch sampling mode** (collect several samples per command before
63
+ generating, SPEC.md §4) is designed but not built — the simpler
64
+ per-command loop mode is the only one implemented.
65
+ - **One sampling connector** (Netmiko/SSH). The CLI's `--connector` registry
66
+ is built to hold more without a redesign, but nothing else is wired in yet.
67
+
68
+ ## Development
69
+
70
+ ```
71
+ pip install -e ".[dev,sampling]"
72
+ pytest
73
+ ```
74
+
75
+ ## CLI
76
+
77
+ Three kinds of commands: single lookups (`name`, `check`), one-shot inspection with
78
+ no persistence (`generate-template`, `canonical`/`readable`/`recognizers`), and the
79
+ config-driven `trial` → `integration` → `promotion` workflow that runs the full
80
+ pipeline end to end (see [Quickstart: end to end](#quickstart-end-to-end) below).
81
+
82
+ **Naming** — resolve a raw CLI command to its canonical cli-name (cached after the
83
+ first call, per SPEC §2):
84
+ ```
85
+ parseforge name --vendor cisco --family catalyst9200 --os ios-xe --version 17.9.1 \
86
+ show interface GE1.1 status
87
+ ```
88
+ Still needs an LLM provider on a cache miss — `--provider` defaults to `anthropic`,
89
+ and `--api-key` falls back to that provider's own env var (`ANTHROPIC_API_KEY`/
90
+ `DEEPSEEK_API_KEY`). A cache hit (a command already seen before) never touches the
91
+ LLM, so no key is needed at all in that case.
92
+
93
+ **`check`** — validate a connector or provider before spending time/tokens on a real
94
+ run. With neither `--env` nor explicit connection flags, prints what a connector needs
95
+ instead of attempting a connection:
96
+ ```
97
+ parseforge check --connector netmiko --env cisco
98
+ parseforge check --provider anthropic
99
+ ```
100
+ `--env=<name>` reads `<NAME>_SANDBOX_HOST/USERNAME/PASSWORD/DEVICE_TYPE` from the
101
+ environment (the same convention the test suite uses for `CISCO_SANDBOX_*`).
102
+
103
+ **`generate-template`** — one-shot template generation, no trial persisted under
104
+ `trials/`:
105
+ ```
106
+ parseforge generate-template --sample-file sample.txt \
107
+ --provider anthropic --api-key $ANTHROPIC_API_KEY --model claude-haiku-4-5-20251001
108
+ ```
109
+ Also accepts `--connector`/`--cmdline` (live sample) or `--config <file>` in place of
110
+ `--sample-file`; add `--out <dir>` to also write `template.textfsm`/`readable-dsl.txt`/
111
+ `recognizers.txt` to disk. `parseforge init-generate-template-config [--out <file>]`
112
+ writes a placeholder for that `--config` file, ready to fill in.
113
+
114
+ **`canonical` / `readable` / `recognizers`** — inspect an existing template file. No
115
+ LLM call; `--sample` is required to build the example records `textfsm-ai`'s DSL
116
+ compiler needs:
117
+ ```
118
+ parseforge canonical template.textfsm --sample sample.txt
119
+ parseforge readable template.textfsm --sample sample.txt
120
+ parseforge recognizers template.textfsm --sample sample.txt
121
+ ```
122
+
123
+ **`run`** — a single full trial (sample -> generate -> self-validate), SPEC §5 steps
124
+ 1-7:
125
+ ```
126
+ parseforge run --vendor cisco --family catalyst9200 --os ios-xe --version 17.9.1 \
127
+ --host 10.0.0.1 --username admin --device-type cisco_ios \
128
+ --provider anthropic --api-key $ANTHROPIC_API_KEY \
129
+ --model claude-haiku-4-5-20251001 \
130
+ show clock
131
+ ```
132
+ `--provider`/`--api-key`/`--model` are for generation. Naming has its own separate
133
+ `--naming-provider`/`--naming-api-key`/`--naming-model`, defaulting independently
134
+ (`--naming-provider` defaults to `anthropic`; the other two fall back to that
135
+ provider's own env var/default model) — set them explicitly if naming needs a
136
+ different provider than generation.
137
+
138
+ **`init-trial-config`** — write a placeholder `trial.yaml` to fill in, instead of
139
+ writing one by hand:
140
+ ```
141
+ parseforge init-trial-config --out trial.yaml
142
+ ```
143
+
144
+ **`trial`** — every command in a YAML config file, optionally in parallel:
145
+ ```
146
+ parseforge trial --config trial.yaml
147
+ ```
148
+ ```yaml
149
+ vendor: cisco
150
+ family: catalyst9200
151
+ os: ios-xe
152
+ version: 17.9.1
153
+ connector: netmiko
154
+ host: 10.0.0.1
155
+ username: admin
156
+ password: secret
157
+ device_type: cisco_ios
158
+ provider: anthropic
159
+ api_key: sk-...
160
+ model: claude-haiku-4-5-20251001
161
+ commands:
162
+ - show clock
163
+ - show version
164
+ user: alice
165
+ workers: 1
166
+ ```
167
+ `provider`/`api_key`/`model` are one shared LLM source used for both naming and
168
+ generation. Use the `run` command's separate `--naming-*`/`--generation-*` flags
169
+ instead if a trial actually needs two different providers.
170
+
171
+ **`integration`** — rebuild `integration/` for every case under `trials/` (SPEC §5
172
+ step 8):
173
+ ```
174
+ parseforge integration
175
+ ```
176
+
177
+ **`promotion`** — auto-promote every group that clears its gate (SPEC §5 step 9;
178
+ `AUTO_PROMOTED` mode only — `USER_REVIEWED` has no CLI surface yet):
179
+ ```
180
+ parseforge promotion --user alice --threshold 1.0 --min-samples 1
181
+ ```
182
+
183
+ `trial`, `integration`, and `promotion` all default to `paths.DEFAULT_STORE_ROOT`
184
+ (`~/.parseforge/tests`); pass `--path <dir>` to point at a different store root.
185
+
186
+ ### Quickstart: end to end
187
+
188
+ Run `trial` once per device/command batch (repeat as more devices or commands come
189
+ in — each run only adds new evidence, it never discards prior trials), then
190
+ `integration` and `promotion` any time you want the current evidence reflected in
191
+ `authoritative/`:
192
+
193
+ ```bash
194
+ parseforge trial --config trial.yaml
195
+ parseforge integration
196
+ parseforge promotion --user alice
197
+ ```
198
+
199
+ `integration` rebuilds `integration/reference-summary.json` from every trial
200
+ currently on disk, and `promotion` always refreshes integration itself before
201
+ evaluating any gate — so running `promotion` alone after a `trial` run is enough to
202
+ pick up new evidence; a separate `integration` run is only useful if you want to
203
+ inspect `reference-summary.json` without also promoting.
204
+
205
+ ## Reference
206
+
207
+ - [Documentation site](https://geeks-trident-llc.github.io/parseforge/) ([source](./docs/index.md))
208
+ - [SPEC.md](SPEC.md) — full design plan and open questions
@@ -0,0 +1,167 @@
1
+ # ParseForge
2
+
3
+ LLM-driven pipeline that forges, cross-validates, and promotes [TextFSM](https://github.com/google/textfsm)
4
+ templates from network device CLI output.
5
+
6
+ Full design plan: [SPEC.md](SPEC.md).
7
+
8
+ ## Status
9
+
10
+ Early beta. The full pipeline is implemented and tested end to end — naming,
11
+ sampling, generation, self-validation, integration (output-schema group/variant
12
+ clustering), promotion (auto and human-reviewed), and drift monitoring — and
13
+ wired into the CLI below. A few things are intentionally not there yet:
14
+
15
+ - **`USER_REVIEWED` promotion has a library entry point but no CLI command**
16
+ (`promotion.promote_user_reviewed()` works today; there's no
17
+ `parseforge promotion --mode user-reviewed` yet). Deferred until real
18
+ human-reviewed cases exist to show what a CLI/config shape for a list of
19
+ case/suffix/gate requests should actually look like, rather than guessing
20
+ ahead of need.
21
+ - **Batch sampling mode** (collect several samples per command before
22
+ generating, SPEC.md §4) is designed but not built — the simpler
23
+ per-command loop mode is the only one implemented.
24
+ - **One sampling connector** (Netmiko/SSH). The CLI's `--connector` registry
25
+ is built to hold more without a redesign, but nothing else is wired in yet.
26
+
27
+ ## Development
28
+
29
+ ```
30
+ pip install -e ".[dev,sampling]"
31
+ pytest
32
+ ```
33
+
34
+ ## CLI
35
+
36
+ Three kinds of commands: single lookups (`name`, `check`), one-shot inspection with
37
+ no persistence (`generate-template`, `canonical`/`readable`/`recognizers`), and the
38
+ config-driven `trial` → `integration` → `promotion` workflow that runs the full
39
+ pipeline end to end (see [Quickstart: end to end](#quickstart-end-to-end) below).
40
+
41
+ **Naming** — resolve a raw CLI command to its canonical cli-name (cached after the
42
+ first call, per SPEC §2):
43
+ ```
44
+ parseforge name --vendor cisco --family catalyst9200 --os ios-xe --version 17.9.1 \
45
+ show interface GE1.1 status
46
+ ```
47
+ Still needs an LLM provider on a cache miss — `--provider` defaults to `anthropic`,
48
+ and `--api-key` falls back to that provider's own env var (`ANTHROPIC_API_KEY`/
49
+ `DEEPSEEK_API_KEY`). A cache hit (a command already seen before) never touches the
50
+ LLM, so no key is needed at all in that case.
51
+
52
+ **`check`** — validate a connector or provider before spending time/tokens on a real
53
+ run. With neither `--env` nor explicit connection flags, prints what a connector needs
54
+ instead of attempting a connection:
55
+ ```
56
+ parseforge check --connector netmiko --env cisco
57
+ parseforge check --provider anthropic
58
+ ```
59
+ `--env=<name>` reads `<NAME>_SANDBOX_HOST/USERNAME/PASSWORD/DEVICE_TYPE` from the
60
+ environment (the same convention the test suite uses for `CISCO_SANDBOX_*`).
61
+
62
+ **`generate-template`** — one-shot template generation, no trial persisted under
63
+ `trials/`:
64
+ ```
65
+ parseforge generate-template --sample-file sample.txt \
66
+ --provider anthropic --api-key $ANTHROPIC_API_KEY --model claude-haiku-4-5-20251001
67
+ ```
68
+ Also accepts `--connector`/`--cmdline` (live sample) or `--config <file>` in place of
69
+ `--sample-file`; add `--out <dir>` to also write `template.textfsm`/`readable-dsl.txt`/
70
+ `recognizers.txt` to disk. `parseforge init-generate-template-config [--out <file>]`
71
+ writes a placeholder for that `--config` file, ready to fill in.
72
+
73
+ **`canonical` / `readable` / `recognizers`** — inspect an existing template file. No
74
+ LLM call; `--sample` is required to build the example records `textfsm-ai`'s DSL
75
+ compiler needs:
76
+ ```
77
+ parseforge canonical template.textfsm --sample sample.txt
78
+ parseforge readable template.textfsm --sample sample.txt
79
+ parseforge recognizers template.textfsm --sample sample.txt
80
+ ```
81
+
82
+ **`run`** — a single full trial (sample -> generate -> self-validate), SPEC §5 steps
83
+ 1-7:
84
+ ```
85
+ parseforge run --vendor cisco --family catalyst9200 --os ios-xe --version 17.9.1 \
86
+ --host 10.0.0.1 --username admin --device-type cisco_ios \
87
+ --provider anthropic --api-key $ANTHROPIC_API_KEY \
88
+ --model claude-haiku-4-5-20251001 \
89
+ show clock
90
+ ```
91
+ `--provider`/`--api-key`/`--model` are for generation. Naming has its own separate
92
+ `--naming-provider`/`--naming-api-key`/`--naming-model`, defaulting independently
93
+ (`--naming-provider` defaults to `anthropic`; the other two fall back to that
94
+ provider's own env var/default model) — set them explicitly if naming needs a
95
+ different provider than generation.
96
+
97
+ **`init-trial-config`** — write a placeholder `trial.yaml` to fill in, instead of
98
+ writing one by hand:
99
+ ```
100
+ parseforge init-trial-config --out trial.yaml
101
+ ```
102
+
103
+ **`trial`** — every command in a YAML config file, optionally in parallel:
104
+ ```
105
+ parseforge trial --config trial.yaml
106
+ ```
107
+ ```yaml
108
+ vendor: cisco
109
+ family: catalyst9200
110
+ os: ios-xe
111
+ version: 17.9.1
112
+ connector: netmiko
113
+ host: 10.0.0.1
114
+ username: admin
115
+ password: secret
116
+ device_type: cisco_ios
117
+ provider: anthropic
118
+ api_key: sk-...
119
+ model: claude-haiku-4-5-20251001
120
+ commands:
121
+ - show clock
122
+ - show version
123
+ user: alice
124
+ workers: 1
125
+ ```
126
+ `provider`/`api_key`/`model` are one shared LLM source used for both naming and
127
+ generation. Use the `run` command's separate `--naming-*`/`--generation-*` flags
128
+ instead if a trial actually needs two different providers.
129
+
130
+ **`integration`** — rebuild `integration/` for every case under `trials/` (SPEC §5
131
+ step 8):
132
+ ```
133
+ parseforge integration
134
+ ```
135
+
136
+ **`promotion`** — auto-promote every group that clears its gate (SPEC §5 step 9;
137
+ `AUTO_PROMOTED` mode only — `USER_REVIEWED` has no CLI surface yet):
138
+ ```
139
+ parseforge promotion --user alice --threshold 1.0 --min-samples 1
140
+ ```
141
+
142
+ `trial`, `integration`, and `promotion` all default to `paths.DEFAULT_STORE_ROOT`
143
+ (`~/.parseforge/tests`); pass `--path <dir>` to point at a different store root.
144
+
145
+ ### Quickstart: end to end
146
+
147
+ Run `trial` once per device/command batch (repeat as more devices or commands come
148
+ in — each run only adds new evidence, it never discards prior trials), then
149
+ `integration` and `promotion` any time you want the current evidence reflected in
150
+ `authoritative/`:
151
+
152
+ ```bash
153
+ parseforge trial --config trial.yaml
154
+ parseforge integration
155
+ parseforge promotion --user alice
156
+ ```
157
+
158
+ `integration` rebuilds `integration/reference-summary.json` from every trial
159
+ currently on disk, and `promotion` always refreshes integration itself before
160
+ evaluating any gate — so running `promotion` alone after a `trial` run is enough to
161
+ pick up new evidence; a separate `integration` run is only useful if you want to
162
+ inspect `reference-summary.json` without also promoting.
163
+
164
+ ## Reference
165
+
166
+ - [Documentation site](https://geeks-trident-llc.github.io/parseforge/) ([source](./docs/index.md))
167
+ - [SPEC.md](SPEC.md) — full design plan and open questions
@@ -61,65 +61,73 @@ Deliberately no `<version>` segment in the path: a cli-name's output structure u
61
61
  ```
62
62
  trials/<vendor>/<family>/<os>/<cli-name>/
63
63
  <yyyymmdd-HHMMSS-shortid>/
64
- input.txt
65
- raw-llm-response.txt
66
- usage.txt
67
- raw-template
68
- template.textfsm
69
- readable-dsl.txt
70
- recognizers.txt
71
- llm-records.json
72
- records.json
73
- debug.txt
74
- status.txt
64
+ samples/
65
+ sample.txt
66
+ derive/
67
+ llm-template.textfsm
68
+ template.textfsm
69
+ readable-dsl.txt
70
+ recognizers.txt
71
+ summary.json
75
72
  ```
76
73
  Keep the timestamp+shortid directories (not `result1..N`) — chronological ordering and collision-safety in batch mode come for free, and sequential numbers throw both away.
77
74
 
78
75
  | File | Purpose |
79
76
  |---|---|
80
- | `input.txt` | Raw CLI output sample fed to the LLM |
81
- | `raw-llm-response.txt` | Full, unprocessed LLM response |
82
- | `usage.txt` | Token counts / cost metadata for that call |
83
- | `raw-template` | Template as extracted from the LLM response, pre-cleanup |
84
- | `template.textfsm` | Cleaned TextFSM template candidate (`.textfsm` extension recognized by TextFSM tooling/linters, unlike `textfsm.template`) |
85
- | `readable-dsl.txt` | Human-readable description of what the template captures |
86
- | `recognizers.txt` | Heuristics/signatures for detecting this output type at runtime |
87
- | `llm-records.json` | Structured record of the LLM interaction (prompt, model, params, timestamps) |
88
- | `records.json` | Result of running this trial's `template.textfsm` against its own `input.txt` |
89
- | `debug.txt` | Parse errors, warnings, retry attempts |
90
- | `status.txt` | `passed`/`failed`, `llm-duration-ms`, `pipeline-duration-ms`, error summary |
77
+ | `samples/sample.txt` | Raw CLI output sample fed to the LLM |
78
+ | `derive/llm-template.textfsm` | Template as extracted from the LLM response, pre-cleanup |
79
+ | `derive/template.textfsm` | Cleaned, DSL-compiled TextFSM template candidate (`.textfsm` extension recognized by TextFSM tooling/linters, unlike `textfsm.template`) |
80
+ | `derive/readable-dsl.txt` | Human-readable description of what the template captures |
81
+ | `derive/recognizers.txt` | Heuristics/signatures for detecting this output type at runtime |
82
+ | `summary.json` | Everything else about the run: `created_at`/`ended_at`/`duration_ms`, `passed`, `error` (when `passed` is false), `metadata` (project/username/email/description), `command_info` (vendor/family/os/version/device_type/command), `usage` (naming + generation token counts/cost), and `provider_info` (the generation provider/model) |
91
83
 
92
84
  ### 3.2 `integration/` (no human review yet)
93
85
 
94
86
  ```
95
87
  integration/<vendor>/<family>/<os>/<cli-name>/
96
- common-result/
97
- template.textfsm the winning candidate, copied from trials/
98
- selection-report.json which trials were considered, why this one won
99
- artifact/ supporting evidence (see below)
88
+ reference.json ← every group's clustering evidence for this cli-name
89
+ group1-template1.textfsm a distinct template variant, copied from trials/
90
+ group1-template2.textfsm another distinct template text within the same group, if any
91
+ group2-template1.textfsma different output-schema group entirely (§6 multi-variant)
92
+
93
+ integration/
94
+ reference-summary.json ← project-wide match-rate ratios aggregated across every cli-name
100
95
  ```
101
96
 
102
- **How "common" gets chosen — don't just self-validate:** test *every* candidate `template.textfsm` from `trials/<cli-name>/` against *every* `input.txt` collected for that `cli-name`, not only the sample it was generated from. Promote whichever template parses the largest share of all known samples correctly. A template that only works against its own source sample is exactly the overfit case integration should catch. If you generate multiple candidates from the same input (repeated LLM calls), a structure that recurs across generations is additional evidence of stabilityworth folding into the score, not just accuracy alone.
97
+ **How grouping actually works — don't assume one winner.** Every trial whose own `summary.json` says `passed` is clustered by the field-key signature of its parsed records — the columns `derive/template.textfsm` actually produces against `samples/sample.txt` not by exact template text; two templates that produce the same fields land in the same group even if their regex differs. Within a group, byte-identical template texts are further merged into "variants," so `reference.json` can report `exact_template_count`/`exact_records_count` per variant. This replaces self-validation-only trust: a template that only works against its own source sample still gets clustered on its own field signature, but its group's `ratio_of_passed` (in `reference-summary.json`) reads low if no other trial agrees with it — the overfit signal a human or `promotion.py`'s gate needs, in place of a single hand-picked "winner." The original single-winner design's `artifact/` subdirectory (aggregated `records.json` across cross-validated samples, plus a diff/summary of samples the winner failed on) doesn't carry over: there's no separate evidence folder and no per-sample mismatch diff, since there's no longer a single winner for a sample to fail against a sample that doesn't fit any existing group's field signature just becomes (or joins) a different group instead.
98
+
99
+ `reference.json` is `{total_case_count, total_passed_case_count, groups: {group_id: {keys, sample_path, group_case_count, variants: {variant_id: {template_path, exact_template_count, exact_records_count}}}}}` — a full rebuild every time integration runs, not an incremental diff, so it's always a fresh snapshot of every trial currently under `trials/.../<cli-name>/`.
103
100
 
104
- `selection-report.json` should record: which trial run IDs were candidates, the cross-validation match rate for each, which samples the winner failed on (if any), and the score that made it win. This is what a human reviewer actually reads the raw template alone doesn't convey robustness.
101
+ `reference-summary.json` turns those counts into ratios for every cli-name in one report: `ratio_of_total` (a group/variant's share of *every* trial attempted, diluted by raw generation failures) and `ratio_of_passed` (its share of only the trials that actually passed). `promotion.py`'s gate evaluates on `ratio_of_passed`, since promotion should judge template consistency, not raw pipeline reliability.
105
102
 
106
- `artifact/` holds whatever a reviewer needs to judge the result without re-running the pipeline: the aggregated `records.json` outputs across all cross-validated samples, and a diff/summary of any samples where the winning template didn't fully match.
103
+ **Grouping and `recognizers.txt` are two different mechanisms, not one.** §6's note about `recognizers.txt` enabling *one-of-many matching* is about a runtime picking the right authoritative template for new input; it plays no part in how integration itself tells variants apart. Group clustering here never reads `recognizers.txt` — it re-parses `derive/template.textfsm` against `samples/sample.txt` and clusters by the resulting field-key signature, purely evidence-gathering, before any template is authoritative. `recognizers.txt` is still generated per trial and carried through to `authoritative/` per-variant (§3.3), but what consumes it to actually dispatch between promoted variants at runtime is out of parseforge's own scope — still an open question (§7).
107
104
 
108
105
  ### 3.3 `authoritative/` (approved via human review, or confidence-gated auto-promotion)
109
106
 
110
107
  ```
111
108
  authoritative/<vendor>/<family>/<os>/<cli-name>/
112
- template.textfsm ← current approved template
113
- artifact/ carried over from the winning integration candidate
109
+ template.textfsm ← current approved template (the primary variant)
110
+ template-v2.textfsm an additional simultaneously-valid variant (§6), if any
111
+ template-v2-<suffix>.textfsm ← a USER_REVIEWED snapshot of that variant, kept alongside its current version
112
+ recognizers.txt ← recognizer signature, one per template, same suffix rule
113
+ data/
114
+ sample.txt, records.json ← the sample + parsed output behind each template, same suffix rule
115
+ golden.hash ← sha256 of the most recently promoted template, regardless of variant
116
+ artifact.json ← who/when/mode/match-rate/source of the most recent promotion
117
+ drift-log.json ← rolling match rate over time per variant, against live production samples
114
118
  history/
115
- <yyyymmdd-HHMMSS>-<shortid>.textfsm ← every prior approved version, kept
116
- promotion-log.json ← who/what approved each version, when, why, and what it replaced
117
- drift-log.json ← match-rate over time against live production samples
119
+ template-<yyyymmdd-HHMMSS>-<shortid>.textfsm ← prior content, archived whenever a promotion overwrites it with something different
120
+
121
+ authoritative/
122
+ authoritative-log.json ← project-wide, append-only: every promotion event ever, across every cli-name
123
+ authoritative-summary.json ← project-wide snapshot of the most recent promotion run
118
124
  ```
119
125
 
120
- Promotion from `integration/common-result` to `authoritative/` should default to human review, but can be confidence-gated: if the cross-validation match rate is at or near 100% across all known samples with no ambiguous fields, auto-promote; otherwise queue for human review. This isn't a replacement for review it's a filter that keeps the easy, unambiguous cases from waiting on a person while still requiring a human on anything uncertain. A custom drift/quality-scoring model can slot into this same gate later without changing the pipeline shape.
126
+ No per-variant subdirectory every simultaneously-valid template for a `cli-name` (§6, hardware/firmware variance) lives directly in this one flat directory, distinguished by filename. The first-discovered variant owns the unsuffixed "current version" names; each additional variant owns a stable, permanent `template-v2.textfsm`, `template-v3.textfsm`, ... derived from its own group id and never renumbered. `golden.hash` and `artifact.json` are the one exception to per-variant filenames: both are singular and unsuffixed, always reflecting whichever promotion happened most recently regardless of which variant triggered it.
127
+
128
+ Promotion defaults to human review, but can be confidence-gated per variant: if a variant's match rate — against only the trials that actually passed, not diluted by raw generation failures — is at or near 100% with enough samples, auto-promote it; otherwise it's queued for review. Two modes cover this: **AUTO_PROMOTED** walks every case and promotes every qualifying variant unsuffixed; **USER_REVIEWED** is scoped to caller-reviewed `(case, suffix)` requests, writing a suffixed snapshot alongside whatever that variant's current auto-promoted files already are, never replacing them. This isn't a replacement for review — it's a filter that keeps the easy, unambiguous cases from waiting on a person while still requiring a human on anything uncertain.
121
129
 
122
- **Drift detection** runs the current `authoritative/.../template.textfsm` against new production samples on an ongoing basis, logging match rate to `drift-log.json`. When match rate drops below threshold, that failing sample is written into `trials/.../<cli-name>/` as a new run, which re-enters the pipeline at §5 step 4 and works its way back through `integration/` to a possible new `authoritative/` version — closing the loop rather than treating drift as a one-off alert.
130
+ **Drift detection** runs an authoritative template against new production samples on an ongoing basis, tracking a rolling match rate per variant in that cli-name's `drift-log.json`. When the rate drops below threshold, that failing sample is written into `trials/.../<cli-name>/` as a new run, which re-enters the pipeline at §5 step 4 and works its way back through `integration/` to a possible new `authoritative/` version — closing the loop rather than treating drift as a one-off alert.
123
131
 
124
132
  ---
125
133
 
@@ -133,7 +141,7 @@ Promotion from `integration/common-result` to `authoritative/` should default to
133
141
  - Pro: Fails fast and isolates problems per-command; simpler to debug; easier to parallelize across commands.
134
142
  - Con: Each template is generated from a single sample unless you explicitly loop multiple times per command and merge.
135
143
 
136
- **Recommendation:** Build Mode 2 first as the MVP — it's the simpler pipeline and gives you per-command `status.txt` results immediately. Add Mode 1 as a config flag afterward that changes only the *sampling* stage (collect N samples per command before invoking the LLM) — the generation, storage, and validation stages stay identical between modes if you design sampling as a separable stage up front.
144
+ **Recommendation:** Build Mode 2 first as the MVP — it's the simpler pipeline and gives you per-command `summary.json` results immediately. Add Mode 1 as a config flag afterward that changes only the *sampling* stage (collect N samples per command before invoking the LLM) — the generation, storage, and validation stages stay identical between modes if you design sampling as a separable stage up front.
137
145
 
138
146
  ---
139
147
 
@@ -142,12 +150,12 @@ Promotion from `integration/common-result` to `authoritative/` should default to
142
150
  1. **Input intake** — device OS/version/family, auth, command list, mode selection.
143
151
  2. **Name generation** — tokenize each command → canonical `cli-name` per §2.
144
152
  3. **Path resolution** — compute `<vendor>/<device-family>/<os>/<cli-name>/` per §3.
145
- 4. **Sampling** — connect (Netmiko/similar), run command(s), capture raw output → `trials/.../<run-id>/input.txt`.
146
- 5. **Generation** — send `input.txt` (+ prior context if Mode 1) to LLM → `raw-llm-response.txt`, `usage.txt`.
147
- 6. **Extraction & cleanup** — pull template from response → `raw-template` cleaned `template.textfsm`.
148
- 7. **Self-validation** — run `template.textfsm` against its own `input.txt` → `records.json`; capture errors → `debug.txt`; write trial `status.txt`.
149
- 8. **Integration selection** — cross-validate every candidate `template.textfsm` in `trials/.../<cli-name>/` against every known `input.txt` for that `cli-name`; promote the best-scoring one to `integration/.../common-result/`, writing `selection-report.json`.
150
- 9. **Authoritative promotion** — auto-promote if the winning candidate clears the confidence threshold across all known samples; otherwise queue for human review. On approval, copy to `authoritative/.../template.textfsm`, archive the prior version to `history/`, and write `promotion-log.json`.
153
+ 4. **Sampling** — connect (Netmiko/similar), run command(s), capture raw output → `trials/.../<run-id>/samples/sample.txt`.
154
+ 5. **Generation** — send `sample.txt` (+ prior context if Mode 1) to LLM → `derive/llm-template.textfsm`, token usage recorded in `summary.json`.
155
+ 6. **Extraction & cleanup** — pull template from response → cleaned `derive/template.textfsm`, plus `derive/readable-dsl.txt` and `derive/recognizers.txt`.
156
+ 7. **Self-validation** — run `derive/template.textfsm` against its own `samples/sample.txt`; record `passed`/`error` in `summary.json`.
157
+ 8. **Integration selection** — cluster every passed trial's `derive/template.textfsm` in `trials/.../<cli-name>/` by output-schema signature, cross-validated against every known `samples/sample.txt` for that `cli-name`; write `integration/.../reference.json` and one `group#-template#.textfsm` per distinct variant.
158
+ 9. **Authoritative promotion** — auto-promote if the winning candidate clears the confidence threshold across all known samples; otherwise queue for human review. On approval, copy to `authoritative/.../template.textfsm` (or `template-v2.textfsm`, ... for an additional variant), archive the prior content to `history/` if it differs, and write `golden.hash`, `artifact.json`, and `authoritative-log.json`.
151
159
  10. **Drift monitoring** — continuously run the authoritative template against new production samples, logging match rate to `drift-log.json`. On breach, feed the failing sample back into `trials/` (→ step 4) to generate a replacement candidate.
152
160
  11. **Repeat/loop or batch-complete** depending on mode.
153
161
 
@@ -160,7 +168,7 @@ Promotion from `integration/common-result` to `authoritative/` should default to
160
168
  Where a fourth tier is tempting but better handled as **metadata instead of a new directory layer:**
161
169
 
162
170
  - **Quarantine / stale-but-still-serving:** when drift is detected, the authoritative template is still the one in production use — you don't want to yank it out mid-flight while a replacement works its way through the pipeline. Rather than a new top-level tier, this is a `status` field in `drift-log.json` (`ok` / `drifting` / `superseded`) on the existing authoritative entry. The template keeps serving; the status just flags that a replacement is in flight.
163
- - **Archived / deprecated (EOL device-OS combos):** for template families you no longer actively maintain (e.g. an OS version taken out of production fleet-wide), this is also a status flag rather than a directory move — `history/` already retains every prior version, so "archived" just means promotion-log stops getting new entries, not that files need to relocate.
171
+ - **Archived / deprecated (EOL device-OS combos):** for template families you no longer actively maintain (e.g. an OS version taken out of production fleet-wide), this is also a status flag rather than a directory move — `history/` already retains every prior version, so "archived" just means `authoritative-log.json` stops getting new entries for that cli-name, not that files need to relocate.
164
172
  - **Multiple simultaneously-valid templates for one `cli-name`:** if a command's output legitimately varies by hardware config (e.g. a chassis with vs. without an optional module) rather than by drift, that's not a tier problem — it's a case for storing *multiple* authoritative templates under the same `cli-name` path with distinct `recognizers.txt` signatures, and letting runtime dispatch pick the right one. Worth flagging now so `recognizers.txt` is designed to support one-of-many matching from the start rather than assuming exactly one template per `cli-name`.
165
173
 
166
174
  **Bottom line:** keep the three directories; push the "in-between" states (drifting-but-live, archived, multi-variant) into metadata/status fields on the existing tiers. Adding more top-level directories per edge case leads to directory sprawl without adding real distinctions in *how* a template is used.
@@ -172,4 +180,4 @@ Where a fourth tier is tempting but better handled as **metadata instead of a ne
172
180
  - Do you want a **registry/index file** (e.g. `catalog.json`) at the repo root listing every `<vendor>/<family>/<os>/<cli-name>` combination that exists, plus its authoritative status, for fast lookup without walking the filesystem?
173
181
  - What **confidence threshold** (match-rate %, sample count minimum) should gate auto-promotion vs. human review in step 9 — worth making this configurable per-project rather than hardcoded?
174
182
  - Should `recognizers.txt` support **one-of-many matching** from day one (per the multi-variant note in §6), or is that a v2 concern?
175
- - What's the **LLM provider/model** for generation — worth pinning per-project so `usage.txt` costs are comparable across runs?
183
+ - What's the **LLM provider/model** for generation — worth pinning per-project so `summary.json`'s usage costs are comparable across runs?
@@ -0,0 +1 @@
1
+ __version__ = "0.2.3"