parseforge 0.3.0__tar.gz → 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. parseforge-0.3.2/PKG-INFO +195 -0
  2. parseforge-0.3.2/README.md +97 -0
  3. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/__init__.py +1 -1
  4. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/cli/main.py +1 -1
  5. parseforge-0.3.2/parseforge/naming/providers/__init__.py +41 -0
  6. parseforge-0.3.2/parseforge/naming/providers/anyask_builder.py +248 -0
  7. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/pipeline.py +1 -1
  8. parseforge-0.3.2/parseforge.egg-info/PKG-INFO +195 -0
  9. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge.egg-info/SOURCES.txt +1 -19
  10. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge.egg-info/requires.txt +20 -26
  11. {parseforge-0.3.0 → parseforge-0.3.2}/pyproject.toml +40 -49
  12. parseforge-0.3.0/PKG-INFO +0 -384
  13. parseforge-0.3.0/README.md +0 -280
  14. parseforge-0.3.0/parseforge/naming/providers/__init__.py +0 -39
  15. parseforge-0.3.0/parseforge/naming/providers/anthropic.py +0 -115
  16. parseforge-0.3.0/parseforge/naming/providers/azure.py +0 -182
  17. parseforge-0.3.0/parseforge/naming/providers/bedrock.py +0 -166
  18. parseforge-0.3.0/parseforge/naming/providers/cerebras.py +0 -118
  19. parseforge-0.3.0/parseforge/naming/providers/cohere.py +0 -163
  20. parseforge-0.3.0/parseforge/naming/providers/deepseek.py +0 -131
  21. parseforge-0.3.0/parseforge/naming/providers/errors.py +0 -63
  22. parseforge-0.3.0/parseforge/naming/providers/fireworks.py +0 -118
  23. parseforge-0.3.0/parseforge/naming/providers/gemini.py +0 -150
  24. parseforge-0.3.0/parseforge/naming/providers/groq.py +0 -116
  25. parseforge-0.3.0/parseforge/naming/providers/mistral.py +0 -153
  26. parseforge-0.3.0/parseforge/naming/providers/moonshot.py +0 -120
  27. parseforge-0.3.0/parseforge/naming/providers/oci.py +0 -200
  28. parseforge-0.3.0/parseforge/naming/providers/openai.py +0 -107
  29. parseforge-0.3.0/parseforge/naming/providers/openrouter.py +0 -120
  30. parseforge-0.3.0/parseforge/naming/providers/perplexity.py +0 -118
  31. parseforge-0.3.0/parseforge/naming/providers/together.py +0 -118
  32. parseforge-0.3.0/parseforge/naming/providers/vertexai.py +0 -181
  33. parseforge-0.3.0/parseforge/naming/providers/xai.py +0 -116
  34. parseforge-0.3.0/parseforge.egg-info/PKG-INFO +0 -384
  35. {parseforge-0.3.0 → parseforge-0.3.2}/LICENSE +0 -0
  36. {parseforge-0.3.0 → parseforge-0.3.2}/MANIFEST.in +0 -0
  37. {parseforge-0.3.0 → parseforge-0.3.2}/SPEC.md +0 -0
  38. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/api.py +0 -0
  39. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/cli/__init__.py +0 -0
  40. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/cli/config.py +0 -0
  41. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/drift.py +0 -0
  42. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/generation.py +0 -0
  43. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/integration.py +0 -0
  44. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/naming/__init__.py +0 -0
  45. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/naming/assemble.py +0 -0
  46. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/naming/cache.py +0 -0
  47. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/naming/llm.py +0 -0
  48. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/naming/prompts.py +0 -0
  49. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/naming/prompts.yaml +0 -0
  50. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/naming/providers/models.py +0 -0
  51. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/naming/providers/models.yaml +0 -0
  52. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/naming/providers/text.py +0 -0
  53. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/naming/resolver.py +0 -0
  54. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/paths.py +0 -0
  55. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/promotion.py +0 -0
  56. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/sampling/__init__.py +0 -0
  57. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/sampling/backends/__init__.py +0 -0
  58. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/sampling/backends/netmiko.py +0 -0
  59. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/sampling/core.py +0 -0
  60. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge/validation.py +0 -0
  61. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge.egg-info/dependency_links.txt +0 -0
  62. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge.egg-info/entry_points.txt +0 -0
  63. {parseforge-0.3.0 → parseforge-0.3.2}/parseforge.egg-info/top_level.txt +0 -0
  64. {parseforge-0.3.0 → parseforge-0.3.2}/setup.cfg +0 -0
