parseforge 0.2.8__tar.gz → 0.2.10__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 (57) hide show
  1. {parseforge-0.2.8/parseforge.egg-info → parseforge-0.2.10}/PKG-INFO +36 -17
  2. {parseforge-0.2.8 → parseforge-0.2.10}/README.md +27 -16
  3. parseforge-0.2.10/parseforge/__init__.py +1 -0
  4. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/cli/config.py +18 -0
  5. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/cli/main.py +220 -18
  6. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/__init__.py +4 -0
  7. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/__init__.py +4 -0
  8. parseforge-0.2.10/parseforge/naming/providers/azure.py +197 -0
  9. parseforge-0.2.10/parseforge/naming/providers/gemini.py +160 -0
  10. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/models.yaml +12 -0
  11. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/pipeline.py +17 -0
  12. {parseforge-0.2.8 → parseforge-0.2.10/parseforge.egg-info}/PKG-INFO +36 -17
  13. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge.egg-info/SOURCES.txt +2 -0
  14. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge.egg-info/requires.txt +10 -0
  15. {parseforge-0.2.8 → parseforge-0.2.10}/pyproject.toml +9 -4
  16. parseforge-0.2.8/parseforge/__init__.py +0 -1
  17. {parseforge-0.2.8 → parseforge-0.2.10}/LICENSE +0 -0
  18. {parseforge-0.2.8 → parseforge-0.2.10}/MANIFEST.in +0 -0
  19. {parseforge-0.2.8 → parseforge-0.2.10}/SPEC.md +0 -0
  20. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/cli/__init__.py +0 -0
  21. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/drift.py +0 -0
  22. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/generation.py +0 -0
  23. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/integration.py +0 -0
  24. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/assemble.py +0 -0
  25. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/cache.py +0 -0
  26. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/llm.py +0 -0
  27. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/prompts.py +0 -0
  28. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/prompts.yaml +0 -0
  29. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/anthropic.py +0 -0
  30. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/cerebras.py +0 -0
  31. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/cohere.py +0 -0
  32. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/cost.py +0 -0
  33. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/deepseek.py +0 -0
  34. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/errors.py +0 -0
  35. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/fireworks.py +0 -0
  36. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/groq.py +0 -0
  37. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/mistral.py +0 -0
  38. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/models.py +0 -0
  39. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/moonshot.py +0 -0
  40. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/openai.py +0 -0
  41. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/openrouter.py +0 -0
  42. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/perplexity.py +0 -0
  43. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/text.py +0 -0
  44. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/together.py +0 -0
  45. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/providers/xai.py +0 -0
  46. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/naming/resolver.py +0 -0
  47. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/paths.py +0 -0
  48. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/promotion.py +0 -0
  49. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/sampling/__init__.py +0 -0
  50. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/sampling/backends/__init__.py +0 -0
  51. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/sampling/backends/netmiko.py +0 -0
  52. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/sampling/core.py +0 -0
  53. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge/validation.py +0 -0
  54. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge.egg-info/dependency_links.txt +0 -0
  55. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge.egg-info/entry_points.txt +0 -0
  56. {parseforge-0.2.8 → parseforge-0.2.10}/parseforge.egg-info/top_level.txt +0 -0
  57. {parseforge-0.2.8 → parseforge-0.2.10}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: parseforge
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: LLM-driven pipeline that forges, validates, and promotes TextFSM templates from network CLI output
5
5
  Author-email: Tuyen Mathew Duong <tuyen@geekstrident.com>
6
6
  Maintainer-email: Tuyen Mathew Duong <tuyen@geekstrident.com>
@@ -66,6 +66,12 @@ Requires-Dist: textfsm-ai[mistral]>=0.6.1; extra == "mistral"
66
66
  Provides-Extra: cohere
67
67
  Requires-Dist: cohere==5.21.1; extra == "cohere"
68
68
  Requires-Dist: textfsm-ai[cohere]>=0.6.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.6.1; extra == "azure"
72
+ Provides-Extra: gemini
73
+ Requires-Dist: google-genai>=0.2.0; extra == "gemini"
74
+ Requires-Dist: textfsm-ai[gemini]>=0.6.1; extra == "gemini"
69
75
  Provides-Extra: sampling
70
76
  Requires-Dist: netmiko>=4.0; extra == "sampling"
71
77
  Provides-Extra: dev
@@ -78,6 +84,8 @@ Requires-Dist: anthropic>=0.40.0; extra == "dev"
78
84
  Requires-Dist: openai>=1.0.0; extra == "dev"
79
85
  Requires-Dist: mistralai==1.10.0; extra == "dev"
80
86
  Requires-Dist: cohere==5.21.1; extra == "dev"
87
+ Requires-Dist: azure-ai-inference>=1.0.0b9; extra == "dev"
88
+ Requires-Dist: google-genai>=0.2.0; extra == "dev"
81
89
  Provides-Extra: release
