parseforge 0.2.9__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.9/parseforge.egg-info → parseforge-0.2.10}/PKG-INFO +17 -13
  2. {parseforge-0.2.9 → parseforge-0.2.10}/README.md +12 -12
  3. parseforge-0.2.10/parseforge/__init__.py +1 -0
  4. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/cli/main.py +4 -2
  5. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/__init__.py +2 -0
  6. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/__init__.py +2 -0
  7. parseforge-0.2.10/parseforge/naming/providers/gemini.py +160 -0
  8. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/models.yaml +12 -0
  9. {parseforge-0.2.9 → parseforge-0.2.10/parseforge.egg-info}/PKG-INFO +17 -13
  10. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge.egg-info/SOURCES.txt +1 -0
  11. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge.egg-info/requires.txt +5 -0
  12. {parseforge-0.2.9 → parseforge-0.2.10}/pyproject.toml +7 -5
  13. parseforge-0.2.9/parseforge/__init__.py +0 -1
  14. {parseforge-0.2.9 → parseforge-0.2.10}/LICENSE +0 -0
  15. {parseforge-0.2.9 → parseforge-0.2.10}/MANIFEST.in +0 -0
  16. {parseforge-0.2.9 → parseforge-0.2.10}/SPEC.md +0 -0
  17. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/cli/__init__.py +0 -0
  18. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/cli/config.py +0 -0
  19. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/drift.py +0 -0
  20. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/generation.py +0 -0
  21. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/integration.py +0 -0
  22. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/assemble.py +0 -0
  23. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/cache.py +0 -0
  24. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/llm.py +0 -0
  25. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/prompts.py +0 -0
  26. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/prompts.yaml +0 -0
  27. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/anthropic.py +0 -0
  28. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/azure.py +0 -0
  29. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/cerebras.py +0 -0
  30. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/cohere.py +0 -0
  31. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/cost.py +0 -0
  32. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/deepseek.py +0 -0
  33. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/errors.py +0 -0
  34. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/fireworks.py +0 -0
  35. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/groq.py +0 -0
  36. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/mistral.py +0 -0
  37. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/models.py +0 -0
  38. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/moonshot.py +0 -0
  39. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/openai.py +0 -0
  40. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/openrouter.py +0 -0
  41. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/perplexity.py +0 -0
  42. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/text.py +0 -0
  43. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/together.py +0 -0
  44. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/providers/xai.py +0 -0
  45. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/naming/resolver.py +0 -0
  46. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/paths.py +0 -0
  47. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/pipeline.py +0 -0
  48. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/promotion.py +0 -0
  49. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/sampling/__init__.py +0 -0
  50. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/sampling/backends/__init__.py +0 -0
  51. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/sampling/backends/netmiko.py +0 -0
  52. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/sampling/core.py +0 -0
  53. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge/validation.py +0 -0
  54. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge.egg-info/dependency_links.txt +0 -0
  55. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge.egg-info/entry_points.txt +0 -0
  56. {parseforge-0.2.9 → parseforge-0.2.10}/parseforge.egg-info/top_level.txt +0 -0
  57. {parseforge-0.2.9 → 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.9
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>
@@ -69,6 +69,9 @@ Requires-Dist: textfsm-ai[cohere]>=0.6.1; extra == "cohere"
69
69
  Provides-Extra: azure
70
70
  Requires-Dist: azure-ai-inference>=1.0.0b9; extra == "azure"
71
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"
72
75
  Provides-Extra: sampling
73
76
  Requires-Dist: netmiko>=4.0; extra == "sampling"
74
77
  Provides-Extra: dev
@@ -82,6 +85,7 @@ Requires-Dist: openai>=1.0.0; extra == "dev"
82
85
  Requires-Dist: mistralai==1.10.0; extra == "dev"
83
86
  Requires-Dist: cohere==5.21.1; extra == "dev"
84
87
  Requires-Dist: azure-ai-inference>=1.0.0b9; extra == "dev"
88
+ Requires-Dist: google-genai>=0.2.0; extra == "dev"
85
89
  Provides-Extra: release
86
90
  Requires-Dist: bump2version; extra == "release"
87
91
  Requires-Dist: build; extra == "release"
@@ -124,11 +128,11 @@ command that's pure local processing (`canonical`/`readable`/`recognizers`,
124
128
  LLM (`name`, `check --provider`, `run`, `generate-template`, `trial`) needs the
125
129
  extra for whichever provider it uses: `anthropic`, `openai`, `deepseek`,
126
130
  `groq`, `xai`, `together`, `fireworks`, `perplexity`, `openrouter`,
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]`.
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]`.
132
136
 
