Skip to main content

Constructor parameters

The ExuluContext constructor accepts a configuration object with the following parameters:
const context = new ExuluContext({
  id: string,
  name: string,
  description: string,
  active: boolean,
  fields: ExuluContextFieldDefinition[],
  embedder?: ExuluEmbedder,
  processor?: ExuluContextProcessor,
  sources: ExuluContextSource[],
  rateLimit?: RateLimiterRule,
  queryRewriter?: (query: string) => Promise<string>,
  resultReranker?: (results: any[]) => Promise<any[]>,
  configuration?: { /* ... */ }
});

Required parameters

id

id
string
required
Unique identifier for the context. Must be a valid PostgreSQL identifier: start with a letter or underscore, contain only letters, digits, or underscores, 5-80 characters long.
id: "product_docs"
The ID is used for database table names and should not change after creation. Changing the ID requires database migration.

name

name
string
required
Human-readable name displayed in UI and logs
name: "Product Documentation"

description

description
string
required
Description of what information this context contains. Used in the auto-generated tool description for agents.
description: "Product documentation, help articles, and tutorials for end users"

active

active
boolean
required
Whether this context is active and available for use
active: true

fields

fields
ExuluContextFieldDefinition[]
required
Array of field definitions that define the schema for items in this context
fields: [
  {
    name: "title",
    type: "text",
    required: true,
    editable: true
  },
  {
    name: "content",
    type: "text",
    required: true
  },
  {
    name: "category",
    type: "text",
    index: true,
    enumValues: ["guide", "reference", "tutorial"]
  }
]

Field definition properties

fields[].name
string
required
Field name (must be valid PostgreSQL column name)
fields[].type
ExuluFieldTypes
required
Field type: text, number, boolean, date, json, file, longtext, decimal, integer, timestamp
fields[].required
boolean
Whether this field is required when creating items
fields[].editable
boolean
Whether this field can be edited after creation
fields[].unique
boolean
Whether this field must have unique values
fields[].index
boolean
Whether to create a database index on this field for faster queries
fields[].default
any
Default value for this field
fields[].calculated
boolean
Whether this field is calculated/computed rather than user-provided
fields[].enumValues
string[]
For text fields, restrict values to this list of allowed values
fields[].allowedFileTypes
allFileTypes[]
For file fields, restrict allowed file types (e.g., ["pdf", "docx", "txt"])

sources

sources
ExuluContextSource[]
required
Array of data sources that populate this context
sources: [
  {
    id: "github_issues",
    name: "GitHub Issues",
    description: "Syncs issues from GitHub repository",
    config: {
      schedule: "0 */6 * * *", // Every 6 hours
      queue: await ExuluQueues.register("github_sync"),
      retries: 3,
      backoff: {
        type: "exponential",
        delay: 2000
      }
    },
    execute: async ({ exuluConfig }) => {
      const issues = await fetchGitHubIssues();
      return issues.map(issue => ({
        external_id: issue.id,
        name: issue.title,
        content: issue.body,
        metadata: { labels: issue.labels }
      }));
    }
  }
]

Source configuration

sources[].id
string
required
Unique identifier for the source
sources[].name
string
required
Human-readable source name
sources[].description
string
required
Description of what this source provides
sources[].config.schedule
string
Cron expression for scheduled execution (e.g., "0 * * * *" for hourly)
sources[].config.queue
Promise<ExuluQueueConfig>
Queue configuration for background processing
sources[].config.retries
number
Number of retry attempts on failure (default: 3)
sources[].config.backoff
object
Backoff strategy for retries: { type: "exponential" | "linear", delay: number }
sources[].execute
function
required
Async function that fetches and returns items from the source

Optional parameters

embedder

embedder
ExuluEmbedder
Embedder instance that generates vector embeddings for items
import { ExuluEmbedder } from "@exulu/backend";

embedder: new ExuluEmbedder({
  id: "openai_embedder",
  name: "OpenAI Embeddings",
  provider: "openai",
  model: "text-embedding-3-small",
  vectorDimensions: 1536,
  template: "{{title}}\n\n{{content}}"
})
Without an embedder, semantic search is not available. You can still use the context for structured data storage and retrieval.

processor

processor
ExuluContextProcessor
Processor that transforms items before storage or embeddings generation
processor: {
  name: "PDF Text Extractor",
  config: {
    trigger: "onInsert", // or "onUpdate", "always", "manual"
    generateEmbeddings: true, // Auto-trigger embeddings after processing
    queue: await ExuluQueues.register("processing"),
    timeoutInSeconds: 300,
    retries: 2,
    backoff: {
      type: "exponential",
      delay: 2000
    }
  },
  filter: async ({ item, user }) => {
    // Optional: Filter which items to process
    return item.document_s3key !== undefined;
  },
  execute: async ({ item, utils, exuluConfig }) => {
    // Transform the item
    const text = await utils.storage.extractText(item.document_s3key);
    return {
      ...item,
      content: text,
      textlength: text.length
    };
  }
}

rateLimit

rateLimit
RateLimiterRule
Rate limiting rules for API access to this context
rateLimit: {
  points: 100,      // Number of requests
  duration: 60,     // Time window in seconds
  blockDuration: 60 // Block duration after exceeding limit
}

queryRewriter

queryRewriter
(query: string) => Promise<string>
Function that rewrites queries before search to improve results
queryRewriter: async (query: string) => {
  // Use an LLM to expand or clarify the query
  const response = await openai.chat.completions.create({
    model: "gpt-4",
    messages: [
      {
        role: "system",
        content: "Expand this query to improve search results"
      },
      {
        role: "user",
        content: query
      }
    ]
  });
  return response.choices[0].message.content;
}