82
90
  Requires-Dist: bump2version; extra == "release"
83
91
  Requires-Dist: build; extra == "release"
@@ -120,11 +128,11 @@ command that's pure local processing (`canonical`/`readable`/`recognizers`,
120
128
  LLM (`name`, `check --provider`, `run`, `generate-template`, `trial`) needs the
121
129
  extra for whichever provider it uses: `anthropic`, `openai`, `deepseek`,
122
130
  `groq`, `xai`, `together`, `fireworks`, `perplexity`, `openrouter`,
123
- `moonshot`, `cerebras`, `mistral`, or `cohere`. `--provider` defaults to
124
- `anthropic` wherever it isn't required, so that's the one most setups need.
125
- `pip install parseforge[sampling]` adds Netmiko for live device sampling;
126
- combine extras as needed, e.g.
127
- `pip install parseforge[anthropic,openai,deepseek,groq,xai,together,fireworks,perplexity,openrouter,moonshot,cerebras,mistral,cohere,sampling]`.
131
+ `moonshot`, `cerebras`, `mistral`, `cohere`, `azure`, or `gemini`.
132
+ `--provider` defaults to `anthropic` wherever it isn't required, so that's
133
+ the one most setups need. `pip install parseforge[sampling]` adds Netmiko
134
+ for live device sampling; combine extras as needed, e.g.
135
+ `pip install parseforge[anthropic,openai,deepseek,groq,xai,together,fireworks,perplexity,openrouter,moonshot,cerebras,mistral,cohere,azure,gemini,sampling]`.
128
136
 
129
137
  ## Development
130
138
 
@@ -132,13 +140,13 @@ combine extras as needed, e.g.
132
140
  pip install -e ".[dev,sampling]"
133
141
  pytest
134
142
  ```
135
- `dev` already includes the `anthropic`, `openai`, `mistralai`, and `cohere`
136
- SDKs (tests exercise all thirteen providers — `anthropic`, `openai`,
137
- `deepseek`, `groq`, `xai`, `together`, `fireworks`, `perplexity`,
138
- `openrouter`, `moonshot`, `cerebras` share just the first two packages, and
139
- `mistral`/`cohere` each need their own native SDK — and never silently
140
- skip) — add the specific `,<provider>` extra explicitly only if installing
141
- outside of `dev`.
143
+ `dev` already includes the `anthropic`, `openai`, `mistralai`, `cohere`,
144
+ `azure-ai-inference`, and `google-genai` SDKs (tests exercise all fifteen
145
+ providers — `anthropic`, `openai`, `deepseek`, `groq`, `xai`, `together`,
146
+ `fireworks`, `perplexity`, `openrouter`, `moonshot`, `cerebras` share just
147
+ the first two packages, and `mistral`/`cohere`/`azure`/`gemini` each need
148
+ their own SDK — and never silently skip) — add the specific `,<provider>`
149
+ extra explicitly only if installing outside of `dev`.
142
150
 
143
151
  Linting/formatting/type-checking/docs run through tox instead of extras — see
144
152
  `tox.ini` (`tox -e lint`/`format`/`typecheck`/`docs`), each installing its own
@@ -160,8 +168,14 @@ parseforge name --vendor cisco --family catalyst9200 --os ios-xe --version 17.9.
160
168
  ```
161
169
  Still needs an LLM provider on a cache miss — `--provider` defaults to `anthropic`,
162
170
  and `--api-key` falls back to that provider's own env var (`ANTHROPIC_API_KEY`/
163
- `OPENAI_API_KEY`/`DEEPSEEK_API_KEY`). A cache hit (a command already seen before)
164
- never touches the LLM, so no key is needed at all in that case.
171
+ `OPENAI_API_KEY`/`DEEPSEEK_API_KEY`/etc.). A cache hit (a command already seen
172
+ before) never touches the LLM, so no key is needed at all in that case.
173
+
174
+ `--provider azure` is the one exception to the usual `--api-key`/`--model` shape —
175
+ Azure has no fixed base_url or model catalog, so it also needs `--endpoint`
176
+ (`AZURE_ENDPOINT`), `--api-version` (`AZURE_API_VERSION`), and `--deployment`
177
+ (`AZURE_DEPLOYMENT`, replacing `--model`). Same three options exist on
178
+ `check --provider`, `run`, and `generate-template` below.
165
179
 
166
180
  **`check`** — validate a connector or provider before spending time/tokens on a real
167
181
  run. With neither `--env` nor explicit connection flags, prints what a connector needs
@@ -206,7 +220,10 @@ parseforge run --vendor cisco --family catalyst9200 --os ios-xe --version 17.9.1
206
220
  `--naming-provider`/`--naming-api-key`/`--naming-model`, defaulting independently
