# Configuration reference

> `~/.he/config.toml` schema for `[llm]` and `[embedder]`, provider presets and default models, environment variable precedence (`OPENAI_API_KEY`, `OPENAI_BASE_URL`, `HYPER_EXTRACT_LOG_LEVEL`, `HYPER_EXTRACT_LOG_FILE`), and validation rules.

- Repository: yifanfeng97/Hyper-Extract
- GitHub: https://github.com/yifanfeng97/Hyper-Extract
- Human docs: https://grok-wiki.com/public/docs/yifanfeng97-hyper-extract-7891c7254cdf
- Complete Markdown: https://grok-wiki.com/public/docs/yifanfeng97-hyper-extract-7891c7254cdf/llms-full.txt

## Source Files

- `hyperextract/cli/config.py`
- `hyperextract/cli/commands/config.py`
- `hyperextract/utils/client.py`
- `hyperextract/utils/logging.py`
- `.env.example`
- `tests/cli/test_verbose.py`

---

---
title: "Configuration reference"
description: "`~/.he/config.toml` schema for `[llm]` and `[embedder]`, provider presets and default models, environment variable precedence (`OPENAI_API_KEY`, `OPENAI_BASE_URL`, `HYPER_EXTRACT_LOG_LEVEL`, `HYPER_EXTRACT_LOG_FILE`), and validation rules."
---

Hyper-Extract stores LLM and embedder credentials in `~/.he/config.toml`. The CLI loads this file through `ConfigManager`, merges environment-variable fallbacks at read time, and validates credentials before any command that calls remote models (`parse`, `feed`, `build-index`, `search`, `talk`, `show`). The Python SDK reads the same file via `get_client()`, or bypasses it with `create_client()` shorthand specs.

## File location

| Platform | Path |
|----------|------|
| Linux / macOS | `~/.he/config.toml` |
| Windows | `%USERPROFILE%\.he\config.toml` |

The directory is created on first save (`he config init`, `he config llm`, or `he config embedder`). A missing file is not an error: `ConfigManager` starts from built-in dataclass defaults and applies environment fallbacks when resolving clients.

:::files
~/.he/
└── config.toml    # [llm] and [embedder] sections
:::

## Configuration schema

The TOML file has two top-level tables. Both share the same field shape.

### `[llm]` section

<ParamField body="provider" type="string">
Provider preset identifier. Recognized presets: `openai`, `bailian`, `vllm`. Interactive `he config init` can also store `custom` for OpenAI-compatible endpoints without a built-in preset. Empty string is valid but skips preset URL resolution.
</ParamField>

<ParamField body="model" type="string" default="gpt-4o-mini">
LLM model name passed to `ChatOpenAI`. When omitted from the file, defaults to `gpt-4o-mini`.
</ParamField>

<ParamField body="api_key" type="string">
API key for the LLM endpoint. At resolution time, an empty value falls back to `OPENAI_API_KEY`. For `vllm`, a dummy value such as `dummy` is accepted.
</ParamField>

<ParamField body="base_url" type="string">
OpenAI-compatible API base URL. Resolution order: non-empty file value → `OPENAI_BASE_URL` environment variable → provider preset URL. Required when `provider = "vllm"`.
</ParamField>

### `[embedder]` section

<ParamField body="provider" type="string">
Same preset identifiers as `[llm]`. LLM and embedder can use different providers (mixed cloud + local deployments).
</ParamField>

<ParamField body="model" type="string" default="text-embedding-3-small">
Embedding model name. Defaults to `text-embedding-3-small` when absent from the file.
</ParamField>

<ParamField body="api_key" type="string">
Embedder API key. Empty value falls back to `OPENAI_API_KEY` — not to `[llm].api_key`. For `vllm`, `dummy` is accepted.
</ParamField>

<ParamField body="base_url" type="string">
Embedder endpoint URL. Same resolution order as `[llm].base_url`. Non-official URLs route through `CompatibleEmbeddings` instead of `OpenAIEmbeddings`.
</ParamField>

### Example files

<Tabs>
<Tab title="OpenAI">

```toml
[llm]
provider = "openai"
model = "gpt-4o-mini"
api_key = "sk-your-api-key"
base_url = "https://api.openai.com/v1"

[embedder]
provider = "openai"
model = "text-embedding-3-small"
api_key = ""
base_url = ""
```

Empty `api_key` / `base_url` in `[embedder]` resolve via `OPENAI_API_KEY` and the `openai` preset URL at runtime.