resultReranker

resultReranker
(results: any[]) => Promise<any[]>
Function that reranks search results based on custom logic or a reranker model
resultReranker: async (results) => {
  // Use a reranker to improve result ordering
  const reranked = await cohereReranker.rerank({
    query: results[0].query,
    documents: results.map(r => r.chunk_content)
  });

  return reranked.results.map(r => results[r.index]);
}

Configuration object

configuration
object
Additional configuration options for search behavior

calculateVectors

configuration.calculateVectors
'manual' | 'onUpdate' | 'onInsert' | 'always'
When to automatically generate embeddings for items (default: "manual")
configuration: {
  calculateVectors: "onInsert" // Generate embeddings when items are created
}
Never auto-generate embeddings. You must call embeddings.generate.one() or embeddings.generate.all() manually.

maxRetrievalResults

configuration.maxRetrievalResults
number
Maximum number of results to return from search queries (default: 10)
configuration: {
  maxRetrievalResults: 20
}

defaultRightsMode

configuration.defaultRightsMode
'private' | 'public' | 'restricted'
Default access control mode for new items (default: "private")
configuration: {
  defaultRightsMode: "public"
}

enableAsTool

configuration.enableAsTool
boolean
Whether to automatically expose this context as a tool for agents (default: true)
configuration: {
  enableAsTool: true // Agents can query this context
}
When enabled, agents receive a tool called {context_name}_context_search that they can use to query this context.

cutoffs

configuration.cutoffs
object
Minimum relevance scores for different search methods
configuration: {
  cutoffs: {
    cosineDistance: 0.7,  // Min similarity for semantic search (0-1)
    tsvector: 0.5,        // Min score for keyword search
    hybrid: 0.6           // Min score for hybrid search
  }
}
Results below these thresholds are filtered out.

expand

configuration.expand
object
Number of surrounding chunks to include with each result
configuration: {
  expand: {
    before: 1, // Include 1 chunk before the matched chunk
    after: 2   // Include 2 chunks after the matched chunk
  }
}
This provides more context around matched chunks, improving the agent’s understanding.

language

configuration.language
'english' | 'german'
Language for full-text search indexing (default: "english")
configuration: {
  language: "german" // Use German text search configuration
}
This affects stemming and stop words in keyword search.

Complete example

import {
  ExuluContext,
  ExuluEmbedder,
  ExuluQueues
} from "@exulu/backend";

const embedder = new ExuluEmbedder({
  id: "openai_ada",
  name: "OpenAI Ada",
  provider: "openai",
  model: "text-embedding-3-small",
  vectorDimensions: 1536,
  template: "Title: {{title}}\nCategory: {{category}}\n\n{{content}}"
});

const docsContext = new ExuluContext({
  id: "product_docs",
  name: "Product Documentation",
  description: "Comprehensive product docs including guides, API references, and tutorials",
  active: true,

  fields: [
    {
      name: "title",
      type: "text",
      required: true,
      index: true
    },
    {
      name: "content",
      type: "longtext",
      required: true
    },
    {
      name: "category",
      type: "text",
      enumValues: ["guide", "api", "tutorial", "reference"],
      index: true
    },
    {
      name: "url",
      type: "text",
      unique: true
    },
    {
      name: "last_updated",
      type: "timestamp"
    },
    {
      name: "metadata",
      type: "json"
    }
  ],

  embedder: embedder,

  processor: {
    name: "Markdown Processor",
    config: {
      trigger: "onInsert",
      generateEmbeddings: true,
      queue: await ExuluQueues.register("doc_processing")
    },
    execute: async ({ item }) => {
      // Process markdown, extract headings, etc.
      const processed = processMarkdown(item.content);
      return {
        ...item,
        content: processed.text,
        metadata: { headings: processed.headings }
      };
    }
  },

  sources: [
    {
      id: "github_docs",
      name: "GitHub Documentation",
      description: "Sync docs from GitHub repository",
      config: {
        schedule: "0 */4 * * *", // Every 4 hours
        queue: await ExuluQueues.register("github_sync"),
        retries: 3,
        backoff: {
          type: "exponential",
          delay: 2000
        }
      },
      execute: async ({ exuluConfig }) => {
        const docs = await fetchGitHubDocs();
        return docs.map(doc => ({
          external_id: doc.path,
          name: doc.title,
          content: doc.content,
          category: doc.category,
          url: doc.url,
          last_updated: doc.updatedAt
        }));
      }
    }
  ],

  rateLimit: {
    points: 100,
    duration: 60,
    blockDuration: 60
  },

  queryRewriter: async (query) => {
    // Expand query with LLM
    return expandQuery(query);
  },

  resultReranker: async (results) => {
    // Rerank with Cohere
    return rerankResults(results);
  },

  configuration: {
    calculateVectors: "onInsert",
    maxRetrievalResults: 15,
    defaultRightsMode: "public",
    enableAsTool: true,
    cutoffs: {
      cosineDistance: 0.65,
      tsvector: 0.5,
      hybrid: 0.6
    },
    expand: {
      before: 1,
      after: 2
    },
    language: "english"
  }
});

Field type reference

Short text field (VARCHAR). Use for titles, names, categories, etc.
Long text field (TEXT). Use for descriptions, articles, documentation content.
Floating point number (DOUBLE PRECISION)
Whole number (INTEGER)
Precise decimal number (DECIMAL)
True/false value (BOOLEAN)
Date without time (DATE)
Date with time (TIMESTAMP)
JSON object or array (JSONB)
S3 file reference. Creates a {field_name}_s3key column that stores the S3 object key.

Next steps