Configuration Reference¶
Formative-analysis uses two configuration files: config.json for credentials and system-level settings, and forma.yaml for project-level settings. This document describes every field in both files.
Table of Contents¶
config.json¶
Location: ~/.config/formative-analysis/config.json
Stores credentials and system-level settings that apply across all projects. This file is searched in the following order:
- Explicit path argument (if provided)
/run/agenix/forma-config(NixOS agenix)~/.config/formative-analysis/config.json~/.config/formative-analysis/forma.json(deprecated — will be removed)
smtp section¶
SMTP server settings for email delivery (forma deliver send).
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
server |
string | yes | -- | SMTP server hostname (e.g., smtp.gmail.com) |
port |
integer | yes | -- | SMTP port number (587 for STARTTLS, 465 for SSL) |
sender_email |
string | yes | -- | Sender email address |
sender_name |
string | no | "" |
Display name shown to recipients |
use_tls |
boolean | no | true |
Use STARTTLS encryption |
send_interval_sec |
number | no | 1.0 |
Seconds to wait between emails |
Note: The SMTP password is never stored in config.json. See Credential Security for how to supply it at runtime.
llm section¶
LLM provider settings for feedback generation and analysis.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
provider |
string | no | "gemini" |
LLM provider ("gemini" or "anthropic") |
model |
string | no | -- | Model name (e.g., gemini-2.0-flash, claude-sonnet-4-6) |
api_key |
string | no | -- | API key (recommend using environment variables instead) |
naver_ocr section¶
Naver CLOVA OCR credentials for answer sheet scanning.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
api_url |
string | no | -- | Naver CLOVA OCR API endpoint URL |
secret_key |
string | no | -- | Naver OCR secret key |
Complete config.json example¶
{
"smtp": {
"server": "smtp.gmail.com",
"port": 587,
"sender_email": "professor@university.edu",
"sender_name": "Prof. Kim",
"use_tls": true,
"send_interval_sec": 1.0
},
"llm": {
"provider": "anthropic",
"model": "claude-sonnet-4-6",
"api_key": "sk-ant-..."
},
"naver_ocr": {
"api_url": "https://...",
"secret_key": "..."
}
}
forma.yaml¶
Location: Auto-discovered by searching from the current working directory upward (stops at .git sentinel or filesystem root).
Stores project-level settings. All fields are optional and have sensible defaults.
Resolution order: CLI flags (highest priority) > forma.yaml > system defaults (argparse defaults)
project section¶
Course metadata used in report headers and file naming.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
course_name |
string | no | "" |
Course name (e.g., "Human Anatomy") |
year |
integer | no | 0 |
Academic year (must be >= 2020 when set) |
semester |
integer | no | 0 |
Semester number (must be 1 or 2) |
grade |
integer | no | 0 |
Student grade year (must be >= 1 when set) |
classes section¶
Section/class configuration for multi-class batch processing.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
identifiers |
list | no | [] |
List of class section identifiers (e.g., [A, B, C, D]) |
join_pattern |
string | no | "" |
File pattern for joined data; must contain {class} placeholder |
eval_pattern |
string | no | "" |
Directory pattern for evaluation results; must contain {class} placeholder |
paths section¶
File and directory paths for input data and output.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
exam_config |
string | no | "" |
Path to exam configuration YAML file |
join_dir |
string | no | "" |
Path to joined data directory |
output_dir |
string | no | "" |
Path to PDF output directory |
longitudinal_store |
string | no | "" |
Path to longitudinal store YAML file |
font_path |
string/null | no | null |
Path to Korean font file (null for auto-detection) |
ocr section¶
OCR scanning settings for answer sheet digitization.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
ocr_model |
string/null | no | null |
OCR model ID (null uses gemini-2.5-flash) |
naver_config |
string | no | "" |
Path to Naver OCR configuration (deprecated) |
credentials |
string | no | "" |
Credentials reference (resolved from environment variable) |
spreadsheet_url |
string | no | "" |
Google Sheets URL for OCR data |
num_questions |
integer | no | 5 |
Number of questions per exam (must be >= 1) |
evaluation section¶
LLM evaluation pipeline settings.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
provider |
string | no | "gemini" |
LLM provider ("gemini" or "anthropic") |
model |
string/null | no | null |
LLM model name (null uses provider default) |
skip_feedback |
boolean | no | false |
Skip feedback generation |
skip_graph |
boolean | no | false |
Skip graph comparison |
skip_statistical |
boolean | no | false |
Skip statistical analysis |
n_calls |
integer | no | 3 |
Number of LLM calls per item (must be >= 1) |
reports section¶
PDF report generation settings.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
dpi |
integer | no | 150 |
Chart image resolution in DPI (72--600) |
skip_llm |
boolean | no | false |
Skip all LLM analysis in reports |
aggregate |
boolean | no | true |
Generate aggregate (cross-class) report |
prediction section¶
Machine learning model settings.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
model_path |
string/null | no | null |
Path to pre-trained risk prediction model (.pkl file) |
Top-level fields¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
current_week |
integer | no | 1 |
Current week number (must be >= 1) |
Complete forma.yaml example¶
project:
course_name: "Human Anatomy"
year: 2026
semester: 1
grade: 1
classes:
identifiers: [A, B, C, D]
join_pattern: "anp_{class}_final.yaml"
eval_pattern: "eval_{class}/"
paths:
exam_config: "exams/Ch01_FormativeTest.yaml"
join_dir: "results/anp_w1"
output_dir: "reports/"
longitudinal_store: "store/longitudinal.yaml"
font_path: null
ocr:
ocr_model: null
naver_config: ""
credentials: ""
spreadsheet_url: ""
num_questions: 5
evaluation:
provider: "gemini"
model: null
skip_feedback: false
skip_graph: false
skip_statistical: false
n_calls: 3
reports:
dpi: 150
skip_llm: false
aggregate: true
prediction:
model_path: null
current_week: 1
week.yaml¶
Location: One week.yaml per assessment week directory (e.g., week_01/week.yaml).
Discovery: forma ocr, forma eval, and forma select walk upward from the current working directory to find week.yaml, stopping at .git or the filesystem root. Override with --week-config <path> or disable with --no-config.
Priority: week.yaml takes precedence over forma.yaml for week-specific settings.
Config merge precedence (highest → lowest):
| Priority | Source | Scope |
|---|---|---|
| 1 | CLI flags | Overrides everything |
| 2 | week.yaml |
Per-week settings |
| 3 | forma.yaml |
Per-semester settings |
| 4 | Argparse defaults | Fallback |
Complete week.yaml example¶
week: 1 # required — week number (>= 1)
select:
source: "../exams/Ch01_FormativeTest.yaml" # path to FormativeTest YAML source file
questions: [1, 3] # sn numbers to extract
num_papers: 50 # number of exam paper copies to print
form_url: "" # Google Forms pre-filled URL template
exam_output: "week_01_exam.pdf" # PDF output filename (triggers PDF generation)
ocr:
num_questions: 2 # answer areas per sheet
image_dir_pattern: "scans_1{class}_w1" # image directory with {class} placeholder
ocr_output_pattern: "scans_1{class}_w1/ocr_results.yaml"
join_output_pattern: "scans_1{class}_w1/final.yaml"
join_forms_csv: "forms_responses_w1.csv" # CSV fallback for Google Forms data
student_id_column: "student_id" # column name for student ID
crop_coords: [] # auto-populated on first interactive scan
eval:
config: "../exams/Ch01_FormativeTest.yaml"
questions_used: [1, 3] # sn numbers used in evaluation
responses_pattern: "scans_1{class}_w1/final.yaml"
output_pattern: "scans_1{class}_w1/eval/"
skip_feedback: false
skip_graph: false
generate_reports: false # auto-generate student PDF reports
week.yaml field reference¶
For the complete schema with all 21 fields, types, and descriptions, see Data Formats — Week Configuration YAML.
Credential Security¶
The SMTP password is never stored in config.json or any configuration file. It must be supplied at runtime through one of two methods:
1. Standard input (--password-from-stdin)¶
Pipe the password into the forma deliver send command:
echo "mypassword" | forma deliver send --password-from-stdin --staged ./staging --template template.yaml
2. Environment variable (FORMA_SMTP_PASSWORD)¶
Export the password as an environment variable before running the command:
export FORMA_SMTP_PASSWORD="mypassword"
forma deliver send --staged ./staging --template template.yaml
If neither method is used, forma deliver send will exit with an error (unless --dry-run is specified).
API keys¶
For LLM and OCR API keys, it is recommended to use environment variables rather than storing them directly in config.json. If stored in config.json, ensure the file has restrictive permissions (chmod 600).
Generating forma.yaml with forma init¶
The forma init command creates a forma.yaml template in the current directory through an interactive wizard.
Usage¶
| Flag | Description |
|---|---|
--output PATH |
Output file path (default: ./forma.yaml) |
--force |
Overwrite existing file |
Interactive prompts¶
When you run forma init, you will be asked for:
- Course name -- e.g., "Human Anatomy"
- Academic year -- e.g., 2026
- Semester -- 1 or 2
- Class identifiers -- comma-separated, e.g.,
A,B,C,D
The generated file includes all sections with default values and inline comments explaining each field. You can then edit the file to fill in paths and adjust settings as needed.
Example¶
$ forma init
Course name: Human Anatomy
Academic year: 2026
Semester: 1
Class identifiers: A,B,C,D
Configuration template generated: forma.yaml
For detailed YAML schema specifications, see Data Formats.