ask-core
Foundation gem for the ask-rb ecosystem. Provides the value objects and interfaces every other gem builds on: messages, conversations, streaming primitives, the provider contract, model catalog, tool definitions, and structured errors. Zero external dependencies, Ruby stdlib only (json, net/http, date, time).
Installation
gem "ask-core"
Quick Start
require "ask-core"
conv = Ask::Conversation.new
conv.system("You are a helpful assistant.")
conv.user("What's the weather in Tokyo?")
conv.last.role # => :user
conv.last.user? # => true
# Serialize for a provider API
conv.to_a # => [{ role: :user, content: "..." }, ...]
The core types
| Type | Purpose |
|---|---|
Ask::Conversation, Ask::Message |
Message container with role normalization (:system, :user, :assistant, :tool) and immutable message value objects |
Ask::Stream, Ask::Chunk |
Streaming primitives with text accumulation and usage tracking |
Ask::Provider |
Abstract base class for LLM providers, with a thread-safe registry (register / resolve) |
Ask::ModelCatalog, Ask::ModelInfo |
Model metadata: find by ID/provider, filter by family, refresh from models.dev |
Ask::ToolDef |
Immutable tool metadata for provider function calling |
Ask::Result |
Standardized tool return value: success, failure, aborted, blocked |
Ask::Content |
Multi-modal content blocks: Text, Image, Audio, Video, File |
Ask::Document |
Text + metadata value object for RAG pipelines |
Ask::ProviderTool |
Provider-executed tools (e.g. web_search, file_search) |
Ask::State::Adapter |
Abstract contract for state backends (implemented by ask-state-providers) |
Ask::Error and subclasses |
Structured errors (ConfigurationError, RateLimitError, ProviderError, and more) |
Defining a provider
Provider gems subclass Ask::Provider, implement the abstract methods, and register themselves:
class MyProvider < Ask::Provider
def api_base = "https://api.example.com/v1"
def chat(, model:, **opts) = Ask::Message.new(role: :assistant, content: "Hello")
def (text, model:) = [0.1, 0.2, 0.3]
def list_models = [Ask::ModelInfo.new(id: "my-model", provider: "my_provider")]
end
Ask::Provider.register(:my_provider, MyProvider)
Ask::Provider.resolve(:my_provider) # => MyProvider
Architecture & ownership
ask-core owns the shared value objects and base contracts (Ask::Message,
Ask::Conversation, Ask::Content, Ask::Stream, Ask::Provider,
Ask::Result, Ask::ModelCatalog, Ask::ToolDef, Ask::Document, the
Ask::Error hierarchy). It is zero-dependency on purpose: ask-llm-providers,
ask-rag, ask-graph, and ask-state-providers depend on it without
pulling in the tool framework.
Feature gems extend what ask-core provides — they never redefine it.
Ask::Result is the single result type for the whole ecosystem: foundational
API (success/failure/aborted/blocked) and tool API (ok/error) in
one class. See
Architecture & Ownership.
Full documentation
The full ask-rb documentation lives at https://ask-rb.github.io/ask-docs. ask-core in depth covers the types, streaming, and the provider contract. API reference: https://ask-rb.github.io/ask-docs/reference/api.
Development
bundle install
bundle exec rake test
License
MIT