Expected Signature for a Custom SkillSpector Analyzer: Implementation Guide
A custom SkillSpector analyzer must inherit from LLMAnalyzerBase and implement the constructor signature def __init__(self, base_prompt: str, model: str) -> None, calling super().__init__(base_prompt, model) to initialize the LLM backend and token budget.
The NVIDIA SkillSpector framework provides a pluggable architecture for LLM-powered code analysis. All analyzers in the system extend a common base class that standardizes how prompts are built and how model responses are parsed. Understanding the expected signature and extension points ensures your custom analyzer integrates seamlessly with SkillSpector's graph-execution engine.
Base Class Constructor Signature
Every custom analyzer subclasses LLMAnalyzerBase defined in src/skillspector/llm_analyzer_base.py. The constructor at lines 39-42 expects exactly two parameters:
def __init__(self, base_prompt: str, model: str) -> None
base_prompt is the static system prompt that describes analysis criteria to the LLM.
model is the string identifier for the LLM (e.g., "gpt-4o", "claude-3-opus-20240229") that skillspector.llm_utils.get_chat_model resolves.
When invoked, the parent constructor:
- Stores both parameters as instance attributes
- Calculates the model-specific input-token budget
- Initializes the underlying LangChain chat model
Your custom analyzer must invoke super().__init__(base_prompt, model) to ensure these initialization steps execute.
Optional Extension Points
Beyond the required constructor, the base class exposes three primary hooks for customization:
Customizing Response Schemas
The class attribute response_schema defines the Pydantic model used to structure the LLM's output. By default, this is set to LLMAnalysisResult (lines 22-24). Override this attribute to enforce a different JSON structure:
from pydantic import BaseModel, Field
class SecurityFinding(BaseModel):
severity: str
line_number: int
description: str
class SecurityAnalyzer(LLMAnalyzerBase):
response_schema = SecurityFinding
Overriding Prompt Construction
The build_prompt(self, batch, **kwargs) method (documented in the class docstring at lines 6-13) assembles the final prompt sent to the LLM. Override this to customize how file chunks or metadata are injected into the base_prompt:
def build_prompt(self, batch, **kwargs):
# Custom logic to format batch data
custom_context = f"Analyzing {len(batch)} files..."
return f"{self.base_prompt}\n{custom_context}\n{batch.content}"
Parsing LLM Responses
The parse_response(self, response, batch) method (lines 30-33) converts the structured LLM output into Finding objects (defined in src/skillspector/models.py). Override this when using a custom response_schema or when you need non-standard extraction logic:
def parse_response(self, response, batch):
# response is an instance of self.response_schema
findings = []
for item in response.issues:
findings.append(Finding(
message=item.description,
line=item.line_number
))
return findings
Complete Implementation Examples
Minimal Custom Analyzer
The following skeleton demonstrates the minimal required implementation using the default schema:
from skillspector.llm_analyzer_base import LLMAnalyzerBase
class SimpleStaticAnalyzer(LLMAnalyzerBase):
def __init__(self, model: str):
prompt = (
"Detect any use of unsafe system calls (e.g., exec, system, popen) "
"and report them with line numbers."
)
super().__init__(base_prompt=prompt, model=model)
Advanced Analyzer with Custom Schema
This example implements a custom response schema and overrides the parser:
from pydantic import BaseModel, Field
from skillspector.llm_analyzer_base import LLMAnalyzerBase, LLMAnalysisResult
from skillspector.models import Finding
class MyResult(BaseModel):
issues: list[dict] = Field(default_factory=list)
class MyAdvancedAnalyzer(LLMAnalyzerBase):
response_schema = MyResult
def __init__(self, model: str):
super().__init__(base_prompt="Find all TODO comments.", model=model)
def parse_response(self, response, batch):
findings = []
for item in response.issues:
findings.append(
Finding(
message=item.get("text", "TODO found"),
file_path=batch.file_path,
line=item.get("line", 0)
)
)
return findings
Integration with the Execution Engine
Once implemented, your custom analyzer plugs directly into SkillSpector's batch processing system. The base class provides get_batches() to prepare file chunks and both synchronous and asynchronous execution methods:
# Synchronous execution
analyzer = SimpleStaticAnalyzer(model="gpt-4o")
batches = analyzer.get_batches(
file_paths=["src/my_module.py"],
file_cache={"src/my_module.py": open("src/my_module.py").read()},
)
results = analyzer.run_batches(batches)
# Asynchronous execution
import asyncio
async def run():
analyzer = SimpleStaticAnalyzer(model="gpt-4o")
batches = analyzer.get_batches(
file_paths=["src/my_module.py"],
file_cache={"src/my_module.py": open("src/my_module.py").read()},
)
results = await analyzer.arun_batches(batches)
return results
asyncio.run(run())
Summary
- Custom SkillSpector analyzers must inherit from
LLMAnalyzerBaseinsrc/skillspector/llm_analyzer_base.pyand implement the signature__init__(self, base_prompt: str, model: str). - Initialization requires calling
super().__init__(base_prompt, model)to configure the LangChain backend and token budgets. - Customization happens through three optional overrides:
response_schema(class attribute),build_prompt(), andparse_response(). - Execution uses inherited methods
run_batches()orarun_batches()after preparing data withget_batches().
Frequently Asked Questions
What happens if I don't call the parent __init__ in my custom analyzer?
The analyzer will fail at runtime because it won't have the _chat_model attribute or the _token_budget calculated. According to the source code in src/skillspector/llm_analyzer_base.py lines 39-42, the parent constructor initializes the LangChain model via get_chat_model() and determines the maximum tokens available for the prompt. Without these, build_prompt() and run_batches() will raise AttributeError exceptions.
Can I change the constructor signature to accept additional parameters?
Yes, but you must preserve the base_prompt and model parameters and pass them to super().__init__(). You can add custom parameters like temperature or api_key before the super() call, provided you handle the storage of those values yourself. The execution engine only checks that the instance is an LLMAnalyzerBase subclass; it does not inspect your specific constructor signature.
Where is the Finding class defined that parse_response should return?
The Finding dataclass is defined in src/skillspector/models.py. This is the standard return type expected by SkillSpector's reporting nodes. When overriding parse_response, ensure your method returns a list of Finding objects (or instances of a compatible dataclass) so that downstream nodes in the analysis graph can process the results correctly.
Does the model parameter accept any string, or only specific values?
The model string must be resolvable by skillspector.llm_utils.get_chat_model. This utility function maps common identifiers like "gpt-4o" or "claude-3-opus" to their respective LangChain chat model classes. If you provide an unsupported identifier, the parent constructor will raise a ValueError during initialization when attempting to instantiate the chat model.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →