Async design & pytest
Suite.run(), Scenario.run(), and all Check.run() methods are async def. Use a suite to run scenarios. Async lets the library await LLM calls and other I/O.
What actually runs concurrently
Section titled “What actually runs concurrently”- Checks inside a scenario run one after another. The test-case runner awaits each check in a plain
forloop. Ten LLM checks on one scenario cost ten sequential LLM calls. - Interactions inside a scenario run in order. A multi-turn scenario has to, because each turn depends on the previous one.
- Suites run scenarios serially by default.
Suite.run()defaults toparallel=False.
To overlap scenarios, opt in:
from giskard.checks import Suite
suite = Suite(name="examples")
# Run every scenario in the suite at onceresult = await suite.run(parallel=True)
# Cap how many run at the same timeresult = await suite.run(parallel=True, max_concurrency=4)parallel=True schedules every scenario as a task and preserves result order. max_concurrency is the cap on how many run at once; None (the default) is unbounded, which means your LLM provider’s rate limit becomes the real cap. Start with a small explicit number if you are hitting 429s.
Checks and turns inside one scenario stay sequential.
The tradeoff of async is that you need an event loop to call Scenario.run(). Giskard Checks works in scripts, pytest, and notebooks, each of which provides the loop differently. See Run Tests with pytest for the setup steps.
Common pitfalls
Section titled “Common pitfalls”# Wrong — run() returns a coroutine, not a resultresult = test_scenario.run()
# Wrong — can't nest asyncio.run() inside an async functionasync def my_func(): result = asyncio.run(test_scenario.run())
# Correct in a scriptresult = asyncio.run(test_scenario.run())
# Correct in pytest / notebook / async functionresult = await test_scenario.run()