# Transcription

Historical document transcription system for family-history documents.

The app lets you upload a document image/PDF, queues a background transcription job, and then shows job status and results in a web UI.

## What the app does

- Upload document files (`.jpg`, `.jpeg`, `.png`, `.tif`, `.tiff`, `.pdf`)
- Persist document + job records in SQLite
- Process jobs in a background worker (`queued -> processing -> transcribed/failed`)
- Store transcript text (or failure detail)
- Show status and results in the NiceGUI interface

## Quick start

### 1) Install dependencies

```bash
uv sync
```

### 2) Configure environment

Create a `.env` file in the project root with the required OpenRouter API key:

```env
OPENROUTER_API_KEY=your_openrouter_api_key
```

Settings are read from CLI arguments first, then environment variables, then `.env`, then the defaults below.

### Configuration Source Precedence

When the same setting is provided in multiple places, the value is chosen in this order (highest priority first):

1. CLI arguments (for example `--port 9999`)
2. Settings constructor arguments (used mainly in tests)
3. Environment variables
4. `.env` file values
5. Model defaults in code

Practical examples:

- `--port 9999` overrides both `PORT=8000` in the shell and `PORT=7000` in `.env`.
- `DATABASE__PATH=prod.db` in the shell overrides `DATABASE__PATH=dev.db` in `.env`.

#### Server and runtime

| Environment variable | Default | Description |
| --- | --- | --- |
| `HOST` | `0.0.0.0` | Address on which the server listens. |
| `PORT` | `8000` | Server port. |
| `LOG_LEVEL` | `info` | Uvicorn and application log level. |
| `RELOAD` | `false` | Restart the development server when source files change. |
| `ENVIRONMENT` | `development` | Runtime environment: `development`, `test`, or `production`. |

#### Provider

| Environment variable | Default | Description |
| --- | --- | --- |
| `PROVIDER` | `openrouter` | Transcription provider. |
| `OPENROUTER_API_KEY` | Required | OpenRouter API key. |
| `PROVIDER_MODEL` | Provider default | Optional model override. |
| `OPENROUTER_HTTP_REFERER` | Unset | Optional OpenRouter attribution URL. |
| `OPENROUTER_APP_TITLE` | Unset | Optional OpenRouter attribution title. |

#### Database and files

Use nested env vars for database settings (recommended):

```env
DATABASE__DRIVER=sqlite
DATABASE__PATH=app.db
# BOOTSTRAP_SCHEMA_ON_STARTUP=true
SQLITE_CHECK_SAME_THREAD=false
UPLOAD_DIR=./uploads
PROMPT_DIR=./prompts
```

For PostgreSQL:

```env
DATABASE__DRIVER=postgres
DATABASE__HOST=localhost
DATABASE__PORT=5432
DATABASE__DATABASE=transcription
DATABASE__USER=postgres
DATABASE__PASSWORD=change-me
```

This uses Pydantic nested settings (`env_nested_delimiter='__'`) and avoids JSON blobs in `.env`. A top-level `DATABASE={...}` JSON value is still supported as a fallback, and nested keys such as `DATABASE__PATH` take precedence over conflicting JSON keys.

`BOOTSTRAP_SCHEMA_ON_STARTUP` creates missing tables when the app starts. When unset, it is enabled in `development` and `test`, and disabled in `production`; set it explicitly to override that policy. `SQLITE_CHECK_SAME_THREAD` defaults to `false`.

#### Worker

```env
WORKER_MAX_RETRIES=0
WORKER_RETRY_BACKOFF_SECONDS=0
WORKER_PROVIDER_TIMEOUT_SECONDS=20
WORKER_MIN_TRANSCRIPTION_CHARS=0
WORKER_MIN_TRANSCRIPTION_LINES=0
WORKER_FAIL_ON_FINISH_REASON_LENGTH=false
```

### 3) Run the app

```bash
uv run python -m transcription --port 9999 --reload --database.driver sqlite --bootstrap-schema-on-startup
```

This starts the development server with SQLite, creates missing tables, and enables automatic reload. Run `uv run python -m transcription --help` for all CLI options; CLI names use kebab case and nested database options use dot notation, such as `--database.path ./data/transcription.db`.

### 4) Open in browser

- GUI: [http://localhost:9999/ui](http://localhost:9999/ui)
- Health check: [http://localhost:9999/healthz](http://localhost:9999/healthz)

Replace `localhost` with the server's hostname or IP address when connecting from another machine.

## How to navigate the GUI

- **Upload page** (`/ui`)
  - Select a supported file to upload.
  - The app creates a queued transcription job.
  - Use the **View jobs** link to inspect progress.

- **Jobs page** (`/ui/jobs`)
  - See all jobs and their status.
  - Use **Refresh** to reload current states.
  - Open a specific job to see details.

- **Job detail page** (`/ui/jobs/{job_id}`)
  - Shows job metadata and status.
  - Displays transcript text when successful.
  - Displays failure detail when transcription fails.

## Prompt artifacts

Prompt files are stored in `prompts/` and loaded from `PROMPT_DIR` (default: `./prompts`).

The canonical MVP prompt is:
- `prompts/transcribe_document.md`
