Skip to content
GitHubDiscord

Migrate from Giskard v2 to v3

Giskard v3 is a rewrite, not an upgrade. No v2 script runs unchanged: the model and dataset wrappers are gone, the entry points are coroutines, and the two things most v2 users ran, the scan and RAGET, are now two separate scans.

This page maps the v2 API onto the v3 one, section by section, with both versions side by side. Every tab on the page switches together, so you can read the whole thing in one version and then flip.

v2 was one package with one entry point per job. v3 splits into two layers, and knowing which one you need saves most of the searching:

LayerWhat it isThe v2 thing it replaces
ChecksTests you write: scenarios, assertions, LLM judges, suites you run in CIgiskard.testing, the @test decorator, giskard.Suite
ScanTests Giskard writes for you, from a description and optional documentsgiskard.scan, giskard.rag (RAGET)

Checks ships in giskard; the scan is the scan extra:

Terminal window
pip install --pre "giskard[scan,openai]"

The Hub moved too: its workflow is not a drop-in v2 migration. Use the separate giskard_hub SDK and its migration guide to plan that move.

Giskard v2Giskard v3
giskard.Model(...), giskard.Dataset(...)no wrappers: an async def function with Pydantic input and output
giskard.scan(model, dataset)await vulnerability_scan(target, description=..., languages=[...])
giskard.rag.generate_testset + giskard.rag.evaluateawait quality_scan(target, ..., knowledge_base=...)
scan_results.generate_test_suite(...)result.suite, the Suite generated by vulnerability_scan or quality_scan
test_suite.run() (synchronous)await suite.run(target=...)
giskard.llm.set_llm_model("gpt-4o")GISKARD_CHECKS_DEFAULT_MODEL or set_default_generator(Generator(...))
giskard.llm.set_embedding_model(...)GISKARD_CHECKS_DEFAULT_EMBEDDING_MODEL
giskard.testing.test_f1(...), the @test decoratorbuilt-in checks, FnCheck, or a Check subclass

v2 asked you to wrap a model and a dataset. v3 asks for a function.

import giskard
import pandas as pd
def model_predict(df: pd.DataFrame):
return [llm_api(question) for question in df["question"].values]
giskard_model = giskard.Model(
model=model_predict,
model_type="text_generation",
name="Climate Change Question Answering",
description="This model answers any question about climate change based on IPCC reports",
feature_names=["question"],
)

The name and description were load-bearing: they drove the domain-specific probes the scan generated.

Agents that keep conversation state, or that expect the full message list on every call, need a different wrapper shape. See Wrap Your Agent for the Scan.

Security scan: giskard.scan becomes vulnerability_scan

Section titled “Security scan: giskard.scan becomes vulnerability_scan”

The v2 scan ran every detector it had against a model. In v3 that job is split: vulnerability_scan attacks the agent, and quality_scan (next section) checks whether it answers well.

import giskard
scan_results = giskard.scan(giskard_model)
display(scan_results)
scan_results.to_html("model_scan_results.html")
test_suite = scan_results.generate_test_suite("My first test suite")
test_suite.run()

Three differences bite in practice:

  • description and languages are required. The scan writes its scenarios from them, so a vague description produces a vague scan.
  • The entry point is asynchronous: call it with await, or wrap it in asyncio.run() in a script.
  • A scan returns a SuiteResult; its suite attribute is the generated Suite. Use result.suite to save, version, or rerun the scenarios. To build a custom suite before running it, call generate_suite(...). See Save and Version a Scan Suite.

v2 grouped findings into fixed categories. v3 annotates each result with tags and lets you group the report on any of them. vulnerability_scan groups by threat-type by default.

v2 categoryv3 tag
Harmful Content Generationthreat-type:harmful-content-generation
Prompt Injectionthreat-type:prompt-injection
Hallucination and Misinformationquality:direct-hallucination, quality:fabricated-hallucination (moved to quality_scan)
Stereotypes and Discriminationthreat-type:harmful-content-generation
Robustnessno direct equivalent: covered by the multi-turn attack generators rather than input perturbation
Output Formattingno equivalent in the scan: assert it yourself with JsonValid or RegexMatching
Information Disclosurethreat-type:prompt-injection (the tag on disclosure-attempt scenarios)
Unauthorized Advicethreat-type:misguidance-and-unauthorized-advice (a tag-level analogue, not an exact category replacement)

Results also carry owasp:, probe-type:, and component: tags. The full list is in the threat taxonomy.

RAGET was two steps, generate a test set from a knowledge base, then evaluate an answer function against it. v3 folds both into one call.

import pandas as pd
from giskard.rag import KnowledgeBase, evaluate, generate_testset
df = pd.read_csv("knowledge_base.csv")
knowledge_base = KnowledgeBase.from_pandas(df, columns=["text"])
testset = generate_testset(
knowledge_base,
num_questions=60,
language="en",
agent_description="A customer support chatbot for company X",
)
testset.save("my_testset.jsonl")
def get_answer_fn(question: str, history=None) -> str:
messages = history if history else []
messages.append({"role": "user", "content": question})
return get_answer_from_agent(messages)
report = evaluate(get_answer_fn, testset=testset, knowledge_base=knowledge_base)
report.to_html("rag_eval_report.html")
report.correctness_by_topic()

KnowledgeBase moved from giskard.rag to giskard.scan, and takes texts rather than a dataframe. from_texts is the common path; Document is there when you need to attach metadata. See the knowledge base reference.

v2 question generators and v3 scenario generators are not row-for-row migrations: v3 generators create scenarios for distinct behaviors, and some use multi-turn conversations. quality_scan runs the quality registry unless you build a suite yourself with generate_suite(generators=[...]).

For hand-written Checks scenarios, UserSimulator and custom interaction generators create the user messages within a scenario. They are lower-level building blocks, not replacements for RAGET question generators. The quality ScenarioGenerators below are the closest automated successor because they build complete scenarios—interactions and checks—from the knowledge base.

v3 scenario generatorPurpose
HallucinationScenarioGeneratorDirect document-grounded questions and contradictions.
SplitQuestionsScenarioGeneratorTwo-message questions that depend on conversation history.
MultiTopicScenarioGeneratorMulti-turn questions over separate knowledge-base topics, exercising retrieval and history.
SycophancyScenarioGeneratorQuestions biased by an assertion that is contrary to the source material.
OutOfScopeScenarioGeneratorPlausible, precise objects absent from the knowledge base and fabricated-answer behavior.

These checks are not compatible replacements for RAGET or RAGAS metrics. RAGET evaluates a test set with aggregate metric semantics; v3 checks judge individual scenarios with different inputs and scoring, then aggregates scan results. In particular, v3 has no retriever-coverage metrics comparable to context precision or recall.

v2 concernv3 checks to compose for a related concern
correctnessContradiction, or Conformity for a rule you write
ragas_faithfulnessGroundedness
ragas_answer_relevancyAnswerRelevance
ragas_context_precision, ragas_context_recallNo equivalent: v3 does not provide retriever coverage metrics

There is no report.correctness_by_topic(). The equivalent is grouping the printed report on a tag, and reading result.pass_rate, result.failures_and_errors, and result.recommendation.

If you want the hand-written version of a RAG test suite rather than a generated scan, see the RAG evaluation use case.

v2 configured a global model by name through LiteLLM. v3 configures a generator object, and LiteLLM became opt-in.

import os
import giskard
os.environ["OPENAI_API_KEY"] = "sk-..."
giskard.llm.set_llm_model("gpt-4o")
giskard.llm.set_embedding_model("text-embedding-3-small")

Other providers were reached with a LiteLLM prefix on the model string:

import giskard
giskard.llm.set_llm_model("azure/my-deployment")
giskard.llm.set_llm_model("mistral/mistral-large-latest")
giskard.llm.set_llm_model(
"ollama/qwen2.5",
disable_structured_output=True,
api_base="http://localhost:11434",
)

Three things to watch:

  • LiteLLM is no longer the default. The provider-prefixed model strings you know from v2 still work, but only through LiteLLMGenerator.
  • Both defaults can come from the environment. GISKARD_CHECKS_DEFAULT_MODEL sets the judge model and GISKARD_CHECKS_DEFAULT_EMBEDDING_MODEL sets the embedding model. set_default_generator overrides the judge-model setting at runtime.

API keys are still read from provider-specific environment variables, such as OPENAI_API_KEY or ANTHROPIC_API_KEY. Full detail in Install & Configure and the settings reference.

v2 gave you a catalog of ready-made tests and a @test decorator for your own. v3 replaces both with checks: a check reads a conversation and returns pass or fail, and a scenario is one conversation plus the checks that judge it.

from giskard import Dataset, Suite, TestResult, test, testing
suite = Suite()
suite.add_test(testing.test_f1(dataset=wrapped_dataset))
suite.run(model=wrapped_model)
@test(name="Custom Test", tags=["quality"])
def my_test(dataset: Dataset, threshold: float = 0.5):
metric = calculate_value(dataset)
return TestResult(passed=metric < threshold, metric=metric)

Conformity and the other LLM judges replace the tests you would have written by hand; FnCheck replaces the @test decorator for anything deterministic. The full catalog is in the checks reference.

There is no drop-in replacement for the performance, drift, metamorphic, and statistical test catalogs. For a wrapped ML target, compose the assertions you need with Checks and FnCheck.

Each of these silently breaks a v2 script, so check the list before you start rewriting:

  • Slicing and transformation functions, and SuiteInput. Not ported.
  • Test set files. QATestset.save / QATestset.load become Suite.model_dump_json() and Suite.model_validate_json(). Import giskard.scan before loading a scan-generated suite, so its prompt namespace is registered: see Save and Version a Scan Suite.
  • Integrations with MLflow, Weights & Biases, DagsHub, NeMo Guardrails, and AVID. For CI, use the JUnit export described in Run the Scan in CI.