</Tab>
<Tab title="Bailian">

```toml
[llm]
provider = "bailian"
model = "qwen3.6-plus"
api_key = "sk-your-api-key"
base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"

[embedder]
provider = "bailian"
model = "text-embedding-v4"
api_key = "sk-your-api-key"
base_url = ""
```

</Tab>
<Tab title="Local vLLM">

```toml
[llm]
provider = "vllm"
model = "Qwen/Qwen3.5-9B"
api_key = "dummy"
base_url = "http://localhost:8000/v1"

[embedder]
provider = "vllm"
model = "BAAI/bge-m3"
api_key = "dummy"
base_url = "http://localhost:8001/v1"
```

</Tab>
<Tab title="Mixed deployment">

```toml
[llm]
provider = "bailian"
model = "qwen3.6-plus"
api_key = "sk-your-api-key"
base_url = ""

[embedder]
provider = "vllm"
model = "BAAI/bge-m3"
api_key = "dummy"
base_url = "http://localhost:8001/v1"
```

</Tab>
</Tabs>

## Provider presets

`PROVIDER_PRESETS` in `hyperextract/cli/config.py` and `hyperextract/utils/client.py` define built-in defaults. `he config init -p <preset>` and `create_client("<preset>")` both read from this table.

| Preset | `base_url` | Default LLM | Default embedder |
|--------|-----------|-------------|------------------|
| `openai` | `https://api.openai.com/v1` | `gpt-4o-mini` | `text-embedding-3-small` |
| `bailian` | `https://dashscope.aliyuncs.com/compatible-mode/v1` | `qwen3.6-plus` | `text-embedding-v4` |
| `vllm` | `None` (must be set explicitly) | `None` | `None` |

<Note>
There is no `deepseek` preset. DeepSeek and other OpenAI-compatible endpoints are reached via `bailian`, `custom`, or a manual `base_url`.
</Note>

### `he config init` preset behavior

| Mode | Trigger | Result |
|------|---------|--------|
| Quick preset | `-p <preset> -k <key>` | Sets both `[llm]` and `[embedder]` to the preset; fills default models and URL from the table |
| Legacy OpenAI | `-k <key>` without `-p` | Forces `openai` preset with `gpt-4o-mini` + `text-embedding-3-small` |
| Interactive | No flags | Prompts for provider; `vllm` requires manual model and URL for each service |

For `vllm` quick init without `-p`/`-k`, interactive mode substitutes `dummy` when the API key prompt is left empty.

## Resolution and precedence

Configuration is resolved at read time in `ConfigManager.get_llm_config()` and `get_embedder_config()`. The on-disk file stores raw values; environment variables are merged only when fields are fetched.

```text
api_key:     config.toml (non-empty)  →  OPENAI_API_KEY
base_url:    config.toml (non-empty)  →  OPENAI_BASE_URL  →  PROVIDER_PRESETS[provider].base_url
model:       config.toml value        →  dataclass default (gpt-4o-mini / text-embedding-3-small)
```

<Warning>
`OPENAI_BASE_URL` applies to both `[llm]` and `[embedder]` when their file `base_url` is empty. Set per-service URLs in the TOML file for mixed endpoints.
</Warning>

CLI `he config llm` / `he config embedder` flags write directly to the file. Partial updates preserve unspecified fields: `model` updates only when the flag value is non-empty; `api_key` and `base_url` update when the flag is present (including empty string).

### Python client resolution

| API | Config source |
|-----|---------------|
| `get_client()` / `get_client(path)` | Reads TOML via `ConfigManager`, then builds LangChain clients |
| `create_client(spec, api_key=...)` | Bypasses TOML; parses `provider:model@url` shorthand |
| `create_llm()` / `create_embedder()` | Single-service creation from spec or dict |

String shorthand examples:

<CodeGroup>
```python title="Preset only"
create_client("bailian", api_key="sk-xxx")
# → qwen3.6-plus + text-embedding-v4 at DashScope URL
```

```python title="Model override"
create_llm("bailian:qwen-plus", api_key="sk-xxx")
```

```python title="Full vLLM spec"
create_client(
    llm="vllm:Qwen3.5-9B@http://localhost:8000/v1",
    embedder="vllm:bge-m3@http://localhost:8001/v1",
    api_key="dummy",
)
```
</CodeGroup>

## Environment variables

### Provider credentials

