Skip to content
Last updated

Understanding the database structure behind Freddy assistants helps you work effectively with the API and understand how data is organized.

Overview

Assistants use a normalized database design where data is split across multiple related tables. This approach provides:

  • Data Integrity: Foreign key constraints ensure consistency
  • Flexibility: Easy to add new features without restructuring
  • Performance: Efficient queries with proper indexing
  • Maintainability: Clear separation of concerns

Core Tables

1. assistants (Main Table)

The primary table containing core assistant information.

CREATE TABLE assistants (
 id VARCHAR(255) PRIMARY KEY, -- asst_{uuid}
 name VARCHAR(255) NOT NULL,
 description TEXT,
 instructions TEXT, -- System prompt
 model VARCHAR(100) NOT NULL, -- e.g., "gpt-4o"
 organization_id VARCHAR(255) NOT NULL,
 user_id VARCHAR(255), -- Creator
 is_public BOOLEAN DEFAULT false,
 context_strategy VARCHAR(50), -- "auto", "manual", etc.
 max_context_tokens INTEGER,
 temperature FLOAT,
 top_p FLOAT,
 created_at TIMESTAMP NOT NULL,
 updated_at TIMESTAMP NOT NULL,
 is_deleted BOOLEAN DEFAULT false,

 FOREIGN KEY (organization_id) REFERENCES organizations(id),
 FOREIGN KEY (user_id) REFERENCES users(id)
);

Key Fields:

  • id: Prefixed with asst_ for easy identification
  • instructions: The system prompt that defines behavior
  • model: Which AI model to use
  • organization_id: Always required (org-level or user-level)
  • user_id: Nullable (null = org-wide assistant)

2. assistant_tool_configurations

Stores tool configurations including system tools, MCP tools, and Streamline automations.

CREATE TABLE assistant_tool_configurations (
 id VARCHAR(255) PRIMARY KEY, -- atc_{uuid}
 assistant_id VARCHAR(255) UNIQUE NOT NULL,
 system_tools JSON NOT NULL DEFAULT '{}',
 mcp_tools JSON, -- Array of MCP config IDs
 streamline_tools JSON, -- Array of automation IDs

 FOREIGN KEY (assistant_id) REFERENCES assistants(id) ON DELETE CASCADE
);

Structure:

{
 "system_tools": {
 "file_search": {
 "enabled": true,
 "max_results": 10
 },
 "web_search": {
 "enabled": true,
 "safe_mode": true
 },
 "code_interpreter": {
 "enabled": false
 }
 },
 "mcp_tools": [
 "mcp_abc123", // MCP configuration IDs
 "mcp_def456"
 ],
 "streamline_tools": [
 "sauto_xyz789" // Streamline automation IDs
 ]
}

Relationship: One-to-one with assistants

3. assistant_rules

Links assistants to knowledge rules and guidelines.

CREATE TABLE assistant_rules (
 id VARCHAR(255) PRIMARY KEY, -- arule_{uuid}
 assistant_id VARCHAR(255) NOT NULL,
 rule_id VARCHAR(255) NOT NULL,
 order_index INTEGER, -- Display order
 created_at TIMESTAMP NOT NULL,

 FOREIGN KEY (assistant_id) REFERENCES assistants(id) ON DELETE CASCADE,
 FOREIGN KEY (rule_id) REFERENCES rules(id) ON DELETE CASCADE,
 UNIQUE (assistant_id, rule_id)
);

Relationship: Many-to-many between assistants and rules

4. assistant_reasoning

Configures how the assistant thinks and reasons.

CREATE TABLE assistant_reasoning (
 id VARCHAR(255) PRIMARY KEY, -- areason_{uuid}
 assistant_id VARCHAR(255) UNIQUE NOT NULL,
 enabled BOOLEAN DEFAULT false,
 reasoning_type VARCHAR(50), -- "chain_of_thought", "reflection"
 max_reasoning_tokens INTEGER,
 show_reasoning BOOLEAN DEFAULT false, -- Show to user?
 configuration JSON, -- Type-specific config

 FOREIGN KEY (assistant_id) REFERENCES assistants(id) ON DELETE CASCADE
);

Configuration Example:

{
 "reasoning_type": "chain_of_thought",
 "configuration": {
 "steps": 5,
 "verbosity": "detailed",
 "self_correction": true
 }
}

Relationship: One-to-one with assistants

5. assistant_json_schemas

Defines structured output formats for the assistant.

CREATE TABLE assistant_json_schemas (
 id VARCHAR(255) PRIMARY KEY, -- aschema_{uuid}
 assistant_id VARCHAR(255) NOT NULL,
 name VARCHAR(255) NOT NULL,
 description TEXT,
 schema JSON NOT NULL, -- JSON Schema definition
 is_active BOOLEAN DEFAULT true,
 created_at TIMESTAMP NOT NULL,

 FOREIGN KEY (assistant_id) REFERENCES assistants(id) ON DELETE CASCADE
);

Schema Example:

{
 "type": "object",
 "properties": {
 "summary": {"type": "string"},
 "sentiment": {"type": "string", "enum": ["positive", "negative", "neutral"]},
 "confidence": {"type": "number", "minimum": 0, "maximum": 1}
 },
 "required": ["summary", "sentiment"]
}

Relationship: One-to-many with assistants

