> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useskald.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Ruby SDK

> Documentation on how to use the Skald Ruby SDK.

Ruby client library for the Skald API.

## Installation

Add this line to your application's Gemfile:

```ruby theme={null}
gem 'skald'
```

And then execute:

```bash theme={null}
bundle install
```

Or install it yourself as:

```bash theme={null}
gem install skald
```

## Requirements

* Ruby 3.0.0 or higher

## Usage

### Initialize the client

```ruby theme={null}
require 'skald'

skald = Skald.new('your-api-key-here')
```

### Memo Management

#### Create a Memo

Create a new memo that will be automatically processed (summarized, tagged, chunked, and indexed for search):

```ruby theme={null}
result = skald.create_memo(
  title: 'Meeting Notes',
  content: 'Full content of the memo...',
  metadata: {
    type: 'notes',
    author: 'John Doe'
  },
  reference_id: 'external-id-123',
  tags: ['meeting', 'q1'],
  source: 'notion',
  expiration_date: '2024-12-31T23:59:59Z'
)

puts result # { ok: true }
```

**Required Fields:**

* `title` (string, max 255 chars) - The title of the memo
* `content` (string) - The full content of the memo

**Optional Fields:**

* `metadata` (hash) - Custom JSON metadata
* `reference_id` (string, max 255 chars) - An ID from your side that you can use to match Skald memo UUIDs with e.g. documents on your end
* `tags` (array of strings) - Tags for categorization
* `source` (string, max 255 chars) - An indication from your side of the source of this content, useful when building integrations
* `expiration_date` (string) - ISO 8601 timestamp for automatic memo expiration

#### Get a Memo

Retrieve a memo by its UUID or your reference ID:

```ruby theme={null}
# Get by UUID
memo = skald.get_memo('550e8400-e29b-41d4-a716-446655440000')

# Get by reference ID
memo = skald.get_memo('external-id-123', 'reference_id')

puts memo[:title]
puts memo[:content]
puts memo[:summary]
puts memo[:tags]
puts memo[:chunks]
```

The `get_memo()` method returns complete memo details including content, AI-generated summary, tags, and content chunks.

#### List Memos

List all memos with pagination:

```ruby theme={null}
# Get first page with default page size (20)
memos = skald.list_memos

# Get specific page with custom page size
memos = skald.list_memos(page: 2, page_size: 50)

puts "Total memos: #{memos[:count]}"
puts "Results: #{memos[:results].length}"
puts "Next page: #{memos[:next]}"
```

**Parameters:**

* `page` (integer, optional) - Page number (default: 1)
* `page_size` (integer, optional) - Results per page (default: 20, max: 100)

#### Update a Memo

Update an existing memo by UUID or reference ID:

```ruby theme={null}
# Update by UUID
skald.update_memo('550e8400-e29b-41d4-a716-446655440000', {
  title: 'Updated Title',
  metadata: { status: 'reviewed' }
})

# Update by reference ID and trigger reprocessing
skald.update_memo('external-id-123', {
  content: 'New content that will be reprocessed'
}, 'reference_id')
```

**Note:** When you update the `content` field, the memo will be automatically reprocessed (summary, tags, and chunks regenerated).

**Updatable Fields:**

* `title` (string)
* `content` (string)
* `metadata` (hash)
* `client_reference_id` (string)
* `source` (string)
* `expiration_date` (string)

#### Delete a Memo

Permanently delete a memo and all associated data:

```ruby theme={null}
# Delete by UUID
skald.delete_memo('550e8400-e29b-41d4-a716-446655440000')

# Delete by reference ID
skald.delete_memo('external-id-123', 'reference_id')
```

**Warning:** This operation permanently deletes the memo and all related data (content, summary, tags, chunks) and cannot be undone.

### Search Memos

Search through your memos using various search methods with optional filters:

```ruby theme={null}
# Basic semantic search
results = skald.search(
  query: 'quarterly goals',
  search_method: 'chunk_vector_search',
  limit: 10
)

# Search with filters
filtered = skald.search(
  query: 'python tutorial',
  search_method: 'title_contains',
  filters: [
    {
      field: 'source',
      operator: 'eq',
      value: 'notion',
      filter_type: 'native_field'
    },
    {
      field: 'level',
      operator: 'eq',
      value: 'beginner',
      filter_type: 'custom_metadata'
    }
  ]
)

puts "Found #{filtered[:results].length} results"
filtered[:results].each do |memo|
  puts "- #{memo[:title]} (distance: #{memo[:distance]})"
end
```