133
137
  ## Development
134
138
 
@@ -136,13 +140,13 @@ sampling; combine extras as needed, e.g.
136
140
  pip install -e ".[dev,sampling]"
137
141
  pytest
138
142
  ```
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`.
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`.
146
150
 
147
151
  Linting/formatting/type-checking/docs run through tox instead of extras — see
148
152
  `tox.ini` (`tox -e lint`/`format`/`typecheck`/`docs`), each installing its own
@@ -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`, `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]`.
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 @@ sampling; 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`, `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`.
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
@@ -0,0 +1 @@
1
+ __version__ = "0.2.10"
@@ -29,6 +29,7 @@ _BUILDERS: dict[str, type[naming.RegexBuilder]] = {
29
29
  "cohere": naming.CohereRegexBuilder,
30
30
  "deepseek": naming.DeepSeekRegexBuilder,
31
31
  "fireworks": naming.FireworksRegexBuilder,
32
+ "gemini": naming.GeminiRegexBuilder,
32
33
  "groq": naming.GroqRegexBuilder,
33
34
  "mistral": naming.MistralRegexBuilder,
34
35
  "moonshot": naming.MoonshotRegexBuilder,
@@ -246,7 +247,8 @@ def main() -> None:
246
247
  "variable (ANTHROPIC_API_KEY, OPENAI_API_KEY, DEEPSEEK_API_KEY, GROQ_API_KEY, "
247
248
  "XAI_API_KEY, TOGETHER_API_KEY, FIREWORKS_API_KEY, PERPLEXITY_API_KEY, "
248
249
  "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.",
250
+ "COHERE_API_KEY, AZURE_API_KEY, GEMINI_API_KEY); only needed on a cache "
251
+ "miss.",
250
252
  )
251
253
  @click.option(
252
254
  "--model",
@@ -359,7 +361,7 @@ def name_cmd(
359
361
  help="LLM provider for template generation (textfsm-ai's own registry, "
360
362
  'e.g. "anthropic", "openai", "deepseek", "groq", "xai", "together", '
361
363
  '"fireworks", "perplexity", "openrouter", "moonshot", "cerebras", '
362
- '"mistral", "cohere", "azure"). Naming uses its own separate '
364
+ '"mistral", "cohere", "azure", "gemini"). Naming uses its own separate '
363
365
  "--naming-provider, not this one.",
364
366
  )
365
367
  @click.option("--api-key", required=True, help="API key for the generation LLM call.")
@@ -15,6 +15,7 @@ from .providers import (
15
15
  CohereRegexBuilder,
16
16
  DeepSeekRegexBuilder,
17
17
  FireworksRegexBuilder,
18
+ GeminiRegexBuilder,
18
19
  GroqRegexBuilder,
19
20
  MistralRegexBuilder,
20
21
  MoonshotRegexBuilder,
@@ -39,6 +40,7 @@ __all__ = [
39
40
  "CohereRegexBuilder",
40
41
  "DeepSeekRegexBuilder",
41
42
  "FireworksRegexBuilder",
43
+ "GeminiRegexBuilder",
42
44
  "GroqRegexBuilder",
43
45
  "MistralRegexBuilder",
44
46
  "MoonshotRegexBuilder",
@@ -4,6 +4,7 @@ from .cerebras import CerebrasRegexBuilder
4
4
  from .cohere import CohereRegexBuilder
5
5
  from .deepseek import DeepSeekRegexBuilder
6
6
  from .fireworks import FireworksRegexBuilder
7
+ from .gemini import GeminiRegexBuilder
7
8
  from .groq import GroqRegexBuilder
8
9
  from .mistral import MistralRegexBuilder
9
10
  from .moonshot import MoonshotRegexBuilder
@@ -20,6 +21,7 @@ __all__ = [
20
21
  "CohereRegexBuilder",
21
22
  "DeepSeekRegexBuilder",
22
23
  "FireworksRegexBuilder",
24
+ "GeminiRegexBuilder",
23
25
  "GroqRegexBuilder",
24
26
  "MistralRegexBuilder",
25
27
  "MoonshotRegexBuilder",
@@ -0,0 +1,160 @@
1
+ """Google Gemini-backed RegexBuilder implementation.
2
+
3
+ Like Mistral/Cohere/Azure, Gemini's official Python SDK (``google-genai``)
4
+ is not built on the OpenAI client library — it has its own client shape
5
+ (``client.models.generate_content()``). Talks to the SDK directly.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import time
12
+ from typing import TYPE_CHECKING, Any
13
+
14
+ from ..llm import CliContext, LLMCLIResponse, TokenUsage, build_prompt
15
+ from .cost import estimate_cost
16
+ from .models import default_model
17
+ from .text import extract_pattern
18
+
19
+ if TYPE_CHECKING:
20
+ import google.genai as genai_sdk
21
+
22
+ DEFAULT_MODEL = default_model("gemini")
23
+
24
+ _DEFAULT_MAX_TOKENS = 1024
25
+
26
+ # HTTP statuses where retrying the exact same request can't succeed —
27
+ # mirrors providers/errors.py's NON_RETRYABLE class-name set, but the
28
+ # google-genai SDK raises a single APIError for every HTTP failure
29
+ # (status on exc.code) rather than a family of named exception classes,
30
+ # so classification here is done by status code — same approach as
31
+ # Mistral/Cohere/Azure.
32
+ _NON_RETRYABLE_STATUSES = frozenset({400, 401, 403, 404, 409, 413, 422})
33
+
34
+
35
+ def _import_genai() -> Any:
36
+ """Deferred import — google-genai is an optional extra
37
+ (parseforge[gemini]); nothing else in this module (or a cache-hit
38
+ lookup, which never reaches build_pattern at all — see
39
+ resolver.cli_name) should require it to be installed."""
40
+ try:
41
+ import google.genai as genai
42
+ except ImportError as exc:
43
+ raise ImportError(
44
+ "the google-genai package is required to use GeminiRegexBuilder — "
45
+ "install it via `pip install parseforge[gemini]`"
46
+ ) from exc
47
+ return genai
48
+
49
+
50
+ def _is_retryable(exc: Any) -> bool:
51
+ return getattr(exc, "code", None) not in _NON_RETRYABLE_STATUSES
52
+
53
+
54
+ def _format_error_reason(exc: Any) -> str:
55
+ return f"LLM-ERROR-gemini_sdk-{getattr(exc, 'code', None)}-{exc}"
56
+
57
+
58
+ class GeminiRegexBuilder:
59
+ """Builds a cli-name regex pattern by prompting a Gemini model.
60
+
61
+ The client is constructed lazily, on the first actual call — not in
62
+ ``__init__`` — so this can be used as a default RegexBuilder without
63
+ requiring ``GEMINI_API_KEY`` to be set for cache-hit lookups, which
64
+ never reach the LLM at all (see resolver.cli_name).
65
+
66
+ API key resolves from the ``api_key`` argument if given, otherwise the
67
+ ``GEMINI_API_KEY`` environment variable (the SDK's own default).
68
+ """
69
+
70
+ provider = "gemini"
71
+
72
+ def __init__(self, model: str = DEFAULT_MODEL, api_key: str | None = None) -> None:
73
+ self.model = model
74
+ self._api_key = api_key
75
+ self._client: genai_sdk.Client | None = None
76
+
77
+ def _get_client(self) -> genai_sdk.Client:
78
+ if self._client is None:
79
+ genai = _import_genai()
80
+ api_key = self._api_key or os.environ.get("GEMINI_API_KEY")
81
+ if not api_key:
82
+ raise RuntimeError(
83
+ "no Gemini API key — pass api_key or set GEMINI_API_KEY"
84
+ )
85
+ self._client = genai.Client(api_key=api_key)
86
+ return self._client
87
+
88
+ def build_pattern(
89
+ self, command: str, context: CliContext, **kwargs: Any
90
+ ) -> LLMCLIResponse:
91
+ genai = _import_genai()
92
+ prompt = build_prompt(command, context)
93
+ max_tokens = kwargs.pop("max_tokens", None) or _DEFAULT_MAX_TOKENS
94
+ temperature = kwargs.pop("temperature", None)
95
+ # Disabled by default (budget 0) — thinking mode adds latency/
96
+ # cost this naming call doesn't need, matching textfsm-ai's own
97
+ # GeminiProvider default; override via a thinking_budget kwarg.
98
+ thinking_budget = kwargs.pop("thinking_budget", 0)
99
+
100
+ start = time.monotonic()
101
+ try:
102
+ response = self._get_client().models.generate_content(
103
+ model=self.model,
104
+ contents=prompt,
105
+ config=genai.types.GenerateContentConfig(
106
+ max_output_tokens=max_tokens,
107
+ temperature=temperature,
108
+ thinking_config=genai.types.ThinkingConfig(
109
+ thinking_budget=thinking_budget
110
+ ),
111
+ **kwargs,
112
+ ),
113
+ )
114
+ except genai.errors.APIError as exc:
115
+ if not _is_retryable(exc):
116
+ # Same request would fail the same way again — stop rather
117
+ # than let a caller burn another attempt on it.
118
+ raise
119
+ return LLMCLIResponse(
120
+ content="",
121
+ raw=exc,
122
+ usage=TokenUsage(
123
+ input_tokens=0, output_tokens=0, total_tokens=0, estimated_cost=0.0
124
+ ),
125
+ duration_ms=(time.monotonic() - start) * 1000,
126
+ reason=_format_error_reason(exc),
127
+ ready=False,
128
+ )
129
+ duration_ms = (time.monotonic() - start) * 1000
130
+
131
+ candidate = response.candidates[0] if response.candidates else None
132
+ finish_reason = candidate.finish_reason if candidate else None
133
+ usage = response.usage_metadata
134
+ input_tokens = (usage.prompt_token_count or 0) if usage else 0
135
+ output_tokens = (usage.candidates_token_count or 0) if usage else 0
136
+ total_tokens = (usage.total_token_count or 0) if usage else 0
137
+
138
+ return LLMCLIResponse(
139
+ content=extract_pattern(response.text or ""),
140
+ raw=response,
141
+ usage=TokenUsage(
142
+ input_tokens=input_tokens,
143
+ output_tokens=output_tokens,
144
+ total_tokens=total_tokens,
145
+ estimated_cost=estimate_cost(
146
+ input_tokens=input_tokens,
147
+ output_tokens=output_tokens,
148
+ total_tokens=total_tokens,
149
+ provider=self.provider,
150
+ model=self.model,
151
+ ),
152
+ ),
153
+ duration_ms=duration_ms,
154
+ # finish_reason is a str-subclassed enum whose own __str__
155
+ # prints "FinishReason.STOP" rather than "STOP" — .value
156
+ # keeps this consistent with every other provider's plain
157
+ # lowercase/uppercase reason strings.
158
+ reason=finish_reason.value if finish_reason else "",
159
+ ready=finish_reason == genai.types.FinishReason.STOP,
160
+ )
@@ -129,3 +129,15 @@ cohere:
129
129
  - command-r-plus
130
130
  - command-a
131
131
  deprecated: []
132
+
133
+ gemini:
134
+ default: gemini-2.5-flash
135
+ supported:
136
+ - gemini-2.5-flash
137
+ - gemini-2.5-flash-lite
138
+ - gemini-2.5-pro
139
+ - gemini-3-flash
140
+ - gemini-3.1-flash-lite
141
+ - gemini-3.1-pro
142
+ - gemini-3.5-flash
143
+ deprecated: []
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: parseforge
3
- Version: 0.2.9
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>
@@ -69,6 +69,9 @@ Requires-Dist: textfsm-ai[cohere]>=0.6.1; extra == "cohere"
69
69
  Provides-Extra: azure
70
70
  Requires-Dist: azure-ai-inference>=1.0.0b9; extra == "azure"
71
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"
72
75
  Provides-Extra: sampling
73
76
  Requires-Dist: netmiko>=4.0; extra == "sampling"
74
77
  Provides-Extra: dev
@@ -82,6 +85,7 @@ Requires-Dist: openai>=1.0.0; extra == "dev"
82
85
  Requires-Dist: mistralai==1.10.0; extra == "dev"
83
86
  Requires-Dist: cohere==5.21.1; extra == "dev"
84
87
  Requires-Dist: azure-ai-inference>=1.0.0b9; extra == "dev"
88
+ Requires-Dist: google-genai>=0.2.0; extra == "dev"
85
89
  Provides-Extra: release
86
90
  Requires-Dist: bump2version; extra == "release"
87
91
  Requires-Dist: build; extra == "release"
@@ -124,11 +128,11 @@ command that's pure local processing (`canonical`/`readable`/`recognizers`,
124
128
  LLM (`name`, `check --provider`, `run`, `generate-template`, `trial`) needs the
125
129
  extra for whichever provider it uses: `anthropic`, `openai`, `deepseek`,
126
130
  `groq`, `xai`, `together`, `fireworks`, `perplexity`, `openrouter`,
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]`.
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]`.
132
136
 
133
137
  ## Development
134
138
 
@@ -136,13 +140,13 @@ sampling; combine extras as needed, e.g.
136
140
  pip install -e ".[dev,sampling]"
137
141
  pytest
138
142
  ```
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`.
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`.
146
150
 
