Configuration
Configure Cogitator with YAML files, environment variables, and provider settings.
Configuration File
Create a cogitator.yml in your project root:
llm:
defaultProvider: ollama
defaultModel: ollama/llama3.2
providers:
ollama:
baseUrl: http://localhost:11434
openai:
apiKey: ${OPENAI_API_KEY}
anthropic:
apiKey: ${ANTHROPIC_API_KEY}
google:
apiKey: ${GOOGLE_API_KEY}
limits:
maxConcurrentRuns: 10
defaultTimeout: 120000
memory:
adapter: redis
redis:
url: redis://localhost:6379
embedding:
provider: ollama
model: nomic-embed-textLoad it with @cogitator-ai/config:
import { Cogitator } from '@cogitator-ai/core';
import { loadConfig } from '@cogitator-ai/config';
const config = loadConfig();
const cogitator = new Cogitator(config);loadConfig():
- Reads the first of
cogitator.yaml,cogitator.yml,.cogitator.yaml,.cogitator.ymlin the current working directory (none is fine) - Substitutes
${VAR}references in the YAML - Merges environment variables over the file (see below)
- Merges
overridesover both - Validates the result against Zod schemas and throws
Invalid configuration: ...listing the offending fields
const config = loadConfig({
configPath: './config/cogitator.yml', // must exist, or loadConfig throws
skipEnv: false, // true: ignore environment variables
skipYaml: false, // true: ignore config files
overrides: { limits: { maxConcurrentRuns: 4 } },
});loadConfig() does not read .env files. Load them first with process.loadEnvFile() (Node 20.12+), or parse one with loadDotenvFile(path) / parseDotenv(text) from the same package.
Top-Level Sections
| Section | What it configures |
|---|---|
llm | defaultProvider, defaultModel, providers, retry, promptCache, plugins |
prompts | autoDeployWinner for prompt A/B tests |
limits | maxConcurrentRuns, defaultTimeout (ms), maxTokensPerRun — see Cogitator |
memory | Adapter, its connection, embedding and contextBuilder |
sandbox | Defaults, container pool, Docker and WASM settings for sandboxed tools |
reflection | Reflection after tool calls, errors or at the end of a run |
guardrails | Constitutional AI input/output/tool filtering |
costRouting | Cost-aware model routing and budgets |
security | promptInjection detection and pii masking (Security) |
context | Context compression for long conversations |
deploy | Target, server, port, registry and resources for cogitator deploy (Deploy) |
logging | level, format, destination, filePath of the process-wide logger (see below) |
Memory
memory.adapter names the thread store, with its settings under the key of the same name. The runtime builds and connects it on the first run (or on getMemory()):
adapter | Settings |
|---|---|
memory | inMemory.maxEntries (optional) |
redis | redis.url, or redis.host + redis.port, or redis.cluster.nodes; keyPrefix, ttl, password |
postgres | postgres.connectionString, schema, poolSize |
sqlite | sqlite.path (':memory:' for an in-memory database), walMode |
mongodb | mongodb.uri, database, collectionPrefix |
When the section lacks what the store needs (for example adapter: sqlite without sqlite.path), or the connection fails, the runtime logs a warning, runs without memory and tries again on the next run. adapter: qdrant only logs a warning (see below). The Postgres store sizes its vector column to the embedding model in memory.embedding.
qdrant is not a thread store: it holds embeddings. Pick one of the stores above as adapter and add memory.qdrant next to memory.embedding and memory.contextBuilder; the context builder then searches Qdrant for semantic context. The runtime only searches it — add embeddings with a QdrantAdapter of your own.
memory:
adapter: redis
redis:
host: localhost
port: 6379
qdrant:
url: http://localhost:6333
collection: cogitator
dimensions: 768
embedding:
provider: ollama
model: nomic-embed-text
contextBuilder:
strategy: hybrid
includeSemanticContext: truememory.qdrant.dimensions must match the embedding model; a mismatch is logged. memory.embedding.dimensions sets the vector size: OpenAI text-embedding-3-* and Google models return vectors of that size, and for Ollama it declares the size of a model the runtime does not know. memory.contextBuilder.graphContextOptions (maxNodes, maxEdges, maxDepth, includeInferred, entityTypes) shapes the knowledge-graph context. To use an adapter you built yourself, connect it and hand it over:
import { Cogitator } from '@cogitator-ai/core';
import { SQLiteAdapter } from '@cogitator-ai/memory';
const memory = new SQLiteAdapter({ provider: 'sqlite', path: './data/memory.db' });
const connected = await memory.connect();
if (!connected.success) throw new Error(connected.error);
const cogitator = new Cogitator({ llm: { defaultModel: 'ollama/llama3.2' } });
cogitator.memory = memory;Logging
logging configures the process-wide logger that getLogger() returns, when a Cogitator is created with it; with several runtimes, the last one created with logging wins. level is debug, info (default), warn, error or silent. format is pretty (default) or json. destination: file appends one line per entry to filePath, as JSON unless format says otherwise; without filePath, or where there is no file system (edge runtimes), it logs to the console and warns.
logging:
level: warn
destination: file
filePath: ./logs/cogitator.logWithout logging, the logger logs at info (or LOG_LEVEL), as JSON when NODE_ENV=production and pretty-printed otherwise. In code, build the same logger with createLoggerFromConfig() or your own with createLogger(), and install it with setLogger():
import { createLogger, createLoggerFromConfig, setLogger } from '@cogitator-ai/core';
setLogger(createLoggerFromConfig({ level: 'debug', destination: 'file', filePath: './debug.log' }));
setLogger(createLogger({ level: 'debug', format: 'json' }));Environment Variables
Cogitator reads environment variables with the COGITATOR_ prefix:
| Variable | Config path |
|---|---|
COGITATOR_LLM_DEFAULT_PROVIDER | llm.defaultProvider |
COGITATOR_LLM_DEFAULT_MODEL | llm.defaultModel |
COGITATOR_OLLAMA_BASE_URL, COGITATOR_OLLAMA_API_KEY | llm.providers.ollama.* |
COGITATOR_OPENAI_API_KEY, COGITATOR_OPENAI_BASE_URL | llm.providers.openai.* |
COGITATOR_ANTHROPIC_API_KEY | llm.providers.anthropic.apiKey |
COGITATOR_GOOGLE_API_KEY | llm.providers.google.apiKey |
COGITATOR_AZURE_API_KEY, _ENDPOINT, _API_VERSION, _DEPLOYMENT | llm.providers.azure.* |
COGITATOR_BEDROCK_REGION, _ACCESS_KEY_ID, _SECRET_ACCESS_KEY | llm.providers.bedrock.* |
COGITATOR_VLLM_BASE_URL | llm.providers.vllm.baseUrl |
COGITATOR_MISTRAL_API_KEY, _GROQ_API_KEY, _TOGETHER_API_KEY, _DEEPSEEK_API_KEY | llm.providers.<provider>.apiKey |
COGITATOR_LIMITS_MAX_CONCURRENT_RUNS, _DEFAULT_TIMEOUT, _MAX_TOKENS_PER_RUN | limits.* (digits only) |
COGITATOR_DEPLOY_TARGET, _PORT, _REGISTRY | deploy.* |
Standard provider variables work too; a COGITATOR_* variable wins over its standard counterpart:
OPENAI_API_KEY=sk-xxx
OPENAI_BASE_URL=https://proxy.example.com/v1
ANTHROPIC_API_KEY=sk-ant-xxx
GOOGLE_API_KEY=xxx # or GEMINI_API_KEY
OLLAMA_URL=http://localhost:11434 # or OLLAMA_HOST (scheme optional)
OLLAMA_API_KEY=xxx # Ollama Cloud; URL defaults to https://ollama.com unless set in env or YAML
AZURE_OPENAI_API_KEY=xxx # with AZURE_OPENAI_ENDPOINT, optional AZURE_OPENAI_DEPLOYMENT
AWS_REGION=us-east-1 # AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY for Bedrock
MISTRAL_API_KEY=xxx
GROQ_API_KEY=xxx
TOGETHER_API_KEY=xxx
DEEPSEEK_API_KEY=xxxAzure is configured from the environment only when both the key and the endpoint are set.
Referencing Environment Variables in YAML
String values in cogitator.yml may reference environment variables. ${VAR} becomes the value (or an empty string), ${VAR:-default} falls back when the variable is unset or empty, ${VAR-default} only when unset, and $$ is a literal $:
llm:
providers:
openai:
apiKey: ${OPENAI_API_KEY}
baseUrl: ${OPENAI_BASE_URL:-https://api.openai.com/v1}Substituted values are always strings, so numeric fields (for example deploy.port) must be written literally.
Programmatic Configuration
You can skip the YAML file and validate a config object directly:
import { Cogitator } from '@cogitator-ai/core';
import { defineConfig } from '@cogitator-ai/config';
const config = defineConfig({
llm: {
defaultModel: 'openai/gpt-6.1-sol',
providers: {
openai: {
apiKey: process.env.OPENAI_API_KEY!,
},
},
},
memory: {
adapter: 'postgres',
postgres: {
connectionString: process.env.DATABASE_URL!,
},
embedding: {
provider: 'openai',
apiKey: process.env.OPENAI_API_KEY!,
model: 'text-embedding-3-small',
},
},
});
const cogitator = new Cogitator(config);defineConfig() validates like loadConfig() but reads neither files nor the environment. Neither of them is required: new Cogitator({...}) takes the same object without validation.
Options that hold functions, stores or backends exist only in code: llm.backends, llm.retry.onRetry, runCheckpoints, prompts.versions / prompts.abTests / prompts.score, security.promptInjection.llmBackend, and callbacks such as security.promptInjection.onThreat, security.pii.onDetect and guardrails.onToolApproval. Plain-data options, including llm.plugins and prompts.autoDeployWinner, can come from YAML. Add the code-only options next to the loaded config:
const config = loadConfig();
const cogitator = new Cogitator({
...config,
prompts: { ...config.prompts, score: (result) => (result.output.length > 0 ? 1 : 0) },
});Provider Configuration
Ollama (Local)
llm:
providers:
ollama:
baseUrl: http://localhost:11434 # default; https://ollama.com when only apiKey is set
apiKey: xxx # optional, Ollama Cloud or an authenticated serverOpenAI
llm:
providers:
openai:
apiKey: sk-xxx
baseUrl: https://api.openai.com/v1 # optional, for proxies
api: responses # optional: responses | chat-completionsThe official endpoint uses the OpenAI Responses API by default; a baseUrl pointing at another OpenAI-compatible server uses Chat Completions. Set api to force either wire API. Requests with stop sequences always go through Chat Completions.
Anthropic
llm:
providers:
anthropic:
apiKey: sk-ant-xxxGoogle Gemini
llm:
providers:
google:
apiKey: xxxAzure OpenAI
llm:
providers:
azure:
apiKey: xxx
endpoint: https://your-resource.openai.azure.com
apiVersion: '2024-02-15-preview' # optional
deployment: gpt-6.1-sol # optionalAWS Bedrock
llm:
providers:
bedrock:
region: us-east-1 # optional, can use AWS config chain
accessKeyId: xxx # optional, uses AWS credentials chain
secretAccessKey: xxx # optionalBedrock needs the optional @aws-sdk/client-bedrock-runtime package.
OpenAI-Compatible Providers
llm:
providers:
mistral:
apiKey: xxx
groq:
apiKey: xxx
together:
apiKey: xxx
deepseek:
apiKey: xxx
vllm:
baseUrl: http://localhost:8000/v1Retries
Failed LLM calls (rate limits, 5xx, timeouts, dropped connections) are retried with exponential backoff and the provider's Retry-After. The defaults are below; retry: false turns retries off. See LLM Backends for how streams and cancellation are handled.
llm:
retry:
maxRetries: 2 # attempts after the first one
baseDelay: 1000 # ms before the first retry, doubled each time
maxDelay: 30000 # backoff cap, ms
maxRetryAfter: 60000 # a longer Retry-After fails the call at oncePrompt Caching
Agent runs cache their prompt by default (see LLM Backends):
llm:
promptCache:
ttl: 1h # Anthropic keeps the cache for an hour instead of 5 minutes
# promptCache: false — turn it offPII Masking
Keep emails, phones, card numbers and other personal data away from the provider (see Security):
security:
pii:
mode: mask # mask | redact | block
detect: [email, phone, credit_card, iban, ssn, ip_address, api_key]
custom:
- type: customer_id
pattern: 'CUS-\d{6}'Prompt Injection
Detection settings live under security.promptInjection (see Security). patterns are regular expression sources, compiled without flags, so they are case-sensitive; pass RegExp objects in code for flags:
security:
promptInjection:
action: block # block | warn | log
threshold: 0.7
patterns:
- '[Rr]eveal (the|your) system prompt'
failMode: secure # secure (default): a throwing classifier fails the check; open: the input passes
allowlist:
- ignore the previous searchSandbox
Settings for sandboxed tools:
sandbox:
allowNativeFallback: false # default true: Docker tools run on the host when Docker is unavailable
defaults:
image: cogitator/sandbox:base
env:
PYTHONUNBUFFERED: '1'
mounts:
- source: /srv/data
target: /workspace/data
readOnly: true
pool:
maxSize: 5
reuseContainers: false # default; true reuses containers, so one execution's files reach the nextsandbox.defaults also takes wasmModule, wasmFunction and wasi for WASM tools. A custom constitution for guardrails can be written in YAML too, under guardrails.constitution (id, name, version, customizable, strictMode and principles).
Config Merging
Configs are deep-merged with this priority (highest wins):
- Programmatic overrides passed to
loadConfig({ overrides }) - Environment variables (
COGITATOR_*and standard provider vars) - YAML config file values
- Built-in defaults
Objects merge key by key; arrays and scalar values replace each other.