ExpCard & ExpCardInit
ExpCard and ExpCardInit are the central configuration objects in PsychScanner.
You populate an ExpCardInit with all experiment parameters, then pass it to ExpCard
which validates, resolves, and finalizes the configuration before handing it to ScannerModel.
ExpCardInit
ExpCardInit is a Pydantic BaseModel holding every experiment parameter.
Set fields directly on an instance, or pass them as keyword arguments.
from psychscanner import ExpCardInit
card = ExpCardInit()
card.model = "gpt-4o-mini"
card.family = "openai"
card.nsim = 20
# — or equivalently —
card = ExpCardInit(model="gpt-4o-mini", family="openai", nsim=20)
Model & Provider
| Field |
Type |
Default |
Description |
model |
str |
"mock-chat-model" |
LLM model name (e.g. "gpt-4o", "llama3.1:8b", "claude-3-5-sonnet-20241022") |
family |
str |
"mock-llm" |
Provider family — see Supported Providers |
parameters |
dict \| None |
None |
Extra kwargs forwarded to the model constructor (e.g. {"temperature": 0, "base_url": "..."}) |
Task & Persona
| Field |
Type |
Default |
Description |
task_file |
dict \| Path \| None |
VVIQ-16 default |
Task JSON as an inline dict or path to a .json file |
persona_files |
list[Path] \| None |
None |
Path(s) to persona JSON files; auto-loads default personas when None |
cogtype |
"assistant" / "custom" / "no" |
"custom" |
Persona mode: "custom" = use persona files, "assistant" = single system prompt, "no" = skip personas (use nsim) |
nsim |
int \| None |
None |
Number of simulated agents — only used when cogtype="no" |
task_context |
True \| False \| None |
None |
Format each item as context: … situation: … (survey tasks only) |
Memory
| Field |
Type |
Default |
Description |
memory |
"SingleTurn" / "Convo" |
"SingleTurn" |
"SingleTurn" = each trial is independent; "Convo" = trials chained in one conversation |
memory_k |
int |
-1 |
Max messages kept in context: -1 = unlimited, N = most recent N messages |
summary_k |
int |
0 |
Summarization threshold (Convo only): 0 = disabled; once the memory-k overflow reaches N messages, the entire overflow is summarized |
chain_type |
"item" / "trial" / "task" / None |
None |
Overrides the task JSON chain_type; controls how stimuli are grouped within a trial |
Parsing
| Field |
Type |
Default |
Description |
parser |
str \| type[BaseModel] \| Callable \| None |
None |
Structured-output parser — see Parser modes |
parser_raw |
bool |
False |
When True, the raw AIMessage is included in output as "_raw" |
parser_config |
dict \| None |
None |
Forwarded to model.with_structured_output(**parser_config); default {"method": "json_schema"} |
trial_parsers |
list[Any] \| None |
None |
Used when chain_type="trial" and per-trial parsers are present in the task JSON |
Parser modes
| Value |
Behaviour |
None or "0" |
No structured output — raw AIMessage in pred_resp |
"1" |
Read parser name from the task JSON "parser" field, resolve via registry |
Any other str |
Direct registry lookup: get_parser("MyParser") |
BaseModel subclass |
Used directly |
callable (not a class) |
Called per trial as fn(trcode) → type[BaseModel] \| None |
Feedback
| Field |
Type |
Default |
Description |
feedback |
bool |
False |
Enable trial-level feedback injection |
feedback_fn |
type[FeedbackBase] \| None |
None |
Class (not instance) of a FeedbackBase subclass — required when feedback=True |
Output & Checkpointing
| Field |
Type |
Default |
Description |
proj_dir |
Path |
~/psychscanner |
Root directory; data saved to proj_dir/projectname/taskname/family_model_memory/ |
projectname |
str |
"DEFAULTPROJ" |
Sub-directory name identifying this project |
tags |
list[str] |
[] |
Arbitrary metadata tags |
tunnel_status |
"0" / "1" |
"0" |
"1" enables session checkpointing for pause/resume |
tunnel_k |
int |
-1 |
Accepted and stored, but currently not read anywhere in the run loop — checkpointing always happens after every system prompt |
enabletqdm |
bool |
False |
Show a tqdm progress bar during the run |
login_env |
type[Settings] \| None |
None |
A pydantic_settings.Settings class (not a path string) for authenticating proprietary models. Currently stored on the card but never instantiated/read anywhere — it has no effect on which .env file is loaded. Call dotenv.load_dotenv() yourself before constructing the card if you need .env-based API keys. |
ExpCard
ExpCard validates an ExpCardInit, resolves all paths and parsers, creates the output
directory, and instantiates the SessionTunnel. Pass it to ScannerModel.
from psychscanner import ExpCard, ExpCardInit, ScannerModel
card_in = ExpCardInit(model="gpt-4o-mini", family="openai", nsim=5)
exp = ExpCard(card_in)
scanner = ScannerModel(expcard=exp)
Constructor
ExpCard(cls: ExpCardInit | None = None, **kwargs)
**kwargs override any field in the supplied ExpCardInit (or build one from scratch if cls is omitted).
Raises ValueError if:
- feedback=True but feedback_fn is not set.
- The resolved parser name is not in the registry (when parser="1" or a string name).
Key attributes after construction
| Attribute |
Description |
card_in |
The validated ExpCardInit instance |
parser |
Resolved parser class or callable (or None) |
data_root_dir |
Full output path: proj_dir/projectname/taskname/family_model_memory/ |
Saving and loading experiment cards
save_expcard
from psychscanner import save_expcard
portable = save_expcard(exp.card_in, path="my_card.json")
Serializes the card to a machine-independent JSON dict:
- Task JSON and persona JSON are embedded inline (no filesystem paths).
- Registered parsers become their string name; custom classes become
module.qualname.
- Machine-specific fields (
proj_dir, login_env) and runtime state are omitted.
load_expcard
from psychscanner import load_expcard
card_in = load_expcard(
"my_card.json",
proj_dir="./results", # override output root
parser=MyCustomParser, # required if original was a lambda or non-importable class
feedback_fn=MyFeedback, # required if original feedback_fn is not importable
)
exp = ExpCard(card_in)
Reconstructs an ExpCardInit from a portable file or dict. proj_dir defaults to ~/psychscanner if omitted.
Complete example
from pathlib import Path
from psychscanner import ExpCardInit, ExpCard, ScannerModel, to_csv
from psychscanner.parsers import DefaultLiteralVivid15
card = ExpCardInit(
model = "gemma3:4b",
family = "ollama",
parameters = {"temperature": 0},
task_file = Path("tasks/vviq16.json"),
cogtype = "no",
nsim = 10,
memory = "SingleTurn",
parser = DefaultLiteralVivid15,
proj_dir = Path("./results"),
projectname = "vviq_pilot",
tunnel_status = "1",
enabletqdm = True,
)
exp = ExpCard(card)
scanner = ScannerModel(expcard=exp)
results = scanner.run(progress_bar=True)
to_csv(scanner, path=card.proj_dir)
See also