Skald Ruby SDK
Ruby client library for Skald - an API platform that allows you to easily push context/knowledge via our API (in the form of memos) and get access to chat, document generation, and semantic search out-of-the-box.
Installation
Add this line to your application's Gemfile:
gem 'skald'
And then execute:
bundle install
Or install it yourself as:
gem install skald
Requirements
- Ruby 3.0.0 or higher
Quick Start
require 'skald'
# Initialize the client
client = Skald.new('your-api-key')
# Create a memo
client.create_memo(
title: "Meeting Notes",
content: "Discussed project timeline..."
)
# Search your memos
results = client.search(
query: "project timeline",
search_method: "chunk_vector_search"
)
# Chat with your memos
response = client.chat(query: "What were the main discussion points?")
puts response[:response]
Initialization
# Default base URL (https://api.useskald.com)
client = Skald.new('your-api-key')
# Custom base URL (for self-hosted or testing)
client = Skald.new('your-api-key', 'https://custom.api.com')
Memo Management
Create Memo
Create a new memo with content and optional metadata.
result = client.create_memo(
title: "Project Planning Meeting",
content: "Discussed Q1 goals, resource allocation, and timeline...",
metadata: { priority: "high", department: "engineering" },
tags: ["meeting", "planning"],
source: "notion",
reference_id: "notion-page-123",
expiration_date: "2025-12-31T23:59:59Z"
)
# => { ok: true }
Parameters:
title(String, required): Memo title (max 255 characters)content(String, required): Memo contentmetadata(Hash, optional): Custom JSON metadatatags(Array, optional): Array of tags source(String, optional): Source system (e.g., "notion", "slack", max 255 chars)reference_id(String, optional): External reference ID (max 255 chars)expiration_date(String, optional): ISO 8601 expiration timestamp
Response:
{ ok: true }
Get Memo
Retrieve a memo by UUID or reference ID.
# Get by UUID
memo = client.get_memo("550e8400-e29b-41d4-a716-446655440000")
# Get by reference ID
memo = client.get_memo("notion-page-123", "reference_id")
Parameters:
memo_id(String, required): Memo UUID or reference IDid_type(String, optional):"memo_uuid"(default) or"reference_id"
Response:
{
uuid: "550e8400-e29b-41d4-a716-446655440000",
created_at: "2025-01-15T10:30:00Z",
updated_at: "2025-01-15T10:30:00Z",
title: "Project Planning Meeting",
content: "Discussed Q1 goals...",
summary: "AI-generated summary of the memo",
content_length: 150,
metadata: { priority: "high" },
client_reference_id: "notion-page-123",
source: "notion",
type: "memo",
expiration_date: nil,
archived: false,
pending: false,
tags: [
{ uuid: "tag-uuid", tag: "meeting" }
],
chunks: [
{ uuid: "chunk-uuid", chunk_content: "...", chunk_index: 0 }
]
}
List Memos
List all memos with pagination.
# Default pagination (page 1, 20 items)
response = client.list_memos
# Custom pagination
response = client.list_memos(page: 2, page_size: 50)
Parameters:
page(Integer, optional): Page number (default: 1)page_size(Integer, optional): Results per page (default: 20, max: 100)
Response:
{
count: 150,
next: "https://api.useskald.com/api/v1/memo?page=2",
previous: nil,
results: [
{
uuid: "...",
title: "Memo 1",
summary: "...",
# ... other memo fields
}
]
}
Update Memo
Update an existing memo. Updating content triggers reprocessing.
# Update by UUID
client.update_memo(
"550e8400-e29b-41d4-a716-446655440000",
{
title: "Updated Title",
metadata: { priority: "medium", status: "reviewed" }
}
)
# Update by reference ID
client.update_memo(
"notion-page-123",
{ content: "New content..." },
"reference_id"
)
Parameters:
memo_id(String, required): Memo UUID or reference IDupdate_data(Hash, required): Fields to updatetitle(String, optional): New titlecontent(String, optional): New content (triggers reprocessing)metadata(Hash, optional): New metadataclient_reference_id(String, optional): New reference IDsource(String, optional): New sourceexpiration_date(String, optional): New expiration date
id_type(String, optional):"memo_uuid"(default) or"reference_id"
Response:
{ ok: true }
Delete Memo
Permanently delete a memo and all associated data.
# Delete by UUID
client.delete_memo("550e8400-e29b-41d4-a716-446655440000")
# Delete by reference ID
client.delete_memo("notion-page-123", "reference_id")
Parameters:
memo_id(String, required): Memo UUID or reference IDid_type(String, optional):"memo_uuid"(default) or"reference_id"
Response:
nil
Search
Search your memos using semantic search or title matching.
Search Methods
chunk_vector_search: Semantic/vector search on memo content chunkstitle_contains: Case-insensitive substring match on titlestitle_startswith: Case-insensitive prefix match on titles
Semantic Search
results = client.search(
query: "project timeline and milestones",
search_method: "chunk_vector_search",
limit: 20
)
Title Search
# Contains
results = client.search(
query: "meeting",
search_method: "title_contains"
)
# Starts with
results = client.search(
query: "Project",
search_method: "title_startswith"
)
Parameters:
query(String, required): Search querysearch_method(String, required): One of"chunk_vector_search","title_contains", or"title_startswith"limit(Integer, optional): Maximum results (1-50, default: 10)filters(Array, optional): Filters to apply (see Filters)
Response:
{
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
}
]
}
Chat
Ask questions and get answers based on your memos with inline citations.
Non-Streaming Chat
response = client.chat(
query: "What are the main project goals for Q1?"
)
puts response[:response]
# => "The main goals are: 1) Launch MVP [[1]], 2) Hire team [[2]]..."
Parameters:
query(String, required): Question to askfilters(Array, optional): Filters to apply (see Filters)
Response:
{
ok: true,
response: "Answer with inline citations [[1]], [[2]], etc.",
intermediate_steps: [] # For debugging
}
Streaming Chat
print "Answer: "
client.streamed_chat(
query: "Summarize the meeting notes from last week"
).each do |event|
if event[:type] == "token"
print event[:content]
elsif event[:type] == "done"
puts "\nDone!"
end
end
Parameters:
- Same as non-streaming chat
Yields:
{ type: "token", content: "Each" }
{ type: "token", content: " word" }
{ type: "done" }
Document Generation
Generate documents based on your memos with optional style rules.
Non-Streaming Generation
response = client.generate_doc(
prompt: "Create a comprehensive project status report",
rules: "Use bullet points and maintain a professional tone"
)
puts response[:response]
# => "# Project Status Report\n\n## Overview\nThe project is on track [[1]]..."
Parameters:
prompt(String, required): What document to generaterules(String, optional): Style/format rulesfilters(Array, optional): Filters to apply (see Filters)
Response:
{
ok: true,
response: "Generated document with citations [[1]], [[2]]...",
intermediate_steps: []
}
Streaming Generation
puts "Generated Document:"
client.streamed_generate_doc(
prompt: "Write a technical architecture document",
rules: "Include diagrams descriptions and code examples"
).each do |event|
if event[:type] == "token"
print event[:content]
elsif event[:type] == "done"
puts "\nDone!"
end
end
Parameters:
- Same as non-streaming generation
Yields:
{ type: "token", content: "Each" }
{ type: "token", content: " word" }
{ type: "done" }
Filters
Filters allow you to narrow down which memos are used for search, chat, and document generation. Multiple filters use AND logic (all must match).
Filter Structure
{
field: "field_name",
operator: "eq",
value: "value",
filter_type: "native_field"
}
Filter Types
native_field: Built-in memo propertiestitle: Memo titlesource: Source systemclient_reference_id: Your reference IDtags: Memo tags
custom_metadata: User-defined metadata fields
Filter Operators
| Operator | Description | Value Type |
|---|---|---|
eq |
Exact match | String |
neq |
Not equals | String |
contains |
Substring match (case-insensitive) | String |
startswith |
Prefix match (case-insensitive) | String |
endswith |
Suffix match (case-insensitive) | String |
in |
Value is in array | Array |
not_in |
Value not in array | Array |
Examples
Filter by Native Field
results = client.search(
query: "project update",
search_method: "chunk_vector_search",
filters: [
{
field: "source",
operator: "eq",
value: "notion",
filter_type: "native_field"
}
]
)
Filter by Tags
results = client.search(
query: "status",
search_method: "chunk_vector_search",
filters: [
{
field: "tags",
operator: "in",
value: ["project", "important"],
filter_type: "native_field"
}
]
)
Filter by Custom Metadata
results = client.search(
query: "urgent tasks",
search_method: "chunk_vector_search",
filters: [
{
field: "priority",
operator: "eq",
value: "high",
filter_type: "custom_metadata"
}
]
)
Multiple Filters
results = client.search(
query: "engineering work",
search_method: "chunk_vector_search",
filters: [
{
field: "source",
operator: "eq",
value: "jira",
filter_type: "native_field"
},
{
field: "tags",
operator: "in",
value: ["backend", "api"],
filter_type: "native_field"
},
{
field: "priority",
operator: "eq",
value: "high",
filter_type: "custom_metadata"
}
]
)
Filters in Chat
response = client.chat(
query: "What are the high priority backend tasks?",
filters: [
{
field: "department",
operator: "eq",
value: "engineering",
filter_type: "custom_metadata"
}
]
)
Filters in Document Generation
response = client.generate_doc(
prompt: "Create a security audit summary",
rules: "Focus on key findings",
filters: [
{
field: "tags",
operator: "in",
value: ["security", "audit"],
filter_type: "native_field"
}
]
)
Error Handling
The SDK raises exceptions for API errors. Wrap your calls in begin/rescue blocks:
begin
memo = client.get_memo("invalid-id")
rescue => e
puts "Error: #{e.}"
# => "Skald API error (404): Not Found"
end
All methods may raise RuntimeError with the format:
Skald API error (STATUS_CODE): ERROR_MESSAGE
Examples
See the examples directory for complete working examples:
- memo_operations.rb - Create, read, update, delete memos
- search.rb - All search methods and filtering
- chat.rb - Streaming and non-streaming chat
- document_generation.rb - Document generation with rules
- filters.rb - Advanced filtering examples
Development
# Install dependencies
bundle install
# Run tests
bundle exec rspec
# Run tests with coverage
bundle exec rspec --format documentation
# Run linter
bundle exec rubocop
Testing
The SDK includes comprehensive tests with 100% method coverage:
bundle exec rspec
# => 54 examples, 0 failures
Tests use WebMock to mock HTTP requests, so no actual API calls are made during testing.
API Reference
For complete API documentation, visit the Skald API Documentation.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/skald-org/skald-ruby.
License
The gem is available as open source under the terms of the MIT License.
Support
- Documentation: https://docs.useskald.com
- Email: [email protected]
- GitHub Issues: https://github.com/skald-org/skald-ruby/issues