Skip to main content

Overview

The Event object is the fundamental building block of Fabraix’s observability system. Every action, decision, and interaction in your agent’s lifecycle is captured as an Event.

Event Structure

The Event object has a precise structure designed for flexibility and comprehensive tracking:
interface Event {
  id: string;           // Unique identifier (UUID)
  timestamp: datetime;  // ISO 8601 timestamp
  trace_id: string;     // Session identifier
  type: EventType;      // Event category
  content: string;      // Stringified JSON payload
  schema: string;       // JSON Schema definition
}

Field Definitions

id
string
required
A unique identifier for the event (typically a UUID). This ID is generated by your system and should be globally unique.Example: "e7d4f3a2-8b1c-4d9e-a5f6-2c3d4e5f6a7b"
timestamp
datetime
required
The precise time when the event occurred, formatted as ISO 8601.Format: YYYY-MM-DDTHH:mm:ss.sssZExample: "2024-01-15T14:30:45.123Z"
trace_id
string
required
The unique session identifier returned from /register-agent-run. This links the event to a specific agent session.Example: "f4f4f4f4-f4f4-f4f4-f4f4-f4f4f4f4f4f4"
type
enum
required
The logical category of the event. Must be one of:
  • user - Human user input
  • model_input - Data sent to LLM
  • model_output - LLM responses
  • system - System-level events
  • tool - Tool/function calls
  • environment - External system data
  • memory - Memory operations
  • error - Error conditions
content
string
required
The actual event data as a stringified JSON object. This must validate against the provided schema.Example:
"{\"location\":\"London, UK\",\"units\":\"celsius\"}"
schema
string
required
A stringified JSON Schema that defines the structure of the content field. This enables dynamic validation and understanding of diverse event types.Example:
"{\"type\":\"object\",\"properties\":{\"location\":{\"type\":\"string\"},\"units\":{\"type\":\"string\"}}}"

Content and Schema Relationship

The power of Fabraix’s data model lies in the relationship between content and schema. The schema defines what the content should look like, enabling:
  • Dynamic Validation: Content is validated against its schema
  • Type Safety: Clear contracts for event data
  • Flexibility: Support for any data structure
  • Documentation: Self-describing events

Example: Tool Event

Here’s how content and schema work together for a tool call:
# The schema defines the tool's signature
tool_schema = {
    "type": "function",
    "name": "search_database",
    "description": "Search the customer database",
    "parameters": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "Search query"
            },
            "filters": {
                "type": "object",
                "properties": {
                    "status": {
                        "type": "string",
                        "enum": ["active", "inactive", "pending"]
                    },
                    "created_after": {
                        "type": "string",
                        "format": "date-time"
                    }
                }
            },
            "limit": {
                "type": "integer",
                "minimum": 1,
                "maximum": 100,
                "default": 10
            }
        },
        "required": ["query"]
    }
}

# The content contains the actual arguments
tool_content = {
    "query": "enterprise customers",
    "filters": {
        "status": "active",
        "created_after": "2024-01-01T00:00:00Z"
    },
    "limit": 25
}

# Submit the event
log_event(
    trace_id=trace_id,
    event_type="tool",
    content=json.dumps(tool_content),
    schema=json.dumps(tool_schema)
)

Event Type Examples

User Event

Captures input from human users:
{
  "type": "user",
  "content": "{\"message\":\"What's the weather in Paris?\",\"user_id\":\"user-123\"}",
  "schema": "{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\"},\"user_id\":{\"type\":\"string\"}}}"
}

Model Input Event

Data sent to the LLM:
{
  "type": "model_input",
  "content": "{\"messages\":[{\"role\":\"system\",\"content\":\"You are helpful\"},{\"role\":\"user\",\"content\":\"What's 2+2?\"}],\"temperature\":0.7}",
  "schema": "{\"type\":\"object\",\"properties\":{\"messages\":{\"type\":\"array\",\"items\":{\"type\":\"object\"}},\"temperature\":{\"type\":\"number\"}}}"
}

Model Output Event

LLM responses:
{
  "type": "model_output",
  "content": "{\"response\":\"2+2 equals 4\",\"confidence\":0.99,\"tokens_used\":15}",
  "schema": "{\"type\":\"object\",\"properties\":{\"response\":{\"type\":\"string\"},\"confidence\":{\"type\":\"number\"},\"tokens_used\":{\"type\":\"integer\"}}}"
}

