diff --git a/.gitignore b/.gitignore index e621dd3..969edff 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ database/target *.db-shm *.db-wal .env +# Server-local LLM config (llm.example.toml is the template) +/llm.toml # Server-local TTS pronunciation overrides (tts_pronunciations.example.json is the template) /tts_pronunciations.json /tmp diff --git a/llm.example.toml b/llm.example.toml index 5a5682c..c0429b4 100644 --- a/llm.example.toml +++ b/llm.example.toml @@ -1,6 +1,7 @@ # ImageApi LLM configuration. Copy to `llm.toml` next to `.env` (or point -# LLM_CONFIG_PATH at it). Every key is optional; omitted keys use the -# defaults shown in the comments. +# LLM_CONFIG_PATH at it). Every key is optional: delete any line to get its +# default. The values below are an example llama-swap setup, not the +# defaults; each key's default is in its trailing `default:` comment. # # Secrets stay in env: OPENROUTER_API_KEY enables the hybrid backend. # @@ -15,61 +16,61 @@ # Local stack for chat, vision describe and embeddings: "ollama" | "llamacpp". # Global on purpose: don't flip it without re-embedding (mixed vector spaces # break similarity search). -backend = "llamacpp" +backend = "llamacpp" # default: "ollama" # Small model pinned for /photos/search/unified query translation, so it can -# stay loaded alongside CLIP. Unset = the client's selected model. -unified_search_model = "gemma4-e4b" +# stay loaded alongside CLIP. +unified_search_model = "gemma4-e4b" # default: unset (the client's selected model) [llamacpp] -url = "http://localhost:9292/v1" # unset = llama.cpp client + TTS disabled -chat = "qwen3.8-27b-abliterated" # default chat model (default "chat") -vision = "qwen3.8-27b-code-vision" # describe_photo model (default: chat) -timeout_secs = 180 +url = "http://localhost:9292/v1" # default: unset (llama.cpp client + TTS disabled) +chat = "qwen3.8-27b-abliterated" # default: "chat" +vision = "qwen3.8-27b-code-vision" # default: unset (uses `chat`); describe_photo model +timeout_secs = 180 # default: 180 # The picker shows every llama-swap model with `kind: chat` metadata (param # aliases like `:sub` / `:fast` are always hidden), minus `exclude`. # `include`, if set, is a strict allowlist instead. [llamacpp.picker] -exclude = ["nemotron3.5-lightning-30b-A3B"] -# include = ["qwen3.8-27b-code", "gemma4-31"] +exclude = ["nemotron3.5-lightning-30b-A3B"] # default: [] +# include = ["qwen3.8-27b-code", "gemma4-31"] # default: unset (show everything) [embedding] # Must all agree with what the stored corpus was embedded with. Changing any # of them means `cargo run --bin reembed_embeddings`. -model = "embed" # llama-swap id (Ollama always uses nomic-embed-text:v1.5) -dim = 768 # 768 nomic v1.5 | 1024 Qwen3-Embedding-0.6B -# Prefixes default to empty. Set them only together with a re-embed; a -# corpus embedded without them won't match queries embedded with them. -query_prefix = "" # nomic v1.5: "search_query: " | Qwen3: "Instruct: \nQuery: " -document_prefix = "" # nomic v1.5: "search_document: " | Qwen3: "" +model = "embed" # default: "embed" (llama-swap id; Ollama always uses nomic-embed-text:v1.5) +dim = 768 # default: 768 (nomic v1.5) | 1024 for Qwen3-Embedding-0.6B +# Set prefixes only together with a re-embed; a corpus embedded without them +# won't match queries embedded with them. +query_prefix = "" # default: "" | nomic v1.5: "search_query: " | Qwen3: "Instruct: \nQuery: " +document_prefix = "" # default: "" | nomic v1.5: "search_document: " | Qwen3: "" [tts] # Chatterbox behind llama-swap; needs llamacpp.url but not backend = "llamacpp". -model = "chatterbox" -voice = "m" # default voice when a request omits one (unset = server default) -ref_seconds = 30 # max voice-clone reference clip length -timeout_secs = 600 # long chunked text takes minutes +model = "chatterbox" # default: "chatterbox" +voice = "m" # default: unset (server default); used when a request omits one +ref_seconds = 30 # default: 30; max voice-clone reference clip length +timeout_secs = 600 # default: 600; long chunked text takes minutes [ollama] -primary_url = "http://localhost:11434" -primary_model = "nemotron-3-nano:30b" -# Tried on connection failure. fallback_model unset = reuse primary_model. -# fallback_url = "http://server:11434" -# fallback_model = "llama3.2:3b" -timeout_secs = 120 +primary_url = "http://localhost:11434" # default: "http://localhost:11434" +primary_model = "nemotron-3-nano:30b" # default: "nemotron-3-nano:30b" +# Tried on connection failure. +# fallback_url = "http://server:11434" # default: unset (no fallback) +# fallback_model = "llama3.2:3b" # default: unset (reuse primary_model) +timeout_secs = 120 # default: 120 [openrouter] -default_model = "anthropic/claude-sonnet-4" -allowed = ["openai/gpt-4o-mini", "anthropic/claude-haiku-4-5", "google/gemini-2.5-flash"] -max_tokens = 16384 # 0 = uncapped (OpenRouter reserves credit against it) -# base_url = "https://openrouter.ai/api/v1" -# embedding_model = "openai/text-embedding-3-small" -# http_referer = "https://your-site.example" -# app_title = "ImageApi" +default_model = "anthropic/claude-sonnet-4" # default: "anthropic/claude-sonnet-4" +allowed = ["openai/gpt-4o-mini", "anthropic/claude-haiku-4-5", "google/gemini-2.5-flash"] # default: [] +max_tokens = 16384 # default: 16384; 0 = uncapped (OpenRouter reserves credit against it) +# base_url = "https://openrouter.ai/api/v1" # default: unset (OpenRouter's public API) +# embedding_model = "openai/text-embedding-3-small" # default: unset +# http_referer = "https://your-site.example" # default: unset +# app_title = "ImageApi" # default: unset [agentic] -max_iterations = 12 # tool-loop cap for agentic insight generation -chat_max_iterations = 6 # per chat turn; per-request values are clamped to it -chat_default_num_ctx = 32768 # assumed window when neither request nor backend reports one -chat_turn_timeout_secs = 300 # reconnectable chat turns are dropped after this +max_iterations = 12 # default: 12; tool-loop cap for agentic insight generation +chat_max_iterations = 6 # default: 6; per chat turn, per-request values are clamped to it +chat_default_num_ctx = 32768 # default: 32768; assumed window when neither request nor backend reports one +chat_turn_timeout_secs = 300 # default: 300; reconnectable chat turns are dropped after this