# Python API for SkillSpector: Architecture, Components, and Usage Guide

> Explore the Python API for SkillSpector, NVIDIA's tool for skill analysis. Learn about its architecture, components, and usage for programmatic LangGraph workflows.

- Repository: [NVIDIA Corporation/SkillSpector](https://github.com/NVIDIA/SkillSpector)
- Tags: architecture
- Published: 2026-06-21

---

**The Python API for SkillSpector exposes a minimal, well-defined interface consisting of `create_graph()`, `graph`, `InputHandler`, and `SkillspectorState` for programmatic execution of LangGraph-based skill analysis workflows.**

SkillSpector is an open-source Python package developed by NVIDIA for automated skill analysis. The Python API for SkillSpector enables developers to embed comprehensive skill scanning capabilities directly into applications without using the command-line interface. Through a small set of public symbols exposed in the top-level `skillspector` package, users can programmatically invoke the complete analysis pipeline, from input resolution to SARIF report generation.

## Core Components of the Python API for SkillSpector

The public API surface is intentionally small to maximize ease of use while exposing the full power of the underlying **LangGraph** workflow.

### Graph Factory and Pre-compiled Instance

The primary entry points reside in [`src/skillspector/__init__.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/__init__.py) and [`src/skillspector/graph.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/graph.py):

- **`create_graph()`** – A factory function that returns a freshly compiled LangGraph workflow. According to the source code in [`src/skillspector/graph.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/graph.py), this function wires together all SkillSpector nodes including input resolution, context building, static analyzers, LLM-driven analyzers, meta-analysis, and reporting.

- **`graph`** – A ready-to-run singleton instance of the compiled workflow. This pre-compiled graph can be invoked directly with a state dictionary, making it ideal for reuse across multiple analysis operations.

### Input Normalization

The **`InputHandler`** class, defined in [`src/skillspector/input_handler.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/input_handler.py), serves as the primary utility for input sanitization. It normalizes any supported input format—including Git URLs, raw file URLs, zip archives, markdown files, or local directories—into a local directory path. When processing remote resources, it optionally creates a temporary directory for automatic cleanup.

### State Management

**`SkillspectorState`** is a **TypedDict** defined in [`src/skillspector/state.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/state.py) that describes the schema shared by all nodes in the workflow. This dictionary defines the keys that can be passed to `graph.invoke()` and specifies the values that nodes read and write during execution. It serves as the contract between the user and the analysis pipeline.

## Workflow Execution Architecture

Understanding the Python API for SkillSpector requires familiarity with how the LangGraph workflow processes data through its nodes.

### Graph Topology

The `create_graph()` function builds a `StateGraph` with the following topology, as defined in [`src/skillspector/graph.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/graph.py) (lines 45-51):

```

START → resolve_input → build_context → (each analyzer) → meta_analyzer → report → END

```

Each analyzer node registers its own ID in `skillspector.nodes.analyzers.ANALYZER_NODE_IDS`. The edges connect these nodes to form a complete analysis pipeline.

### State Flow Through Nodes

The workflow passes a single `SkillspectorState` dictionary between nodes:

1. **`resolve_input`** ([`src/skillspector/nodes/resolve_input.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/resolve_input.py)) uses `InputHandler` to transform the user-supplied `input_path` into a local `skill_path`, adding an optional `temp_dir_for_cleanup` when temporary directories are created.

2. **`build_context`** ([`src/skillspector/nodes/build_context.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/build_context.py)) scans the directory, builds component lists, caches file contents and ASTs, and stores a comprehensive manifest in the state.

3. **Analyzer nodes** (`src/skillspector/nodes/analyzers/`) read the manifest and augment `state["findings"]`. These nodes respect `state["use_llm"]` and retrieve LLM models via the provider protocols.

4. **`meta_analyzer`** ([`src/skillspector/nodes/meta_analyzer.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/meta_analyzer.py)) aggregates and filters findings, computes a risk score, and writes a SARIF or textual report structure.

5. **`report`** ([`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py)) formats the final output according to `state["output_format"]`.

## Practical Usage Examples

The Python API for SkillSpector supports diverse use cases, from one-off remote repository scans to batch processing of local skills.

### Example 1: Scanning a Remote Repository with SARIF Output

This example demonstrates scanning a Git repository and extracting SARIF-formatted results:

```python
from skillspector import graph
from skillspector.state import SkillspectorState

state: SkillspectorState = {
    "input_path": "https://github.com/example/my-skill",
    "output_format": "sarif",
    "use_llm": True,
}
result = graph.invoke(state)

sarif = result["sarif_report"]
print(sarif["runs"][0]["results"][0]["message"]["text"])

```

### Example 2: Local Directory Analysis with Markdown Report

For local skill analysis without LLM processing:

```python
import pathlib
from skillspector import create_graph

state = {
    "input_path": str(pathlib.Path("./my-local-skill").resolve()),
    "output_format": "markdown",
    "use_llm": False,
}
graph = create_graph()
result = graph.invoke(state)

# Write out the markdown report

pathlib.Path("skill_report.md").write_text(result["report_body"])

```

### Example 3: Reusing the Graph for Multiple Scans

For performance-critical applications, reuse the pre-compiled graph instance across multiple scans:

```python
from skillspector import graph

def scan(path: str, fmt: str = "terminal") -> dict:
    init = {
        "input_path": path,
        "output_format": fmt,
        "use_llm": True,
    }
    return graph.invoke(init)

# Scan two different skills

res1 = scan("https://github.com/foo/bar")
res2 = scan("./local-skill")
print(res1["risk_score"], res2["risk_score"])

```

### Cleanup Requirements

When `InputHandler` creates temporary directories during remote resource processing, the Python API for SkillSpector returns the path in `result["temp_dir_for_cleanup"]`. The caller must delete these directories manually, as shown in [`src/skillspector/cli.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/cli.py):

```python
import shutil
if result.get("temp_dir_for_cleanup"):
    shutil.rmtree(result["temp_dir_for_cleanup"])

```

## LLM Provider Integration

The Python API for SkillSpector implements pluggable LLM-provider abstractions through protocols defined in [`src/skillspector/providers/base.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/providers/base.py). Provider implementations—including OpenAI ([`providers/openai/provider.py`](https://github.com/NVIDIA/SkillSpector/blob/main/providers/openai/provider.py)), Anthropic ([`providers/anthropic/provider.py`](https://github.com/NVIDIA/SkillSpector/blob/main/providers/anthropic/provider.py)), and NVIDIA ([`providers/nv_build/provider.py`](https://github.com/NVIDIA/SkillSpector/blob/main/providers/nv_build/provider.py))—conform to the `LLMProvider` protocol.

These providers expose three capabilities:

- **Model metadata** – Information about available models
- **Credential resolution** – Authentication handling
- **Chat-model construction** – Creating callable LLM instances

The workflow selects a provider based on the `SKILLSPECTOR_PROVIDER` environment variable, falling back to a default NVIDIA inference service when unspecified.

## Summary

The Python API for SkillSpector provides a concise, powerful interface for programmatic skill analysis:

- **Primary symbols** – `create_graph()` for fresh workflow instances, `graph` for singleton reuse, `InputHandler` for input normalization, and `SkillspectorState` for type-safe state management
- **Execution model** – LangGraph-based workflow passing a shared state dictionary through specialized nodes
- **Input flexibility** – Support for Git URLs, zip archives, local directories, and raw files via `InputHandler`
- **Output formats** – SARIF, JSON, Markdown, and terminal formatting controlled via state configuration
- **Resource management** – Automatic temporary directory creation with explicit cleanup requirements
- **LLM integration** – Pluggable provider architecture supporting OpenAI, Anthropic, and NVIDIA backends

## Frequently Asked Questions

### What is the difference between `create_graph()` and `graph`?

`create_graph()` is a factory function that returns a newly compiled LangGraph workflow each time it is called, which is useful when you need a fresh state or customized compilation. `graph` is a pre-compiled singleton instance exported from the top-level package, optimized for reuse across multiple invocations to minimize compilation overhead.

### How do I configure which LLM provider the Python API uses?

The workflow selects the LLM provider based on the `SKILLSPECTOR_PROVIDER` environment variable, as implemented in the provider resolution logic within [`src/skillspector/providers/base.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/providers/base.py). Valid values include identifiers for OpenAI, Anthropic, and NVIDIA providers. If unset, the system defaults to the NVIDIA inference service.

### What should I do with `temp_dir_for_cleanup` in the results?

When analyzing remote resources such as Git URLs or zip archives, `InputHandler` creates temporary directories to store the downloaded content. The `graph.invoke()` method returns the path to these directories in the `temp_dir_for_cleanup` key of the result dictionary. You must manually delete these directories using `shutil.rmtree()` when finished processing the results, as the API does not automatically handle filesystem cleanup.

### Can I run SkillSpector analysis without using LLMs?

Yes. Set `"use_llm": False` in your `SkillspectorState` dictionary before invoking the graph. When this flag is disabled, the analyzer nodes skip LLM-based semantic analysis and rely solely on static analysis techniques, reducing execution time and eliminating API costs while still producing valid findings and risk scores.