garak/generators/base.py defines Generator, the base class all garak generators must inherit from; it wraps an LLM or other text-to-text service.[1] By default, Generator declares modality: dict = {"in": {"text"}, "out": {"text"}}, indicating plain text-in / text-out; subclasses override this for multimodal support.[1] Generator inherits from Configurable — configuration layering behaviour is covered in Configurable base class.[1] A Conversation object represents a structured, ordered sequence of messages; each message carries a role and content. Generator uses Conversation as the required prompt type and as input to _call_model() and _conversation_to_list().
Generator.DEFAULT_PARAMS sets the default sampling parameters: max_tokens=150, temperature=None, top_k=None, context_len=None, skip_seq_start=None, and skip_seq_end=None.[1] Generator.supports_multiple_generations defaults to False; setting it to True on a subclass allows generate() to send a single _call_model call requesting multiple outputs instead of looping.[1]
During __init__(), Generator constructs fullname as "{generator_family_name}:{name}" when generator_family_name is set, and falls back to just name otherwise.[1] Each instantiation also prints a magenta-highlighted loading message containing generator_family_name and name, using colorama styling.[1]
Generator._call_model() is the abstract method subclasses must implement; it takes a Conversation and generations_this_call and must return a List[Union[Message, None]], or raise an exception — it must never silently fail.[1] Developers should override _call_model() (or _call_api()) rather than generate(), which is the orchestration layer and is not intended to be overridden.[1] Generator._verify_target_result() is a static method that asserts _call_model returns a list of exactly one item when called with generations_this_call=1, and that the item is a Message or None.[1]
Generator.generate() enforces that its prompt argument is a Conversation object (not a plain string), reflecting the v0.13.0 migration from string prompts.[1] At the start of every generate() call, the private _rng is re-seeded when seed is not None, ensuring reproducible generation across repeated calls.[1] Generator.generate() uses multiprocessing.Pool for parallel generation when parallel_requests > 1, capping pool size at min(generations_this_call, parallel_requests, max_workers).[1] When parallel generation hits the OS file-descriptor limit (errno 24), generate() raises a GarakException advising the caller to reduce parallel_requests or raise the OS limit (e.g. ulimit -n 4096).[1] Generator.generate() raises BadGeneratorException if the number of outputs returned does not match generations_this_call, noting that supports_multiple_generations may be set incorrectly as a common cause.[1]
Generator._prune_skip_sequences() strips substrings bounded by skip_seq_start and skip_seq_end from all output texts; when skip_seq_start is an empty string, it strips everything up to and including skip_seq_end instead.[1] generate() invokes _prune_skip_sequences() only when both skip_seq_start and skip_seq_end attributes exist and are not None.[1]
Generator._conversation_to_list() is a static helper that converts a Conversation object into a list of {"role": ..., "content": ...} dicts, needed by many generator subclasses for API serialization.[1]
Sources