parseforge 0.2.4__tar.gz → 0.2.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {parseforge-0.2.4/parseforge.egg-info → parseforge-0.2.5}/PKG-INFO +15 -11
- {parseforge-0.2.4 → parseforge-0.2.5}/README.md +11 -10
- parseforge-0.2.5/parseforge/__init__.py +1 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/cli/main.py +4 -2
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/naming/__init__.py +2 -1
- parseforge-0.2.5/parseforge/naming/providers/__init__.py +5 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/naming/providers/models.yaml +9 -0
- parseforge-0.2.5/parseforge/naming/providers/openai.py +117 -0
- {parseforge-0.2.4 → parseforge-0.2.5/parseforge.egg-info}/PKG-INFO +15 -11
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge.egg-info/SOURCES.txt +1 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge.egg-info/requires.txt +4 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/pyproject.toml +2 -1
- parseforge-0.2.4/parseforge/__init__.py +0 -1
- parseforge-0.2.4/parseforge/naming/providers/__init__.py +0 -4
- {parseforge-0.2.4 → parseforge-0.2.5}/LICENSE +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/MANIFEST.in +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/SPEC.md +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/cli/__init__.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/cli/config.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/drift.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/generation.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/integration.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/naming/assemble.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/naming/cache.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/naming/llm.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/naming/prompts.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/naming/prompts.yaml +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/naming/providers/anthropic.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/naming/providers/cost.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/naming/providers/deepseek.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/naming/providers/errors.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/naming/providers/models.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/naming/providers/text.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/naming/resolver.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/paths.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/pipeline.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/promotion.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/sampling/__init__.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/sampling/backends/__init__.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/sampling/backends/netmiko.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/sampling/core.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge/validation.py +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge.egg-info/dependency_links.txt +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge.egg-info/entry_points.txt +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/parseforge.egg-info/top_level.txt +0 -0
- {parseforge-0.2.4 → parseforge-0.2.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: parseforge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
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>
|
|
@@ -33,6 +33,9 @@ Requires-Dist: textfsm-ai[anthropic]>=0.6.1; extra == "anthropic"
|
|
|
33
33
|
Provides-Extra: deepseek
|
|
34
34
|
Requires-Dist: openai>=1.0.0; extra == "deepseek"
|
|
35
35
|
Requires-Dist: textfsm-ai[deepseek]>=0.6.1; extra == "deepseek"
|
|
36
|
+
Provides-Extra: openai
|
|
37
|
+
Requires-Dist: openai>=1.0.0; extra == "openai"
|
|
38
|
+
Requires-Dist: textfsm-ai[openai]>=0.6.1; extra == "openai"
|
|
36
39
|
Provides-Extra: sampling
|
|
37
40
|
Requires-Dist: netmiko>=4.0; extra == "sampling"
|
|
38
41
|
Provides-Extra: dev
|
|
@@ -83,11 +86,11 @@ pip install parseforge[anthropic]
|
|
|
83
86
|
command that's pure local processing (`canonical`/`readable`/`recognizers`,
|
|
84
87
|
`integration`, `promotion`) works with nothing further. Anything that calls an
|
|
85
88
|
LLM (`name`, `check --provider`, `run`, `generate-template`, `trial`) needs the
|
|
86
|
-
extra for whichever provider it uses: `anthropic` or `deepseek`.
|
|
87
|
-
defaults to `anthropic` wherever it isn't required, so that's the
|
|
88
|
-
setups need. `pip install parseforge[sampling]` adds Netmiko for live
|
|
89
|
-
sampling; combine extras as needed, e.g.
|
|
90
|
-
`pip install parseforge[anthropic,deepseek,sampling]`.
|
|
89
|
+
extra for whichever provider it uses: `anthropic`, `openai`, or `deepseek`.
|
|
90
|
+
`--provider` defaults to `anthropic` wherever it isn't required, so that's the
|
|
91
|
+
one most setups need. `pip install parseforge[sampling]` adds Netmiko for live
|
|
92
|
+
device sampling; combine extras as needed, e.g.
|
|
93
|
+
`pip install parseforge[anthropic,openai,deepseek,sampling]`.
|
|
91
94
|
|
|
92
95
|
## Development
|
|
93
96
|
|
|
@@ -95,9 +98,10 @@ sampling; combine extras as needed, e.g.
|
|
|
95
98
|
pip install -e ".[dev,sampling]"
|
|
96
99
|
pytest
|
|
97
100
|
```
|
|
98
|
-
`dev` already includes both `anthropic` and `openai` (tests exercise
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
`dev` already includes both the `anthropic` and `openai` SDKs (tests exercise
|
|
102
|
+
all three providers — `anthropic`, `openai`, `deepseek` share just those two
|
|
103
|
+
packages — and never silently skip) — add `,anthropic`/`,openai`/`,deepseek`
|
|
104
|
+
explicitly only if installing outside of `dev`.
|
|
101
105
|
|
|
102
106
|
Linting/formatting/type-checking/docs run through tox instead of extras — see
|
|
103
107
|
`tox.ini` (`tox -e lint`/`format`/`typecheck`/`docs`), each installing its own
|
|
@@ -119,8 +123,8 @@ parseforge name --vendor cisco --family catalyst9200 --os ios-xe --version 17.9.
|
|
|
119
123
|
```
|
|
120
124
|
Still needs an LLM provider on a cache miss — `--provider` defaults to `anthropic`,
|
|
121
125
|
and `--api-key` falls back to that provider's own env var (`ANTHROPIC_API_KEY`/
|
|
122
|
-
`DEEPSEEK_API_KEY`). A cache hit (a command already seen before)
|
|
123
|
-
LLM, so no key is needed at all in that case.
|
|
126
|
+
`OPENAI_API_KEY`/`DEEPSEEK_API_KEY`). A cache hit (a command already seen before)
|
|
127
|
+
never touches the LLM, so no key is needed at all in that case.
|
|
124
128
|
|
|
125
129
|
**`check`** — validate a connector or provider before spending time/tokens on a real
|
|
126
130
|
run. With neither `--env` nor explicit connection flags, prints what a connector needs
|
|
@@ -33,11 +33,11 @@ pip install parseforge[anthropic]
|
|
|
33
33
|
command that's pure local processing (`canonical`/`readable`/`recognizers`,
|
|
34
34
|
`integration`, `promotion`) works with nothing further. Anything that calls an
|
|
35
35
|
LLM (`name`, `check --provider`, `run`, `generate-template`, `trial`) needs the
|
|
36
|
-
extra for whichever provider it uses: `anthropic` or `deepseek`.
|
|
37
|
-
defaults to `anthropic` wherever it isn't required, so that's the
|
|
38
|
-
setups need. `pip install parseforge[sampling]` adds Netmiko for live
|
|
39
|
-
sampling; combine extras as needed, e.g.
|
|
40
|
-
`pip install parseforge[anthropic,deepseek,sampling]`.
|
|
36
|
+
extra for whichever provider it uses: `anthropic`, `openai`, or `deepseek`.
|
|
37
|
+
`--provider` defaults to `anthropic` wherever it isn't required, so that's the
|
|
38
|
+
one most setups need. `pip install parseforge[sampling]` adds Netmiko for live
|
|
39
|
+
device sampling; combine extras as needed, e.g.
|
|
40
|
+
`pip install parseforge[anthropic,openai,deepseek,sampling]`.
|
|
41
41
|
|
|
42
42
|
## Development
|
|
43
43
|
|
|
@@ -45,9 +45,10 @@ sampling; combine extras as needed, e.g.
|
|
|
45
45
|
pip install -e ".[dev,sampling]"
|
|
46
46
|
pytest
|
|
47
47
|
```
|
|
48
|
-
`dev` already includes both `anthropic` and `openai` (tests exercise
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
`dev` already includes both the `anthropic` and `openai` SDKs (tests exercise
|
|
49
|
+
all three providers — `anthropic`, `openai`, `deepseek` share just those two
|
|
50
|
+
packages — and never silently skip) — add `,anthropic`/`,openai`/`,deepseek`
|
|
51
|
+
explicitly only if installing outside of `dev`.
|
|
51
52
|
|
|
52
53
|
Linting/formatting/type-checking/docs run through tox instead of extras — see
|
|
53
54
|
`tox.ini` (`tox -e lint`/`format`/`typecheck`/`docs`), each installing its own
|
|
@@ -69,8 +70,8 @@ parseforge name --vendor cisco --family catalyst9200 --os ios-xe --version 17.9.
|
|
|
69
70
|
```
|
|
70
71
|
Still needs an LLM provider on a cache miss — `--provider` defaults to `anthropic`,
|
|
71
72
|
and `--api-key` falls back to that provider's own env var (`ANTHROPIC_API_KEY`/
|
|
72
|
-
`DEEPSEEK_API_KEY`). A cache hit (a command already seen before)
|
|
73
|
-
LLM, so no key is needed at all in that case.
|
|
73
|
+
`OPENAI_API_KEY`/`DEEPSEEK_API_KEY`). A cache hit (a command already seen before)
|
|
74
|
+
never touches the LLM, so no key is needed at all in that case.
|
|
74
75
|
|
|
75
76
|
**`check`** — validate a connector or provider before spending time/tokens on a real
|
|
76
77
|
run. With neither `--env` nor explicit connection flags, prints what a connector needs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.5"
|
|
@@ -24,6 +24,7 @@ from parseforge.cli import config as cli_config
|
|
|
24
24
|
_BUILDERS: dict[str, type[naming.RegexBuilder]] = {
|
|
25
25
|
"anthropic": naming.AnthropicRegexBuilder,
|
|
26
26
|
"deepseek": naming.DeepSeekRegexBuilder,
|
|
27
|
+
"openai": naming.OpenAIRegexBuilder,
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
_CONNECTORS = ("netmiko",)
|
|
@@ -198,7 +199,8 @@ def main() -> None:
|
|
|
198
199
|
"--api-key",
|
|
199
200
|
default=None,
|
|
200
201
|
help="Provider API key. Defaults to that provider's own API key environment "
|
|
201
|
-
"variable (ANTHROPIC_API_KEY, DEEPSEEK_API_KEY); only needed
|
|
202
|
+
"variable (ANTHROPIC_API_KEY, OPENAI_API_KEY, DEEPSEEK_API_KEY); only needed "
|
|
203
|
+
"on a cache miss.",
|
|
202
204
|
)
|
|
203
205
|
@click.option(
|
|
204
206
|
"--model",
|
|
@@ -266,7 +268,7 @@ def name_cmd(
|
|
|
266
268
|
"--provider",
|
|
267
269
|
required=True,
|
|
268
270
|
help="LLM provider for template generation (textfsm-ai's own registry, "
|
|
269
|
-
'e.g. "anthropic", "deepseek"). Naming uses its own separate '
|
|
271
|
+
'e.g. "anthropic", "openai", "deepseek"). Naming uses its own separate '
|
|
270
272
|
"--naming-provider, not this one.",
|
|
271
273
|
)
|
|
272
274
|
@click.option("--api-key", required=True, help="API key for the generation LLM call.")
|
|
@@ -8,7 +8,7 @@ from .llm import (
|
|
|
8
8
|
UnimplementedRegexBuilder,
|
|
9
9
|
build_prompt,
|
|
10
10
|
)
|
|
11
|
-
from .providers import AnthropicRegexBuilder, DeepSeekRegexBuilder
|
|
11
|
+
from .providers import AnthropicRegexBuilder, DeepSeekRegexBuilder, OpenAIRegexBuilder
|
|
12
12
|
from .resolver import NamingResolution, cli_name, resolve_cli_name
|
|
13
13
|
|
|
14
14
|
__all__ = [
|
|
@@ -20,6 +20,7 @@ __all__ = [
|
|
|
20
20
|
"UnimplementedRegexBuilder",
|
|
21
21
|
"AnthropicRegexBuilder",
|
|
22
22
|
"DeepSeekRegexBuilder",
|
|
23
|
+
"OpenAIRegexBuilder",
|
|
23
24
|
"LLMCLIResponse",
|
|
24
25
|
"TokenUsage",
|
|
25
26
|
"build_prompt",
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""OpenAI-backed RegexBuilder implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from ..llm import CliContext, LLMCLIResponse, TokenUsage, build_prompt
|
|
9
|
+
from .cost import estimate_cost
|
|
10
|
+
from .errors import format_llm_error_reason, is_retryable
|
|
11
|
+
from .models import default_model
|
|
12
|
+
from .text import extract_pattern
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
import openai as openai_sdk
|
|
16
|
+
|
|
17
|
+
DEFAULT_MODEL = default_model("openai")
|
|
18
|
+
|
|
19
|
+
_DEFAULT_MAX_TOKENS = 1024
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _import_openai() -> Any:
|
|
23
|
+
"""Deferred import — openai is an optional extra (parseforge[openai]);
|
|
24
|
+
nothing else in this module (or a cache-hit lookup, which never reaches
|
|
25
|
+
build_pattern at all — see resolver.cli_name) should require it to be
|
|
26
|
+
installed."""
|
|
27
|
+
try:
|
|
28
|
+
import openai
|
|
29
|
+
except ImportError as exc:
|
|
30
|
+
raise ImportError(
|
|
31
|
+
"the openai package is required to use OpenAIRegexBuilder — "
|
|
32
|
+
"install it via `pip install parseforge[openai]`"
|
|
33
|
+
) from exc
|
|
34
|
+
return openai
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class OpenAIRegexBuilder:
|
|
38
|
+
"""Builds a cli-name regex pattern by prompting an OpenAI model.
|
|
39
|
+
|
|
40
|
+
The client is constructed lazily, on the first actual call — not in
|
|
41
|
+
``__init__`` — so this can be used as a default RegexBuilder without
|
|
42
|
+
requiring ``OPENAI_API_KEY`` to be set for cache-hit lookups, which
|
|
43
|
+
never reach the LLM at all (see resolver.cli_name).
|
|
44
|
+
|
|
45
|
+
API key resolves from the ``api_key`` argument if given, otherwise the
|
|
46
|
+
``OPENAI_API_KEY`` environment variable (the SDK's own default).
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
provider = "openai"
|
|
50
|
+
|
|
51
|
+
def __init__(self, model: str = DEFAULT_MODEL, api_key: str | None = None) -> None:
|
|
52
|
+
self.model = model
|
|
53
|
+
self._api_key = api_key
|
|
54
|
+
self._client: openai_sdk.OpenAI | None = None
|
|
55
|
+
|
|
56
|
+
def _get_client(self) -> openai_sdk.OpenAI:
|
|
57
|
+
if self._client is None:
|
|
58
|
+
openai = _import_openai()
|
|
59
|
+
self._client = (
|
|
60
|
+
openai.OpenAI(api_key=self._api_key)
|
|
61
|
+
if self._api_key
|
|
62
|
+
else openai.OpenAI()
|
|
63
|
+
)
|
|
64
|
+
return self._client
|
|
65
|
+
|
|
66
|
+
def build_pattern(
|
|
67
|
+
self, command: str, context: CliContext, **kwargs: Any
|
|
68
|
+
) -> LLMCLIResponse:
|
|
69
|
+
openai = _import_openai()
|
|
70
|
+
prompt = build_prompt(command, context)
|
|
71
|
+
max_tokens = kwargs.pop("max_tokens", None) or _DEFAULT_MAX_TOKENS
|
|
72
|
+
|
|
73
|
+
start = time.monotonic()
|
|
74
|
+
try:
|
|
75
|
+
response = self._get_client().chat.completions.create(
|
|
76
|
+
model=self.model,
|
|
77
|
+
max_tokens=max_tokens,
|
|
78
|
+
messages=[{"role": "user", "content": prompt}],
|
|
79
|
+
**kwargs,
|
|
80
|
+
)
|
|
81
|
+
except openai.OpenAIError as exc:
|
|
82
|
+
if not is_retryable(exc):
|
|
83
|
+
# Same request would fail the same way again — stop rather
|
|
84
|
+
# than let a caller burn another attempt on it.
|
|
85
|
+
raise
|
|
86
|
+
return LLMCLIResponse(
|
|
87
|
+
content="",
|
|
88
|
+
raw=exc,
|
|
89
|
+
usage=TokenUsage(
|
|
90
|
+
input_tokens=0, output_tokens=0, total_tokens=0, estimated_cost=0.0
|
|
91
|
+
),
|
|
92
|
+
duration_ms=(time.monotonic() - start) * 1000,
|
|
93
|
+
reason=format_llm_error_reason(exc),
|
|
94
|
+
ready=False,
|
|
95
|
+
)
|
|
96
|
+
duration_ms = (time.monotonic() - start) * 1000
|
|
97
|
+
|
|
98
|
+
choice = response.choices[0]
|
|
99
|
+
return LLMCLIResponse(
|
|
100
|
+
content=extract_pattern(choice.message.content or ""),
|
|
101
|
+
raw=response,
|
|
102
|
+
usage=TokenUsage(
|
|
103
|
+
input_tokens=response.usage.prompt_tokens,
|
|
104
|
+
output_tokens=response.usage.completion_tokens,
|
|
105
|
+
total_tokens=response.usage.total_tokens,
|
|
106
|
+
estimated_cost=estimate_cost(
|
|
107
|
+
input_tokens=response.usage.prompt_tokens,
|
|
108
|
+
output_tokens=response.usage.completion_tokens,
|
|
109
|
+
total_tokens=response.usage.total_tokens,
|
|
110
|
+
provider=self.provider,
|
|
111
|
+
model=self.model,
|
|
112
|
+
),
|
|
113
|
+
),
|
|
114
|
+
duration_ms=duration_ms,
|
|
115
|
+
reason=choice.finish_reason or "",
|
|
116
|
+
ready=choice.finish_reason == "stop",
|
|
117
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: parseforge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
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>
|
|
@@ -33,6 +33,9 @@ Requires-Dist: textfsm-ai[anthropic]>=0.6.1; extra == "anthropic"
|
|
|
33
33
|
Provides-Extra: deepseek
|
|
34
34
|
Requires-Dist: openai>=1.0.0; extra == "deepseek"
|
|
35
35
|
Requires-Dist: textfsm-ai[deepseek]>=0.6.1; extra == "deepseek"
|
|
36
|
+
Provides-Extra: openai
|
|
37
|
+
Requires-Dist: openai>=1.0.0; extra == "openai"
|
|
38
|
+
Requires-Dist: textfsm-ai[openai]>=0.6.1; extra == "openai"
|
|
36
39
|
Provides-Extra: sampling
|
|
37
40
|
Requires-Dist: netmiko>=4.0; extra == "sampling"
|
|
38
41
|
Provides-Extra: dev
|
|
@@ -83,11 +86,11 @@ pip install parseforge[anthropic]
|
|
|
83
86
|
command that's pure local processing (`canonical`/`readable`/`recognizers`,
|
|
84
87
|
`integration`, `promotion`) works with nothing further. Anything that calls an
|
|
85
88
|
LLM (`name`, `check --provider`, `run`, `generate-template`, `trial`) needs the
|
|
86
|
-
extra for whichever provider it uses: `anthropic` or `deepseek`.
|
|
87
|
-
defaults to `anthropic` wherever it isn't required, so that's the
|
|
88
|
-
setups need. `pip install parseforge[sampling]` adds Netmiko for live
|
|
89
|
-
sampling; combine extras as needed, e.g.
|
|
90
|
-
`pip install parseforge[anthropic,deepseek,sampling]`.
|
|
89
|
+
extra for whichever provider it uses: `anthropic`, `openai`, or `deepseek`.
|
|
90
|
+
`--provider` defaults to `anthropic` wherever it isn't required, so that's the
|
|
91
|
+
one most setups need. `pip install parseforge[sampling]` adds Netmiko for live
|
|
92
|
+
device sampling; combine extras as needed, e.g.
|
|
93
|
+
`pip install parseforge[anthropic,openai,deepseek,sampling]`.
|
|
91
94
|
|
|
92
95
|
## Development
|
|
93
96
|
|
|
@@ -95,9 +98,10 @@ sampling; combine extras as needed, e.g.
|
|
|
95
98
|
pip install -e ".[dev,sampling]"
|
|
96
99
|
pytest
|
|
97
100
|
```
|
|
98
|
-
`dev` already includes both `anthropic` and `openai` (tests exercise
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
`dev` already includes both the `anthropic` and `openai` SDKs (tests exercise
|
|
102
|
+
all three providers — `anthropic`, `openai`, `deepseek` share just those two
|
|
103
|
+
packages — and never silently skip) — add `,anthropic`/`,openai`/`,deepseek`
|
|
104
|
+
explicitly only if installing outside of `dev`.
|
|
101
105
|
|
|
102
106
|
Linting/formatting/type-checking/docs run through tox instead of extras — see
|
|
103
107
|
`tox.ini` (`tox -e lint`/`format`/`typecheck`/`docs`), each installing its own
|
|
@@ -119,8 +123,8 @@ parseforge name --vendor cisco --family catalyst9200 --os ios-xe --version 17.9.
|
|
|
119
123
|
```
|
|
120
124
|
Still needs an LLM provider on a cache miss — `--provider` defaults to `anthropic`,
|
|
121
125
|
and `--api-key` falls back to that provider's own env var (`ANTHROPIC_API_KEY`/
|
|
122
|
-
`DEEPSEEK_API_KEY`). A cache hit (a command already seen before)
|
|
123
|
-
LLM, so no key is needed at all in that case.
|
|
126
|
+
`OPENAI_API_KEY`/`DEEPSEEK_API_KEY`). A cache hit (a command already seen before)
|
|
127
|
+
never touches the LLM, so no key is needed at all in that case.
|
|
124
128
|
|
|
125
129
|
**`check`** — validate a connector or provider before spending time/tokens on a real
|
|
126
130
|
run. With neither `--env` nor explicit connection flags, prints what a connector needs
|
|
@@ -34,6 +34,7 @@ parseforge/naming/providers/deepseek.py
|
|
|
34
34
|
parseforge/naming/providers/errors.py
|
|
35
35
|
parseforge/naming/providers/models.py
|
|
36
36
|
parseforge/naming/providers/models.yaml
|
|
37
|
+
parseforge/naming/providers/openai.py
|
|
37
38
|
parseforge/naming/providers/text.py
|
|
38
39
|
parseforge/sampling/__init__.py
|
|
39
40
|
parseforge/sampling/core.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "parseforge"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.5"
|
|
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" }
|
|
@@ -64,6 +64,7 @@ keywords = [
|
|
|
64
64
|
# provider(s) you actually use.
|
|
65
65
|
anthropic = ["anthropic>=0.40.0", "textfsm-ai[anthropic]>=0.6.1"]
|
|
66
66
|
deepseek = ["openai>=1.0.0", "textfsm-ai[deepseek]>=0.6.1"]
|
|
67
|
+
openai = ["openai>=1.0.0", "textfsm-ai[openai]>=0.6.1"]
|
|
67
68
|
sampling = ["netmiko>=4.0"]
|
|
68
69
|
dev = [
|
|
69
70
|
"pytest>=8.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.4"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|