6. assistant_vector_stores

Links assistants to vector stores for semantic search.

CREATE TABLE assistant_vector_stores (
 id VARCHAR(255) PRIMARY KEY, -- avs_{uuid}
 assistant_id VARCHAR(255) NOT NULL,
 vector_store_id VARCHAR(255) NOT NULL,
 created_at TIMESTAMP NOT NULL,

 FOREIGN KEY (assistant_id) REFERENCES assistants(id) ON DELETE CASCADE,
 FOREIGN KEY (vector_store_id) REFERENCES vector_stores(id) ON DELETE CASCADE,
 UNIQUE (assistant_id, vector_store_id)
);

Relationship: Many-to-many between assistants and vector_stores

7. assistant_rule_attachments

File attachments associated with assistant rules.

CREATE TABLE assistant_rule_attachments (
 id VARCHAR(255) PRIMARY KEY, -- aratt_{uuid}
 assistant_rule_id VARCHAR(255) NOT NULL,
 file_id VARCHAR(255) NOT NULL,
 created_at TIMESTAMP NOT NULL,

 FOREIGN KEY (assistant_rule_id) REFERENCES assistant_rules(id) ON DELETE CASCADE,
 FOREIGN KEY (file_id) REFERENCES files(id) ON DELETE CASCADE
);

Relationship: Many-to-many between assistant_rules and files

Entity Relationship Diagram

┌─────────────────┐
│ assistants │ (Main table)
└────────┬────────┘

 ├──────────────────────────────────────┐
 │ │
 │ 1:1 1:many│
 ▼ ▼
┌────────────────────────┐ ┌──────────────────┐
│assistant_tool_configs │ │ assistant_rules │
│ │ │ │
│ • system_tools │ │ • rule_id (FK) │
│ • mcp_tools [] │ │ • order_index │
│ • streamline_tools [] │ └──────────────────┘
└────────────────────────┘ │
 │ many:many
 │ 1:1 ▼
 ▼ ┌──────────────┐
┌────────────────────┐ │ rules │
│assistant_reasoning │ └──────────────┘
│ │
│ • reasoning_type │ ┌──────────────────────┐
│ • configuration │ │assistant_rule_attach │
└────────────────────┘ └──────────────────────┘

 │ 1:many │ many:many
 ▼ ▼
┌──────────────────────┐ ┌──────────────┐
│assistant_json_schemas│ │ files │
└──────────────────────┘ └──────────────┘

 │ many:many

┌──────────────────────┐
│assistant_vector_stores│
└──────────────────────┘

 │ many:many

┌──────────────────┐
│ vector_stores │
└──────────────────┘

MCP Tool Integration

MCP tools are stored as an array of MCP configuration IDs in assistant_tool_configurations.mcp_tools:

{
 "mcp_tools": [
 "mcp_abc123", // Custom MCP
 "mcp_def456", // Personal Connector (Google Drive)
 "mcp_ghi789" // Streamline Automation
 ]
}

The actual MCP configurations are stored in separate tables:

mcp_configurations (core config)
├── personal_connectors (OAuth integrations)
└── streamline_automations (workflow automations)

Querying Assistants

Get Assistant with All Configurations

from sqlalchemy import select
from sqlalchemy.orm import selectinload

# Eager load all relationships
stmt = select(Assistant).where(
 Assistant.id == assistant_id
).options(
 selectinload(Assistant.tool_configuration),
 selectinload(Assistant.reasoning),
 selectinload(Assistant.rules),
 selectinload(Assistant.vector_stores),
 selectinload(Assistant.json_schemas)
)

assistant = await session.execute(stmt).scalar_one()

Get Assistants with Active MCP Tools

from sqlalchemy import select, func

# Find assistants using specific MCP
stmt = select(Assistant).join(
 AssistantToolConfiguration
).where(
 func.jsonb_array_length(
 AssistantToolConfiguration.mcp_tools
 ) > 0
)

assistants = await session.execute(stmt).scalars().all()

Best Practices

DO

  • Use foreign keys for referential integrity
  • Soft delete with is_deleted flag
  • Index frequently queried fields (organization_id, user_id)
  • Use JSON for flexible configurations (system_tools, mcp_tools)
  • Eager load relationships when needed to avoid N+1 queries

DON'T

  • Don't store large files in JSON fields
  • Don't bypass foreign keys with manual ID management
  • **Don't use SELECT *** - specify needed columns
  • Don't forget to cascade deletes on parent-child relationships
  • Don't mix concerns - keep tables focused on single responsibility

Migration Example

When adding new features, create migrations that maintain backward compatibility:

# alembic/versions/xxx_add_assistant_feature.py

def upgrade():
 # Add new column with default
 op.add_column(
 'assistants',
 sa.Column('new_feature', sa.Boolean(),
 server_default=sa.text('false'),
 nullable=False)
 )

 # Create new related table
 op.create_table(
 'assistant_new_feature',
 sa.Column('id', sa.String(255), primary_key=True),
 sa.Column('assistant_id', sa.String(255), nullable=False),
 sa.ForeignKeyConstraint(['assistant_id'], ['assistants.id'],
 ondelete='CASCADE')
 )

def downgrade():
 op.drop_table('assistant_new_feature')
 op.drop_column('assistants', 'new_feature')