parseforge 0.3.1__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.
- parseforge-0.3.2/PKG-INFO +195 -0
- parseforge-0.3.2/README.md +97 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/__init__.py +1 -1
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/cli/main.py +1 -1
- parseforge-0.3.2/parseforge/naming/providers/__init__.py +41 -0
- parseforge-0.3.2/parseforge/naming/providers/anyask_builder.py +248 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/pipeline.py +1 -1
- parseforge-0.3.2/parseforge.egg-info/PKG-INFO +195 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge.egg-info/SOURCES.txt +1 -19
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge.egg-info/requires.txt +20 -26
- {parseforge-0.3.1 → parseforge-0.3.2}/pyproject.toml +40 -49
- parseforge-0.3.1/PKG-INFO +0 -199
- parseforge-0.3.1/README.md +0 -95
- parseforge-0.3.1/parseforge/naming/providers/__init__.py +0 -39
- parseforge-0.3.1/parseforge/naming/providers/anthropic.py +0 -115
- parseforge-0.3.1/parseforge/naming/providers/azure.py +0 -182
- parseforge-0.3.1/parseforge/naming/providers/bedrock.py +0 -166
- parseforge-0.3.1/parseforge/naming/providers/cerebras.py +0 -118
- parseforge-0.3.1/parseforge/naming/providers/cohere.py +0 -163
- parseforge-0.3.1/parseforge/naming/providers/deepseek.py +0 -131
- parseforge-0.3.1/parseforge/naming/providers/errors.py +0 -63
- parseforge-0.3.1/parseforge/naming/providers/fireworks.py +0 -118
- parseforge-0.3.1/parseforge/naming/providers/gemini.py +0 -150
- parseforge-0.3.1/parseforge/naming/providers/groq.py +0 -116
- parseforge-0.3.1/parseforge/naming/providers/mistral.py +0 -153
- parseforge-0.3.1/parseforge/naming/providers/moonshot.py +0 -120
- parseforge-0.3.1/parseforge/naming/providers/oci.py +0 -200
- parseforge-0.3.1/parseforge/naming/providers/openai.py +0 -107
- parseforge-0.3.1/parseforge/naming/providers/openrouter.py +0 -120
- parseforge-0.3.1/parseforge/naming/providers/perplexity.py +0 -118
- parseforge-0.3.1/parseforge/naming/providers/together.py +0 -118
- parseforge-0.3.1/parseforge/naming/providers/vertexai.py +0 -181
- parseforge-0.3.1/parseforge/naming/providers/xai.py +0 -116
- parseforge-0.3.1/parseforge.egg-info/PKG-INFO +0 -199
- {parseforge-0.3.1 → parseforge-0.3.2}/LICENSE +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/MANIFEST.in +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/SPEC.md +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/api.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/cli/__init__.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/cli/config.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/drift.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/generation.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/integration.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/naming/__init__.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/naming/assemble.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/naming/cache.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/naming/llm.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/naming/prompts.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/naming/prompts.yaml +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/naming/providers/models.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/naming/providers/models.yaml +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/naming/providers/text.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/naming/resolver.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/paths.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/promotion.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/sampling/__init__.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/sampling/backends/__init__.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/sampling/backends/netmiko.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/sampling/core.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge/validation.py +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge.egg-info/dependency_links.txt +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge.egg-info/entry_points.txt +0 -0
- {parseforge-0.3.1 → parseforge-0.3.2}/parseforge.egg-info/top_level.txt +0 -0
- {parseforge-0.3.1 → 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
|
|
@@ -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.
|
|
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.
|
|
49
|
+
from parseforge.naming.providers.anyask_builder import DEFAULT_API_VERSION
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
class Mode(str, Enum):
|