| Variable | Affects | Precedence |
|----------|---------|------------|
| `OPENAI_API_KEY` | `[llm].api_key`, `[embedder].api_key` | Used when the corresponding TOML `api_key` is empty |
| `OPENAI_BASE_URL` | `[llm].base_url`, `[embedder].base_url` | Used when the corresponding TOML `base_url` is empty, before preset lookup |

`.env.example` documents the credential pair:

```bash
OPENAI_API_KEY=sk-your-api-key-here
OPENAI_BASE_URL=https://api.openai.com/v1
```

<Info>
Despite the `OPENAI_` prefix, both variables act as generic fallbacks for any OpenAI-compatible provider configured in TOML — Bailian, proxy endpoints, and local vLLM included.
</Info>

### Logging

| Variable | Affects | Default | Notes |
|----------|---------|---------|-------|
| `HYPER_EXTRACT_LOG_LEVEL` | Root logger level | `WARNING` | Accepted: `DEBUG`, `INFO`, `WARNING`, `ERROR` (case-insensitive). Invalid values fall back to `WARNING`. |
| `HYPER_EXTRACT_LOG_FILE` | Optional log file path | unset (stderr only) | Parent directories are created automatically. |

The CLI calls `configure_logging()` on every invocation. There is no `--verbose` flag; set `HYPER_EXTRACT_LOG_LEVEL=DEBUG` instead.

```bash
HYPER_EXTRACT_LOG_LEVEL=DEBUG he parse examples/en/tesla.md -o /tmp/tesla-ka -t general/biography_graph -l en
```

Programmatic override: `configure_logging(level="INFO", output_file="/tmp/he.log")` — the `output_file` argument takes precedence over `HYPER_EXTRACT_LOG_FILE`.

## Validation rules

`ConfigManager.validate()` runs through `validate_config()` before model-backed CLI commands. On failure the CLI prints the error message and exits with code 1.

| Condition | Rule | Error message |
|-----------|------|---------------|
| `provider = "vllm"` (LLM) | `base_url` must be non-empty after resolution | `vLLM provider requires base_url.` |
| `provider = "vllm"` (embedder) | `base_url` must be non-empty after resolution | `vLLM embedder requires base_url.` |
| Any other LLM provider | `api_key` must be non-empty after `OPENAI_API_KEY` fallback | `LLM API key is not configured. Run 'he config llm --api-key YOUR_KEY'` |
| Any other embedder provider | `api_key` must be non-empty after fallback | `Embedder API key is not configured. Run 'he config embedder --api-key YOUR_KEY'` |
| All checks pass | — | `Configuration is valid` |

Commands that **do** validate: `parse`, `feed`, `build-index`, `search`, `talk`, `show`.

Commands that **do not** validate: `config`, `list`, `info`, `he --version`.

<Warning>
`provider = "vllm"` with a missing `base_url` also raises `ValueError` inside `_resolve_base_url()` during client construction — even outside `validate()` — if resolution is attempted without a URL.
</Warning>

### Reset configuration

```bash
he config llm --unset
he config embedder --unset
```

`--unset` restores dataclass defaults (`gpt-4o-mini`, `text-embedding-3-small`, empty provider/key/URL) and rewrites the TOML file.

## Verify configuration

<Steps>
<Step title="Inspect resolved values">

```bash
he config show
```

`he config show` displays the **resolved** configuration (including environment fallbacks), not just raw TOML values. API keys are truncated to the first 10 characters.

</Step>
<Step title="Inspect one service">

```bash
he config llm --show
he config embedder --show
```

</Step>
<Step title="Run a validated command">

```bash
HYPER_EXTRACT_LOG_LEVEL=INFO he parse --help
```

Any command that calls `validate_config()` will fail fast with a clear message if credentials are incomplete.

</Step>
</Steps>

## Related pages

<CardGroup>
<Card title="Configure providers" href="/configure-providers">
Step-by-step setup with `he config init`, per-service commands, and programmatic `create_client()` patterns.
</Card>
<Card title="Provider system" href="/provider-system">
BYOC/BYOK model, `provider:model@url` shorthand, `CompatibleEmbeddings`, and model compatibility requirements.
</Card>
<Card title="CLI reference" href="/cli-reference">
Full `he config` subcommand surface, flags, and exit behavior.
</Card>
<Card title="Python API reference" href="/python-api-reference">
`get_client`, `create_client`, `create_llm`, `create_embedder`, and logging helpers.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Missing API keys, vLLM `base_url` errors, and debug logging with `HYPER_EXTRACT_LOG_LEVEL`.
</Card>
</CardGroup>
