Cogitator
Getting Started

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-text

Load 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.yml in the current working directory (none is fine)
  • Substitutes ${VAR} references in the YAML
  • Merges environment variables over the file (see below)
  • Merges overrides over 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

SectionWhat it configures
llmdefaultProvider, defaultModel, providers, retry, promptCache, plugins
promptsautoDeployWinner for prompt A/B tests
limitsmaxConcurrentRuns, defaultTimeout (ms), maxTokensPerRun — see Cogitator
memoryAdapter, its connection, embedding and contextBuilder
sandboxDefaults, container pool, Docker and WASM settings for sandboxed tools
reflectionReflection after tool calls, errors or at the end of a run
guardrailsConstitutional AI input/output/tool filtering
costRoutingCost-aware model routing and budgets
securitypromptInjection detection and pii masking (Security)
contextContext compression for long conversations
deployTarget, server, port, registry and resources for cogitator deploy (Deploy)
logginglevel, 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()):

adapterSettings
memoryinMemory.maxEntries (optional)
redisredis.url, or redis.host + redis.port, or redis.cluster.nodes; keyPrefix, ttl, password
postgrespostgres.connectionString, schema, poolSize
sqlitesqlite.path (':memory:' for an in-memory database), walMode
mongodbmongodb.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.

cogitator.yml
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: true

memory.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.

cogitator.yml
logging:
  level: warn
  destination: file
  filePath: ./logs/cogitator.log

Without 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:

VariableConfig path
COGITATOR_LLM_DEFAULT_PROVIDERllm.defaultProvider
COGITATOR_LLM_DEFAULT_MODELllm.defaultModel
COGITATOR_OLLAMA_BASE_URL, COGITATOR_OLLAMA_API_KEYllm.providers.ollama.*
COGITATOR_OPENAI_API_KEY, COGITATOR_OPENAI_BASE_URLllm.providers.openai.*
COGITATOR_ANTHROPIC_API_KEYllm.providers.anthropic.apiKey
COGITATOR_GOOGLE_API_KEYllm.providers.google.apiKey
COGITATOR_AZURE_API_KEY, _ENDPOINT, _API_VERSION, _DEPLOYMENTllm.providers.azure.*
COGITATOR_BEDROCK_REGION, _ACCESS_KEY_ID, _SECRET_ACCESS_KEYllm.providers.bedrock.*
COGITATOR_VLLM_BASE_URLllm.providers.vllm.baseUrl
COGITATOR_MISTRAL_API_KEY, _GROQ_API_KEY, _TOGETHER_API_KEY, _DEEPSEEK_API_KEYllm.providers.<provider>.apiKey
COGITATOR_LIMITS_MAX_CONCURRENT_RUNS, _DEFAULT_TIMEOUT, _MAX_TOKENS_PER_RUNlimits.* (digits only)
COGITATOR_DEPLOY_TARGET, _PORT, _REGISTRYdeploy.*

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=xxx

Azure 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 $:

cogitator.yml
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 server

OpenAI

llm:
  providers:
    openai:
      apiKey: sk-xxx
      baseUrl: https://api.openai.com/v1 # optional, for proxies
      api: responses # optional: responses | chat-completions

The 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-xxx

Google Gemini

llm:
  providers:
    google:
      apiKey: xxx

Azure OpenAI

llm:
  providers:
    azure:
      apiKey: xxx
      endpoint: https://your-resource.openai.azure.com
      apiVersion: '2024-02-15-preview' # optional
      deployment: gpt-6.1-sol # optional

AWS Bedrock

llm:
  providers:
    bedrock:
      region: us-east-1 # optional, can use AWS config chain
      accessKeyId: xxx # optional, uses AWS credentials chain
      secretAccessKey: xxx # optional

Bedrock 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/v1

Retries

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 once

Prompt 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 off

PII 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 search

Sandbox

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 next

sandbox.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):

  1. Programmatic overrides passed to loadConfig({ overrides })
  2. Environment variables (COGITATOR_* and standard provider vars)
  3. YAML config file values
  4. Built-in defaults

Objects merge key by key; arrays and scalar values replace each other.

On this page