@@ -0,0 +1,195 @@
1
+ Metadata-Version: 2.4
2
+ Name: parseforge
3
+ Version: 0.3.2
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
+ Requires-Dist: anyask>=0.1.1
31
+ Provides-Extra: anthropic
32
+ Requires-Dist: anyask[anthropic]>=0.1.1; extra == "anthropic"
33
+ Requires-Dist: textfsm-ai[anthropic]>=0.7.1; extra == "anthropic"
34
+ Provides-Extra: deepseek
35
+ Requires-Dist: anyask[deepseek]>=0.1.1; extra == "deepseek"
36
+ Requires-Dist: textfsm-ai[deepseek]>=0.7.1; extra == "deepseek"
37
+ Provides-Extra: openai
38
+ Requires-Dist: anyask[openai]>=0.1.1; extra == "openai"
39
+ Requires-Dist: textfsm-ai[openai]>=0.7.1; extra == "openai"
40
+ Provides-Extra: groq
41
+ Requires-Dist: anyask[groq]>=0.1.1; extra == "groq"
42
+ Requires-Dist: textfsm-ai[groq]>=0.7.1; extra == "groq"
43
+ Provides-Extra: xai
44
+ Requires-Dist: anyask[xai]>=0.1.1; extra == "xai"
45
+ Requires-Dist: textfsm-ai[xai]>=0.7.1; extra == "xai"
46
+ Provides-Extra: together
47
+ Requires-Dist: anyask[together]>=0.1.1; extra == "together"
48
+ Requires-Dist: textfsm-ai[together]>=0.7.1; extra == "together"
49
+ Provides-Extra: fireworks
50
+ Requires-Dist: anyask[fireworks]>=0.1.1; extra == "fireworks"
51
+ Requires-Dist: textfsm-ai[fireworks]>=0.7.1; extra == "fireworks"
52
+ Provides-Extra: perplexity
53
+ Requires-Dist: anyask[perplexity]>=0.1.1; extra == "perplexity"
54
+ Requires-Dist: textfsm-ai[perplexity]>=0.7.1; extra == "perplexity"
55
+ Provides-Extra: openrouter
56
+ Requires-Dist: anyask[openrouter]>=0.1.1; extra == "openrouter"
57
+ Requires-Dist: textfsm-ai[openrouter]>=0.7.1; extra == "openrouter"
58
+ Provides-Extra: moonshot
59
+ Requires-Dist: anyask[moonshot]>=0.1.1; extra == "moonshot"
60
+ Requires-Dist: textfsm-ai[moonshot]>=0.7.1; extra == "moonshot"
61
+ Provides-Extra: cerebras
62
+ Requires-Dist: anyask[cerebras]>=0.1.1; extra == "cerebras"
63
+ Requires-Dist: textfsm-ai[cerebras]>=0.7.1; extra == "cerebras"
64
+ Provides-Extra: mistral
65
+ Requires-Dist: anyask[mistral]>=0.1.1; extra == "mistral"
66
+ Requires-Dist: textfsm-ai[mistral]>=0.7.1; extra == "mistral"
67
+ Provides-Extra: cohere
68
+ Requires-Dist: anyask[cohere]>=0.1.1; extra == "cohere"
69
+ Requires-Dist: textfsm-ai[cohere]>=0.7.1; extra == "cohere"
70
+ Provides-Extra: azure
71
+ Requires-Dist: anyask[azure]>=0.1.1; extra == "azure"
72
+ Requires-Dist: textfsm-ai[azure]>=0.7.1; extra == "azure"
73
+ Provides-Extra: gemini
74
+ Requires-Dist: anyask[gemini]>=0.1.1; extra == "gemini"
75
+ Requires-Dist: textfsm-ai[gemini]>=0.7.1; extra == "gemini"
76
+ Provides-Extra: vertexai
77
+ Requires-Dist: anyask[vertexai]>=0.1.1; extra == "vertexai"
78
+ Requires-Dist: textfsm-ai[vertexai]>=0.7.1; extra == "vertexai"
79
+ Provides-Extra: bedrock
80
+ Requires-Dist: anyask[bedrock]>=0.1.1; extra == "bedrock"
81
+ Requires-Dist: textfsm-ai[bedrock]>=0.7.1; extra == "bedrock"
82
+ Provides-Extra: oci
83
+ Requires-Dist: anyask[oci]>=0.1.1; extra == "oci"
84
+ Requires-Dist: textfsm-ai[oci]>=0.7.1; extra == "oci"
85
+ Provides-Extra: sampling
86
+ Requires-Dist: netmiko>=4.0; extra == "sampling"
87
+ Provides-Extra: dev
88
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
89
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
90
+ Requires-Dist: ruff>=0.4.0; extra == "dev"
91
+ Requires-Dist: black>=24.0.0; extra == "dev"
92
+ Requires-Dist: mypy>=1.10.0; extra == "dev"
93
+ Requires-Dist: anyask[all]>=0.1.1; extra == "dev"
94
+ Provides-Extra: release
95
+ Requires-Dist: bump2version; extra == "release"
96
+ Requires-Dist: build; extra == "release"
97
+ Dynamic: license-file
98
+
99
+ # ParseForge
100
+
101
+ LLM-driven pipeline that forges, cross-validates, and promotes [TextFSM](https://github.com/google/textfsm)
102
+ templates from network device CLI output.
103
+
104
+ Full design plan: [SPEC.md](https://github.com/Geeks-Trident-LLC/parseforge/blob/main/SPEC.md).
105
+
106
+ ## What is ParseForge?
107
+
108
+ Network devices — routers, switches, firewalls — only speak in plain text:
109
+ the output of a `show` command. To use that output in a script, dashboard,
110
+ or automation tool, something has to turn it into structured data first.
111
+ That "something" is a [TextFSM](https://github.com/google/textfsm)
112
+ template: a set of parsing rules for one specific command's output.
113
+
114
+ ParseForge writes those templates for you. Point it at a device (or a
115
+ saved copy of its output) and an AI provider of your choice, and it
116
+ produces a template, checks that the template actually parses the sample
117
+ it was built from, and tracks the result so you can see exactly how much
118
+ it's been tested before you trust it in production.
119
+
120
+ ## Why do you need ParseForge?
121
+
122
+ Every network automation project eventually hits the same wall: someone
123
+ has to write and maintain a parser for every command's output, by hand,
124
+ in regex. It's slow, it's easy to get subtly wrong, and it only gets
125
+ worse as you add more device types, vendors, and firmware versions —
126
+ each with its own quirks in how the same command's output is formatted.
127
+
128
+ ParseForge replaces that manual work with a repeatable pipeline: an AI
129
+ drafts the parser, ParseForge verifies it against real output before
130
+ trusting it, and only well-tested results get promoted to production use
131
+ automatically — anything uncertain is queued for a quick human look
132
+ instead of shipped blind. If your team does network automation and needs
133
+ structured data out of CLI output, ParseForge is the part that used to be
134
+ tedious, made fast and safe instead.
135
+
136
+ ## Features
137
+
138
+ - **Nothing ships unreviewed.** Every AI-generated template starts as
139
+ unproven evidence. Once it's been tested enough times with consistent
140
+ results, it's promoted automatically; anything less certain waits for a
141
+ person to check it.
142
+ - **Learns a command once, reuses it forever.** The first time a command
143
+ runs, ParseForge asks the AI to name it; every time after that, it's
144
+ a free, instant lookup — no repeat AI calls, no repeat cost.
145
+ - **Checks its own work.** Every generated template is tested against the
146
+ real output it was built from before it's ever counted as a pass.
147
+ - **Notices when things change.** If a device's output format changes
148
+ later, ParseForge catches it and automatically kicks off a retest,
149
+ instead of quietly parsing it wrong.
150
+ - **Use it your way.** A command-line tool for quick, ad hoc use, or a
151
+ Python library for wiring straight into your own automation — same
152
+ functionality either way.
153
+
154
+ ## Supported providers
155
+
156
+ Eighteen AI providers behind one common interface — mix and match, or use
157
+ two different ones in the same run:
158
+
159
+ Anthropic, OpenAI, DeepSeek, Groq, xAI, Together, Fireworks, Perplexity,
160
+ OpenRouter, Moonshot, Cerebras, Mistral, Cohere, Azure OpenAI, Google
161
+ Gemini, Google Vertex AI, Amazon Bedrock, and Oracle Cloud Infrastructure
162
+ (OCI) — including the four with non-standard authentication (Azure
163
+ deployment names, GCP Application Default Credentials, AWS's own
164
+ credential chain, OCI request-signing), handled transparently. See
165
+ [Providers](https://geeks-trident-llc.github.io/parseforge/guides/providers/)
166
+ for each one's install extra, auth requirements, and default model.
167
+
168
+ ## Installation
169
+
170
+ ```bash
171
+ # minimal install, no AI-provider SDK
172
+ pip install parseforge
173
+
174
+ # with a provider extra, e.g. anthropic
175
+ pip install parseforge[anthropic]
176
+
177
+ # local development
178
+ pip install -e ".[dev,sampling]"
179
+ ```
180
+
181
+ For the full extras list (all eighteen providers, `sampling`, combining
182
+ extras, per-provider `requirements/` files, and the dev/test/release
183
+ setup), see
184
+ [Installation](https://geeks-trident-llc.github.io/parseforge/getting-started/installation/).
185
+
186
+ ## Reference
187
+
188
+ - [Documentation site](https://geeks-trident-llc.github.io/parseforge/) ([source](./docs/index.md))
189
+ - [Quickstart](https://geeks-trident-llc.github.io/parseforge/getting-started/quickstart/) — a full walkthrough, from a single lookup to the end-to-end workflow
190
+ - [Providers](https://geeks-trident-llc.github.io/parseforge/guides/providers/) — every provider's extra, auth requirements, and default model
191
+ - [CLI Guide](https://geeks-trident-llc.github.io/parseforge/guides/cli/) — every command, in full
192
+ - [Python API guide](https://geeks-trident-llc.github.io/parseforge/guides/python-api/) — calling parseforge from Python instead of the CLI
193
+ - [API Reference](https://geeks-trident-llc.github.io/parseforge/reference/api/) — every public function/class, by pipeline stage
194
+ - [Changelog](https://geeks-trident-llc.github.io/parseforge/changelog/) — what shipped in each release
195
+ - [SPEC.md](https://github.com/Geeks-Trident-LLC/parseforge/blob/main/SPEC.md) — full design plan and open questions
@@ -0,0 +1,97 @@
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](https://github.com/Geeks-Trident-LLC/parseforge/blob/main/SPEC.md).
7
+
8
+ ## What is ParseForge?
9
+
10
+ Network devices — routers, switches, firewalls — only speak in plain text:
11
+ the output of a `show` command. To use that output in a script, dashboard,
12
+ or automation tool, something has to turn it into structured data first.
13
+ That "something" is a [TextFSM](https://github.com/google/textfsm)
14
+ template: a set of parsing rules for one specific command's output.
15
+
16
+ ParseForge writes those templates for you. Point it at a device (or a
17
+ saved copy of its output) and an AI provider of your choice, and it
18
+ produces a template, checks that the template actually parses the sample
19
+ it was built from, and tracks the result so you can see exactly how much
20
+ it's been tested before you trust it in production.
21
+
22
+ ## Why do you need ParseForge?
23
+
24
+ Every network automation project eventually hits the same wall: someone
25
+ has to write and maintain a parser for every command's output, by hand,
26
+ in regex. It's slow, it's easy to get subtly wrong, and it only gets
27
+ worse as you add more device types, vendors, and firmware versions —
28
+ each with its own quirks in how the same command's output is formatted.
29
+
30
+ ParseForge replaces that manual work with a repeatable pipeline: an AI
31
+ drafts the parser, ParseForge verifies it against real output before
32
+ trusting it, and only well-tested results get promoted to production use
33
+ automatically — anything uncertain is queued for a quick human look
34
+ instead of shipped blind. If your team does network automation and needs
35
+ structured data out of CLI output, ParseForge is the part that used to be
36
+ tedious, made fast and safe instead.
37
+
38
+ ## Features
39
+
40
+ - **Nothing ships unreviewed.** Every AI-generated template starts as
41
+ unproven evidence. Once it's been tested enough times with consistent
42
+ results, it's promoted automatically; anything less certain waits for a
43
+ person to check it.
44
+ - **Learns a command once, reuses it forever.** The first time a command
45
+ runs, ParseForge asks the AI to name it; every time after that, it's
46
+ a free, instant lookup — no repeat AI calls, no repeat cost.
47
+ - **Checks its own work.** Every generated template is tested against the
48
+ real output it was built from before it's ever counted as a pass.
49
+ - **Notices when things change.** If a device's output format changes
50
+ later, ParseForge catches it and automatically kicks off a retest,
51
+ instead of quietly parsing it wrong.
52
+ - **Use it your way.** A command-line tool for quick, ad hoc use, or a
53
+ Python library for wiring straight into your own automation — same
54
+ functionality either way.
55
+
56
+ ## Supported providers
57
+
58
+ Eighteen AI providers behind one common interface — mix and match, or use
59
+ two different ones in the same run:
60
+
61
+ Anthropic, OpenAI, DeepSeek, Groq, xAI, Together, Fireworks, Perplexity,
62
+ OpenRouter, Moonshot, Cerebras, Mistral, Cohere, Azure OpenAI, Google
63
+ Gemini, Google Vertex AI, Amazon Bedrock, and Oracle Cloud Infrastructure
64
+ (OCI) — including the four with non-standard authentication (Azure
65
+ deployment names, GCP Application Default Credentials, AWS's own
66
+ credential chain, OCI request-signing), handled transparently. See
67
+ [Providers](https://geeks-trident-llc.github.io/parseforge/guides/providers/)
68
+ for each one's install extra, auth requirements, and default model.
69
+
70
+ ## Installation
71
+
72
+ ```bash
73
+ # minimal install, no AI-provider SDK
74
+ pip install parseforge
75
+
76
+ # with a provider extra, e.g. anthropic
77
+ pip install parseforge[anthropic]
78
+
79
+ # local development
80
+ pip install -e ".[dev,sampling]"
81
+ ```
82
+
83
+ For the full extras list (all eighteen providers, `sampling`, combining
84
+ extras, per-provider `requirements/` files, and the dev/test/release
85
+ setup), see
86
+ [Installation](https://geeks-trident-llc.github.io/parseforge/getting-started/installation/).
87
+
88
+ ## Reference
89
+
90
+ - [Documentation site](https://geeks-trident-llc.github.io/parseforge/) ([source](./docs/index.md))
91
+ - [Quickstart](https://geeks-trident-llc.github.io/parseforge/getting-started/quickstart/) — a full walkthrough, from a single lookup to the end-to-end workflow
92
+ - [Providers](https://geeks-trident-llc.github.io/parseforge/guides/providers/) — every provider's extra, auth requirements, and default model
93
+ - [CLI Guide](https://geeks-trident-llc.github.io/parseforge/guides/cli/) — every command, in full
94
+ - [Python API guide](https://geeks-trident-llc.github.io/parseforge/guides/python-api/) — calling parseforge from Python instead of the CLI
95
+ - [API Reference](https://geeks-trident-llc.github.io/parseforge/reference/api/) — every public function/class, by pipeline stage
96
+ - [Changelog](https://geeks-trident-llc.github.io/parseforge/changelog/) — what shipped in each release
97
+ - [SPEC.md](https://github.com/Geeks-Trident-LLC/parseforge/blob/main/SPEC.md) — full design plan and open questions
@@ -1,4 +1,4 @@
1
- __version__ = "0.3.0"
1
+ __version__ = "0.3.2"
2
2
  version = __version__
3
3
 
4
4
  from .api import ( # noqa: E402
@@ -20,7 +20,7 @@ from parseforge import (
20
20
  validation,
21
21
  )
22
22
  from parseforge.cli import config as cli_config
23
- from parseforge.naming.providers.azure import DEFAULT_API_VERSION
23
+ from parseforge.naming.providers.anyask_builder import DEFAULT_API_VERSION
24
24
 
25
25
  _BUILDERS: dict[str, type[naming.RegexBuilder]] = {
26
26
  "anthropic": naming.AnthropicRegexBuilder,
@@ -0,0 +1,41 @@
1
+ from .anyask_builder import (
2
+ AnthropicRegexBuilder,
3
+ AzureRegexBuilder,
4
+ BedrockRegexBuilder,
5
+ CerebrasRegexBuilder,
6
+ CohereRegexBuilder,
7
+ DeepSeekRegexBuilder,
8
+ FireworksRegexBuilder,
9
+ GeminiRegexBuilder,
10
+ GroqRegexBuilder,
11
+ MistralRegexBuilder,
12
+ MoonshotRegexBuilder,
13
+ OCIRegexBuilder,
14
+ OpenAIRegexBuilder,
15
+ OpenRouterRegexBuilder,
16
+ PerplexityRegexBuilder,
17
+ TogetherRegexBuilder,
18
+ VertexAIRegexBuilder,
19
+ XAIRegexBuilder,
20
+ )
21
+
22
+ __all__ = [
23
+ "AnthropicRegexBuilder",
24
+ "AzureRegexBuilder",
25
+ "BedrockRegexBuilder",
26
+ "CerebrasRegexBuilder",
27
+ "CohereRegexBuilder",
28
+ "DeepSeekRegexBuilder",
29
+ "FireworksRegexBuilder",
30
+ "GeminiRegexBuilder",
31
+ "GroqRegexBuilder",
32
+ "MistralRegexBuilder",
33
+ "MoonshotRegexBuilder",
34
+ "OCIRegexBuilder",
35
+ "OpenAIRegexBuilder",
36
+ "OpenRouterRegexBuilder",
37
+ "PerplexityRegexBuilder",
38
+ "TogetherRegexBuilder",
39
+ "VertexAIRegexBuilder",
40
+ "XAIRegexBuilder",
41
+ ]
@@ -0,0 +1,248 @@
1
+ """Generic RegexBuilder implementation backed by the ``anyask`` package.
2
+
3
+ Replaces 18 hand-rolled per-provider SDK integrations (anthropic.py,
4
+ azure.py, bedrock.py, ...) with a single class parameterized by
5
+ ``provider``: anyask (https://github.com/Geeks-Trident-LLC/anyask) already
6
+ implements the exact same 18 vendors behind one normalized interface
7
+ (``anyask.get_provider(provider, **construction_kwargs).generate_sync(
8
+ prompt, model=..., **call_kwargs)`` -> ``AskResponse``), so there's no need
9
+ to hand-roll a client/error-classification/response-parsing dance per SDK
10
+ here anymore.
11
+
12
+ Two things anyask deliberately does NOT do, that this module still has to:
13
+
14
+ 1. Retry classification -- anyask raises a flat ``ProviderError`` for every
15
+ failed call (the original SDK exception is always attached via
16
+ ``__cause__``). Nothing in this package retries a naming call today (see
17
+ resolver.py), so no classification is reconstructed here either -- a
18
+ failed call always becomes a non-``ready`` LLMCLIResponse.
19
+ 2. ``finish_reason`` normalization -- anyask leaves it raw/provider-specific
20
+ (a plain string for most vendors, a ``FinishReason`` enum member for
21
+ Gemini/Vertex AI) precisely so callers can build their own truncation
22
+ check. See ``_is_ready`` below.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import time
28
+ from typing import Any
29
+
30
+ import anyask
31
+
32
+ from ..llm import CliContext, LLMCLIResponse, TokenUsage, build_prompt
33
+ from .models import default_model
34
+ from .text import extract_pattern
35
+
36
+ _DEFAULT_MAX_TOKENS = 1024
37
+
38
+ # Matches anyask's own AzureOpenAIProvider.__init__ fallback (confirmed by
39
+ # reading anyask/providers/azure.py) — also imported by pipeline.py/
40
+ # cli/main.py as the generation-side Azure config's own default, since
41
+ # textfsm-ai's Azure provider has no from_env()-style fallback of its own.
42
+ DEFAULT_API_VERSION = "2024-02-15-preview"
43
+
44
+ # finish_reason values (case-insensitively, after str()) that mean the
45
+ # response was cut off before completing rather than stopping naturally --
46
+ # everything else (anthropic's "end_turn", openai-family's "stop", cohere's
47
+ # "COMPLETE", gemini/vertexai's FinishReason.STOP, ...) counts as ready.
48
+ # Naming's prompt never triggers a tool-call or content-filter finish, so
49
+ # this denylist -- rather than an per-vendor allowlist of every "success"
50
+ # value -- is a safe simplification of the exact-match checks the 18
51
+ # deleted provider files each carried individually.
52
+ _TRUNCATED_FINISH_REASONS = frozenset({"length", "max_tokens", "max_output_tokens"})
53
+
54
+ # Vertex AI is the one provider where parseforge's own constructor kwarg
55
+ # name (`location`, matching its CLI flag --gcp-location) differs from
56
+ # anyask's construction-kwarg name for the same concept (`region`, reused
57
+ # from Bedrock/OCI's own "region" keyword -- see anyask/providers/
58
+ # vertexai.py). Every other provider's kwarg names already line up exactly
59
+ # (api_key, endpoint, api_version, deployment, project, region,
60
+ # compartment_id).
61
+ _CONSTRUCTION_KWARG_RENAMES: dict[str, dict[str, str]] = {
62
+ "vertexai": {"location": "region"},
63
+ }
64
+
65
+ # deepseek-v4-flash defaults to thinking mode ON, which burns the entire
66
+ # max_tokens budget on chain-of-thought (returned separately as
67
+ # reasoning_content) and leaves nothing for the actual answer in
68
+ # `content` -- anyask's own DeepSeekProvider doesn't disable this by
69
+ # default (confirmed by reading anyask/providers/deepseek.py), so it's
70
+ # still disabled here explicitly, exactly as the old deepseek.py builder
71
+ # did. See https://api-docs.deepseek.com/guides/thinking_mode/
72
+ _DEFAULT_CALL_KWARGS: dict[str, dict[str, Any]] = {
73
+ "deepseek": {"extra_body": {"thinking": {"type": "disabled"}}},
74
+ }
75
+
76
+
77
+ def _map_construction_kwargs(provider: str, kwargs: dict[str, Any]) -> dict[str, Any]:
78
+ renames = _CONSTRUCTION_KWARG_RENAMES.get(provider, {})
79
+ mapped = {renames.get(key, key): value for key, value in kwargs.items()}
80
+ # Drop falsy values so anyask's own env-var fallbacks (ANTHROPIC_API_KEY,
81
+ # BEDROCK_REGION, VERTEXAI_PROJECT, ...) still apply exactly as they did
82
+ # when the old per-provider builders left an unset kwarg as None.
83
+ return {key: value for key, value in mapped.items() if value}
84
+
85
+
86
+ def _is_ready(finish_reason: Any) -> bool:
87
+ if finish_reason is None:
88
+ return False
89
+ reason = str(getattr(finish_reason, "value", finish_reason)).lower()
90
+ return reason not in _TRUNCATED_FINISH_REASONS
91
+
92
+
93
+ def _format_error_reason(exc: BaseException) -> str:
94
+ cause = exc.__cause__ or exc
95
+ return f"LLM-ERROR-{type(cause).__name__}-{cause}"
96
+
97
+
98
+ class AnyAskRegexBuilder:
99
+ """Builds a cli-name regex pattern by prompting an LLM via ``anyask``.
100
+
101
+ The underlying anyask ``Provider`` instance is constructed lazily, on
102
+ the first actual call -- not in ``__init__`` -- so this can be used as
103
+ a default RegexBuilder without requiring credentials to be set for
104
+ cache-hit lookups, which never reach the LLM at all (see
105
+ resolver.cli_name). Extra ``**kwargs`` passed to ``build_pattern`` (or
106
+ a per-call ``model`` override) forward straight through to
107
+ ``anyask``'s ``generate_sync``.
108
+ """
109
+
110
+ #: Set on each concrete subclass below -- an anyask/parseforge provider
111
+ #: name, e.g. "anthropic", "azure", "bedrock".
112
+ provider: str = ""
113
+
114
+ def __init__(self, model: str | None = None, **kwargs: Any) -> None:
115
+ # Azure has no fixed model catalog in models.yaml -- its
116
+ # constructor's `deployment` kwarg stands in for a model choice
117
+ # instead (see build_pattern's own model handling below), so no
118
+ # default_model() lookup applies to it.
119
+ self.model = model or (
120
+ "" if self.provider == "azure" else default_model(self.provider)
121
+ )
122
+ self._construction_kwargs = _map_construction_kwargs(self.provider, kwargs)
123
+ self._client: anyask.Provider | None = None
124
+
125
+ def _get_client(self) -> anyask.Provider:
126
+ if self._client is None:
127
+ self._client = anyask.get_provider(
128
+ self.provider, **self._construction_kwargs
129
+ )
130
+ return self._client
131
+
132
+ def build_pattern(
133
+ self, command: str, context: CliContext, **kwargs: Any
134
+ ) -> LLMCLIResponse:
135
+ prompt = build_prompt(command, context)
136
+ max_tokens = kwargs.pop("max_tokens", None) or _DEFAULT_MAX_TOKENS
137
+ # Azure has no fixed model catalog -- its constructor's `deployment`
138
+ # kwarg already fixed which deployment to call, and passing a model
139
+ # here would just be ignored by anyask's AzureOpenAIProvider (falls
140
+ # back to self.deployment when model is falsy), so it's left unset.
141
+ model = kwargs.pop("model", None) or (
142
+ None if self.provider == "azure" else self.model
143
+ )
144
+ for key, value in _DEFAULT_CALL_KWARGS.get(self.provider, {}).items():
145
+ kwargs.setdefault(key, value)
146
+
147
+ start = time.monotonic()
148
+ try:
149
+ response = self._get_client().generate_sync(
150
+ prompt, model=model, max_tokens=max_tokens, **kwargs
151
+ )
152
+ except anyask.ProviderError as exc:
153
+ return LLMCLIResponse(
154
+ content="",
155
+ raw=exc,
156
+ usage=TokenUsage(input_tokens=0, output_tokens=0, total_tokens=0),
157
+ duration_ms=(time.monotonic() - start) * 1000,
158
+ reason=_format_error_reason(exc),
159
+ ready=False,
160
+ )
161
+ duration_ms = (time.monotonic() - start) * 1000
162
+
163
+ return LLMCLIResponse(
164
+ content=extract_pattern(response.content or ""),
165
+ raw=response.raw,
166
+ usage=TokenUsage(
167
+ input_tokens=response.usage.prompt_tokens or 0,
168
+ output_tokens=response.usage.completion_tokens or 0,
169
+ total_tokens=response.usage.total_tokens or 0,
170
+ ),
171
+ duration_ms=duration_ms,
172
+ reason=str(
173
+ getattr(response.finish_reason, "value", response.finish_reason) or ""
174
+ ),
175
+ ready=_is_ready(response.finish_reason),
176
+ )
177
+
178
+
179
+ class AnthropicRegexBuilder(AnyAskRegexBuilder):
180
+ provider = "anthropic"
181
+
182
+
183
+ class AzureRegexBuilder(AnyAskRegexBuilder):
184
+ provider = "azure"
185
+
186
+
187
+ class BedrockRegexBuilder(AnyAskRegexBuilder):
188
+ provider = "bedrock"
189
+
190
+
191
+ class CerebrasRegexBuilder(AnyAskRegexBuilder):
192
+ provider = "cerebras"
193
+
194
+
195
+ class CohereRegexBuilder(AnyAskRegexBuilder):
196
+ provider = "cohere"
197
+
198
+
199
+ class DeepSeekRegexBuilder(AnyAskRegexBuilder):
200
+ provider = "deepseek"
201
+
202
+
203
+ class FireworksRegexBuilder(AnyAskRegexBuilder):
204
+ provider = "fireworks"
205
+
206
+
207
+ class GeminiRegexBuilder(AnyAskRegexBuilder):
208
+ provider = "gemini"
209
+
210
+
211
+ class GroqRegexBuilder(AnyAskRegexBuilder):
212
+ provider = "groq"
213
+
214
+
215
+ class MistralRegexBuilder(AnyAskRegexBuilder):
216
+ provider = "mistral"
217
+
218
+
219
+ class MoonshotRegexBuilder(AnyAskRegexBuilder):
220
+ provider = "moonshot"
221
+
222
+
223
+ class OCIRegexBuilder(AnyAskRegexBuilder):
224
+ provider = "oci"
225
+
226
+
227
+ class OpenAIRegexBuilder(AnyAskRegexBuilder):
228
+ provider = "openai"
229
+
230
+
231
+ class OpenRouterRegexBuilder(AnyAskRegexBuilder):
232
+ provider = "openrouter"
233
+
234
+
235
+ class PerplexityRegexBuilder(AnyAskRegexBuilder):
236
+ provider = "perplexity"
237
+
238
+
239
+ class TogetherRegexBuilder(AnyAskRegexBuilder):
240
+ provider = "together"
241
+
242
+
243
+ class VertexAIRegexBuilder(AnyAskRegexBuilder):
244
+ provider = "vertexai"
245
+
246
+
247
+ class XAIRegexBuilder(AnyAskRegexBuilder):
248
+ provider = "xai"
@@ -46,7 +46,7 @@ from pathlib import Path
46
46
  from typing import Any
47
47
 
48
48
  from parseforge import generation, naming, paths, sampling, validation
49
- from parseforge.naming.providers.azure import DEFAULT_API_VERSION
49
+ from parseforge.naming.providers.anyask_builder import DEFAULT_API_VERSION
50
50
 
51
51
 
52
52
  class Mode(str, Enum):