#### Search Methods

* **`chunk_vector_search`** - Semantic search on memo chunks for detailed content search
* **`title_contains`** - Case-insensitive substring match on memo titles
* **`title_startswith`** - Case-insensitive prefix match on memo titles

#### Search Parameters

* `query` (string, required) - The search query
* `search_method` (SearchMethod, required) - One of the search methods above
* `limit` (integer, optional) - Maximum results to return (1-50, default 10)
* `filters` (array, optional) - Array of filter hashes to narrow results (see Filters section below)

#### Search Response

```ruby theme={null}
{
  results: [
    {
      uuid: "550e8400-...",
      title: "Project Planning Meeting",
      summary: "Discussed Q1 goals...",
      content_snippet: "...relevant excerpt from the content...",
      distance: 0.45  # For vector search: 0-2, lower is better. nil for title searches
    }
  ]
}
```

* `uuid` - Unique identifier for the memo
* `title` - Memo title
* `summary` - Auto-generated summary for the memo
* `content_snippet` - A snippet containing the beginning of the memo
* `distance` - A decimal from 0 to 2 determining how close the result was deemed to be to the query when using semantic search (`chunk_vector_search`). The closer to 0 the more related the content is to the query. `nil` if using `title_contains` or `title_startswith`.

### Chat with Your Knowledge Base

Ask questions about your memos using an AI agent. The agent retrieves relevant context and generates answers with inline citations.

#### Non-Streaming Chat

```ruby theme={null}
result = skald.chat(
  query: 'What were the main points discussed in the Q1 meeting?'
)

puts result[:response]
# "The main points discussed in the Q1 meeting were:
# 1. Revenue targets    
# 2. Hiring plans
# 3. Product roadmap"

puts result[:ok] # true
```

#### Streaming Chat

For real-time responses, use streaming chat:

```ruby theme={null}
skald.streamed_chat(
  query: 'What are our quarterly goals?'
).each do |event|
  if event[:type] == 'token'
    # Write each token as it arrives
    print event[:content]
  elsif event[:type] == 'done'
    puts "\nDone!"
  end
end
```

#### Chat Parameters

* `query` (string, required) - The question to ask
* `filters` (array, optional) - Array of filter hashes to focus chat context on specific sources (see Filters section below)

#### Chat Response

Non-streaming responses include:

* `ok` (boolean) - Success status
* `response` (string) - The AI's answer
* `intermediate_steps` (array) - Steps taken by the agent (for debugging)

Streaming responses yield events:

* `{ type: 'token', content: string }` - Each text token as it's generated
* `{ type: 'done' }` - Indicates the stream has finished

### Generate Documents

Generate documents based on prompts and retrieved context from your knowledge base. Similar to chat but optimized for document generation with optional style/format rules.

#### Non-Streaming Document Generation

```ruby theme={null}
result = skald.generate_doc(
  prompt: 'Create a product requirements document for a new mobile app',
  rules: 'Use formal business language. Include sections for: Overview, Requirements, Technical Specifications, Timeline'
)

puts result[:response]
# "# Product Requirements Document
#
# ## Overview
# This document outlines the requirements for...
#
# ## Requirements
# 1. User authentication
# 2. Push notifications..."

puts result[:ok] # true
```

#### Streaming Document Generation

For real-time document generation, use streaming:

```ruby theme={null}
skald.streamed_generate_doc(
  prompt: 'Write a technical specification for user authentication',
  rules: 'Include sections for: Architecture, Security, API Endpoints, Data Models'
).each do |event|
  if event[:type] == 'token'
    # Write each token as it arrives
    print event[:content]
  elsif event[:type] == 'done'
    puts "\nDone!"
  end
end
```

#### Generate Document Parameters

* `prompt` (string, required) - The prompt describing what document to generate
* `rules` (string, optional) - Optional style/format rules (e.g., "Use formal language. Include sections: X, Y, Z")
* `filters` (array, optional) - Array of filter hashes to control which memos are used for generation (see Filters section below)

