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