parseforge 0.2.12__tar.gz → 0.3.1__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 (66) hide show
  1. parseforge-0.3.1/PKG-INFO +199 -0
  2. parseforge-0.3.1/README.md +95 -0
  3. {parseforge-0.2.12 → parseforge-0.3.1}/SPEC.md +2 -2
  4. parseforge-0.3.1/parseforge/__init__.py +96 -0
  5. parseforge-0.3.1/parseforge/api.py +127 -0
  6. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/cli/main.py +2 -1
  7. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/generation.py +0 -2
  8. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/llm.py +0 -1
  9. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/anthropic.py +1 -11
  10. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/azure.py +1 -16
  11. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/bedrock.py +1 -11
  12. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/cerebras.py +1 -11
  13. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/cohere.py +1 -11
  14. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/deepseek.py +1 -11
  15. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/fireworks.py +1 -11
  16. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/gemini.py +1 -11
  17. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/groq.py +1 -11
  18. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/mistral.py +1 -11
  19. parseforge-0.3.1/parseforge/naming/providers/models.py +17 -0
  20. parseforge-0.3.1/parseforge/naming/providers/models.yaml +30 -0
  21. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/moonshot.py +1 -11
  22. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/oci.py +1 -11
  23. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/openai.py +1 -11
  24. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/openrouter.py +1 -11
  25. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/perplexity.py +1 -11
  26. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/together.py +1 -11
  27. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/vertexai.py +1 -11
  28. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/xai.py +1 -11
  29. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/pipeline.py +25 -3
  30. parseforge-0.3.1/parseforge.egg-info/PKG-INFO +199 -0
  31. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge.egg-info/SOURCES.txt +1 -1
  32. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge.egg-info/requires.txt +19 -19
  33. {parseforge-0.2.12 → parseforge-0.3.1}/pyproject.toml +26 -20
  34. parseforge-0.2.12/PKG-INFO +0 -339
  35. parseforge-0.2.12/README.md +0 -235
  36. parseforge-0.2.12/parseforge/__init__.py +0 -1
  37. parseforge-0.2.12/parseforge/naming/providers/cost.py +0 -29
  38. parseforge-0.2.12/parseforge/naming/providers/models.py +0 -26
  39. parseforge-0.2.12/parseforge/naming/providers/models.yaml +0 -182
  40. parseforge-0.2.12/parseforge.egg-info/PKG-INFO +0 -339
  41. {parseforge-0.2.12 → parseforge-0.3.1}/LICENSE +0 -0
  42. {parseforge-0.2.12 → parseforge-0.3.1}/MANIFEST.in +0 -0
  43. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/cli/__init__.py +0 -0
  44. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/cli/config.py +0 -0
  45. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/drift.py +0 -0
  46. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/integration.py +0 -0
  47. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/__init__.py +0 -0
  48. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/assemble.py +0 -0
  49. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/cache.py +0 -0
  50. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/prompts.py +0 -0
  51. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/prompts.yaml +0 -0
  52. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/__init__.py +0 -0
  53. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/errors.py +0 -0
  54. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/providers/text.py +0 -0
  55. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/naming/resolver.py +0 -0
  56. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/paths.py +0 -0
  57. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/promotion.py +0 -0
  58. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/sampling/__init__.py +0 -0
  59. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/sampling/backends/__init__.py +0 -0
  60. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/sampling/backends/netmiko.py +0 -0
  61. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/sampling/core.py +0 -0
  62. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge/validation.py +0 -0
  63. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge.egg-info/dependency_links.txt +0 -0
  64. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge.egg-info/entry_points.txt +0 -0
  65. {parseforge-0.2.12 → parseforge-0.3.1}/parseforge.egg-info/top_level.txt +0 -0
  66. {parseforge-0.2.12 → parseforge-0.3.1}/setup.cfg +0 -0