Memory Event

Memory operations:
{
  "type": "memory",
  "content": "{\"operation\":\"write\",\"key\":\"user_preferences\",\"value\":{\"language\":\"en\",\"timezone\":\"EST\"}}",
  "schema": "{\"type\":\"object\",\"properties\":{\"operation\":{\"type\":\"string\",\"enum\":[\"read\",\"write\",\"delete\"]},\"key\":{\"type\":\"string\"},\"value\":{\"type\":\"object\"}}}"
}

Environment Event

External system interactions:
{
  "type": "environment",
  "content": "{\"source\":\"weather_api\",\"data\":{\"temperature\":22,\"conditions\":\"sunny\"}}",
  "schema": "{\"type\":\"object\",\"properties\":{\"source\":{\"type\":\"string\"},\"data\":{\"type\":\"object\"}}}"
}

Handling Complex Data

Images and Binary Data

For images and binary data, use base64 encoding within the JSON:
import base64

# Encode image
with open("image.png", "rb") as f:
    image_base64 = base64.b64encode(f.read()).decode('utf-8')

# Include in event
content = {
    "image": image_base64,
    "format": "png",
    "description": "User uploaded screenshot"
}

schema = {
    "type": "object",
    "properties": {
        "image": {
            "type": "string",
            "contentEncoding": "base64"
        },
        "format": {
            "type": "string",
            "enum": ["png", "jpg", "gif"]
        },
        "description": {
            "type": "string"
        }
    }
}

Nested Structures

Support complex nested data structures:
content = {
    "order": {
        "id": "order-123",
        "customer": {
            "id": "cust-456",
            "name": "Alice Smith",
            "tier": "premium"
        },
        "items": [
            {
                "product_id": "prod-789",
                "quantity": 2,
                "price": 29.99
            }
        ],
        "metadata": {
            "source": "web",
            "campaign": "summer-sale"
        }
    }
}

# Schema can validate complex nested structures
schema = {
    "type": "object",
    "properties": {
        "order": {
            "type": "object",
            "properties": {
                "id": {"type": "string"},
                "customer": {
                    "type": "object",
                    "properties": {
                        "id": {"type": "string"},
                        "name": {"type": "string"},
                        "tier": {"type": "string"}
                    }
                },
                "items": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "product_id": {"type": "string"},
                            "quantity": {"type": "integer"},
                            "price": {"type": "number"}
                        }
                    }
                }
            }
        }
    }
}

Best Practices

Generate UUIDs for event IDs to ensure uniqueness:
import uuid
event_id = str(uuid.uuid4())
Add contextual information that might be useful for analysis:
content = {
    "action": "delete_file",
    "file": "/data/report.pdf",
    "reason": "User requested deletion",
    "user_id": "user-123",
    "ip_address": "192.168.1.1"
}
Include constraints, formats, and descriptions:
schema = {
    "type": "object",
    "properties": {
        "email": {
            "type": "string",
            "format": "email",
            "description": "User's email address"
        },
        "age": {
            "type": "integer",
            "minimum": 0,
            "maximum": 150
        },
        "score": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "multipleOf": 0.1
        }
    },
    "required": ["email"],
    "additionalProperties": false
}
Log errors as events for debugging:
try:
    # Some operation
    result = risky_operation()
except Exception as e:
    log_event(
        trace_id=trace_id,
        event_type="error",
        content=json.dumps({
            "error": str(e),
            "error_type": e.__class__.__name__,
            "context": {
                "operation": "risky_operation",
                "input": input_data
            }
        }),
        schema=error_schema
    )

Validation

Fabraix validates all events against their schemas. Common validation errors:
Schema Mismatch: Content doesn’t match schema structure
{
  "error": "Content validation failed",
  "details": "Required property 'user_id' missing"
}
Invalid JSON: Content or schema is not valid JSON
{
  "error": "Invalid JSON in content field",
  "details": "Unexpected token at position 45"
}
Type Mismatch: Wrong data type for a field
{
  "error": "Type validation failed",
  "details": "Expected string for 'age', got number"
}

Next Steps

Now that you understand the Event data model: