PsychScanner — Input Parameters Reference¶
Every input parameter you can pass to a PsychScanner experiment, organized by purpose. The notebook covers:
ExpCardInitfields — the experiment configuration (~25 fields)ScannerModel.run()arguments — runtime knobs at execution time- Auto-populated fields — set by the package, not by you
- A complete worked example showing every user-settable field together
All field definitions live in scanner_cards.py.
import psychscanner as psy
from psychscanner import ExpCard, ExpCardInit, ScannerModel
from pathlib import Path
# Quick way to see the live schema and defaults from the package itself:
for name, fld in ExpCardInit.model_fields.items():
print(f"{name:18} default={fld.default!r:40} type={fld.annotation}")
/opt/anaconda3/envs/psyscan/lib/python3.11/site-packages/langgraph/checkpoint/base/__init__.py:17: LangChainPendingDeprecationWarning: The default value of `allowed_objects` will change in a future version. Pass an explicit value (e.g., allowed_objects='messages' or allowed_objects='core') to suppress this warning. from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
model default='mock-chat-model' type=<class 'str'>
family default='mock-llm' type=<class 'str'>
parameters default=None type=dict | None
memory default='SingleTurn' type=typing.Literal['SingleTurn', 'Convo']
memory_k default=-1 type=int | None
summary_k default=0 type=int | None
persona_files default=None type=list[typing.Annotated[pathlib.Path, PathType(path_type='file')]] | None
task_file default=PosixPath('/Users/saurabhext/Documents/PSYCHSCANNER/psychscanner/src/psychscanner/datasets/default_survey.json') type=typing.Union[dict, typing.Annotated[pathlib.Path, PathType(path_type='file')], NoneType]
task_context default=None type=typing.Literal[True, False, None]
tunnel_status default='0' type=typing.Literal['0', '1']
tunnel_k default=-1 type=int | None
projectname default='DEFAULTPROJ' type=str | None
tags default=[] type=list[str] | None
parser default=None type=typing.Union[str, type[pydantic.main.BaseModel], typing.Callable, NoneType]
parser_raw default=False type=<class 'bool'>
parser_config default=None type=dict | None
proj_dir default=PosixPath('/Users/saurabhext/psychscanner') type=pathlib.Path | None
login_env default=None type=type[psychscanner.staging.scanner_cards.Settings] | None
enabletqdm default=False type=typing.Literal[False, True]
trial_parsers default=None type=list[typing.Any] | None
persona_data default=None type=list | None
task_data default=None type=dict | None
session_tunnel default=None type=object | None
cogtype default='custom' type=typing.Literal['assistant', 'custom', 'no']
nsim default=None type=int | None
chain_type default=None type=typing.Optional[typing.Literal['item', 'trial', 'task']]
feedback default=False type=<class 'bool'>
feedback_fn default=None type=typing.Optional[typing.Callable]
model: str (default "mock-chat-model")¶
Specific model identifier within the chosen family. Examples: "gpt-4o-mini", "llama3.2:3b-instruct-fp16", "claude-sonnet-4-5".
family: str (default "mock-llm")¶
Provider/backend. Currently dispatched in memories/base/model_provider.py:
"mock-llm"— built-in echo model, no network. Does not support structured output."ollama"— local Ollama server."huggingface"— HF inference endpoint.- Anything else — falls through to LangChain's
init_chat_model(model, model_provider=family, ...). Common values:"openai","anthropic".
parameters: dict | None (default None)¶
Forwarded as **kwargs to the underlying chat model constructor. Provider-specific keys: temperature, max_tokens, top_p, num_ctx (Ollama), etc.
login_env: type[Settings] | None (default None)¶
A pydantic-settings class pointing to a .env file containing API keys. Use it instead of leaking keys into your code. See python-dotenv.
card = ExpCardInit()
card.model = "llama3.2:3b-instruct-fp16"
card.family = "ollama"
card.parameters = {"temperature": 0, "num_ctx": 4096}
print(card.model, card.family, card.parameters)
llama3.2:3b-instruct-fp16 ollama {'temperature': 0, 'num_ctx': 4096}
memory: Literal["SingleTurn", "Convo"] (default "SingleTurn")¶
"SingleTurn"— every trial is independent (no history)."Convo"— LangGraphMemorySaverkeeps prior turns in the same thread.
memory_k: int | None (default -1)¶
How many past turns to keep when memory is on. -1 = use the package default (unlimited). Higher values use more tokens.
chain_type: Literal["item", "trial", "task"] | None (default None)¶
Determines what counts as a thread for memory:
"item"— fresh thread per item (no cross-trial memory)."trial"— thread persists across multiple stimuli within one trial."task"— thread persists across all trials in a task.
When None it's read from the task JSON's "chain_type" field. Setting it on the card overrides the JSON.
task_file: dict | FilePath | None (default = bundled VVIQ-16)¶
Either a path to a task JSON file or an in-memory dict with the same structure. The default is datasets/default_survey.json (VVIQ vividness questionnaire).
Required JSON keys: tasktype, taskname, instructions, contexts, contexts_id, context_present, items, parser, chain_type. See get_task_template().
task_context: Literal[True, False, None] (default None)¶
For surveys: when True, item prompts are formatted as context: <item> situation: <item>. When None/False, items are presented bare. The card-level value overrides whatever the task JSON specifies in context_present.
# Look at the default task structure
from psychscanner import get_task_template
import json
print(json.dumps(get_task_template(), indent=2))
{
"tasktype": "sc",
"taskname": "",
"instructions": "",
"contexts": "",
"contexts_id": "",
"context_present": "",
"items": {},
"chain_type": "",
"trial_parsers": [],
"parser": "",
"postfix": "",
"prefix": ""
}
cogtype: Literal["assistant", "custom", "no"] (default "custom")¶
Drives system-prompt construction:
"custom"— uses persona statements + task instructions."assistant"— generic AI-assistant framing, ignores cognitive role text."no"— no persona/role framing at all. Useful for raw model behavior. Requiresnsimto be set.
persona_files: list[FilePath] | None (default None)¶
JSON files describing personas. Each file must contain "persona_statements" mapped to a list of strings. The cartesian product of all files becomes the set of system prompts. When None, uses package defaults.
nsim: int | None (default None)¶
Number of simulations to run when cogtype is "assistant" or "no" (i.e. when there are no personas to multiply over). Defaults to 1 if those modes are selected and nsim is unset.
parser: str | type[BaseModel] (default "0")¶
"0"— no parsing."1"— read class name from the task JSON's"parser"field, resolve viaeval()."dynamic"— hardcoded RM routing ontrcode.- A
pydantic.BaseModelsubclass — used directly.
parser_raw: bool (default False)¶
When True, the response dict includes the raw AIMessage under a "raw" key alongside the parsed fields.
parser_config: dict | None (default {"method": "json_schema"})¶
Forwarded as **kwargs to LangChain's model.with_structured_output(...). Supported method values depend on the provider — "json_schema", "function_calling", "json_mode".
trial_parsers: list[Any] | None (default None)¶
Declared but not currently wired into execution. Reserved for future per-trial parser routing.
tunnel_status: Literal["0", "1"] (default "0")¶
"0"— no tunnel; logs go todatasets/no_tunnel_runs/DEFAULT_<timestamp>.log."1"— write checkpoints to disk so an interrupted run can resume.
tunnel_k: int | None (default -1)¶
Checkpoint every k trials. -1 = checkpoint after every system-prompt iteration. If k > num_trials, falls back to -1.
session_tunnel: object | None (default None)¶
Auto-populated by ExpCard based on tunnel_status. You normally don't set this directly.
⚠️ Gotcha: if tunnel_status="1" and a previous run completed in the same proj_dir, you'll get ValueError: Session already has ended. Delete old files to run. Either delete the project directory or pick a new projectname.
proj_dir: Path | None (default ~/psychscanner)¶
Root directory for outputs. Created if missing.
projectname: str | None (default "DEFAULTPROJ")¶
Subfolder under proj_dir for this experiment.
tags: list[str] | None (default [])¶
Free-form tags stored alongside the data for filtering/grouping later.
enabletqdm: bool (default False)¶
Show a tqdm progress bar during execution.
Effective output path is built as:
<proj_dir>/<projectname>/<taskname>/<family>_<model>_<memory>/
feedback: Literal["0", "1"] (default "0")¶
"0"— no feedback."1"— callfeedback_fnbetween trials.
feedback_fn: Callable | None (default None)¶
User-defined feedback object. Must implement update_trial_stim(finput, fb_response) and generate_feedback(trdata, pred_dict, input_dict, parser_status, trial_item_collector). If feedback="1" but feedback_fn is None, the runtime silently downgrades to no-feedback.
I. Auto-populated fields¶
Set by the package after ExpCard.__init__. Accessible for inspection but you rarely set them yourself.
| Field | Populated from |
|---|---|
persona_data |
persona_files JSON (when cogtype="custom") |
task_data |
task_file JSON |
session_tunnel |
tunnel_status |
After construction, also accessible on the ExpCard (not ExpCardInit):
expcard.parser— resolved Pydantic class (or"0"/"dynamic").expcard.data_root_dir—<proj_dir>/<projectname>/<taskname>/<family>_<model>_<memory>/.
J. ScannerModel.run() — runtime parameters¶
Some knobs are passed at execution time, not at card construction:
| Argument | Type | Default | Meaning |
|---|---|---|---|
progress_bar |
bool |
False |
Show tqdm bar. Falls back to expcard.enabletqdm if False. |
feedback |
Any |
None |
Override expcard.feedback. |
feedback_fn |
Callable |
None |
Override expcard.feedback_fn. |
save_str |
str |
None |
Custom suffix for output filenames; defaults to a timestamp. |
tunnel |
Any |
None |
Override the session tunnel object created by the card. |
Source: scanner_model.py:run().
K. Two ways to construct the card¶
Style 1 — kwargs to ExpCard¶
Concise, validates immediately:
exp = ExpCard(
model="llama3.2:3b-instruct-fp16",
family="ollama",
task_file="tasks/example_survey.json",
parser="1",
projectname="my_run",
)
Style 2 — build an ExpCardInit first, then wrap it¶
Lets you mutate fields incrementally (handy in notebooks):
card = ExpCardInit()
card.model = "llama3.2:3b-instruct-fp16"
card.family = "ollama"
card.parser = "1"
card.projectname = "my_run"
exp = ExpCard(card)
Both styles produce equivalent ExpCard objects.
from pathlib import Path
import shutil
RUN_DIR = Path.cwd() / "_param_demo_runs"
if RUN_DIR.exists():
shutil.rmtree(RUN_DIR)
task_file = Path.cwd() / "tasks" / "example_survey.json"
card = ExpCardInit()
# A. Model & provider
card.model = "mock-chat-model" # use mock for a runnable demo
card.family = "mock-llm"
card.parameters = None
card.login_env = None
# B. Memory & chain
card.memory = "SingleTurn"
card.memory_k = -1
card.chain_type = "item" # overrides whatever's in the task JSON
# C. Task input
card.task_file = task_file
card.task_context = True
# D. Persona / cognitive type
card.cogtype = "no" # skip persona system prompts
card.persona_files = None
card.nsim = 2 # required when cogtype is 'no' or 'assistant'
# E. Parser
card.parser = "0" # mock-llm only supports no-parser mode
card.parser_raw = False
card.parser_config = {"method": "json_schema"}
# F. Session tunnel
card.tunnel_status = "0" # off — keeps the demo idempotent
card.tunnel_k = -1
# G. Output / saving
card.proj_dir = RUN_DIR
card.projectname = "all_params_demo"
card.tags = ["tutorial", "reference"]
card.enabletqdm = False
# H. Feedback
card.feedback = False
card.feedback_fn = None
exp = ExpCard(card)
print("Resolved output dir:", exp.data_root_dir)
print("Resolved parser :", exp.parser)
----<PROJECT AND DATA ROOT DIRECTORY>----
Project root dir: /Users/saurabhext/Documents/PSYCHSCANNER/psychscanner/examples/_param_demo_runs
Simulation data root dir: /Users/saurabhext/Documents/PSYCHSCANNER/psychscanner/examples/_param_demo_runs/all_params_demo/example_survey/mock-llm_mock-chat-model_SingleTurn
----<>----
Resolved output dir: /Users/saurabhext/Documents/PSYCHSCANNER/psychscanner/examples/_param_demo_runs/all_params_demo/example_survey/mock-llm_mock-chat-model_SingleTurn Resolved parser : None
scanner = ScannerModel(expcard=exp)
results = scanner.run(
progress_bar=False, # J. runtime overrides
feedback=None,
feedback_fn=None,
save_str="demo",
tunnel=None,
)
print(f"Got {len(results)} system-prompt iterations × {len(results[0])} trials each.")
print("\nFirst trial keys:")
for k in results[0][0]:
print(" -", k)
--<chat model>-- model_name='mock-chat-model' repeat_buffer_length=10
2026-05-07 11:00:04.644 | CRITICAL | psychscanner.session_tunnel.session_tunnel:create_tunnel:152 - BEGIN
TOTAL RUNS: 2 RESUME IDX: None
----<>---- task running
0it [00:00, ?it/s]
4it [00:00, 36.73it/s]
6it [00:00, 41.05it/s]
2026-05-07 11:00:05.104 | INFO | psychscanner.session_tunnel.session_tunnel:scan_checkpoint:184 - scan-checkpoint
----<scanned runs>---- i = 0
----<>---- task running
0it [00:00, ?it/s]
6it [00:00, 75.79it/s]
2026-05-07 11:00:05.220 | INFO | psychscanner.session_tunnel.session_tunnel:scan_checkpoint:184 - scan-checkpoint
----<scanned runs>---- i = 1
2026-05-07 11:00:05.235 | CRITICAL | psychscanner.session_tunnel.session_tunnel:end_checkpoint:163 - END
Got 2 system-prompt iterations × 6 trials each. First trial keys: - trial_idx - inputs - system_message - trcode - parser - stimulus - taskname - tasktype - context_present - context - context_item - trid - chain_type - hmsg - pred_resp - pred_dict - trace_id - fb_response - system_message_idx - system_template - tunnel_id
Cheat sheet — minimum required fields¶
Most fields have working defaults. The minimum viable card is:
ExpCard(
model="...", # if you don't want mock
family="...", # provider for the model above
task_file="...", # path to your task JSON (default = VVIQ-16)
projectname="...", # so outputs go somewhere recognizable
)
Beyond that, the four most commonly tweaked fields are:
parser— controls structured outputmemory+chain_type— controls cross-trial statecogtype+ (persona_files|nsim) — controls how many runs and what roletunnel_status— turn on if your run is long enough to crash