Files
sure/.env.local.example
T
Joe Maples cc826df909 feat(ai): support OPENAI_EXTRA_HEADERS on the OpenAI-compatible provider (#3362)
* feat(ai): support OPENAI_EXTRA_HEADERS on OpenAI-compatible provider

Adds a fail-closed parser for the OPENAI_EXTRA_HEADERS env var (a JSON
object of header names to values) as a new Provider::Openai.extra_headers
class method. Malformed, non-object, blank, or unset values yield {}
with an error log and never the raw value, so chat keeps working on bad
config. Parsed headers are passed to the ruby-openai client at
construction, attaching them to every request the provider's client
makes (chat and batch flows alike).

ENV-only by design: no Setting fallback or settings-UI entry. Values are
stringified (nested JSON becomes Ruby-inspect strings) and blank values
are dropped.

Adds hosting docs and commented examples in .env.example and
.env.local.example, plus Minitest coverage mirroring the request_timeout
tests, including a docs-consistency test binding the knob to its docs.

* feat(ai): substitute {session_id} in OPENAI_EXTRA_HEADERS per chat request

Header values containing the literal {session_id} are now withheld at
client construction and merged onto the client at request time, with the
placeholder replaced by the chat's UUID. This identifies requests per
conversation rather than per install, for gateways that key sessions
(e.g. OpenCode Zen's x-opencode-session).

A session header is only merged when a session_id is present, so batch
flows (auto-categorize, merchant detection, PDF processing) — which
bypass chat_response — never send it; they receive static headers only.
The merge adds/overwrites without deleting managed headers.

Docs updated to cover both static and session-valued usage.

* fix(ai): keep OPENAI_EXTRA_HEADERS session values request-scoped

client.add_headers persists headers on the shared client in
ruby-openai 8.1.0, so a chat's resolved session header could survive
onto later requests made through the same provider instance. Session
headers are now merged onto a request-scoped dup of the client; the
shared client is never mutated. Batch flows and session-less chats
cannot observe another chat's session id.

Also updates the CodeRabbit-flagged tests to assert the shared client
stays untouched and the scoped copy is what issues the chat request.

* docs(ai): add YARD tags to OPENAI_EXTRA_HEADERS method docs

Converts the comment blocks on the four methods touched by this
feature (extra_headers, initialize, request_timeout, and
with_session_headers) into YARD docstrings with @param/@return tags,
satisfying CodeRabbit's docstring-coverage pre-merge check.
2026-09-03 21:38:23 +02:00

145 lines
6.1 KiB
Bash

# To enable / disable self-hosting features.
SELF_HOSTED = true
# Custom port config
# For users who have other applications listening at 3000, this allows them to set a value puma will listen to.
PORT=3000
# SimpleFIN runtime flags (default-off)
# Accepted truthy values: 1, true, yes, on
# SIMPLEFIN_DEBUG_RAW: when truthy, logs the raw payload returned by SimpleFIN (debug-only; can be noisy)
SIMPLEFIN_DEBUG_RAW=false
# SIMPLEFIN_INCLUDE_PENDING: when truthy, forces `pending=1` on SimpleFIN fetches when caller doesn't specify `pending:`
SIMPLEFIN_INCLUDE_PENDING=false
# SnapTrade OAuth app (register one at https://dashboard.snaptrade.com).
# The client ID on its own enables the device code flow: users confirm a short
# code on SnapTrade, no redirect URI needed.
SNAPTRADE_OAUTH_CLIENT_ID=
# Only needed for the browser redirect flow (authorization code + PKCE), which
# also requires https://<your-host>/snaptrade_items/oauth_callback to be
# registered as a redirect URI on the OAuth app.
SNAPTRADE_OAUTH_CLIENT_SECRET=
# Lunchflow runtime flags (default-off)
# LUNCHFLOW_DEBUG_RAW: when truthy, logs the raw payload returned by Lunchflow (debug-only; can be noisy)
LUNCHFLOW_DEBUG_RAW=false
# LUNCHFLOW_INCLUDE_PENDING: when truthy, adds `include_pending=true` to Lunchflow transaction fetch requests
LUNCHFLOW_INCLUDE_PENDING=false
# Controls onboarding flow (valid: open, closed, invite_only)
ONBOARDING_STATE = open
# Enable Twelve market data (careful, this will use your API credits)
TWELVE_DATA_API_KEY =
# OpenAI-compatible API endpoint config
OPENAI_ACCESS_TOKEN =
OPENAI_URI_BASE =
OPENAI_MODEL =
# LLM token budget. Applies to ALL outbound LLM calls: chat history,
# auto-categorize, merchant detection, provider enhancer, PDF processing.
# Defaults to Ollama's historical 2048-token baseline so small local models
# work out of the box — raise explicitly for cloud or larger-context models.
# LLM_CONTEXT_WINDOW = 2048 # Total tokens the model will accept
# LLM_MAX_RESPONSE_TOKENS = 512 # Reserved for the model's reply
# LLM_MAX_HISTORY_TOKENS = # Derived if unset (context - response - system_reserve)
# LLM_SYSTEM_PROMPT_RESERVE = 256 # Tokens reserved for the system prompt
# LLM_MAX_ITEMS_PER_CALL = 25 # Upper bound on auto-categorize / merchant batches
# OpenAI-compatible capability flags (custom/self-hosted providers)
# OPENAI_REQUEST_TIMEOUT = 60 # HTTP timeout in seconds; raise for slow local models
# OPENAI_EXTRA_HEADERS = '{"x-opencode-session":"{session_id}"}' # Extra JSON headers for chat requests; value containing {session_id} becomes the chat UUID
# AI_RESPONSE_TIMEOUT = 90 # Whole-turn budget: (1 + iterations) * OPENAI_REQUEST_TIMEOUT + tool time + queue wait
# ASSISTANT_MAX_TOOL_CALL_ITERATIONS = 5 # Chained tool calls per turn; a turn costs up to (1 + this) model calls
# OPENAI_SUPPORTS_PDF_PROCESSING = true # Set to false for endpoints without vision support
# OPENAI_SUPPORTS_RESPONSES_ENDPOINT = # true to force Responses API on custom providers
# LLM_JSON_MODE = # auto | strict | json_object | none
# Document-search vector store. Local OpenAI-compatible chat endpoints usually
# do not implement OpenAI's hosted /v1/vector_stores API, so use pgvector and a
# separate embeddings endpoint when testing local AI.
# VECTOR_STORE_PROVIDER = pgvector
# EMBEDDING_URI_BASE = http://host.docker.internal:11434/v1
# EMBEDDING_MODEL = mxbai-embed-large
# EMBEDDING_DIMENSIONS = 1024 # Must match the embedding model
# EMBEDDING_ACCESS_TOKEN = # Optional; falls back to OPENAI_ACCESS_TOKEN
# AI_HEALTH_PROBE_TIMEOUT = 5 # Per-request timeout for admin live checks
# AI_HEALTH_PROBE_CACHE_TTL = 60 # Cache live results and deduplicate failure logs
# (example: LM Studio/Docker config) OpenAI-compatible API endpoint config
# OPENAI_URI_BASE = http://host.docker.internal:1234/
# OPENAI_MODEL = qwen/qwen3-vl-4b
# OpenID Connect for development
OIDC_CLIENT_ID=
OIDC_CLIENT_SECRET=
OIDC_ISSUER=
OIDC_REDIRECT_URI=http://localhost:3000/auth/openid_connect/callback
# WebAuthn / passkey MFA development defaults
# RP ID must match the domain where credentials are registered.
WEBAUTHN_RP_ID=localhost
WEBAUTHN_ALLOWED_ORIGINS=http://localhost:3000
# Langfuse config
LANGFUSE_PUBLIC_KEY =
LANGFUSE_SECRET_KEY =
LANGFUSE_HOST = https://cloud.langfuse.com
# Set to `true` to get error messages rendered in the /chats UI
AI_DEBUG_MODE =
# Apple Push Notification service (AI insight notifications)
# APNS_PRIVATE_KEY_BASE64 is the base64-encoded contents of the Apple .p8 key.
APNS_KEY_ID=
APNS_TEAM_ID=
APNS_BUNDLE_ID=
APNS_PRIVATE_KEY_BASE64=
# =============================================================================
# SSL/TLS Configuration for Self-Signed Certificates
# =============================================================================
# Use these settings when connecting to services with self-signed or internal
# CA certificates (e.g., self-hosted Keycloak, Authentik, or AI endpoints).
#
# SSL_CA_FILE: Path to custom CA certificate file (PEM format)
# - The certificate that signed your server's SSL certificate
# - Must be readable by the application
# - Will be validated at startup
# SSL_CA_FILE = /certs/my-ca.crt
#
# SSL_VERIFY: Enable/disable SSL certificate verification
# - Default: true (verification enabled)
# - Set to "false" ONLY for development/testing
# - WARNING: Disabling removes protection against man-in-the-middle attacks
# SSL_VERIFY = true
#
# SSL_DEBUG: Enable verbose SSL logging for troubleshooting
# - Default: false
# - When enabled, logs detailed SSL connection information
# - Useful for diagnosing certificate issues
# SSL_DEBUG = false
#
# Example docker-compose.yml configuration:
# services:
# app:
# environment:
# SSL_CA_FILE: /certs/my-ca.crt
# SSL_DEBUG: "true"
# volumes:
# - ./my-ca.crt:/certs/my-ca.crt:ro
# Active Storage Configuration
# ACTIVE_STORAGE_SERVICE=google
# GCS_PROJECT=
# GCS_BUCKET=
# GCS_KEYFILE_JSON=
# GCS_KEYFILE=
# Skylight
# ========
SKYLIGHT_AUTHENTICATION=
SKYLIGHT_ENABLED=