147
151
  Linting/formatting/type-checking/docs run through tox instead of extras — see
148
152
  `tox.ini` (`tox -e lint`/`format`/`typecheck`/`docs`), each installing its own
@@ -36,6 +36,7 @@ parseforge/naming/providers/cost.py
36
36
  parseforge/naming/providers/deepseek.py
37
37
  parseforge/naming/providers/errors.py
38
38
  parseforge/naming/providers/fireworks.py
39
+ parseforge/naming/providers/gemini.py
39
40
  parseforge/naming/providers/groq.py
40
41
  parseforge/naming/providers/mistral.py
41
42
  parseforge/naming/providers/models.py
@@ -34,11 +34,16 @@ openai>=1.0.0
34
34
  mistralai==1.10.0
35
35
  cohere==5.21.1
36
36
  azure-ai-inference>=1.0.0b9
37
+ google-genai>=0.2.0
37
38
 
38
39
  [fireworks]
39
40
  openai>=1.0.0
40
41
  textfsm-ai[fireworks]>=0.6.1
41
42
 
43
+ [gemini]
44
+ google-genai>=0.2.0
45
+ textfsm-ai[gemini]>=0.6.1
46
+
42
47
  [groq]
43
48
  openai>=1.0.0
44
49
  textfsm-ai[groq]>=0.6.1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "parseforge"
