parseforge 0.2.1__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 (59) hide show
  1. parseforge-0.2.3/PKG-INFO +208 -0
  2. parseforge-0.2.3/README.md +167 -0
  3. parseforge-0.2.3/SPEC.md +183 -0
  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.3/parseforge/generation.py +112 -0
  9. parseforge-0.2.3/parseforge/integration.py +314 -0
  10. parseforge-0.2.3/parseforge/naming/__init__.py +30 -0
  11. parseforge-0.2.3/parseforge/naming/assemble.py +60 -0
  12. {parseforge-0.2.1 → parseforge-0.2.3}/parseforge/naming/cache.py +4 -3
  13. parseforge-0.2.3/parseforge/naming/llm.py +88 -0
  14. parseforge-0.2.3/parseforge/naming/prompts.py +14 -0
  15. parseforge-0.2.3/parseforge/naming/prompts.yaml +29 -0
  16. parseforge-0.2.3/parseforge/naming/providers/__init__.py +4 -0
  17. parseforge-0.2.3/parseforge/naming/providers/anthropic.py +105 -0
  18. parseforge-0.2.3/parseforge/naming/providers/cost.py +29 -0
  19. parseforge-0.2.3/parseforge/naming/providers/deepseek.py +123 -0
  20. parseforge-0.2.3/parseforge/naming/providers/errors.py +63 -0
  21. parseforge-0.2.3/parseforge/naming/providers/models.py +26 -0
  22. parseforge-0.2.3/parseforge/naming/providers/models.yaml +24 -0
  23. parseforge-0.2.3/parseforge/naming/providers/text.py +13 -0
  24. parseforge-0.2.3/parseforge/naming/resolver.py +82 -0
  25. parseforge-0.2.3/parseforge/paths.py +177 -0
  26. parseforge-0.2.3/parseforge/pipeline.py +221 -0
  27. parseforge-0.2.3/parseforge/promotion.py +510 -0
  28. parseforge-0.2.3/parseforge/sampling/__init__.py +3 -0
  29. parseforge-0.2.3/parseforge/sampling/backends/__init__.py +3 -0
  30. parseforge-0.2.3/parseforge/sampling/backends/netmiko.py +35 -0
  31. parseforge-0.2.1/parseforge/sampling.py → parseforge-0.2.3/parseforge/sampling/core.py +3 -2
  32. parseforge-0.2.3/parseforge/validation.py +36 -0
  33. parseforge-0.2.3/parseforge.egg-info/PKG-INFO +208 -0
  34. parseforge-0.2.3/parseforge.egg-info/SOURCES.txt +41 -0
  35. {parseforge-0.2.1 → parseforge-0.2.3}/parseforge.egg-info/requires.txt +3 -0
  36. {parseforge-0.2.1 → parseforge-0.2.3}/pyproject.toml +11 -1
  37. parseforge-0.2.1/PKG-INFO +0 -79
  38. parseforge-0.2.1/README.md +0 -41
  39. parseforge-0.2.1/SPEC.md +0 -173
  40. parseforge-0.2.1/parseforge/__init__.py +0 -1
  41. parseforge-0.2.1/parseforge/cli/main.py +0 -55
  42. parseforge-0.2.1/parseforge/generation.py +0 -40
  43. parseforge-0.2.1/parseforge/naming/__init__.py +0 -15
  44. parseforge-0.2.1/parseforge/naming/assemble.py +0 -35
  45. parseforge-0.2.1/parseforge/naming/llm.py +0 -62
  46. parseforge-0.2.1/parseforge/naming/resolver.py +0 -45
  47. parseforge-0.2.1/parseforge/paths.py +0 -68
  48. parseforge-0.2.1/parseforge/pipeline.py +0 -49
  49. parseforge-0.2.1/parseforge/promotion.py +0 -43
  50. parseforge-0.2.1/parseforge/validation.py +0 -50
  51. parseforge-0.2.1/parseforge.egg-info/PKG-INFO +0 -79
  52. parseforge-0.2.1/parseforge.egg-info/SOURCES.txt +0 -25
  53. {parseforge-0.2.1 → parseforge-0.2.3}/LICENSE +0 -0
  54. {parseforge-0.2.1 → parseforge-0.2.3}/MANIFEST.in +0 -0
  55. {parseforge-0.2.1 → parseforge-0.2.3}/parseforge/cli/__init__.py +0 -0
  56. {parseforge-0.2.1 → parseforge-0.2.3}/parseforge.egg-info/dependency_links.txt +0 -0
  57. {parseforge-0.2.1 → parseforge-0.2.3}/parseforge.egg-info/entry_points.txt +0 -0
  58. {parseforge-0.2.1 → parseforge-0.2.3}/parseforge.egg-info/top_level.txt +0 -0
  59. {parseforge-0.2.1 → 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
@@ -0,0 +1,183 @@
1
+ # Network CLI → TextFSM Template Generator — Project Plan & Requirements
2
+
3
+ ## 1. Project & Repo Naming
4
+
5
+ | Candidate | Repo name (kebab-case) | Rationale |
6
+ |---|---|---|
7
+ | **ParseForge** | `parseforge` | Short, memorable, clearly about forging parsers. Available-sounding, no CLI/network jargon lock-in if you expand beyond Cisco later. |
8
+ | **CLIFsm** | `cli-fsm-forge` | Literal — CLI + FSM (finite state machine, what TextFSM templates are). Good for discoverability if someone searches "textfsm" + "cli". |
9
+ | **ShowParse AI** | `showparse-ai` | Leans into the fact that almost every input is a `show` command. Very descriptive, less generic. |
10
+ | **NetFSM** | `netfsm` | Shortest option. Good if this becomes a pip-installable library/CLI tool people type often. |
11
+
12
+ **Recommendation:** `parseforge` as the project name, `parseforge` (or `parseforge-textfsm`) as the repo name. It reads well as a CLI tool name (`parseforge run ...`), isn't locked to Cisco/TextFSM specifically if you extend to other vendors or output formats (e.g., ntc-templates-style YAML, TTP, or Genie parsers) later, and is easy to say out loud.
13
+
14
+ If you want to keep `textfsm-ai` since that's the term you're already using internally/in docs, that's a perfectly reasonable literal alternative — just know it reads as "TextFSM, but AI" rather than as a product name, which is fine for an internal tool but less distinctive if this is ever open-sourced.
15
+
16
+ ---
17
+
18
+ ## 2. CLI-to-Name Translation
19
+
20
+ **Rule:** Tokenize the full command string. Classify each token as **literal** (fixed across all invocations of this command type) or **variable** (an identifier — interface name, VLAN ID, IP, hostname, etc.).
21
+
22
+ **Naming convention — always index variables, even when there's only one:**
23
+
24
+ ```
25
+ show version → show-version
26
+ show interface GE1.1 status → show-interface-var1-status
27
+ show interface GE1.1 counters → show-interface-var1-counters
28
+ show ip route 10.0.0.0 → show-ip-route-var1
29
+ show bgp neighbors 10.1.1.1 advertised-routes
30
+ → show-bgp-neighbors-var1-advertised-routes
31
+ ```
32
+
33
+ **Why always number, even for a single variable (`var1` not `var`):**
34
+ A naming function that omits the index for single-variable commands has to *already know* the final variable count before naming — but that can change: a newer OS version might add an optional second argument to the same command family, or you might discover a command has two variable slots you didn't notice on first sample. Indexing consistently means:
35
+ - The name-generation code is a pure, stateless function of token position — no lookahead needed.
36
+ - No silent renames/migrations later if a command turns out to have more variables than expected.
37
+ - Multi-variable commands (2+) stay unambiguous by construction — `var1`, `var2` in left-to-right order of appearance.
38
+
39
+ This differs slightly from the older convention used in the `ntc-templates` community library (which just drops variable tokens entirely rather than placeholding them — e.g. `cisco_ios_show_interfaces.textfsm` regardless of interface name). That works for them because their catalog is hand-curated and collisions are manually resolved. Since your pipeline is LLM-driven and needs to run unattended, explicit indexed placeholders are safer and self-documenting.
40
+
41
+ **Edge case — literal tokens that look variable-ish:** keywords like `brief`, `status`, `detail`, `counters`, `summary` are still literal (they're fixed sub-command keywords, not user-supplied values) and stay as literal tokens in the name.
42
+
43
+ ---
44
+
45
+ ## 3. Storage Layout — Three-Tier Promotion
46
+
47
+ This mirrors the candidate → staging → production pattern used for ML model registries, applied to templates: **trials** (raw, unreviewed attempts) → **integration** (cross-validated candidates, pending approval) → **authoritative** (approved, in production use, drift-monitored).
48
+
49
+ Shared path prefix under all three tiers:
50
+ ```
51
+ <vendor>/<device-family>/<os>/<cli-name>/
52
+ ```
53
+ e.g. `cisco/catalyst9200/ios-xe/show-interface-var1-status/`
54
+
55
+ Note: use `ios-xe` / `nx-os` (hyphenated) rather than `xe` / `ios` alone once multiple Cisco OS families share the tree — `xe` alone is ambiguous without the `ios-` prefix once IOS classic, IOS XE, and IOS XR all live under `cisco/`.
56
+
57
+ Deliberately no `<version>` segment in the path: a cli-name's output structure usually doesn't change across minor OS versions, and when it legitimately does, that's exactly the variance §5 step 8's group clustering is built to catch — lumping versions together under one path gives more evidence per group instead of silently fragmenting it across per-version directories. The OS version a trial was sampled from is recorded per trial instead, in that trial's `summary.json` (`command_info.version`).
58
+
59
+ ### 3.1 `trials/`
60
+
61
+ ```
62
+ trials/<vendor>/<family>/<os>/<cli-name>/
63
+ <yyyymmdd-HHMMSS-shortid>/
64
+ samples/
65
+ sample.txt
66
+ derive/
67
+ llm-template.textfsm
68
+ template.textfsm
69
+ readable-dsl.txt
70
+ recognizers.txt
71
+ summary.json
72
+ ```
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.
74
+
75
+ | File | Purpose |
76
+ |---|---|
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) |
83
+
84
+ ### 3.2 `integration/` (no human review yet)
85
+
86
+ ```
87
+ integration/<vendor>/<family>/<os>/<cli-name>/
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.textfsm ← a 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
95
+ ```
96
+
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>/`.
100
+
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.
102
+
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).
104
+
105
+ ### 3.3 `authoritative/` (approved via human review, or confidence-gated auto-promotion)
106
+
107
+ ```
108
+ authoritative/<vendor>/<family>/<os>/<cli-name>/
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
118
+ history/
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
124
+ ```
125
+
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.
129
+
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.
131
+
132
+ ---
133
+
134
+ ## 4. Execution Modes
135
+
136
+ **Mode 1 — Batch (gather-then-generate):**
137
+ - Pro: LLM can be shown patterns across multiple samples of the *same* command at once (e.g., three different interface states) in one shot, likely improving template robustness on the first try.
138
+ - Con: Slower feedback loop — you don't know command #3 has a connectivity problem until after collecting #1–#10.
139
+
140
+ **Mode 2 — Loop (sample → generate, per command):**
141
+ - Pro: Fails fast and isolates problems per-command; simpler to debug; easier to parallelize across commands.
142
+ - Con: Each template is generated from a single sample unless you explicitly loop multiple times per command and merge.
143
+
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.
145
+
146
+ ---
147
+
148
+ ## 5. Pipeline Steps
149
+
150
+ 1. **Input intake** — device OS/version/family, auth, command list, mode selection.
151
+ 2. **Name generation** — tokenize each command → canonical `cli-name` per §2.
152
+ 3. **Path resolution** — compute `<vendor>/<device-family>/<os>/<cli-name>/` per §3.
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`.
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.
160
+ 11. **Repeat/loop or batch-complete** depending on mode.
161
+
162
+ ---
163
+
164
+ ## 6. Design Notes: Is Three-Tier Enough?
165
+
166
+ **Yes, for the promotion pipeline itself.** `trials → integration → authoritative` covers the full lifecycle a template needs to go through to earn trust: generate, cross-validate, approve. Adding a fourth *promotion* tier (e.g. a separate "staging" between integration and authoritative) wouldn't buy you anything distinct — integration already *is* the staging step, since nothing in it is live yet.
167
+
168
+ Where a fourth tier is tempting but better handled as **metadata instead of a new directory layer:**
169
+
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.
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.
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`.
173
+
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.
175
+
176
+ ---
177
+
178
+ ## 7. Open Questions for Next Iteration
179
+
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?
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?
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?
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"