#### Generate Document Response

Non-streaming responses include:

* `ok` (boolean) - Success status
* `response` (string) - The generated document
* `intermediate_steps` (array) - Steps taken by the agent (for debugging)

Streaming responses yield events:

* `{ type: 'token', content: string }` - Each text token as it's generated
* `{ type: 'done' }` - Indicates the stream has finished

### Filters

Filters allow you to narrow down results based on memo metadata. You can filter by native fields or custom metadata fields. Filters are supported in `search()`, `chat()`, `generate_doc()`, and their streaming variants.

See [Filters](/docs/api-reference/filters) for complete documentation.

#### Filter Structure

```ruby theme={null}
{
  field: "field_name",          # Field name to filter on
  operator: "eq",               # Comparison operator
  value: "value",               # Value(s) to compare against
  filter_type: "native_field"   # 'native_field' or 'custom_metadata'
}
```

#### Native Fields

Native fields are built-in memo properties:

* `title` - Memo title
* `source` - Source system (e.g., "notion", "confluence")
* `client_reference_id` - Your external reference ID
* `tags` - Memo tags (array)

#### Custom Metadata Fields

You can filter on any field from the `metadata` hash you provided when creating the memo.

#### Filter Operators

* **`eq`** - Equals (exact match)
* **`neq`** - Not equals
* **`contains`** - Contains substring (case-insensitive)
* **`startswith`** - Starts with prefix (case-insensitive)
* **`endswith`** - Ends with suffix (case-insensitive)
* **`in`** - Value is in array (requires array value)
* **`not_in`** - Value is not in array (requires array value)

#### Filter Examples

```ruby theme={null}
# Filter by source
{
  field: 'source',
  operator: 'eq',
  value: 'notion',
  filter_type: 'native_field'
}

# Filter by multiple tags
{
  field: 'tags',
  operator: 'in',
  value: ['security', 'compliance'],
  filter_type: 'native_field'
}

# Filter by title containing text
{
  field: 'title',
  operator: 'contains',
  value: 'meeting',
  filter_type: 'native_field'
}

# Filter by custom metadata field
{
  field: 'department',
  operator: 'eq',
  value: 'engineering',
  filter_type: 'custom_metadata'
}

# Exclude specific sources
{
  field: 'source',
  operator: 'not_in',
  value: ['draft', 'archive'],
  filter_type: 'native_field'
}
```

#### Combining Multiple Filters

When you provide multiple filters, they are combined with AND logic (all filters must match):

```ruby theme={null}
results = skald.search(
  query: 'security best practices',
  search_method: 'chunk_vector_search',
  filters: [
    {
      field: 'source',
      operator: 'eq',
      value: 'security-docs',
      filter_type: 'native_field'
    },
    {
      field: 'tags',
      operator: 'in',
      value: ['approved', 'current'],
      filter_type: 'native_field'
    },
    {
      field: 'status',
      operator: 'neq',
      value: 'draft',
      filter_type: 'custom_metadata'
    }
  ]
)
```

#### Filters with Chat

Focus chat context on specific sources:

```ruby theme={null}
result = skald.chat(
  query: 'What are our security practices?',
  filters: [
    {
      field: 'tags',
      operator: 'in',
      value: ['security', 'compliance'],
      filter_type: 'native_field'
    }
  ]
)
```

#### Filters with Document Generation

Control which memos are used for document generation:

```ruby theme={null}
doc = skald.generate_doc(
  prompt: 'Create an API integration guide',
  rules: 'Use technical language with code examples',
  filters: [
    {
      field: 'source',
      operator: 'in',
      value: ['api-docs', 'technical-specs'],
      filter_type: 'native_field'
    },
    {
      field: 'document_type',
      operator: 'eq',
      value: 'specification',
      filter_type: 'custom_metadata'
    }
  ]
)
```

### Error Handling

```ruby theme={null}
begin
  result = skald.create_memo(
    title: 'My Memo',
    content: 'Content here'
  )
  puts "Success: #{result}"
rescue => e
  puts "Error: #{e.message}"
end
```