7
- version = "0.2.9"
7
+ version = "0.2.10"
8
8
  description = "LLM-driven pipeline that forges, validates, and promotes TextFSM templates from network CLI output"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -82,6 +82,7 @@ mistral = ["mistralai==1.10.0", "textfsm-ai[mistral]>=0.6.1"]
82
82
  # textfsm-ai's own pin.
83
83
  cohere = ["cohere==5.21.1", "textfsm-ai[cohere]>=0.6.1"]
84
84
  azure = ["azure-ai-inference>=1.0.0b9", "textfsm-ai[azure]>=0.6.1"]
85
+ gemini = ["google-genai>=0.2.0", "textfsm-ai[gemini]>=0.6.1"]
85
86
  sampling = ["netmiko>=4.0"]
86
87
  dev = [
87
88
  "pytest>=8.0.0",
@@ -89,15 +90,16 @@ dev = [
89
90
  "ruff>=0.4.0",
90
91
  "black>=24.0.0",
91
92
  "mypy>=1.10.0",
92
- # anthropic/openai/mistralai/cohere/azure-ai-inference are optional at
93
- # runtime (see the extras above), but every provider is exercised by
94
- # the CLI/tests, which should never silently skip -- so dev always
95
- # pulls all five in.
93
+ # anthropic/openai/mistralai/cohere/azure-ai-inference/google-genai
94
+ # are optional at runtime (see the extras above), but every provider
95
+ # is exercised by the CLI/tests, which should never silently skip --
96
+ # so dev always pulls all six in.
96
97
  "anthropic>=0.40.0",
97
98
  "openai>=1.0.0",
98
99
  "mistralai==1.10.0",
99
100
  "cohere==5.21.1",
100
101
  "azure-ai-inference>=1.0.0b9",
102
+ "google-genai>=0.2.0",
101
103
  ]
102
104
  # Tooling for scripts/release.ps1 and CI's own package-build step.
103
105
  # bump2version isn't otherwise installed anywhere -- release.ps1 assumes
@@ -1 +0,0 @@
1
- __version__ = "0.2.9"
File without changes
File without changes
File without changes
File without changes