parseforge 0.2.3__tar.gz → 0.2.4__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.3/parseforge.egg-info → parseforge-0.2.4}/PKG-INFO +36 -4
- {parseforge-0.2.3 → parseforge-0.2.4}/README.md +23 -0
- parseforge-0.2.4/parseforge/__init__.py +1 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/generation.py +6 -1
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/providers/anthropic.py +27 -7
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/providers/deepseek.py +25 -7
- {parseforge-0.2.3 → parseforge-0.2.4/parseforge.egg-info}/PKG-INFO +36 -4
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge.egg-info/requires.txt +13 -1
- {parseforge-0.2.3 → parseforge-0.2.4}/pyproject.toml +22 -4
- parseforge-0.2.3/parseforge/__init__.py +0 -1
- {parseforge-0.2.3 → parseforge-0.2.4}/LICENSE +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/MANIFEST.in +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/SPEC.md +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/cli/__init__.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/cli/config.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/cli/main.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/drift.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/integration.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/__init__.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/assemble.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/cache.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/llm.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/prompts.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/prompts.yaml +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/providers/__init__.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/providers/cost.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/providers/errors.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/providers/models.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/providers/models.yaml +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/providers/text.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/naming/resolver.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/paths.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/pipeline.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/promotion.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/sampling/__init__.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/sampling/backends/__init__.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/sampling/backends/netmiko.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/sampling/core.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge/validation.py +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge.egg-info/SOURCES.txt +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge.egg-info/dependency_links.txt +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge.egg-info/entry_points.txt +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/parseforge.egg-info/top_level.txt +0 -0
- {parseforge-0.2.3 → parseforge-0.2.4}/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.4
|
|
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>
|
|
@@ -26,9 +26,13 @@ License-File: LICENSE
|
|
|
26
26
|
Requires-Dist: click>=8.1
|
|
27
27
|
Requires-Dist: PyYAML>=6.0
|
|
28
28
|
Requires-Dist: textfsm>=1.1.0
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
|
|
31
|
-
Requires-Dist:
|
|
29
|
+
Requires-Dist: textfsm-ai>=0.6.1
|
|
30
|
+
Provides-Extra: anthropic
|
|
31
|
+
Requires-Dist: anthropic>=0.40.0; extra == "anthropic"
|
|
32
|
+
Requires-Dist: textfsm-ai[anthropic]>=0.6.1; extra == "anthropic"
|
|
33
|
+
Provides-Extra: deepseek
|
|
34
|
+
Requires-Dist: openai>=1.0.0; extra == "deepseek"
|
|
35
|
+
Requires-Dist: textfsm-ai[deepseek]>=0.6.1; extra == "deepseek"
|
|
32
36
|
Provides-Extra: sampling
|
|
33
37
|
Requires-Dist: netmiko>=4.0; extra == "sampling"
|
|
34
38
|
Provides-Extra: dev
|
|
@@ -37,6 +41,11 @@ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
|
|
|
37
41
|
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
38
42
|
Requires-Dist: black>=24.0.0; extra == "dev"
|
|
39
43
|
Requires-Dist: mypy>=1.10.0; extra == "dev"
|
|
44
|
+
Requires-Dist: anthropic>=0.40.0; extra == "dev"
|
|
45
|
+
Requires-Dist: openai>=1.0.0; extra == "dev"
|
|
46
|
+
Provides-Extra: release
|
|
47
|
+
Requires-Dist: bump2version; extra == "release"
|
|
48
|
+
Requires-Dist: build; extra == "release"
|
|
40
49
|
Dynamic: license-file
|
|
41
50
|
|
|
42
51
|
# ParseForge
|
|
@@ -65,12 +74,35 @@ wired into the CLI below. A few things are intentionally not there yet:
|
|
|
65
74
|
- **One sampling connector** (Netmiko/SSH). The CLI's `--connector` registry
|
|
66
75
|
is built to hold more without a redesign, but nothing else is wired in yet.
|
|
67
76
|
|
|
77
|
+
## Installation
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
pip install parseforge[anthropic]
|
|
81
|
+
```
|
|
82
|
+
`pip install parseforge` alone installs no AI-provider SDK at all — every
|
|
83
|
+
command that's pure local processing (`canonical`/`readable`/`recognizers`,
|
|
84
|
+
`integration`, `promotion`) works with nothing further. Anything that calls an
|
|
85
|
+
LLM (`name`, `check --provider`, `run`, `generate-template`, `trial`) needs the
|
|
86
|
+
extra for whichever provider it uses: `anthropic` or `deepseek`. `--provider`
|
|
87
|
+
defaults to `anthropic` wherever it isn't required, so that's the one most
|
|
88
|
+
setups need. `pip install parseforge[sampling]` adds Netmiko for live device
|
|
89
|
+
sampling; combine extras as needed, e.g.
|
|
90
|
+
`pip install parseforge[anthropic,deepseek,sampling]`.
|
|
91
|
+
|
|
68
92
|
## Development
|
|
69
93
|
|
|
70
94
|
```
|
|
71
95
|
pip install -e ".[dev,sampling]"
|
|
72
96
|
pytest
|
|
73
97
|
```
|
|
98
|
+
`dev` already includes both `anthropic` and `openai` (tests exercise both
|
|
99
|
+
providers, never silently skip) — add `,anthropic`/`,deepseek` explicitly only
|
|
100
|
+
if installing outside of `dev`.
|
|
101
|
+
|
|
102
|
+
Linting/formatting/type-checking/docs run through tox instead of extras — see
|
|
103
|
+
`tox.ini` (`tox -e lint`/`format`/`typecheck`/`docs`), each installing its own
|
|
104
|
+
tools in an isolated env. Cutting a release needs `pip install -e ".[release]"`
|
|
105
|
+
(`bump2version`, `build`) — see `scripts/release.ps1`.
|
|
74
106
|
|
|
75
107
|
## CLI
|
|
76
108
|
|
|
@@ -24,12 +24,35 @@ wired into the CLI below. A few things are intentionally not there yet:
|
|
|
24
24
|
- **One sampling connector** (Netmiko/SSH). The CLI's `--connector` registry
|
|
25
25
|
is built to hold more without a redesign, but nothing else is wired in yet.
|
|
26
26
|
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
pip install parseforge[anthropic]
|
|
31
|
+
```
|
|
32
|
+
`pip install parseforge` alone installs no AI-provider SDK at all — every
|
|
33
|
+
command that's pure local processing (`canonical`/`readable`/`recognizers`,
|
|
34
|
+
`integration`, `promotion`) works with nothing further. Anything that calls an
|
|
35
|
+
LLM (`name`, `check --provider`, `run`, `generate-template`, `trial`) needs the
|
|
36
|
+
extra for whichever provider it uses: `anthropic` or `deepseek`. `--provider`
|
|
37
|
+
defaults to `anthropic` wherever it isn't required, so that's the one most
|
|
38
|
+
setups need. `pip install parseforge[sampling]` adds Netmiko for live device
|
|
39
|
+
sampling; combine extras as needed, e.g.
|
|
40
|
+
`pip install parseforge[anthropic,deepseek,sampling]`.
|
|
41
|
+
|
|
27
42
|
## Development
|
|
28
43
|
|
|
29
44
|
```
|
|
30
45
|
pip install -e ".[dev,sampling]"
|
|
31
46
|
pytest
|
|
32
47
|
```
|
|
48
|
+
`dev` already includes both `anthropic` and `openai` (tests exercise both
|
|
49
|
+
providers, never silently skip) — add `,anthropic`/`,deepseek` explicitly only
|
|
50
|
+
if installing outside of `dev`.
|
|
51
|
+
|
|
52
|
+
Linting/formatting/type-checking/docs run through tox instead of extras — see
|
|
53
|
+
`tox.ini` (`tox -e lint`/`format`/`typecheck`/`docs`), each installing its own
|
|
54
|
+
tools in an isolated env. Cutting a release needs `pip install -e ".[release]"`
|
|
55
|
+
(`bump2version`, `build`) — see `scripts/release.ps1`.
|
|
33
56
|
|
|
34
57
|
## CLI
|
|
35
58
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.4"
|
|
@@ -78,7 +78,12 @@ def generate(
|
|
|
78
78
|
forwards to ``run_pipeline()`` for everything else (``endpoint``,
|
|
79
79
|
``region``, ``max_tries``, ...).
|
|
80
80
|
|
|
81
|
-
Never raises for a failed generation
|
|
81
|
+
Never raises for a failed *generation* (a bad/truncated LLM response) —
|
|
82
|
+
check ``.ready`` for that. Does raise ``ImportError`` if ``provider``'s
|
|
83
|
+
SDK isn't installed (textfsm-ai's own lazy provider registry, pointing
|
|
84
|
+
at the right ``pip install textfsm-ai[<provider>]`` extra) — a missing
|
|
85
|
+
package is an environment problem, not a generation outcome, and is
|
|
86
|
+
treated the same way here as in :mod:`parseforge.naming.providers`.
|
|
82
87
|
"""
|
|
83
88
|
result = run_pipeline(
|
|
84
89
|
sample, provider, api_key, model, mode="debug", as_json=True, **kwargs
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import time
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
from anthropic import Anthropic, AnthropicError
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
9
7
|
|
|
10
8
|
from ..llm import CliContext, LLMCLIResponse, TokenUsage, build_prompt
|
|
11
9
|
from .cost import estimate_cost
|
|
@@ -13,6 +11,9 @@ from .errors import format_llm_error_reason, is_retryable
|
|
|
13
11
|
from .models import default_model
|
|
14
12
|
from .text import extract_pattern
|
|
15
13
|
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
import anthropic as anthropic_sdk
|
|
16
|
+
|
|
16
17
|
DEFAULT_MODEL = default_model("anthropic")
|
|
17
18
|
|
|
18
19
|
# Headroom in case a future/opt-in extended-thinking model burns part of
|
|
@@ -21,6 +22,21 @@ DEFAULT_MODEL = default_model("anthropic")
|
|
|
21
22
|
_DEFAULT_MAX_TOKENS = 1024
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
def _import_anthropic() -> Any:
|
|
26
|
+
"""Deferred import — anthropic is an optional extra (parseforge[anthropic]);
|
|
27
|
+
nothing else in this module (or a cache-hit lookup, which never reaches
|
|
28
|
+
build_pattern at all — see resolver.cli_name) should require it to be
|
|
29
|
+
installed."""
|
|
30
|
+
try:
|
|
31
|
+
import anthropic
|
|
32
|
+
except ImportError as exc:
|
|
33
|
+
raise ImportError(
|
|
34
|
+
"the anthropic package is required to use AnthropicRegexBuilder — "
|
|
35
|
+
"install it via `pip install parseforge[anthropic]`"
|
|
36
|
+
) from exc
|
|
37
|
+
return anthropic
|
|
38
|
+
|
|
39
|
+
|
|
24
40
|
class AnthropicRegexBuilder:
|
|
25
41
|
"""Builds a cli-name regex pattern by prompting a Claude model.
|
|
26
42
|
|
|
@@ -38,18 +54,22 @@ class AnthropicRegexBuilder:
|
|
|
38
54
|
def __init__(self, model: str = DEFAULT_MODEL, api_key: str | None = None) -> None:
|
|
39
55
|
self.model = model
|
|
40
56
|
self._api_key = api_key
|
|
41
|
-
self._client: Anthropic | None = None
|
|
57
|
+
self._client: anthropic_sdk.Anthropic | None = None
|
|
42
58
|
|
|
43
|
-
def _get_client(self) -> Anthropic:
|
|
59
|
+
def _get_client(self) -> anthropic_sdk.Anthropic:
|
|
44
60
|
if self._client is None:
|
|
61
|
+
anthropic = _import_anthropic()
|
|
45
62
|
self._client = (
|
|
46
|
-
Anthropic(api_key=self._api_key)
|
|
63
|
+
anthropic.Anthropic(api_key=self._api_key)
|
|
64
|
+
if self._api_key
|
|
65
|
+
else anthropic.Anthropic()
|
|
47
66
|
)
|
|
48
67
|
return self._client
|
|
49
68
|
|
|
50
69
|
def build_pattern(
|
|
51
70
|
self, command: str, context: CliContext, **kwargs: Any
|
|
52
71
|
) -> LLMCLIResponse:
|
|
72
|
+
anthropic = _import_anthropic()
|
|
53
73
|
prompt = build_prompt(command, context)
|
|
54
74
|
max_tokens = kwargs.pop("max_tokens", None) or _DEFAULT_MAX_TOKENS
|
|
55
75
|
|
|
@@ -61,7 +81,7 @@ class AnthropicRegexBuilder:
|
|
|
61
81
|
messages=[{"role": "user", "content": prompt}],
|
|
62
82
|
**kwargs,
|
|
63
83
|
)
|
|
64
|
-
except AnthropicError as exc:
|
|
84
|
+
except anthropic.AnthropicError as exc:
|
|
65
85
|
if not is_retryable(exc):
|
|
66
86
|
# Same request would fail the same way again — stop rather
|
|
67
87
|
# than let a caller burn another attempt on it.
|
|
@@ -9,9 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
import os
|
|
11
11
|
import time
|
|
12
|
-
from typing import Any
|
|
13
|
-
|
|
14
|
-
from openai import OpenAI, OpenAIError
|
|
12
|
+
from typing import TYPE_CHECKING, Any
|
|
15
13
|
|
|
16
14
|
from ..llm import CliContext, LLMCLIResponse, TokenUsage, build_prompt
|
|
17
15
|
from .cost import estimate_cost
|
|
@@ -19,6 +17,9 @@ from .errors import format_llm_error_reason, is_retryable
|
|
|
19
17
|
from .models import default_model
|
|
20
18
|
from .text import extract_pattern
|
|
21
19
|
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
import openai as openai_sdk
|
|
22
|
+
|
|
22
23
|
DEFAULT_MODEL = default_model("deepseek")
|
|
23
24
|
|
|
24
25
|
_BASE_URL = "https://api.deepseek.com"
|
|
@@ -34,6 +35,21 @@ _DEFAULT_MAX_TOKENS = 1024
|
|
|
34
35
|
_THINKING_DISABLED = {"thinking": {"type": "disabled"}}
|
|
35
36
|
|
|
36
37
|
|
|
38
|
+
def _import_openai() -> Any:
|
|
39
|
+
"""Deferred import — openai is an optional extra (parseforge[deepseek]);
|
|
40
|
+
nothing else in this module (or a cache-hit lookup, which never reaches
|
|
41
|
+
build_pattern at all — see resolver.cli_name) should require it to be
|
|
42
|
+
installed."""
|
|
43
|
+
try:
|
|
44
|
+
import openai
|
|
45
|
+
except ImportError as exc:
|
|
46
|
+
raise ImportError(
|
|
47
|
+
"the openai package is required to use DeepSeekRegexBuilder — "
|
|
48
|
+
"install it via `pip install parseforge[deepseek]`"
|
|
49
|
+
) from exc
|
|
50
|
+
return openai
|
|
51
|
+
|
|
52
|
+
|
|
37
53
|
class DeepSeekRegexBuilder:
|
|
38
54
|
"""Builds a cli-name regex pattern by prompting a DeepSeek model.
|
|
39
55
|
|
|
@@ -53,21 +69,23 @@ class DeepSeekRegexBuilder:
|
|
|
53
69
|
def __init__(self, model: str = DEFAULT_MODEL, api_key: str | None = None) -> None:
|
|
54
70
|
self.model = model
|
|
55
71
|
self._api_key = api_key
|
|
56
|
-
self._client: OpenAI | None = None
|
|
72
|
+
self._client: openai_sdk.OpenAI | None = None
|
|
57
73
|
|
|
58
|
-
def _get_client(self) -> OpenAI:
|
|
74
|
+
def _get_client(self) -> openai_sdk.OpenAI:
|
|
59
75
|
if self._client is None:
|
|
76
|
+
openai = _import_openai()
|
|
60
77
|
api_key = self._api_key or os.environ.get("DEEPSEEK_API_KEY")
|
|
61
78
|
if not api_key:
|
|
62
79
|
raise RuntimeError(
|
|
63
80
|
"no DeepSeek API key — pass api_key or set DEEPSEEK_API_KEY"
|
|
64
81
|
)
|
|
65
|
-
self._client = OpenAI(api_key=api_key, base_url=_BASE_URL)
|
|
82
|
+
self._client = openai.OpenAI(api_key=api_key, base_url=_BASE_URL)
|
|
66
83
|
return self._client
|
|
67
84
|
|
|
68
85
|
def build_pattern(
|
|
69
86
|
self, command: str, context: CliContext, **kwargs: Any
|
|
70
87
|
) -> LLMCLIResponse:
|
|
88
|
+
openai = _import_openai()
|
|
71
89
|
prompt = build_prompt(command, context)
|
|
72
90
|
# Callers can override either of these per-call (e.g. re-enable
|
|
73
91
|
# thinking mode, raise max_tokens further); anything else in
|
|
@@ -84,7 +102,7 @@ class DeepSeekRegexBuilder:
|
|
|
84
102
|
extra_body=extra_body,
|
|
85
103
|
**kwargs,
|
|
86
104
|
)
|
|
87
|
-
except OpenAIError as exc:
|
|
105
|
+
except openai.OpenAIError as exc:
|
|
88
106
|
if not is_retryable(exc):
|
|
89
107
|
# Same request would fail the same way again — stop rather
|
|
90
108
|
# than let a caller burn another attempt on it.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: parseforge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
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>
|
|
@@ -26,9 +26,13 @@ License-File: LICENSE
|
|
|
26
26
|
Requires-Dist: click>=8.1
|
|
27
27
|
Requires-Dist: PyYAML>=6.0
|
|
28
28
|
Requires-Dist: textfsm>=1.1.0
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
|
|
31
|
-
Requires-Dist:
|
|
29
|
+
Requires-Dist: textfsm-ai>=0.6.1
|
|
30
|
+
Provides-Extra: anthropic
|
|
31
|
+
Requires-Dist: anthropic>=0.40.0; extra == "anthropic"
|
|
32
|
+
Requires-Dist: textfsm-ai[anthropic]>=0.6.1; extra == "anthropic"
|
|
33
|
+
Provides-Extra: deepseek
|
|
34
|
+
Requires-Dist: openai>=1.0.0; extra == "deepseek"
|
|
35
|
+
Requires-Dist: textfsm-ai[deepseek]>=0.6.1; extra == "deepseek"
|
|
32
36
|
Provides-Extra: sampling
|
|
33
37
|
Requires-Dist: netmiko>=4.0; extra == "sampling"
|
|
34
38
|
Provides-Extra: dev
|
|
@@ -37,6 +41,11 @@ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
|
|
|
37
41
|
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
38
42
|
Requires-Dist: black>=24.0.0; extra == "dev"
|
|
39
43
|
Requires-Dist: mypy>=1.10.0; extra == "dev"
|
|
44
|
+
Requires-Dist: anthropic>=0.40.0; extra == "dev"
|
|
45
|
+
Requires-Dist: openai>=1.0.0; extra == "dev"
|
|
46
|
+
Provides-Extra: release
|
|
47
|
+
Requires-Dist: bump2version; extra == "release"
|
|
48
|
+
Requires-Dist: build; extra == "release"
|
|
40
49
|
Dynamic: license-file
|
|
41
50
|
|
|
42
51
|
# ParseForge
|
|
@@ -65,12 +74,35 @@ wired into the CLI below. A few things are intentionally not there yet:
|
|
|
65
74
|
- **One sampling connector** (Netmiko/SSH). The CLI's `--connector` registry
|
|
66
75
|
is built to hold more without a redesign, but nothing else is wired in yet.
|
|
67
76
|
|
|
77
|
+
## Installation
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
pip install parseforge[anthropic]
|
|
81
|
+
```
|
|
82
|
+
`pip install parseforge` alone installs no AI-provider SDK at all — every
|
|
83
|
+
command that's pure local processing (`canonical`/`readable`/`recognizers`,
|
|
84
|
+
`integration`, `promotion`) works with nothing further. Anything that calls an
|
|
85
|
+
LLM (`name`, `check --provider`, `run`, `generate-template`, `trial`) needs the
|
|
86
|
+
extra for whichever provider it uses: `anthropic` or `deepseek`. `--provider`
|
|
87
|
+
defaults to `anthropic` wherever it isn't required, so that's the one most
|
|
88
|
+
setups need. `pip install parseforge[sampling]` adds Netmiko for live device
|
|
89
|
+
sampling; combine extras as needed, e.g.
|
|
90
|
+
`pip install parseforge[anthropic,deepseek,sampling]`.
|
|
91
|
+
|
|
68
92
|
## Development
|
|
69
93
|
|
|
70
94
|
```
|
|
71
95
|
pip install -e ".[dev,sampling]"
|
|
72
96
|
pytest
|
|
73
97
|
```
|
|
98
|
+
`dev` already includes both `anthropic` and `openai` (tests exercise both
|
|
99
|
+
providers, never silently skip) — add `,anthropic`/`,deepseek` explicitly only
|
|
100
|
+
if installing outside of `dev`.
|
|
101
|
+
|
|
102
|
+
Linting/formatting/type-checking/docs run through tox instead of extras — see
|
|
103
|
+
`tox.ini` (`tox -e lint`/`format`/`typecheck`/`docs`), each installing its own
|
|
104
|
+
tools in an isolated env. Cutting a release needs `pip install -e ".[release]"`
|
|
105
|
+
(`bump2version`, `build`) — see `scripts/release.ps1`.
|
|
74
106
|
|
|
75
107
|
## CLI
|
|
76
108
|
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
click>=8.1
|
|
2
2
|
PyYAML>=6.0
|
|
3
3
|
textfsm>=1.1.0
|
|
4
|
+
textfsm-ai>=0.6.1
|
|
5
|
+
|
|
6
|
+
[anthropic]
|
|
4
7
|
anthropic>=0.40.0
|
|
8
|
+
textfsm-ai[anthropic]>=0.6.1
|
|
9
|
+
|
|
10
|
+
[deepseek]
|
|
5
11
|
openai>=1.0.0
|
|
6
|
-
textfsm-ai>=0.
|
|
12
|
+
textfsm-ai[deepseek]>=0.6.1
|
|
7
13
|
|
|
8
14
|
[dev]
|
|
9
15
|
pytest>=8.0.0
|
|
@@ -11,6 +17,12 @@ pytest-cov>=5.0.0
|
|
|
11
17
|
ruff>=0.4.0
|
|
12
18
|
black>=24.0.0
|
|
13
19
|
mypy>=1.10.0
|
|
20
|
+
anthropic>=0.40.0
|
|
21
|
+
openai>=1.0.0
|
|
22
|
+
|
|
23
|
+
[release]
|
|
24
|
+
bump2version
|
|
25
|
+
build
|
|
14
26
|
|
|
15
27
|
[sampling]
|
|
16
28
|
netmiko>=4.0
|
|
@@ -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.4"
|
|
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" }
|
|
@@ -22,9 +22,7 @@ dependencies = [
|
|
|
22
22
|
"click>=8.1",
|
|
23
23
|
"PyYAML>=6.0",
|
|
24
24
|
"textfsm>=1.1.0",
|
|
25
|
-
"
|
|
26
|
-
"openai>=1.0.0",
|
|
27
|
-
"textfsm-ai>=0.5.1",
|
|
25
|
+
"textfsm-ai>=0.6.1",
|
|
28
26
|
]
|
|
29
27
|
|
|
30
28
|
classifiers = [
|
|
@@ -59,6 +57,13 @@ keywords = [
|
|
|
59
57
|
]
|
|
60
58
|
|
|
61
59
|
[project.optional-dependencies]
|
|
60
|
+
# textfsm-ai v0.6.0+ made every provider SDK an optional extra (lazy
|
|
61
|
+
# import; a clear ImportError only when that provider is actually used).
|
|
62
|
+
# parseforge mirrors that per-provider: `pip install parseforge` alone
|
|
63
|
+
# now installs no AI-provider SDK at all -- add the extra(s) for the
|
|
64
|
+
# provider(s) you actually use.
|
|
65
|
+
anthropic = ["anthropic>=0.40.0", "textfsm-ai[anthropic]>=0.6.1"]
|
|
66
|
+
deepseek = ["openai>=1.0.0", "textfsm-ai[deepseek]>=0.6.1"]
|
|
62
67
|
sampling = ["netmiko>=4.0"]
|
|
63
68
|
dev = [
|
|
64
69
|
"pytest>=8.0.0",
|
|
@@ -66,6 +71,19 @@ dev = [
|
|
|
66
71
|
"ruff>=0.4.0",
|
|
67
72
|
"black>=24.0.0",
|
|
68
73
|
"mypy>=1.10.0",
|
|
74
|
+
# anthropic/openai are optional at runtime (see the extras above), but
|
|
75
|
+
# both providers are exercised by the CLI/tests, which should never
|
|
76
|
+
# silently skip -- so dev always pulls both in.
|
|
77
|
+
"anthropic>=0.40.0",
|
|
78
|
+
"openai>=1.0.0",
|
|
79
|
+
]
|
|
80
|
+
# Tooling for scripts/release.ps1 and CI's own package-build step.
|
|
81
|
+
# bump2version isn't otherwise installed anywhere -- release.ps1 assumes
|
|
82
|
+
# it's already on PATH. twine is deliberately not here: the actual PyPI
|
|
83
|
+
# publish goes through pypa/gh-action-pypi-publish, not `twine upload`.
|
|
84
|
+
release = [
|
|
85
|
+
"bump2version",
|
|
86
|
+
"build",
|
|
69
87
|
]
|
|
70
88
|
|
|
71
89
|
[project.scripts]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.3"
|
|
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
|
|
File without changes
|
|
File without changes
|