207
221
  (`--naming-provider` defaults to `anthropic`; the other two fall back to that
208
222
  provider's own env var/default model) — set them explicitly if naming needs a
209
- different provider than generation.
223
+ different provider than generation. `--provider azure`/`--naming-provider azure`
224
+ each get their own `--endpoint`/`--api-version`/`--deployment` (generation) and
225
+ `--naming-endpoint`/`--naming-api-version`/`--naming-deployment` (naming) — see
226
+ the `name` section above for what they're for.
210
227
 
211
228
  **`init-trial-config`** — write a placeholder `trial.yaml` to fill in, instead of
212
229
  writing one by hand:
@@ -239,7 +256,9 @@ workers: 1
239
256
  ```
240
257
  `provider`/`api_key`/`model` are one shared LLM source used for both naming and
241
258
  generation. Use the `run` command's separate `--naming-*`/`--generation-*` flags
242
- instead if a trial actually needs two different providers.
259
+ instead if a trial actually needs two different providers. For `provider: azure`,
260
+ also set `endpoint`/`api_version`/`deployment` (`deployment` replaces `model`) —
261
+ see `parseforge init-trial-config`'s generated placeholder for the exact keys.
243
262
 
244
263
  **`integration`** — rebuild `integration/` for every case under `trials/` (SPEC §5
245
264
  step 8):
@@ -35,11 +35,11 @@ command that's pure local processing (`canonical`/`readable`/`recognizers`,
35
35
  LLM (`name`, `check --provider`, `run`, `generate-template`, `trial`) needs the
36
36
  extra for whichever provider it uses: `anthropic`, `openai`, `deepseek`,
37
37
  `groq`, `xai`, `together`, `fireworks`, `perplexity`, `openrouter`,
38
- `moonshot`, `cerebras`, `mistral`, or `cohere`. `--provider` defaults to
39
- `anthropic` wherever it isn't required, so that's the one most setups need.
40
- `pip install parseforge[sampling]` adds Netmiko for live device sampling;
41
- combine extras as needed, e.g.
42
- `pip install parseforge[anthropic,openai,deepseek,groq,xai,together,fireworks,perplexity,openrouter,moonshot,cerebras,mistral,cohere,sampling]`.
38
+ `moonshot`, `cerebras`, `mistral`, `cohere`, `azure`, or `gemini`.
39
+ `--provider` defaults to `anthropic` wherever it isn't required, so that's
40
+ the one most setups need. `pip install parseforge[sampling]` adds Netmiko
41
+ for live device sampling; combine extras as needed, e.g.
42
+ `pip install parseforge[anthropic,openai,deepseek,groq,xai,together,fireworks,perplexity,openrouter,moonshot,cerebras,mistral,cohere,azure,gemini,sampling]`.
43
43
 
44
44
  ## Development
45
45
 
@@ -47,13 +47,13 @@ combine extras as needed, e.g.
47
47
  pip install -e ".[dev,sampling]"
48
48
  pytest
49
49
  ```
50
- `dev` already includes the `anthropic`, `openai`, `mistralai`, and `cohere`
51
- SDKs (tests exercise all thirteen providers — `anthropic`, `openai`,
52
- `deepseek`, `groq`, `xai`, `together`, `fireworks`, `perplexity`,
53
- `openrouter`, `moonshot`, `cerebras` share just the first two packages, and
54
- `mistral`/`cohere` each need their own native SDK — and never silently
55
- skip) — add the specific `,<provider>` extra explicitly only if installing
56
- outside of `dev`.
50
+ `dev` already includes the `anthropic`, `openai`, `mistralai`, `cohere`,
51
+ `azure-ai-inference`, and `google-genai` SDKs (tests exercise all fifteen
52
+ providers — `anthropic`, `openai`, `deepseek`, `groq`, `xai`, `together`,
53
+ `fireworks`, `perplexity`, `openrouter`, `moonshot`, `cerebras` share just
54
+ the first two packages, and `mistral`/`cohere`/`azure`/`gemini` each need
55
+ their own SDK — and never silently skip) — add the specific `,<provider>`
56
+ extra explicitly only if installing outside of `dev`.
57
57
 
58
58
  Linting/formatting/type-checking/docs run through tox instead of extras — see
59
59
  `tox.ini` (`tox -e lint`/`format`/`typecheck`/`docs`), each installing its own
@@ -75,8 +75,14 @@ parseforge name --vendor cisco --family catalyst9200 --os ios-xe --version 17.9.
75
75
  ```
76
76
  Still needs an LLM provider on a cache miss — `--provider` defaults to `anthropic`,
77
77
  and `--api-key` falls back to that provider's own env var (`ANTHROPIC_API_KEY`/
78
- `OPENAI_API_KEY`/`DEEPSEEK_API_KEY`). A cache hit (a command already seen before)
79
- never touches the LLM, so no key is needed at all in that case.
78
+ `OPENAI_API_KEY`/`DEEPSEEK_API_KEY`/etc.). A cache hit (a command already seen
79
+ before) never touches the LLM, so no key is needed at all in that case.
80
+
81
+ `--provider azure` is the one exception to the usual `--api-key`/`--model` shape —
82
+ Azure has no fixed base_url or model catalog, so it also needs `--endpoint`
83
+ (`AZURE_ENDPOINT`), `--api-version` (`AZURE_API_VERSION`), and `--deployment`
84
+ (`AZURE_DEPLOYMENT`, replacing `--model`). Same three options exist on
85
+ `check --provider`, `run`, and `generate-template` below.
80
86
 
81
87
  **`check`** — validate a connector or provider before spending time/tokens on a real
82
88
  run. With neither `--env` nor explicit connection flags, prints what a connector needs
@@ -121,7 +127,10 @@ parseforge run --vendor cisco --family catalyst9200 --os ios-xe --version 17.9.1
121
127
  `--naming-provider`/`--naming-api-key`/`--naming-model`, defaulting independently
122
128
  (`--naming-provider` defaults to `anthropic`; the other two fall back to that
123
129
  provider's own env var/default model) — set them explicitly if naming needs a
124
- different provider than generation.
130
+ different provider than generation. `--provider azure`/`--naming-provider azure`
131
+ each get their own `--endpoint`/`--api-version`/`--deployment` (generation) and
132
+ `--naming-endpoint`/`--naming-api-version`/`--naming-deployment` (naming) — see
133
+ the `name` section above for what they're for.
125
134
 
126
135
  **`init-trial-config`** — write a placeholder `trial.yaml` to fill in, instead of
127
136
  writing one by hand:
@@ -154,7 +163,9 @@ workers: 1
154
163
  ```
155
164
  `provider`/`api_key`/`model` are one shared LLM source used for both naming and
156
165
  generation. Use the `run` command's separate `--naming-*`/`--generation-*` flags
157
- instead if a trial actually needs two different providers.
166
+ instead if a trial actually needs two different providers. For `provider: azure`,
167
+ also set `endpoint`/`api_version`/`deployment` (`deployment` replaces `model`) —
168
+ see `parseforge init-trial-config`'s generated placeholder for the exact keys.
158
169
 
159
170
  **`integration`** — rebuild `integration/` for every case under `trials/` (SPEC §5
160
171
  step 8):
@@ -0,0 +1 @@
1
+ __version__ = "0.2.10"
@@ -42,6 +42,14 @@ class TrialConfig:
42
42
  note: str | None = None
43
43
  workers: int = 1
44
44
  path: str | None = None
45
+ # Only meaningful for provider: azure — no fixed base_url or model
46
+ # catalog there (see naming/providers/azure.py). endpoint/api_version
47
+ # are ignored by every other provider; deployment replaces the usual
48
+ # "model" concept and is what actually gets sent as the generation
49
+ # model argument (see cli/main.py's trial_cmd).
50
+ endpoint: str | None = None
51
+ api_version: str | None = None
52
+ deployment: str | None = None
45
53
 
46
54
 
47
55
  @dataclass(frozen=True)
@@ -57,6 +65,10 @@ class GenerationRequestConfig:
57
65
  env: str | None = None
58
66
  cmdline: str | None = None
59
67
  sample_file: str | None = None
68
+ # Only meaningful for provider: azure — see TrialConfig above.
69
+ endpoint: str | None = None
70
+ api_version: str | None = None
71
+ deployment: str | None = None
60
72
 
61
73
 
62
74
  _TRIAL_REQUIRED = (
@@ -124,6 +136,9 @@ def load_trial_config(path: Path) -> TrialConfig:
124
136
  note=raw.get("notes"),
125
137
  workers=int(raw.get("workers", 1)),
126
138
  path=raw.get("path"),
139
+ endpoint=raw.get("endpoint"),
140
+ api_version=raw.get("api_version"),
141
+ deployment=raw.get("deployment"),
127
142
  )
128
143
 
129
144
 
@@ -143,4 +158,7 @@ def load_generation_config(path: Path) -> GenerationRequestConfig:
143
158
  env=raw.get("env"),
144
159
  cmdline=raw.get("cmdline"),
145
160
  sample_file=raw.get("sample_file"),
161
+ endpoint=raw.get("endpoint"),
162
+ api_version=raw.get("api_version"),
163
+ deployment=raw.get("deployment"),
146
164
  )