@@ -0,0 +1,199 @@
1
+ Metadata-Version: 2.4
2
+ Name: parseforge
3
+ Version: 0.3.1
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: textfsm-ai>=0.7.1
30
+ Provides-Extra: anthropic
31
+ Requires-Dist: anthropic>=0.40.0; extra == "anthropic"
32
+ Requires-Dist: textfsm-ai[anthropic]>=0.7.1; extra == "anthropic"
33
+ Provides-Extra: deepseek
34
+ Requires-Dist: openai>=1.0.0; extra == "deepseek"
35
+ Requires-Dist: textfsm-ai[deepseek]>=0.7.1; extra == "deepseek"
36
+ Provides-Extra: openai
37
+ Requires-Dist: openai>=1.0.0; extra == "openai"
38
+ Requires-Dist: textfsm-ai[openai]>=0.7.1; extra == "openai"
39
+ Provides-Extra: groq
40
+ Requires-Dist: openai>=1.0.0; extra == "groq"
41
+ Requires-Dist: textfsm-ai[groq]>=0.7.1; extra == "groq"
42
+ Provides-Extra: xai
43
+ Requires-Dist: openai>=1.0.0; extra == "xai"
44
+ Requires-Dist: textfsm-ai[xai]>=0.7.1; extra == "xai"
45
+ Provides-Extra: together
46
+ Requires-Dist: openai>=1.0.0; extra == "together"
47
+ Requires-Dist: textfsm-ai[together]>=0.7.1; extra == "together"
48
+ Provides-Extra: fireworks
49
+ Requires-Dist: openai>=1.0.0; extra == "fireworks"
50
+ Requires-Dist: textfsm-ai[fireworks]>=0.7.1; extra == "fireworks"
51
+ Provides-Extra: perplexity
52
+ Requires-Dist: openai>=1.0.0; extra == "perplexity"
53
+ Requires-Dist: textfsm-ai[perplexity]>=0.7.1; extra == "perplexity"
54
+ Provides-Extra: openrouter
55
+ Requires-Dist: openai>=1.0.0; extra == "openrouter"
56
+ Requires-Dist: textfsm-ai[openrouter]>=0.7.1; extra == "openrouter"
57
+ Provides-Extra: moonshot
58
+ Requires-Dist: openai>=1.0.0; extra == "moonshot"
59
+ Requires-Dist: textfsm-ai[moonshot]>=0.7.1; extra == "moonshot"
60
+ Provides-Extra: cerebras
61
+ Requires-Dist: openai>=1.0.0; extra == "cerebras"
62
+ Requires-Dist: textfsm-ai[cerebras]>=0.7.1; extra == "cerebras"
63
+ Provides-Extra: mistral
64
+ Requires-Dist: mistralai==1.10.0; extra == "mistral"
65
+ Requires-Dist: textfsm-ai[mistral]>=0.7.1; extra == "mistral"
66
+ Provides-Extra: cohere
67
+ Requires-Dist: cohere==5.21.1; extra == "cohere"
68
+ Requires-Dist: textfsm-ai[cohere]>=0.7.1; extra == "cohere"
69
+ Provides-Extra: azure
70
+ Requires-Dist: azure-ai-inference>=1.0.0b9; extra == "azure"
71
+ Requires-Dist: textfsm-ai[azure]>=0.7.1; extra == "azure"
72
+ Provides-Extra: gemini
73
+ Requires-Dist: google-genai>=0.2.0; extra == "gemini"
74
+ Requires-Dist: textfsm-ai[gemini]>=0.7.1; extra == "gemini"
75
+ Provides-Extra: vertexai
76
+ Requires-Dist: google-genai>=0.2.0; extra == "vertexai"
77
+ Requires-Dist: textfsm-ai[vertexai]>=0.7.1; extra == "vertexai"
78
+ Provides-Extra: bedrock
79
+ Requires-Dist: boto3==1.42.97; extra == "bedrock"
80
+ Requires-Dist: textfsm-ai[bedrock]>=0.7.1; extra == "bedrock"
81
+ Provides-Extra: oci
82
+ Requires-Dist: oci==2.182.0; extra == "oci"
83
+ Requires-Dist: textfsm-ai[oci]>=0.7.1; extra == "oci"
84
+ Provides-Extra: sampling
85
+ Requires-Dist: netmiko>=4.0; extra == "sampling"
86
+ Provides-Extra: dev
87
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
88
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
89
+ Requires-Dist: ruff>=0.4.0; extra == "dev"
90
+ Requires-Dist: black>=24.0.0; extra == "dev"
91
+ Requires-Dist: mypy>=1.10.0; extra == "dev"
92
+ Requires-Dist: anthropic>=0.40.0; extra == "dev"
93
+ Requires-Dist: openai>=1.0.0; extra == "dev"
94
+ Requires-Dist: mistralai==1.10.0; extra == "dev"
95
+ Requires-Dist: cohere==5.21.1; extra == "dev"
96
+ Requires-Dist: azure-ai-inference>=1.0.0b9; extra == "dev"
97
+ Requires-Dist: google-genai>=0.2.0; extra == "dev"
98
+ Requires-Dist: boto3==1.42.97; extra == "dev"
99
+ Requires-Dist: oci==2.182.0; extra == "dev"
100
+ Provides-Extra: release
101
+ Requires-Dist: bump2version; extra == "release"
102
+ Requires-Dist: build; extra == "release"
103
+ Dynamic: license-file
104
+
105
+ # ParseForge
106
+
107
+ LLM-driven pipeline that forges, cross-validates, and promotes [TextFSM](https://github.com/google/textfsm)
108
+ templates from network device CLI output.
109
+
110
+ Full design plan: [SPEC.md](SPEC.md).
111
+
112
+ ## Features
113
+
114
+ - **Trial → integration → promotion: a Human-in-the-Loop review workflow,
115
+ not a one-shot generator.** Every LLM-generated template starts as
116
+ unreviewed evidence in `trials/`. `integration` clusters every trial for
117
+ a command by the *output schema* its parsed records actually have, not
118
+ exact template text — a command's output can legitimately vary by
119
+ hardware/firmware, so distinct schemas become separate, independently
120
+ tracked groups instead of one hand-picked "winner." `promotion` then
121
+ auto-promotes any group whose match rate against every known sample
122
+ clears a configurable gate straight into `authoritative/`; anything
123
+ short of that gate is queued for human review instead of silently
124
+ shipped. A human-reviewed promotion is recorded as a named snapshot
125
+ alongside whatever's currently live, never silently overwriting it —
126
+ so review effort goes only where the evidence is actually ambiguous.
127
+ - **Eighteen LLM providers, one interface.** Anthropic, OpenAI, DeepSeek,
128
+ Groq, xAI, Together, Fireworks, Perplexity, OpenRouter, Moonshot,
129
+ Cerebras, Mistral, Cohere, Azure OpenAI, Gemini, Vertex AI, Amazon
130
+ Bedrock, and Oracle Cloud (OCI) — including four with non-standard auth
131
+ (deployment names, GCP Application Default Credentials, AWS's
132
+ credential chain, OCI request-signing) handled transparently. Naming
133
+ and generation can use two different providers in the same trial. See
134
+ [Providers](https://geeks-trident-llc.github.io/parseforge/guides/providers/).
135
+ - **Self-caching cli-name resolution.** A raw CLI command
136
+ (`show interface GE1.1 status`) only ever costs LLM tokens once — it's
137
+ resolved to a canonical, indexed name
138
+ (`show-interface-var1-status`) and cached locally; every later trial for
139
+ that command is a free lookup.
140
+ - **Self-validation, not just "the LLM said so."** Every generated
141
+ template is immediately run against its own sample before being
142
+ recorded as passed — a template that doesn't actually parse the output
143
+ it was generated from never gets a chance to look good on paper.
144
+ - **Drift monitoring.** An authoritative template is periodically checked
145
+ against new production samples; a failing sample feeds back into the
146
+ pipeline as a new trial automatically, closing the loop instead of just
147
+ logging an alert.
148
+ - **CLI and Python API, same underlying calls.** Everything the CLI does
149
+ — `run`, `trial`, `integration`, `promotion` — is one function call in
150
+ Python too. See the
151
+ [Python API guide](https://geeks-trident-llc.github.io/parseforge/guides/python-api/).
152
+
153
+ ## Status
154
+
155
+ Early beta. The full pipeline is implemented and tested end to end — naming,
156
+ sampling, generation, self-validation, integration (output-schema group/variant
157
+ clustering), promotion (auto and human-reviewed), and drift monitoring — and
158
+ wired into the CLI. A few things are intentionally not there yet:
159
+
160
+ - **`USER_REVIEWED` promotion has a library entry point but no CLI command**
161
+ (`promotion.promote_user_reviewed()` works today; there's no
162
+ `parseforge promotion --mode user-reviewed` yet). Deferred until real
163
+ human-reviewed cases exist to show what a CLI/config shape for a list of
164
+ case/suffix/gate requests should actually look like, rather than guessing
165
+ ahead of need.
166
+ - **Batch sampling mode** (collect several samples per command before
167
+ generating, SPEC.md §4) is designed but not built — the simpler
168
+ per-command loop mode is the only one implemented.
169
+ - **One sampling connector** (Netmiko/SSH). The CLI's `--connector` registry
170
+ is built to hold more without a redesign, but nothing else is wired in yet.
171
+
172
+ ## Installation
173
+
174
+ ```bash
175
+ # minimal install, no AI-provider SDK
176
+ pip install parseforge
177
+
178
+ # with a provider extra, e.g. anthropic
179
+ pip install parseforge[anthropic]
180
+
181
+ # local development
182
+ pip install -e ".[dev,sampling]"
183
+ ```
184
+
185
+ For the full extras list (all eighteen providers, `sampling`, combining
186
+ extras, per-provider `requirements/` files, and the dev/test/release
187
+ setup), see
188
+ [Installation](https://geeks-trident-llc.github.io/parseforge/getting-started/installation/).
189
+
190
+ ## Reference
191
+
192
+ - [Documentation site](https://geeks-trident-llc.github.io/parseforge/) ([source](./docs/index.md))
193
+ - [Quickstart](https://geeks-trident-llc.github.io/parseforge/getting-started/quickstart/) — a full walkthrough, from a single lookup to the end-to-end workflow
194
+ - [Providers](https://geeks-trident-llc.github.io/parseforge/guides/providers/) — every provider's extra, auth requirements, and default model
195
+ - [CLI Guide](https://geeks-trident-llc.github.io/parseforge/guides/cli/) — every command, in full
196
+ - [Python API guide](https://geeks-trident-llc.github.io/parseforge/guides/python-api/) — calling parseforge from Python instead of the CLI
197
+ - [API Reference](https://geeks-trident-llc.github.io/parseforge/reference/api/) — every public function/class, by pipeline stage
198
+ - [Changelog](https://geeks-trident-llc.github.io/parseforge/changelog/) — what shipped in each release
199
+ - [SPEC.md](SPEC.md) — full design plan and open questions
@@ -0,0 +1,95 @@
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
+ ## Features
9
+
10
+ - **Trial → integration → promotion: a Human-in-the-Loop review workflow,
11
+ not a one-shot generator.** Every LLM-generated template starts as
12
+ unreviewed evidence in `trials/`. `integration` clusters every trial for
13
+ a command by the *output schema* its parsed records actually have, not
14
+ exact template text — a command's output can legitimately vary by
15
+ hardware/firmware, so distinct schemas become separate, independently
16
+ tracked groups instead of one hand-picked "winner." `promotion` then
17
+ auto-promotes any group whose match rate against every known sample
18
+ clears a configurable gate straight into `authoritative/`; anything
19
+ short of that gate is queued for human review instead of silently
20
+ shipped. A human-reviewed promotion is recorded as a named snapshot
21
+ alongside whatever's currently live, never silently overwriting it —
22
+ so review effort goes only where the evidence is actually ambiguous.
23
+ - **Eighteen LLM providers, one interface.** Anthropic, OpenAI, DeepSeek,
24
+ Groq, xAI, Together, Fireworks, Perplexity, OpenRouter, Moonshot,
25
+ Cerebras, Mistral, Cohere, Azure OpenAI, Gemini, Vertex AI, Amazon
26
+ Bedrock, and Oracle Cloud (OCI) — including four with non-standard auth
27
+ (deployment names, GCP Application Default Credentials, AWS's
28
+ credential chain, OCI request-signing) handled transparently. Naming
29
+ and generation can use two different providers in the same trial. See
30
+ [Providers](https://geeks-trident-llc.github.io/parseforge/guides/providers/).
31
+ - **Self-caching cli-name resolution.** A raw CLI command
32
+ (`show interface GE1.1 status`) only ever costs LLM tokens once — it's
33
+ resolved to a canonical, indexed name
34
+ (`show-interface-var1-status`) and cached locally; every later trial for
35
+ that command is a free lookup.
36
+ - **Self-validation, not just "the LLM said so."** Every generated
37
+ template is immediately run against its own sample before being
38
+ recorded as passed — a template that doesn't actually parse the output
39
+ it was generated from never gets a chance to look good on paper.
40
+ - **Drift monitoring.** An authoritative template is periodically checked
41
+ against new production samples; a failing sample feeds back into the
42
+ pipeline as a new trial automatically, closing the loop instead of just
43
+ logging an alert.
44
+ - **CLI and Python API, same underlying calls.** Everything the CLI does
45
+ — `run`, `trial`, `integration`, `promotion` — is one function call in
46
+ Python too. See the
47
+ [Python API guide](https://geeks-trident-llc.github.io/parseforge/guides/python-api/).
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. 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
+ ## Installation
69
+
70
+ ```bash
71
+ # minimal install, no AI-provider SDK
72
+ pip install parseforge
73
+
74
+ # with a provider extra, e.g. anthropic
75
+ pip install parseforge[anthropic]
76
+
77
+ # local development
78
+ pip install -e ".[dev,sampling]"
79
+ ```
80
+
81
+ For the full extras list (all eighteen providers, `sampling`, combining
82
+ extras, per-provider `requirements/` files, and the dev/test/release
83
+ setup), see
84
+ [Installation](https://geeks-trident-llc.github.io/parseforge/getting-started/installation/).
85
+
86
+ ## Reference
87
+
88
+ - [Documentation site](https://geeks-trident-llc.github.io/parseforge/) ([source](./docs/index.md))
89
+ - [Quickstart](https://geeks-trident-llc.github.io/parseforge/getting-started/quickstart/) — a full walkthrough, from a single lookup to the end-to-end workflow
90
+ - [Providers](https://geeks-trident-llc.github.io/parseforge/guides/providers/) — every provider's extra, auth requirements, and default model
91
+ - [CLI Guide](https://geeks-trident-llc.github.io/parseforge/guides/cli/) — every command, in full
92
+ - [Python API guide](https://geeks-trident-llc.github.io/parseforge/guides/python-api/) — calling parseforge from Python instead of the CLI
93
+ - [API Reference](https://geeks-trident-llc.github.io/parseforge/reference/api/) — every public function/class, by pipeline stage
94
+ - [Changelog](https://geeks-trident-llc.github.io/parseforge/changelog/) — what shipped in each release
95
+ - [SPEC.md](SPEC.md) — full design plan and open questions
@@ -79,7 +79,7 @@ Keep the timestamp+shortid directories (not `result1..N`) — chronological orde
79
79
  | `derive/template.textfsm` | Cleaned, DSL-compiled TextFSM template candidate (`.textfsm` extension — recognized by TextFSM tooling/linters, unlike `textfsm.template`) |
80
80
  | `derive/readable-dsl.txt` | Human-readable description of what the template captures |
81
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) |
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), and `provider_info` (the generation provider/model) |
83
83
 
84
84
  ### 3.2 `integration/` (no human review yet)
85
85
 
@@ -180,4 +180,4 @@ Where a fourth tier is tempting but better handled as **metadata instead of a ne
180
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
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
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?
183
+ - What's the **LLM provider/model** for generation — worth pinning per-project so `summary.json`'s token usage is comparable across runs?
@@ -0,0 +1,96 @@
1
+ __version__ = "0.3.1"
2
+ version = __version__
3
+
4
+ from .api import ( # noqa: E402
5
+ DEFAULT_STORE_ROOT,
6
+ CliContext,
7
+ DeviceConnection,
8
+ DeviceKey,
9
+ DriftCheckResult,
10
+ DriftGate,
11
+ GenerationResult,
12
+ GenerationTokenUsage,
13
+ GroupEvaluation,
14
+ LLMCLIResponse,
15
+ LLMProviderConfig,
16
+ Mode,
17
+ NamingResolution,
18
+ NamingTokenUsage,
19
+ ParseResult,
20
+ PromotionDecision,
21
+ PromotionGate,
22
+ PromotionMetadata,
23
+ PromotionMode,
24
+ PromotionRunResult,
25
+ Reference,
26
+ ReferenceGroup,
27
+ ReferenceVariant,
28
+ RegexBuilder,
29
+ Sampler,
30
+ TrialMetadata,
31
+ TrialResult,
32
+ UserReviewedRequest,
33
+ build_integration,
34
+ build_reference_summary,
35
+ check_drift,
36
+ cli_name,
37
+ decide_promotion,
38
+ discover_device_keys,
39
+ evaluate_cases,
40
+ generate,
41
+ parse,
42
+ promote_auto,
43
+ promote_user_reviewed,
44
+ resolve_cli_name,
45
+ run_command_pipeline,
46
+ sample,
47
+ write_reference_summary,
48
+ )
49
+
50
+ __all__ = [
51
+ "version",
52
+ "__version__",
53
+ "cli_name",
54
+ "resolve_cli_name",
55
+ "NamingResolution",
56
+ "CliContext",
57
+ "RegexBuilder",
58
+ "LLMCLIResponse",
59
+ "NamingTokenUsage",
60
+ "sample",
61
+ "DeviceConnection",
62
+ "Sampler",
63
+ "generate",
64
+ "GenerationResult",
65
+ "GenerationTokenUsage",
66
+ "parse",
67
+ "ParseResult",
68
+ "run_command_pipeline",
69
+ "LLMProviderConfig",
70
+ "TrialMetadata",
71
+ "TrialResult",
72
+ "Mode",
73
+ "build_integration",
74
+ "build_reference_summary",
75
+ "write_reference_summary",
76
+ "Reference",
77
+ "ReferenceGroup",
78
+ "ReferenceVariant",
79
+ "promote_auto",
80
+ "promote_user_reviewed",
81
+ "decide_promotion",
82
+ "evaluate_cases",
83
+ "PromotionGate",
84
+ "PromotionMetadata",
85
+ "UserReviewedRequest",
86
+ "GroupEvaluation",
87
+ "PromotionRunResult",
88
+ "PromotionDecision",
89
+ "PromotionMode",
90
+ "check_drift",
91
+ "DriftGate",
92
+ "DriftCheckResult",
93
+ "DEFAULT_STORE_ROOT",
94
+ "DeviceKey",
95
+ "discover_device_keys",
96
+ ]
@@ -0,0 +1,127 @@
1
+ """Public Python API — the single place to import parseforge's stable,
2
+ supported surface from. Mirrors textfsm-ai's own api.py convention
3
+ (https://github.com/Geeks-Trident-LLC/textfsm-ai): one entry point per
4
+ pipeline stage (SPEC.md §5), plus the types each one returns or accepts.
5
+
6
+ Provider-specific naming builders (``AnthropicRegexBuilder``,
7
+ ``OCIRegexBuilder``, ...) aren't re-exported here — they're already a
8
+ clean, documented import path via :mod:`parseforge.naming` directly, and
9
+ listing all eighteen here would bloat this module without adding
10
+ anything. Anything not listed in ``__all__`` (module-internal helpers,
11
+ provider implementation details, CLI plumbing) isn't part of the public
12
+ API and may change without notice.
13
+
14
+ ``naming``'s and ``generation``'s ``TokenUsage`` classes are separate
15
+ types that happen to share a name (naming makes one call per cache
16
+ miss; generation's is already accumulated across every LLM call in its
17
+ own pipeline, see ``GenerationResult.usage``) — aliased here as
18
+ ``NamingTokenUsage``/``GenerationTokenUsage`` to avoid the collision,
19
+ the same convention this package's own test suite already uses.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ from .drift import DriftCheckResult, DriftGate, check_drift
25
+ from .generation import GenerationResult, generate
26
+ from .generation import TokenUsage as GenerationTokenUsage
27
+ from .integration import (
28
+ Reference,
29
+ ReferenceGroup,
30
+ ReferenceVariant,
31
+ build_integration,
32
+ build_reference_summary,
33
+ write_reference_summary,
34
+ )
35
+ from .naming import (
36
+ CliContext,
37
+ LLMCLIResponse,
38
+ NamingResolution,
39
+ RegexBuilder,
40
+ cli_name,
41
+ resolve_cli_name,
42
+ )
43
+ from .naming import TokenUsage as NamingTokenUsage
44
+ from .paths import DEFAULT_STORE_ROOT, DeviceKey, discover_device_keys
45
+ from .pipeline import (
46
+ LLMProviderConfig,
47
+ Mode,
48
+ TrialMetadata,
49
+ TrialResult,
50
+ run_command_pipeline,
51
+ )
52
+ from .promotion import (
53
+ GroupEvaluation,
54
+ PromotionDecision,
55
+ PromotionGate,
56
+ PromotionMetadata,
57
+ PromotionMode,
58
+ PromotionRunResult,
59
+ UserReviewedRequest,
60
+ decide_promotion,
61
+ evaluate_cases,
62
+ promote_auto,
63
+ promote_user_reviewed,
64
+ )
65
+ from .sampling import DeviceConnection, Sampler, sample
66
+ from .validation import ParseResult, parse
67
+
68
+ __all__ = [
69
+ # Naming (SPEC.md §2) — resolve a raw CLI command to its canonical
70
+ # cli-name, cached after the first LLM call.
71
+ "cli_name",
72
+ "resolve_cli_name",
73
+ "NamingResolution",
74
+ "CliContext",
75
+ "RegexBuilder",
76
+ "LLMCLIResponse",
77
+ "NamingTokenUsage",
78
+ # Sampling (SPEC.md §3) — capture raw command output from a device.
79
+ "sample",
80
+ "DeviceConnection",
81
+ "Sampler",
82
+ # Generation (SPEC.md §5 steps 5-6) — sample -> candidate TextFSM
83
+ # template, via textfsm-ai's delivery pipeline.
84
+ "generate",
85
+ "GenerationResult",
86
+ "GenerationTokenUsage",
87
+ # Validation (SPEC.md §5 step 7) — self-validate a template against
88
+ # its own sample.
89
+ "parse",
90
+ "ParseResult",
91
+ # Pipeline orchestration (SPEC.md §5) — one call runs steps 1-7
92
+ # (naming -> sampling -> generation -> validation) for a single trial.
93
+ "run_command_pipeline",
94
+ "LLMProviderConfig",
95
+ "TrialMetadata",
96
+ "TrialResult",
97
+ "Mode",
98
+ # Integration (SPEC.md §5 step 8) — cluster trials by output schema.
99
+ "build_integration",
100
+ "build_reference_summary",
101
+ "write_reference_summary",
102
+ "Reference",
103
+ "ReferenceGroup",
104
+ "ReferenceVariant",
105
+ # Promotion (SPEC.md §5 step 9) — auto-promote groups that clear
106
+ # their gate; queue everything else for human review.
107
+ "promote_auto",
108
+ "promote_user_reviewed",
109
+ "decide_promotion",
110
+ "evaluate_cases",
111
+ "PromotionGate",
112
+ "PromotionMetadata",
113
+ "UserReviewedRequest",
114
+ "GroupEvaluation",
115
+ "PromotionRunResult",
116
+ "PromotionDecision",
117
+ "PromotionMode",
118
+ # Drift monitoring (SPEC.md §5 step 11) — check an authoritative
119
+ # template against new production samples.
120
+ "check_drift",
121
+ "DriftGate",
122
+ "DriftCheckResult",
123
+ # Store-root layout helpers.
124
+ "DEFAULT_STORE_ROOT",
125
+ "DeviceKey",
126
+ "discover_device_keys",
127
+ ]
@@ -659,6 +659,7 @@ def run_cmd(
659
659
  click.echo(f"cli_name : {result.cli_name}")
660
660
  click.echo(f"passed : {result.passed}")
661
661
  click.echo(f"run_dir : {result.run_dir}")
662
+ click.echo(f"usage : {result.total_usage}")
662
663
 
663
664
 
664
665
  def _check_connector(
@@ -1200,7 +1201,7 @@ def trial_cmd(config_path: str, store_root_opt: str | None) -> None:
1200
1201
  for command, result in zip(cfg.commands, results):
1201
1202
  click.echo(
1202
1203
  f"{command} -> cli_name={result.cli_name} passed={result.passed} "
1203
- f"run_dir={result.run_dir}"
1204
+ f"run_dir={result.run_dir} usage={result.total_usage}"
1204
1205
  )
1205
1206
  passed_count += int(result.passed)
1206
1207
  click.echo(f"{passed_count}/{len(results)} passed")
@@ -44,7 +44,6 @@ class TokenUsage:
44
44
  input_tokens: int
45
45
  output_tokens: int
46
46
  total_tokens: int
47
- estimated_cost: float
48
47
 
49
48
 
50
49
  @dataclass(frozen=True)
@@ -95,7 +94,6 @@ def generate(
95
94
  input_tokens=usage_data.get("input_tokens", 0),
96
95
  output_tokens=usage_data.get("output_tokens", 0),
97
96
  total_tokens=usage_data.get("total_tokens", 0),
98
- estimated_cost=usage_data.get("estimated_cost", 0.0),
99
97
  )
100
98
 
101
99
  gen_stage = (debug.get("generation_pipeline") or {}).get("last_stage") or {}
@@ -37,7 +37,6 @@ class TokenUsage:
37
37
  input_tokens: int
38
38
  output_tokens: int
39
39
  total_tokens: int
40
- estimated_cost: float
41
40
 
42
41
 
43
42
  @dataclass(frozen=True)
@@ -6,7 +6,6 @@ import time
6
6
  from typing import TYPE_CHECKING, Any
7
7
 
8
8
  from ..llm import CliContext, LLMCLIResponse, TokenUsage, build_prompt
9
- from .cost import estimate_cost
10
9
  from .errors import format_llm_error_reason, is_retryable
11
10
  from .models import default_model
12
11
  from .text import extract_pattern
@@ -89,9 +88,7 @@ class AnthropicRegexBuilder:
89
88
  return LLMCLIResponse(
90
89
  content="",
91
90
  raw=exc,
92
- usage=TokenUsage(
93
- input_tokens=0, output_tokens=0, total_tokens=0, estimated_cost=0.0
94
- ),
91
+ usage=TokenUsage(input_tokens=0, output_tokens=0, total_tokens=0),
95
92
  duration_ms=(time.monotonic() - start) * 1000,
96
93
  reason=format_llm_error_reason(exc),
97
94
  ready=False,
@@ -111,13 +108,6 @@ class AnthropicRegexBuilder:
111
108
  input_tokens=response.usage.input_tokens,
112
109
  output_tokens=response.usage.output_tokens,
113
110
  total_tokens=total_tokens,
114
- estimated_cost=estimate_cost(
115
- input_tokens=response.usage.input_tokens,
116
- output_tokens=response.usage.output_tokens,
117
- total_tokens=total_tokens,
118
- provider=self.provider,
119
- model=self.model,
120
- ),
121
111
  ),
122
112
  duration_ms=duration_ms,
123
113
  reason=response.stop_reason or "",
@@ -17,7 +17,6 @@ import time
17
17
  from typing import TYPE_CHECKING, Any
18
18
 
19
19
  from ..llm import CliContext, LLMCLIResponse, TokenUsage, build_prompt
20
- from .cost import estimate_cost
21
20
  from .text import extract_pattern
22
21
 
23
22
  if TYPE_CHECKING:
@@ -161,9 +160,7 @@ class AzureRegexBuilder:
161
160
  return LLMCLIResponse(
162
161
  content="",
163
162
  raw=exc,
164
- usage=TokenUsage(
165
- input_tokens=0, output_tokens=0, total_tokens=0, estimated_cost=0.0
166
- ),
163
+ usage=TokenUsage(input_tokens=0, output_tokens=0, total_tokens=0),
167
164
  duration_ms=(time.monotonic() - start) * 1000,
168
165
  reason=_format_error_reason(exc),
169
166
  ready=False,
@@ -178,18 +175,6 @@ class AzureRegexBuilder:
178
175
  input_tokens=response.usage.prompt_tokens,
179
176
  output_tokens=response.usage.completion_tokens,
180
177
  total_tokens=response.usage.total_tokens,
181
- estimated_cost=estimate_cost(
182
- input_tokens=response.usage.prompt_tokens,
183
- output_tokens=response.usage.completion_tokens,
184
- total_tokens=response.usage.total_tokens,
185
- provider=self.provider,
186
- # No public pricing table for a customer's own Azure
187
- # deployment — estimate_cost() falls back to 0.0 with
188
- # a warning for an unrecognized (provider, model) pair,
189
- # which is the correct/expected outcome here, not an
190
- # error condition.
191
- model=self._resolved_deployment or "",
192
- ),
193
178
  ),
194
179
  duration_ms=duration_ms,
195
180
  reason=choice.finish_reason or "",