Skip to content

Python API

Everything the CLI does is also a plain Python call — parseforge/api.py is the single supported place to import from (also re-exported at the package root). See the API Reference for the full list of what's exported.

The full pipeline, one call

run_command_pipeline() runs SPEC.md §5 steps 1-7 (naming → sampling → generation → self-validation) for a single command, and writes the result under a trial directory the same way parseforge run does:

from parseforge import CliContext, LLMProviderConfig, run_command_pipeline
from parseforge.naming import AnthropicRegexBuilder
from parseforge.sampling import DeviceConnection
from parseforge.sampling.backends import NetmikoSampler

result = run_command_pipeline(
    "show clock",
    CliContext(vendor="cisco", family="catalyst9200", os="ios-xe", version="17.9.1"),
    DeviceConnection(host="10.0.0.1", username="admin", password="secret", device_type="cisco_ios"),
    AnthropicRegexBuilder(api_key="sk-..."),
    NetmikoSampler(),
    LLMProviderConfig(provider="anthropic", api_key="sk-...", model="claude-haiku-4-5-20251001"),
)
print(result.cli_name, result.passed, result.total_usage)

NetmikoSampler needs the sampling extra (pip install parseforge[sampling]). AnthropicRegexBuilder needs the matching provider extra — see Providers for every provider's builder class and non-standard-auth parameters.

Individual stages

Each pipeline stage is also callable on its own — useful for scripting a custom workflow, or testing one stage in isolation.

Naming only

from parseforge import CliContext, cli_name
from parseforge.naming import AnthropicRegexBuilder

context = CliContext(vendor="cisco", family="catalyst9200", os="ios-xe", version="17.9.1")
name = cli_name(
    "show interface GE1.1 status", context, builder=AnthropicRegexBuilder(api_key="sk-...")
)
# "show-interface-var1-status" -- cached locally, so a repeat call for the
# same command never touches the LLM again.

Sampling only

from parseforge.sampling import DeviceConnection, sample
from parseforge.sampling.backends import NetmikoSampler

connection = DeviceConnection(
    host="10.0.0.1", username="admin", password="secret", device_type="cisco_ios"
)
output = sample(NetmikoSampler(), connection, "show clock")

Generation only

from parseforge import generate

result = generate(
    output,  # a raw sample string, e.g. from sample() above
    "anthropic",
    "sk-...",
    "claude-haiku-4-5-20251001",
)
print(result.template, result.readable_dsl, result.recognizers, result.usage)

Self-validation only

from parseforge import parse

validated = parse(result.template, output)
print(validated.passed, validated.records, validated.errors)

Integration and promotion

Run after one or more trial/run_command_pipeline() calls have written evidence under a store root:

from pathlib import Path

from parseforge import (
    PromotionGate,
    PromotionMetadata,
    build_integration,
    discover_device_keys,
    promote_auto,
    write_reference_summary,
)

store_root = Path.home() / ".parseforge" / "tests"

# Cluster every trial for each cli-name by output schema.
for key in discover_device_keys(store_root):
    build_integration(store_root, key)
write_reference_summary(store_root)

# Auto-promote every group that clears its match-rate/sample-count gate.
result = promote_auto(
    store_root,
    PromotionMetadata(user="alice"),
    PromotionGate(match_rate_threshold=1.0, min_sample_count=1),
)
print(result.promoted, result.unqualified)

Drift monitoring

Check an already-promoted, authoritative template against a fresh production sample:

from parseforge import DeviceKey, check_drift

key = DeviceKey(vendor="cisco", family="catalyst9200", os="ios-xe", cli_name="show-clock")
result = check_drift(store_root, key, new_sample_text)
print(result.match_rate, result.status)

A failing sample gets fed back into the pipeline as a new trial by design — re-run trial/run_command_pipeline() for that command to add it as evidence, then promotion again once enough new evidence accumulates.