@@ -20,13 +20,16 @@ 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
24
 
24
25
  _BUILDERS: dict[str, type[naming.RegexBuilder]] = {
25
26
  "anthropic": naming.AnthropicRegexBuilder,
27
+ "azure": naming.AzureRegexBuilder,
26
28
  "cerebras": naming.CerebrasRegexBuilder,
27
29
  "cohere": naming.CohereRegexBuilder,
28
30
  "deepseek": naming.DeepSeekRegexBuilder,
29
31
  "fireworks": naming.FireworksRegexBuilder,
32
+ "gemini": naming.GeminiRegexBuilder,
30
33
  "groq": naming.GroqRegexBuilder,
31
34
  "mistral": naming.MistralRegexBuilder,
32
35
  "moonshot": naming.MoonshotRegexBuilder,
@@ -61,6 +64,13 @@ device_type: <netmiko-device-type>
61
64
  provider: anthropic
62
65
  api_key: <api-key>
63
66
  model: <model>
67
+ # For provider: azure only -- no fixed base_url or model catalog there.
68
+ # deployment replaces model above (still keep a model: placeholder value,
69
+ # it's simply ignored); endpoint/api_version default to AZURE_ENDPOINT/
70
+ # AZURE_API_VERSION if omitted.
71
+ # endpoint: <azure-resource-endpoint>
72
+ # api_version: <azure-api-version>
73
+ # deployment: <azure-deployment-name>
64
74
 
65
75
  # --- commands to run (required, at least one) ---
66
76
  commands:
@@ -86,6 +96,10 @@ _GENERATE_TEMPLATE_CONFIG_PLACEHOLDER = """\
86
96
  provider: anthropic
87
97
  api_key: <api-key>
88
98
  model: <model>
99
+ # For provider: azure only -- see trial.yaml's placeholder for details.
100
+ # endpoint: <azure-resource-endpoint>
101
+ # api_version: <azure-api-version>
102
+ # deployment: <azure-deployment-name>
89
103
 
90
104
  # --- input: choose exactly one ---
91
105
 
@@ -114,13 +128,34 @@ def _write_placeholder_config(out_path: str, force: bool, content: str) -> None:
114
128
 
115
129
 
116
130
  def _build_regex_builder(
117
- provider: str, api_key: str | None, model: str | None
131
+ provider: str,
132
+ api_key: str | None,
133
+ model: str | None,
134
+ endpoint: str | None = None,
135
+ api_version: str | None = None,
136
+ deployment: str | None = None,
118
137
  ) -> naming.RegexBuilder:
138
+ """endpoint/api_version/deployment are only meaningful for
139
+ provider="azure" (no fixed base_url or model catalog there — see
140
+ naming/providers/azure.py) — forwarded only when given, so every
141
+ other provider's constructor never sees an unexpected kwarg.
142
+
143
+ When deployment is given, model is *not* forwarded — AzureRegexBuilder
144
+ has no model parameter at all (deployment replaces it), so passing
145
+ both would raise a plain TypeError. This matters in practice: every
146
+ TrialConfig always has a (placeholder) model value, since it's a
147
+ required config key shared by every provider."""
119
148
  kwargs: dict[str, str] = {}
120
149
  if api_key is not None:
121
150
  kwargs["api_key"] = api_key
122
- if model is not None:
151
+ if deployment is not None:
152
+ kwargs["deployment"] = deployment
153
+ elif model is not None:
123
154
  kwargs["model"] = model
155
+ if endpoint is not None:
156
+ kwargs["endpoint"] = endpoint
157
+ if api_version is not None:
158
+ kwargs["api_version"] = api_version
124
159
  return _BUILDERS[provider](**kwargs)
125
160
 
126
161
 
@@ -212,12 +247,32 @@ def main() -> None:
212
247
  "variable (ANTHROPIC_API_KEY, OPENAI_API_KEY, DEEPSEEK_API_KEY, GROQ_API_KEY, "
213
248
  "XAI_API_KEY, TOGETHER_API_KEY, FIREWORKS_API_KEY, PERPLEXITY_API_KEY, "
214
249
  "OPENROUTER_API_KEY, MOONSHOT_API_KEY, CEREBRAS_API_KEY, MISTRAL_API_KEY, "
215
- "COHERE_API_KEY); only needed on a cache miss.",
250
+ "COHERE_API_KEY, AZURE_API_KEY, GEMINI_API_KEY); only needed on a cache "
251
+ "miss.",
216
252
  )
217
253
  @click.option(
218
254
  "--model",
219
255
  default=None,
220
- help="Defaults to the selected provider's own default model.",
256
+ help="Defaults to the selected provider's own default model. Ignored for "
257
+ "--provider azure — use --deployment instead.",
258
+ )
259
+ @click.option(
260
+ "--endpoint",
261
+ default=None,
262
+ help="Azure resource endpoint. Only used by --provider azure; defaults to "
263
+ "AZURE_ENDPOINT.",
264
+ )
265
+ @click.option(
266
+ "--api-version",
267
+ default=None,
268
+ help="Azure API version. Only used by --provider azure; defaults to "
269
+ "AZURE_API_VERSION, then a built-in default.",
270
+ )
271
+ @click.option(
272
+ "--deployment",
273
+ default=None,
274
+ help="Azure deployment name — replaces --model for --provider azure; "
275
+ "defaults to AZURE_DEPLOYMENT.",
221
276
  )
222
277
  @click.argument("command", nargs=-1, required=True)
223
278
  def name_cmd(
@@ -228,6 +283,9 @@ def name_cmd(
228
283
  provider: str,
229
284
  api_key: str | None,
230
285
  model: str | None,
286
+ endpoint: str | None,
287
+ api_version: str | None,
288
+ deployment: str | None,
231
289
  command: tuple[str, ...],
232
290
  ) -> None:
233
291
  """Print the canonical cli-name for a raw CLI COMMAND.
@@ -236,7 +294,9 @@ def name_cmd(
236
294
  on a cache miss.
237
295
  """
238
296
  context = naming.CliContext(vendor=vendor, family=family, os=os_, version=version)
239
- builder = _build_regex_builder(provider, api_key, model)
297
+ builder = _build_regex_builder(
298
+ provider, api_key, model, endpoint, api_version, deployment
299
+ )
240
300
  click.echo(naming.cli_name(" ".join(command), context, builder=builder))
241
301
 
242
302
 
@@ -274,7 +334,26 @@ def name_cmd(
274
334
  @click.option(
275
335
  "--naming-model",
276
336
  default=None,
277
- help="Defaults to the naming provider's own default model.",
337
+ help="Defaults to the naming provider's own default model. Ignored for "
338
+ "--naming-provider azure — use --naming-deployment instead.",
339
+ )
340
+ @click.option(
341
+ "--naming-endpoint",
342
+ default=None,
343
+ help="Azure resource endpoint for naming. Only used by --naming-provider "
344
+ "azure; defaults to AZURE_ENDPOINT.",
345
+ )
346
+ @click.option(
347
+ "--naming-api-version",
348
+ default=None,
349
+ help="Azure API version for naming. Only used by --naming-provider azure; "
350
+ "defaults to AZURE_API_VERSION, then a built-in default.",
351
+ )
352
+ @click.option(
353
+ "--naming-deployment",
354
+ default=None,
355
+ help="Azure deployment name for naming — replaces --naming-model for "
356
+ "--naming-provider azure; defaults to AZURE_DEPLOYMENT.",
278
357
  )
279
358
  @click.option(
280
359
  "--provider",
@@ -282,11 +361,39 @@ def name_cmd(
282
361
  help="LLM provider for template generation (textfsm-ai's own registry, "
283
362
  'e.g. "anthropic", "openai", "deepseek", "groq", "xai", "together", '
284
363
  '"fireworks", "perplexity", "openrouter", "moonshot", "cerebras", '
285
- '"mistral", "cohere"). Naming uses its own separate --naming-provider, '
286
- "not this one.",
364
+ '"mistral", "cohere", "azure", "gemini"). Naming uses its own separate '
365
+ "--naming-provider, not this one.",
287
366
  )
288
367
  @click.option("--api-key", required=True, help="API key for the generation LLM call.")
289
- @click.option("--model", required=True, help="Model for the generation LLM call.")
368
+ @click.option(
369
+ "--model",
370
+ required=True,
371
+ help="Model for the generation LLM call. For --provider azure, pass the "
372
+ "deployment name here via --deployment instead — this becomes the "
373
+ "deployment name that's actually sent.",
374
+ )
375
+ @click.option(
376
+ "--endpoint",
377
+ default=None,
378
+ envvar="AZURE_ENDPOINT",
379
+ help="Azure resource endpoint for generation. Only used by --provider "
380
+ "azure; defaults to the AZURE_ENDPOINT environment variable.",
381
+ )
382
+ @click.option(
383
+ "--api-version",
384
+ default=None,
385
+ envvar="AZURE_API_VERSION",
386
+ help="Azure API version for generation. Only used by --provider azure; "
387
+ "defaults to the AZURE_API_VERSION environment variable.",
388
+ )
389
+ @click.option(
390
+ "--deployment",
391
+ default=None,
392
+ envvar="AZURE_DEPLOYMENT",
393
+ help="Azure deployment name for generation — overrides --model for "
394
+ "--provider azure; defaults to the AZURE_DEPLOYMENT environment "
395
+ "variable.",
396
+ )
290
397
  @click.option(
291
398
  "--store-root",
292
399
  default=None,
@@ -308,9 +415,15 @@ def run_cmd(
308
415
  naming_provider: str,
309
416
  naming_api_key: str | None,
310
417
  naming_model: str | None,
418
+ naming_endpoint: str | None,
419
+ naming_api_version: str | None,
420
+ naming_deployment: str | None,
311
421
  provider: str,
312
422
  api_key: str,
313
423
  model: str,
424
+ endpoint: str | None,
425
+ api_version: str | None,
426
+ deployment: str | None,
314
427
  store_root: str | None,
315
428
  project: str | None,
316
429
  email: str | None,
@@ -335,9 +448,20 @@ def run_cmd(
335
448
  connection = DeviceConnection(
336
449
  host=host, username=username, password=password, device_type=device_type
337
450
  )
338
- naming_builder = _build_regex_builder(naming_provider, naming_api_key, naming_model)
451
+ naming_builder = _build_regex_builder(
452
+ naming_provider,
453
+ naming_api_key,
454
+ naming_model,
455
+ naming_endpoint,
456
+ naming_api_version,
457
+ naming_deployment,
458
+ )
339
459
  generation_config = LLMProviderConfig(
340
- provider=provider, api_key=api_key, model=model
460
+ provider=provider,
461
+ api_key=api_key,
462
+ model=deployment or model,
463
+ endpoint=endpoint,
464
+ api_version=api_version,
341
465
  )
342
466
  metadata = TrialMetadata(
343
467
  project=project,
@@ -391,8 +515,17 @@ def _check_connector(
391
515
  click.echo("OK")
392
516
 
393
517
 
394
- def _check_provider(provider: str, api_key: str | None, model: str | None) -> None:
395
- builder = _build_regex_builder(provider, api_key, model)
518
+ def _check_provider(
519
+ provider: str,
520
+ api_key: str | None,
521
+ model: str | None,
522
+ endpoint: str | None = None,
523
+ api_version: str | None = None,
524
+ deployment: str | None = None,
525
+ ) -> None:
526
+ builder = _build_regex_builder(
527
+ provider, api_key, model, endpoint, api_version, deployment
528
+ )
396
529
  context = naming.CliContext(vendor="check", family="check", os="check", version="0")
397
530
  try:
398
531
  response = builder.build_pattern("show clock", context)
@@ -425,6 +558,21 @@ def _check_provider(provider: str, api_key: str | None, model: str | None) -> No
425
558
  )
426
559
  @click.option("--api-key", default=None, help="Used for a --provider check.")
427
560
  @click.option("--model", default=None, help="Used for a --provider check.")
561
+ @click.option(
562
+ "--endpoint",
563
+ default=None,
564
+ help="Used for a --provider azure check; defaults to AZURE_ENDPOINT.",
565
+ )
566
+ @click.option(
567
+ "--api-version",
568
+ default=None,
569
+ help="Used for a --provider azure check; defaults to AZURE_API_VERSION.",
570
+ )
571
+ @click.option(
572
+ "--deployment",
573
+ default=None,
574
+ help="Used for a --provider azure check; defaults to AZURE_DEPLOYMENT.",
575
+ )
428
576
  def check_cmd(
429
577
  connector: str | None,
430
578
  provider: str | None,
@@ -436,6 +584,9 @@ def check_cmd(
436
584
  probe_command: str,
437
585
  api_key: str | None,
438
586
  model: str | None,
587
+ endpoint: str | None,
588
+ api_version: str | None,
589
+ deployment: str | None,
439
590
  ) -> None:
440
591
  """Validate a --connector or --provider is reachable, or report what it
441
592
  needs. Exactly one of --connector/--provider is required."""
@@ -447,7 +598,7 @@ def check_cmd(
447
598
  )
448
599
  else:
449
600
  assert provider is not None
450
- _check_provider(provider, api_key, model)
601
+ _check_provider(provider, api_key, model, endpoint, api_version, deployment)
451
602
 
452
603
 
453
604
  @main.command("init-generate-template-config")
@@ -492,7 +643,34 @@ def init_generate_template_config_cmd(out_path: str, force: bool) -> None:
492
643
  help='Generation LLM provider (textfsm-ai\'s own registry), e.g. "anthropic".',
493
644
  )
494
645
  @click.option("--api-key", default=None)
495
- @click.option("--model", default=None)
646
+ @click.option(
647
+ "--model",
648
+ default=None,
649
+ help="Model for the generation LLM call. For --provider azure, pass the "
650
+ "deployment name via --deployment instead — this becomes the "
651
+ "deployment name that's actually sent.",
652
+ )
653
+ @click.option(
654
+ "--endpoint",
655
+ default=None,
656
+ envvar="AZURE_ENDPOINT",
657
+ help="Azure resource endpoint. Only used by --provider azure; defaults to "
658
+ "the AZURE_ENDPOINT environment variable.",
659
+ )
660
+ @click.option(
661
+ "--api-version",
662
+ default=None,
663
+ envvar="AZURE_API_VERSION",
664
+ help="Azure API version. Only used by --provider azure; defaults to the "
665
+ "AZURE_API_VERSION environment variable.",
666
+ )
667
+ @click.option(
668
+ "--deployment",
669
+ default=None,
670
+ envvar="AZURE_DEPLOYMENT",
671
+ help="Azure deployment name — overrides --model for --provider azure; "
672
+ "defaults to the AZURE_DEPLOYMENT environment variable.",
673
+ )
496
674
  @click.option(
497
675
  "--out",
498
676
  "out_dir",
@@ -513,6 +691,9 @@ def generate_template_cmd(
513
691
  provider: str | None,
514
692
  api_key: str | None,
515
693
  model: str | None,
694
+ endpoint: str | None,
695
+ api_version: str | None,
696
+ deployment: str | None,
516
697
  out_dir: str | None,
517
698
  ) -> None:
518
699
  """One-shot template generation — no trial persisted under trials/.
@@ -533,6 +714,9 @@ def generate_template_cmd(
533
714
  provider = provider or cfg.provider
534
715
  api_key = api_key or cfg.api_key
535
716
  model = model or cfg.model
717
+ endpoint = endpoint or cfg.endpoint
718
+ api_version = api_version or cfg.api_version
719
+ deployment = deployment or cfg.deployment
536
720
 
537
721
  if not provider or not model or not api_key:
538
722
  raise click.UsageError(
@@ -552,7 +736,14 @@ def generate_template_cmd(
552
736
  "or --host/--username/--password/--device-type), or --config"
553
737
  )
554
738
 
555
- result = generation.generate(sample_text, provider, api_key, model)
739
+ result = generation.generate(
740
+ sample_text,
741
+ provider,
742
+ api_key,
743
+ deployment or model,
744
+ endpoint=endpoint or "",
745
+ api_version=api_version or DEFAULT_API_VERSION,
746
+ )
556
747
  if not result.ready:
557
748
  raise click.ClickException(f"generation not ready: {result.reason}")
558
749
 
@@ -685,9 +876,20 @@ def trial_cmd(config_path: str, store_root_opt: str | None) -> None:
685
876
  password=cfg.password,
686
877
  device_type=cfg.device_type,
687
878
  )
688
- naming_builder = _build_regex_builder(cfg.provider, cfg.api_key, cfg.model)
879
+ naming_builder = _build_regex_builder(
880
+ cfg.provider,
881
+ cfg.api_key,
882
+ cfg.model,
883
+ cfg.endpoint,
884
+ cfg.api_version,
885
+ cfg.deployment,
886
+ )
689
887
  generation_config = pipeline.LLMProviderConfig(
690
- provider=cfg.provider, api_key=cfg.api_key, model=cfg.model
888
+ provider=cfg.provider,
889
+ api_key=cfg.api_key,
890
+ model=cfg.deployment or cfg.model,
891
+ endpoint=cfg.endpoint,
892
+ api_version=cfg.api_version,
691
893
  )
692
894
  metadata = pipeline.TrialMetadata(
693
895
  username=cfg.user, email=cfg.email, description=cfg.description, note=cfg.note
@@ -10,10 +10,12 @@ from .llm import (
10
10
  )
11
11
  from .providers import (
12
12
  AnthropicRegexBuilder,
13
+ AzureRegexBuilder,
13
14
  CerebrasRegexBuilder,
14
15
  CohereRegexBuilder,
15
16
  DeepSeekRegexBuilder,
16
17
  FireworksRegexBuilder,
18
+ GeminiRegexBuilder,
17
19
  GroqRegexBuilder,
18
20
  MistralRegexBuilder,
19
21
  MoonshotRegexBuilder,
@@ -33,10 +35,12 @@ __all__ = [
33
35
  "RegexBuilder",
34
36
  "UnimplementedRegexBuilder",
35
37
  "AnthropicRegexBuilder",
38
+ "AzureRegexBuilder",
36
39
  "CerebrasRegexBuilder",
37
40
  "CohereRegexBuilder",
38
41
  "DeepSeekRegexBuilder",
39
42
  "FireworksRegexBuilder",
43
+ "GeminiRegexBuilder",
40
44
  "GroqRegexBuilder",
41
45
  "MistralRegexBuilder",
42
46
  "MoonshotRegexBuilder",
@@ -1,8 +1,10 @@
1
1
  from .anthropic import AnthropicRegexBuilder
2
+ from .azure import AzureRegexBuilder
2
3
  from .cerebras import CerebrasRegexBuilder
3
4
  from .cohere import CohereRegexBuilder
4
5
  from .deepseek import DeepSeekRegexBuilder
5
6
  from .fireworks import FireworksRegexBuilder
7
+ from .gemini import GeminiRegexBuilder
6
8
  from .groq import GroqRegexBuilder
7
9
  from .mistral import MistralRegexBuilder
8
10
  from .moonshot import MoonshotRegexBuilder
@@ -14,10 +16,12 @@ from .xai import XAIRegexBuilder
14
16
 
15
17
  __all__ = [
16
18
  "AnthropicRegexBuilder",
19
+ "AzureRegexBuilder",
17
20
  "CerebrasRegexBuilder",
18
21
  "CohereRegexBuilder",
19
22
  "DeepSeekRegexBuilder",
20
23
  "FireworksRegexBuilder",
24
+ "GeminiRegexBuilder",
21
25
  "GroqRegexBuilder",
22
26
  "MistralRegexBuilder",
23
27
  "MoonshotRegexBuilder",