riffer 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.agents/architecture.md +113 -0
- data/.agents/code-style.md +42 -0
- data/.agents/providers.md +46 -0
- data/.agents/rdoc.md +51 -0
- data/.agents/testing.md +56 -0
- data/.release-please-manifest.json +1 -1
- data/AGENTS.md +21 -308
- data/CHANGELOG.md +17 -0
- data/README.md +21 -112
- data/Rakefile +1 -1
- data/docs/01_OVERVIEW.md +106 -0
- data/docs/02_GETTING_STARTED.md +128 -0
- data/docs/03_AGENTS.md +226 -0
- data/docs/04_TOOLS.md +342 -0
- data/docs/05_MESSAGES.md +173 -0
- data/docs/06_STREAM_EVENTS.md +191 -0
- data/docs/07_CONFIGURATION.md +195 -0
- data/docs_providers/01_PROVIDERS.md +168 -0
- data/docs_providers/02_AMAZON_BEDROCK.md +196 -0
- data/docs_providers/03_ANTHROPIC.md +211 -0
- data/docs_providers/04_OPENAI.md +157 -0
- data/docs_providers/05_TEST_PROVIDER.md +163 -0
- data/docs_providers/06_CUSTOM_PROVIDERS.md +304 -0
- data/lib/riffer/agent.rb +103 -63
- data/lib/riffer/config.rb +20 -12
- data/lib/riffer/core.rb +7 -7
- data/lib/riffer/helpers/class_name_converter.rb +6 -3
- data/lib/riffer/helpers/dependencies.rb +18 -0
- data/lib/riffer/helpers/validations.rb +9 -0
- data/lib/riffer/messages/assistant.rb +23 -1
- data/lib/riffer/messages/base.rb +15 -0
- data/lib/riffer/messages/converter.rb +15 -5
- data/lib/riffer/messages/system.rb +8 -1
- data/lib/riffer/messages/tool.rb +45 -2
- data/lib/riffer/messages/user.rb +8 -1
- data/lib/riffer/messages.rb +7 -0
- data/lib/riffer/providers/amazon_bedrock.rb +8 -4
- data/lib/riffer/providers/anthropic.rb +209 -0
- data/lib/riffer/providers/base.rb +17 -12
- data/lib/riffer/providers/open_ai.rb +7 -1
- data/lib/riffer/providers/repository.rb +9 -4
- data/lib/riffer/providers/test.rb +25 -7
- data/lib/riffer/providers.rb +6 -0
- data/lib/riffer/stream_events/base.rb +13 -1
- data/lib/riffer/stream_events/reasoning_delta.rb +15 -1
- data/lib/riffer/stream_events/reasoning_done.rb +15 -1
- data/lib/riffer/stream_events/text_delta.rb +14 -1
- data/lib/riffer/stream_events/text_done.rb +14 -1
- data/lib/riffer/stream_events/tool_call_delta.rb +18 -11
- data/lib/riffer/stream_events/tool_call_done.rb +22 -12
- data/lib/riffer/stream_events.rb +9 -0
- data/lib/riffer/tool.rb +92 -25
- data/lib/riffer/tools/param.rb +19 -16
- data/lib/riffer/tools/params.rb +28 -22
- data/lib/riffer/tools/response.rb +90 -0
- data/lib/riffer/tools.rb +6 -0
- data/lib/riffer/version.rb +1 -1
- data/lib/riffer.rb +21 -21
- metadata +35 -1
data/README.md
CHANGED
|
@@ -2,26 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The all-in-one Ruby framework for building AI-powered applications and agents.
|
|
4
4
|
|
|
5
|
-
[](https://badge.fury.io/rb/riffer)
|
|
6
|
-
|
|
7
|
-
## Overview
|
|
8
|
-
|
|
9
|
-
Riffer is a comprehensive Ruby framework designed to simplify the development of AI-powered applications and agents. It provides a complete toolkit for integrating artificial intelligence capabilities into your Ruby projects.
|
|
10
|
-
|
|
11
|
-
Key concepts:
|
|
12
|
-
|
|
13
|
-
- **Agents** – orchestrate messages, LLM calls, and tool execution (`Riffer::Agent`).
|
|
14
|
-
- **Tools** – define callable functions that agents can use to interact with external systems (`Riffer::Tool`).
|
|
15
|
-
- **Providers** – adapters that implement text generation and streaming (`Riffer::Providers::*`).
|
|
16
|
-
- **Messages** – typed message objects for system, user, assistant, and tool messages (`Riffer::Messages::*`).
|
|
17
|
-
|
|
18
|
-
## Features
|
|
19
|
-
|
|
20
|
-
- Minimal, well-documented core for building AI agents
|
|
21
|
-
- Tool calling support with parameter validation
|
|
22
|
-
- Provider abstraction (OpenAI, Amazon Bedrock) for pluggable providers
|
|
23
|
-
- Streaming support and structured stream events
|
|
24
|
-
- Message converters and helpers for robust message handling
|
|
5
|
+
[](https://badge.fury.io/rb/riffer)
|
|
25
6
|
|
|
26
7
|
## Requirements
|
|
27
8
|
|
|
@@ -41,121 +22,49 @@ Or add to your application's Gemfile:
|
|
|
41
22
|
gem 'riffer'
|
|
42
23
|
```
|
|
43
24
|
|
|
44
|
-
Install the development branch directly from GitHub:
|
|
45
|
-
|
|
46
|
-
```ruby
|
|
47
|
-
gem 'riffer', git: 'https://github.com/janeapp/riffer.git'
|
|
48
|
-
```
|
|
49
|
-
|
|
50
25
|
## Quick Start
|
|
51
26
|
|
|
52
|
-
Basic usage with the OpenAI provider:
|
|
53
|
-
|
|
54
27
|
```ruby
|
|
55
28
|
require 'riffer'
|
|
56
29
|
|
|
57
|
-
# Configure
|
|
30
|
+
# Configure your provider
|
|
58
31
|
Riffer.configure do |config|
|
|
59
32
|
config.openai.api_key = ENV['OPENAI_API_KEY']
|
|
60
33
|
end
|
|
61
34
|
|
|
35
|
+
# Define an agent
|
|
62
36
|
class EchoAgent < Riffer::Agent
|
|
63
|
-
model 'openai/gpt-
|
|
37
|
+
model 'openai/gpt-4o'
|
|
64
38
|
instructions 'You are an assistant that repeats what the user says.'
|
|
65
39
|
end
|
|
66
40
|
|
|
41
|
+
# Use the agent
|
|
67
42
|
agent = EchoAgent.new
|
|
68
43
|
puts agent.generate('Hello world')
|
|
69
|
-
# => "Hello world"
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Streaming example:
|
|
73
|
-
|
|
74
|
-
```ruby
|
|
75
|
-
agent = EchoAgent.new
|
|
76
|
-
agent.stream('Tell me a story').each do |event|
|
|
77
|
-
print event.content
|
|
78
|
-
end
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### Provider & Model Options
|
|
82
|
-
|
|
83
|
-
Agents support two optional configuration methods for passing options through to the underlying provider:
|
|
84
|
-
|
|
85
|
-
```ruby
|
|
86
|
-
class MyAgent < Riffer::Agent
|
|
87
|
-
model 'openai/gpt-4o'
|
|
88
|
-
instructions 'You are a helpful assistant.'
|
|
89
|
-
|
|
90
|
-
# Options passed directly to the provider client (e.g., OpenAI::Client)
|
|
91
|
-
provider_options api_key: ENV['CUSTOM_OPENAI_KEY']
|
|
92
|
-
|
|
93
|
-
# Options passed to the model invocation (e.g., reasoning, temperature)
|
|
94
|
-
model_options reasoning: 'medium'
|
|
95
|
-
end
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
- `provider_options` - Hash of options passed to the provider client during instantiation
|
|
99
|
-
- `model_options` - Hash of options passed to `generate_text` / `stream_text` calls
|
|
100
|
-
|
|
101
|
-
### Tools
|
|
102
|
-
|
|
103
|
-
Tools allow agents to interact with external systems. Define a tool by extending `Riffer::Tool`:
|
|
104
|
-
|
|
105
|
-
```ruby
|
|
106
|
-
class WeatherLookupTool < Riffer::Tool
|
|
107
|
-
description "Provides current weather information for a specified city."
|
|
108
|
-
|
|
109
|
-
params do
|
|
110
|
-
required :city, String, description: "The city to look up"
|
|
111
|
-
optional :units, String, default: "celsius", enum: ["celsius", "fahrenheit"]
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def call(context:, city:, units: nil)
|
|
115
|
-
weather = WeatherService.lookup(city, units: units || "celsius")
|
|
116
|
-
"The weather in #{city} is #{weather.temperature} #{units}."
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Register tools with an agent using `uses_tools`:
|
|
122
|
-
|
|
123
|
-
```ruby
|
|
124
|
-
class WeatherAgent < Riffer::Agent
|
|
125
|
-
model 'openai/gpt-4o'
|
|
126
|
-
instructions 'You are a helpful weather assistant.'
|
|
127
|
-
|
|
128
|
-
uses_tools [WeatherLookupTool]
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
agent = WeatherAgent.new
|
|
132
|
-
puts agent.generate("What's the weather in Toronto?")
|
|
133
44
|
```
|
|
134
45
|
|
|
135
|
-
|
|
46
|
+
## Documentation
|
|
136
47
|
|
|
137
|
-
|
|
138
|
-
class DynamicAgent < Riffer::Agent
|
|
139
|
-
model 'openai/gpt-4o'
|
|
48
|
+
For comprehensive documentation, see the [docs](docs/) directory:
|
|
140
49
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
50
|
+
- [Overview](docs/01_OVERVIEW.md) - Core concepts and architecture
|
|
51
|
+
- [Getting Started](docs/02_GETTING_STARTED.md) - Installation and first steps
|
|
52
|
+
- [Agents](docs/03_AGENTS.md) - Building AI agents
|
|
53
|
+
- [Tools](docs/04_TOOLS.md) - Creating tools for agents
|
|
54
|
+
- [Messages](docs/05_MESSAGES.md) - Message types and formats
|
|
55
|
+
- [Stream Events](docs/06_STREAM_EVENTS.md) - Streaming responses
|
|
56
|
+
- [Configuration](docs/07_CONFIGURATION.md) - Framework configuration
|
|
57
|
+
- [Providers](docs_providers/01_PROVIDERS.md) - LLM provider adapters
|
|
147
58
|
|
|
148
|
-
|
|
149
|
-
agent.generate("Do admin things", tool_context: { current_user: current_user })
|
|
150
|
-
```
|
|
59
|
+
### API Reference
|
|
151
60
|
|
|
152
|
-
|
|
61
|
+
Generate the full API documentation with:
|
|
153
62
|
|
|
154
|
-
```
|
|
155
|
-
|
|
63
|
+
```bash
|
|
64
|
+
bundle exec rake docs
|
|
156
65
|
```
|
|
157
66
|
|
|
158
|
-
|
|
67
|
+
Then open `doc/index.html` in your browser.
|
|
159
68
|
|
|
160
69
|
## Development
|
|
161
70
|
|
|
@@ -202,4 +111,4 @@ Licensed under the MIT License. See `LICENSE.txt` for details.
|
|
|
202
111
|
|
|
203
112
|
## Maintainers
|
|
204
113
|
|
|
205
|
-
- Jake Bottrall
|
|
114
|
+
- Jake Bottrall - https://github.com/bottrall
|
data/Rakefile
CHANGED
|
@@ -14,7 +14,7 @@ RDoc::Task.new do |rdoc|
|
|
|
14
14
|
rdoc.main = "README.md"
|
|
15
15
|
|
|
16
16
|
# Explicitly include top-level docs and the library
|
|
17
|
-
rdoc.rdoc_files.include("README.md", "CHANGELOG.md", "LICENSE.txt")
|
|
17
|
+
rdoc.rdoc_files.include("README.md", "CHANGELOG.md", "LICENSE.txt", "docs/**/*.md", "docs_providers/**/*.md")
|
|
18
18
|
rdoc.rdoc_files.include("lib/**/*.rb")
|
|
19
19
|
|
|
20
20
|
# Use Markdown where available and ensure UTF-8
|
data/docs/01_OVERVIEW.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Overview
|
|
2
|
+
|
|
3
|
+
Riffer is a Ruby framework for building AI-powered applications and agents. It provides a complete toolkit for integrating Large Language Models (LLMs) into your Ruby projects.
|
|
4
|
+
|
|
5
|
+
## Core Concepts
|
|
6
|
+
|
|
7
|
+
### Agent
|
|
8
|
+
|
|
9
|
+
The Agent is the central orchestrator for AI interactions. It manages messages, calls the LLM provider, and handles tool execution.
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
class MyAgent < Riffer::Agent
|
|
13
|
+
model 'openai/gpt-4o'
|
|
14
|
+
instructions 'You are a helpful assistant.'
|
|
15
|
+
end
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
See [Agents](03_AGENTS.md) for details.
|
|
19
|
+
|
|
20
|
+
### Tool
|
|
21
|
+
|
|
22
|
+
Tools are callable functions that agents can invoke to interact with external systems. They have structured parameter definitions and automatic validation.
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
class WeatherTool < Riffer::Tool
|
|
26
|
+
description "Gets the weather for a city"
|
|
27
|
+
|
|
28
|
+
params do
|
|
29
|
+
required :city, String, description: "The city name"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def call(context:, city:)
|
|
33
|
+
WeatherAPI.fetch(city)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
See [Tools](04_TOOLS.md) for details.
|
|
39
|
+
|
|
40
|
+
### Provider
|
|
41
|
+
|
|
42
|
+
Providers are adapters that connect to LLM services. Riffer supports:
|
|
43
|
+
|
|
44
|
+
- **OpenAI** - GPT models via the OpenAI API
|
|
45
|
+
- **Amazon Bedrock** - Claude and other models via AWS Bedrock
|
|
46
|
+
- **Test** - Mock provider for testing
|
|
47
|
+
|
|
48
|
+
See [Providers](providers/01_PROVIDERS.md) for details.
|
|
49
|
+
|
|
50
|
+
### Messages
|
|
51
|
+
|
|
52
|
+
Messages represent the conversation between user and assistant. Riffer uses strongly-typed message objects:
|
|
53
|
+
|
|
54
|
+
- `Riffer::Messages::System` - System instructions
|
|
55
|
+
- `Riffer::Messages::User` - User input
|
|
56
|
+
- `Riffer::Messages::Assistant` - LLM responses
|
|
57
|
+
- `Riffer::Messages::Tool` - Tool execution results
|
|
58
|
+
|
|
59
|
+
See [Messages](05_MESSAGES.md) for details.
|
|
60
|
+
|
|
61
|
+
### Stream Events
|
|
62
|
+
|
|
63
|
+
When streaming responses, Riffer emits typed events:
|
|
64
|
+
|
|
65
|
+
- `TextDelta` - Incremental text chunks
|
|
66
|
+
- `TextDone` - Complete text
|
|
67
|
+
- `ToolCallDelta` - Incremental tool call arguments
|
|
68
|
+
- `ToolCallDone` - Complete tool call
|
|
69
|
+
|
|
70
|
+
See [Stream Events](06_STREAM_EVENTS.md) for details.
|
|
71
|
+
|
|
72
|
+
## Architecture
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
User Request
|
|
76
|
+
|
|
|
77
|
+
v
|
|
78
|
+
+------------+
|
|
79
|
+
| Agent | <-- Manages conversation flow
|
|
80
|
+
+------------+
|
|
81
|
+
|
|
|
82
|
+
v
|
|
83
|
+
+------------+
|
|
84
|
+
| Provider | <-- Calls LLM API
|
|
85
|
+
+------------+
|
|
86
|
+
|
|
|
87
|
+
v
|
|
88
|
+
+------------+
|
|
89
|
+
| LLM | <-- Returns response
|
|
90
|
+
+------------+
|
|
91
|
+
|
|
|
92
|
+
v
|
|
93
|
+
+------------+
|
|
94
|
+
| Tool? | <-- Execute if tool call present
|
|
95
|
+
+------------+
|
|
96
|
+
|
|
|
97
|
+
v
|
|
98
|
+
Response
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Next Steps
|
|
102
|
+
|
|
103
|
+
- [Getting Started](02_GETTING_STARTED.md) - Quick start guide
|
|
104
|
+
- [Agents](03_AGENTS.md) - Agent configuration and usage
|
|
105
|
+
- [Tools](04_TOOLS.md) - Creating tools
|
|
106
|
+
- [Configuration](07_CONFIGURATION.md) - Global configuration
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
This guide walks you through installing Riffer and creating your first AI agent.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add Riffer to your Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'riffer'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Then run:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bundle install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install directly:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
gem install riffer
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Provider Setup
|
|
26
|
+
|
|
27
|
+
Riffer requires an LLM provider. Install the provider gem for your chosen service:
|
|
28
|
+
|
|
29
|
+
### OpenAI
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
gem 'openai'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Configure your API key:
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
Riffer.configure do |config|
|
|
39
|
+
config.openai.api_key = ENV['OPENAI_API_KEY']
|
|
40
|
+
end
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Amazon Bedrock
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
gem 'aws-sdk-bedrockruntime'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Configure your credentials:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
Riffer.configure do |config|
|
|
53
|
+
config.amazon_bedrock.region = 'us-east-1'
|
|
54
|
+
# Optional: Use bearer token auth instead of IAM
|
|
55
|
+
config.amazon_bedrock.api_token = ENV['BEDROCK_API_TOKEN']
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Creating Your First Agent
|
|
60
|
+
|
|
61
|
+
Define an agent by subclassing `Riffer::Agent`:
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
require 'riffer'
|
|
65
|
+
|
|
66
|
+
Riffer.configure do |config|
|
|
67
|
+
config.openai.api_key = ENV['OPENAI_API_KEY']
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class GreetingAgent < Riffer::Agent
|
|
71
|
+
model 'openai/gpt-4o'
|
|
72
|
+
instructions 'You are a friendly assistant. Greet the user warmly.'
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
agent = GreetingAgent.new
|
|
76
|
+
response = agent.generate('Hello!')
|
|
77
|
+
puts response
|
|
78
|
+
# => "Hello! It's wonderful to meet you..."
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Streaming Responses
|
|
82
|
+
|
|
83
|
+
Use `stream` for real-time output:
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
agent = GreetingAgent.new
|
|
87
|
+
|
|
88
|
+
agent.stream('Tell me a story').each do |event|
|
|
89
|
+
case event
|
|
90
|
+
when Riffer::StreamEvents::TextDelta
|
|
91
|
+
print event.content
|
|
92
|
+
when Riffer::StreamEvents::TextDone
|
|
93
|
+
puts "\n[Done]"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Adding Tools
|
|
99
|
+
|
|
100
|
+
Tools let agents interact with external systems:
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
class TimeTool < Riffer::Tool
|
|
104
|
+
description "Gets the current time"
|
|
105
|
+
|
|
106
|
+
def call(context:)
|
|
107
|
+
Time.now.strftime('%Y-%m-%d %H:%M:%S')
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
class TimeAgent < Riffer::Agent
|
|
112
|
+
model 'openai/gpt-4o'
|
|
113
|
+
instructions 'You can tell the user the current time.'
|
|
114
|
+
uses_tools [TimeTool]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
agent = TimeAgent.new
|
|
118
|
+
puts agent.generate("What time is it?")
|
|
119
|
+
# => "The current time is 2024-01-15 14:30:00."
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Next Steps
|
|
123
|
+
|
|
124
|
+
- [Agents](03_AGENTS.md) - Agent configuration options
|
|
125
|
+
- [Tools](04_TOOLS.md) - Creating tools with parameters
|
|
126
|
+
- [Messages](05_MESSAGES.md) - Message types and history
|
|
127
|
+
- [Stream Events](06_STREAM_EVENTS.md) - Streaming event types
|
|
128
|
+
- [Providers](providers/01_PROVIDERS.md) - Provider-specific guides
|
data/docs/03_AGENTS.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# Agents
|
|
2
|
+
|
|
3
|
+
Agents are the central orchestrator in Riffer. They manage the conversation flow, call LLM providers, and handle tool execution.
|
|
4
|
+
|
|
5
|
+
## Defining an Agent
|
|
6
|
+
|
|
7
|
+
Create an agent by subclassing `Riffer::Agent`:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
class MyAgent < Riffer::Agent
|
|
11
|
+
model 'openai/gpt-4o'
|
|
12
|
+
instructions 'You are a helpful assistant.'
|
|
13
|
+
end
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Configuration Methods
|
|
17
|
+
|
|
18
|
+
### model
|
|
19
|
+
|
|
20
|
+
Sets the provider and model in `provider/model` format:
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
class MyAgent < Riffer::Agent
|
|
24
|
+
model 'openai/gpt-4o' # OpenAI
|
|
25
|
+
# or
|
|
26
|
+
model 'amazon_bedrock/anthropic.claude-3-sonnet-20240229-v1:0' # Bedrock
|
|
27
|
+
# or
|
|
28
|
+
model 'test/any' # Test provider
|
|
29
|
+
end
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### instructions
|
|
33
|
+
|
|
34
|
+
Sets system instructions for the agent:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
class MyAgent < Riffer::Agent
|
|
38
|
+
model 'openai/gpt-4o'
|
|
39
|
+
instructions 'You are an expert Ruby programmer. Provide concise answers.'
|
|
40
|
+
end
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### identifier
|
|
44
|
+
|
|
45
|
+
Sets a custom identifier (defaults to snake_case class name):
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
class MyAgent < Riffer::Agent
|
|
49
|
+
model 'openai/gpt-4o'
|
|
50
|
+
identifier 'custom_agent_name'
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
MyAgent.identifier # => "custom_agent_name"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### uses_tools
|
|
57
|
+
|
|
58
|
+
Registers tools the agent can use:
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
class MyAgent < Riffer::Agent
|
|
62
|
+
model 'openai/gpt-4o'
|
|
63
|
+
uses_tools [WeatherTool, TimeTool]
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Tools can also be resolved dynamically with a lambda:
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
class MyAgent < Riffer::Agent
|
|
71
|
+
model 'openai/gpt-4o'
|
|
72
|
+
|
|
73
|
+
uses_tools ->(context) {
|
|
74
|
+
tools = [PublicTool]
|
|
75
|
+
tools << AdminTool if context&.dig(:user)&.admin?
|
|
76
|
+
tools
|
|
77
|
+
}
|
|
78
|
+
end
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### provider_options
|
|
82
|
+
|
|
83
|
+
Passes options to the provider client:
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
class MyAgent < Riffer::Agent
|
|
87
|
+
model 'openai/gpt-4o'
|
|
88
|
+
provider_options api_key: ENV['CUSTOM_OPENAI_KEY']
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### model_options
|
|
93
|
+
|
|
94
|
+
Passes options to each LLM request:
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
class MyAgent < Riffer::Agent
|
|
98
|
+
model 'openai/gpt-4o'
|
|
99
|
+
model_options reasoning: 'medium', temperature: 0.7
|
|
100
|
+
end
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Instance Methods
|
|
104
|
+
|
|
105
|
+
### generate
|
|
106
|
+
|
|
107
|
+
Generates a response synchronously:
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
agent = MyAgent.new
|
|
111
|
+
|
|
112
|
+
# With a string prompt
|
|
113
|
+
response = agent.generate('Hello')
|
|
114
|
+
|
|
115
|
+
# With message objects/hashes
|
|
116
|
+
response = agent.generate([
|
|
117
|
+
{role: 'user', content: 'Hello'},
|
|
118
|
+
{role: 'assistant', content: 'Hi there!'},
|
|
119
|
+
{role: 'user', content: 'How are you?'}
|
|
120
|
+
])
|
|
121
|
+
|
|
122
|
+
# With tool context
|
|
123
|
+
response = agent.generate('Look up my orders', tool_context: {user_id: 123})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### stream
|
|
127
|
+
|
|
128
|
+
Streams a response as an Enumerator:
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
agent = MyAgent.new
|
|
132
|
+
|
|
133
|
+
agent.stream('Tell me a story').each do |event|
|
|
134
|
+
case event
|
|
135
|
+
when Riffer::StreamEvents::TextDelta
|
|
136
|
+
print event.content
|
|
137
|
+
when Riffer::StreamEvents::TextDone
|
|
138
|
+
puts "\n"
|
|
139
|
+
when Riffer::StreamEvents::ToolCallDone
|
|
140
|
+
puts "[Tool: #{event.name}]"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### messages
|
|
146
|
+
|
|
147
|
+
Access the message history after a generate/stream call:
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
agent = MyAgent.new
|
|
151
|
+
agent.generate('Hello')
|
|
152
|
+
|
|
153
|
+
agent.messages.each do |msg|
|
|
154
|
+
puts "#{msg.role}: #{msg.content}"
|
|
155
|
+
end
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### on_message
|
|
159
|
+
|
|
160
|
+
Registers a callback to receive messages as they're added during generation:
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
agent.on_message do |message|
|
|
164
|
+
case message.role
|
|
165
|
+
when :assistant
|
|
166
|
+
puts "[Assistant] #{message.content}"
|
|
167
|
+
when :tool
|
|
168
|
+
puts "[Tool:#{message.name}] #{message.content}"
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Multiple callbacks can be registered. Returns `self` for method chaining:
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
agent
|
|
177
|
+
.on_message { |msg| persist_message(msg) }
|
|
178
|
+
.on_message { |msg| log_message(msg) }
|
|
179
|
+
.generate('Hello')
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Works with both `generate` and `stream`. Only emits agent-generated messages (Assistant, Tool), not inputs (System, User).
|
|
183
|
+
|
|
184
|
+
## Class Methods
|
|
185
|
+
|
|
186
|
+
### find
|
|
187
|
+
|
|
188
|
+
Find an agent class by identifier:
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
agent_class = Riffer::Agent.find('my_agent')
|
|
192
|
+
agent = agent_class.new
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### all
|
|
196
|
+
|
|
197
|
+
List all agent subclasses:
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
Riffer::Agent.all.each do |agent_class|
|
|
201
|
+
puts agent_class.identifier
|
|
202
|
+
end
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Tool Execution Flow
|
|
206
|
+
|
|
207
|
+
When an agent receives a response with tool calls:
|
|
208
|
+
|
|
209
|
+
1. Agent detects `tool_calls` in the assistant message
|
|
210
|
+
2. For each tool call:
|
|
211
|
+
- Finds the matching tool class
|
|
212
|
+
- Validates arguments against the tool's parameter schema
|
|
213
|
+
- Calls the tool's `call` method with `context` and arguments
|
|
214
|
+
- Creates a Tool message with the result
|
|
215
|
+
3. Sends the updated message history back to the LLM
|
|
216
|
+
4. Repeats until no more tool calls
|
|
217
|
+
|
|
218
|
+
## Error Handling
|
|
219
|
+
|
|
220
|
+
Tool execution errors are captured and sent back to the LLM:
|
|
221
|
+
|
|
222
|
+
- `unknown_tool` - Tool not found in registered tools
|
|
223
|
+
- `validation_error` - Arguments failed validation
|
|
224
|
+
- `execution_error` - Tool raised an exception
|
|
225
|
+
|
|
226
|
+
The LLM can use this information to retry or respond appropriately.
|