raif 1.2.0 → 1.2.1.pre
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/README.md +2 -0
- data/app/models/raif/llms/open_router.rb +5 -0
- data/config/locales/en.yml +2 -0
- data/lib/raif/llm_registry.rb +12 -0
- data/lib/raif/utils/html_fragment_processor.rb +1 -0
- data/lib/raif/version.rb +1 -1
- data/spec/support/complex_test_tool.rb +65 -0
- data/spec/support/rspec_helpers.rb +66 -0
- data/spec/support/test_conversation.rb +18 -0
- data/spec/support/test_embedding_model.rb +27 -0
- data/spec/support/test_llm.rb +22 -0
- data/spec/support/test_model_tool.rb +32 -0
- data/spec/support/test_task.rb +45 -0
- metadata +8 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1fb384de50e5d129f3cc88fd56ba12f8a45c5c25061b8dda631f3ce7593a681b
|
4
|
+
data.tar.gz: 4805d6a421cdff32a2ec857b53ced8e7244b32f918cda927bace04693d840771
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb2cb0bda00b00cbee10a08d8a634a7a06276ab555cfd7a224ea51d6ab800effc7fbaedb6ba2642687632c705bc4ee953bc0b74316431bfc2ba1e72e091aba7f
|
7
|
+
data.tar.gz: b4dc7c5b059054b5252f1d676f8dfa67253de3548da9a5c4fae813f675a77634d6bd4da477ae26129cebdd6515cb7c09de389445ec80f6bb69f89b10d620755c
|
data/README.md
CHANGED
@@ -195,6 +195,8 @@ Currently included OpenRouter models:
|
|
195
195
|
- `open_router_claude_3_7_sonnet`
|
196
196
|
- `open_router_llama_3_3_70b_instruct`
|
197
197
|
- `open_router_llama_3_1_8b_instruct`
|
198
|
+
- `open_router_llama_4_maverick`
|
199
|
+
- `open_router_llama_4_scout`
|
198
200
|
- `open_router_gemini_2_0_flash`
|
199
201
|
- `open_router_deepseek_chat_v3`
|
200
202
|
|
@@ -72,6 +72,11 @@ private
|
|
72
72
|
params[:stream_options] = { include_usage: true }
|
73
73
|
end
|
74
74
|
|
75
|
+
if model_completion.response_format_json?
|
76
|
+
params[:response_format] = { type: "json_object" }
|
77
|
+
model_completion.response_format_parameter = "json_object"
|
78
|
+
end
|
79
|
+
|
75
80
|
params
|
76
81
|
end
|
77
82
|
|
data/config/locales/en.yml
CHANGED
@@ -91,4 +91,6 @@ en:
|
|
91
91
|
open_router_gemini_2_0_flash: Google Gemini 2.0 Flash (via OpenRouter)
|
92
92
|
open_router_llama_3_1_8b_instruct: Meta Llama 3.1 8B Instruct (via OpenRouter)
|
93
93
|
open_router_llama_3_3_70b_instruct: Meta Llama 3.3 70B Instruct (via OpenRouter)
|
94
|
+
open_router_llama_4_maverick: Meta Llama 4 Maverick (via OpenRouter)
|
95
|
+
open_router_llama_4_scout: Meta Llama 4 Scout (via OpenRouter)
|
94
96
|
raif_test_llm: Raif Test LLM
|
data/lib/raif/llm_registry.rb
CHANGED
@@ -297,6 +297,18 @@ module Raif
|
|
297
297
|
input_token_cost: 0.02 / 1_000_000,
|
298
298
|
output_token_cost: 0.03 / 1_000_000,
|
299
299
|
},
|
300
|
+
{
|
301
|
+
key: :open_router_llama_4_maverick,
|
302
|
+
api_name: "meta-llama/llama-4-maverick",
|
303
|
+
input_token_cost: 0.15 / 1_000_000,
|
304
|
+
output_token_cost: 0.60 / 1_000_000,
|
305
|
+
},
|
306
|
+
{
|
307
|
+
key: :open_router_llama_4_scout,
|
308
|
+
api_name: "meta-llama/llama-4-scout",
|
309
|
+
input_token_cost: 0.08 / 1_000_000,
|
310
|
+
output_token_cost: 0.30 / 1_000_000,
|
311
|
+
},
|
300
312
|
{
|
301
313
|
key: :open_router_gemini_2_0_flash,
|
302
314
|
api_name: "google/gemini-2.0-flash-001",
|
@@ -137,6 +137,7 @@ class Raif::Utils::HtmlFragmentProcessor
|
|
137
137
|
# strip_tracking_parameters("/path?utm_source=test¶m=keep")
|
138
138
|
# # => "/path?param=keep"
|
139
139
|
def strip_tracking_parameters(url)
|
140
|
+
return url if url.blank?
|
140
141
|
return url unless url.include?("?")
|
141
142
|
|
142
143
|
begin
|
data/lib/raif/version.rb
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Raif::ModelTools::ComplexTestTool < Raif::ModelTool
|
4
|
+
tool_arguments_schema do
|
5
|
+
string :title, description: "The title of the operation", minLength: 3
|
6
|
+
|
7
|
+
object :settings, description: "Configuration settings" do
|
8
|
+
boolean :enabled, description: "Whether the tool is enabled"
|
9
|
+
integer :priority, description: "Priority level (1-10)", minimum: 1, maximum: 10
|
10
|
+
array :tags, description: "Associated tags" do
|
11
|
+
items type: "string"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
array :products, description: "List of products" do
|
16
|
+
object do
|
17
|
+
integer :id, description: "Product identifier"
|
18
|
+
string :name, description: "Product name"
|
19
|
+
number :price, description: "Product price", minimum: 0
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
example_model_invocation do
|
25
|
+
{
|
26
|
+
"name" => tool_name,
|
27
|
+
"arguments" => {
|
28
|
+
"title" => "Daily Inventory Update",
|
29
|
+
"settings" => {
|
30
|
+
"enabled" => true,
|
31
|
+
"priority" => 5,
|
32
|
+
"tags" => ["inventory", "daily-update", "retail"]
|
33
|
+
},
|
34
|
+
"items" => [
|
35
|
+
{ "id" => 101, "name" => "Wireless Mouse", "price" => 25.99 },
|
36
|
+
{ "id" => 102, "name" => "Keyboard", "price" => 45.0 },
|
37
|
+
{ "id" => 103, "name" => "USB-C Cable", "price" => 9.99 }
|
38
|
+
]
|
39
|
+
}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
tool_description do
|
44
|
+
"An example tool demonstrating complex schema capabilities"
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.process_invocation(tool_invocation)
|
48
|
+
# This would be the actual implementation
|
49
|
+
# For demonstration purposes, just echo back the arguments
|
50
|
+
tool_invocation.update!(
|
51
|
+
result: {
|
52
|
+
message: "Received complex example request",
|
53
|
+
arguments: tool_invocation.tool_arguments
|
54
|
+
}
|
55
|
+
)
|
56
|
+
|
57
|
+
tool_invocation.result
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.observation_for_invocation(tool_invocation)
|
61
|
+
return "No results" unless tool_invocation.result.present?
|
62
|
+
|
63
|
+
"Successfully processed request for: #{tool_invocation.tool_arguments["title"]}"
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Raif
|
4
|
+
module RspecHelpers
|
5
|
+
|
6
|
+
def stubbed_llm(llm_model_key, &block)
|
7
|
+
test_llm = Raif.llm(llm_model_key.to_sym)
|
8
|
+
|
9
|
+
allow(test_llm).to receive(:perform_model_completion!) do |model_completion|
|
10
|
+
result = block.call(model_completion.messages, model_completion)
|
11
|
+
model_completion.raw_response = result if result.is_a?(String)
|
12
|
+
model_completion.completion_tokens = rand(100..2000)
|
13
|
+
model_completion.prompt_tokens = rand(100..2000)
|
14
|
+
model_completion.total_tokens = model_completion.completion_tokens + model_completion.prompt_tokens
|
15
|
+
model_completion.save!
|
16
|
+
|
17
|
+
model_completion
|
18
|
+
end
|
19
|
+
|
20
|
+
test_llm
|
21
|
+
end
|
22
|
+
|
23
|
+
def stub_raif_task(task, &block)
|
24
|
+
allow(Raif.config).to receive(:llm_api_requests_enabled){ true }
|
25
|
+
|
26
|
+
if task.is_a?(Raif::Task)
|
27
|
+
allow(task).to receive(:llm){ stubbed_llm(task.llm_model_key, &block) }
|
28
|
+
else
|
29
|
+
allow_any_instance_of(task).to receive(:llm) do |task_instance|
|
30
|
+
stubbed_llm(task_instance.llm_model_key, &block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def stub_raif_conversation(conversation, &block)
|
36
|
+
allow(Raif.config).to receive(:llm_api_requests_enabled){ true }
|
37
|
+
|
38
|
+
if conversation.is_a?(Raif::Conversation)
|
39
|
+
allow(conversation).to receive(:llm){ stubbed_llm(conversation.llm_model_key, &block) }
|
40
|
+
else
|
41
|
+
allow_any_instance_of(conversation).to receive(:llm) do |conversation_instance|
|
42
|
+
stubbed_llm(conversation_instance.llm_model_key, &block)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def stub_raif_agent(agent, &block)
|
48
|
+
allow(Raif.config).to receive(:llm_api_requests_enabled){ true }
|
49
|
+
|
50
|
+
if agent.is_a?(Raif::Agent)
|
51
|
+
allow(agent).to receive(:llm){ stubbed_llm(agent.llm_model_key, &block) }
|
52
|
+
else
|
53
|
+
allow_any_instance_of(agent).to receive(:llm) do |agent_instance|
|
54
|
+
stubbed_llm(agent_instance.llm_model_key, &block)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def stub_raif_llm(llm, &block)
|
60
|
+
llm.chat_handler = block
|
61
|
+
allow(Raif.config).to receive(:llm_api_requests_enabled){ true }
|
62
|
+
llm
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Raif::TestConversation < Raif::Conversation
|
4
|
+
|
5
|
+
before_create :populate_available_model_tools
|
6
|
+
|
7
|
+
def populate_available_model_tools
|
8
|
+
self.available_model_tools = [
|
9
|
+
"Raif::TestModelTool",
|
10
|
+
"Raif::ModelTools::WikipediaSearch",
|
11
|
+
]
|
12
|
+
end
|
13
|
+
|
14
|
+
def process_model_response_message(message:, entry:)
|
15
|
+
message.gsub("jerk", "[REDACTED]")
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Raif
|
4
|
+
module EmbeddingModels
|
5
|
+
class Test < Raif::EmbeddingModel
|
6
|
+
attr_accessor :embedding_handler
|
7
|
+
|
8
|
+
def generate_embedding!(input, dimensions: nil)
|
9
|
+
if input.is_a?(Array)
|
10
|
+
input.map { |text| generate_test_embedding!(text, dimensions:) }
|
11
|
+
else
|
12
|
+
generate_test_embedding!(input, dimensions:)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate_test_embedding!(input, dimensions: nil)
|
17
|
+
if embedding_handler.present?
|
18
|
+
embedding_handler.call(input, dimensions)
|
19
|
+
else
|
20
|
+
dimensions ||= default_output_vector_size
|
21
|
+
Array.new(dimensions) { rand(-1.0..1.0) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Raif
|
4
|
+
module Llms
|
5
|
+
class TestLlm < Raif::Llm
|
6
|
+
include Raif::Concerns::Llms::OpenAiCompletions::MessageFormatting
|
7
|
+
|
8
|
+
attr_accessor :chat_handler
|
9
|
+
|
10
|
+
def perform_model_completion!(model_completion)
|
11
|
+
result = chat_handler.call(model_completion.messages, model_completion)
|
12
|
+
model_completion.raw_response = result if result.is_a?(String)
|
13
|
+
model_completion.completion_tokens = rand(100..2000)
|
14
|
+
model_completion.prompt_tokens = rand(100..2000)
|
15
|
+
model_completion.total_tokens = model_completion.completion_tokens + model_completion.prompt_tokens
|
16
|
+
model_completion.save!
|
17
|
+
|
18
|
+
model_completion
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Raif::TestModelTool < Raif::ModelTool
|
4
|
+
tool_arguments_schema do
|
5
|
+
array :items do
|
6
|
+
object do
|
7
|
+
string :title, description: "The title of the item"
|
8
|
+
string :description
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
example_model_invocation do
|
14
|
+
{
|
15
|
+
"name": tool_name,
|
16
|
+
"arguments": { "items": [{ "title": "foo", "description": "bar" }] }
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.process_invocation(tool_arguments)
|
21
|
+
end
|
22
|
+
|
23
|
+
tool_description do
|
24
|
+
"Mock Tool Description"
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.observation_for_invocation(tool_invocation)
|
28
|
+
return if tool_invocation.result.blank?
|
29
|
+
|
30
|
+
"Mock Observation for #{tool_invocation.id}. Result was: #{tool_invocation.result["status"]}"
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Raif::TestTask < Raif::Task
|
4
|
+
llm_temperature 0.5
|
5
|
+
|
6
|
+
def build_prompt
|
7
|
+
"Tell me a joke"
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_system_prompt
|
11
|
+
super + "\nYou are also good at telling jokes."
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Raif::TestJsonTask < Raif::Task
|
16
|
+
llm_response_format :json
|
17
|
+
llm_temperature 0.75
|
18
|
+
|
19
|
+
json_response_schema do
|
20
|
+
string :joke
|
21
|
+
string :answer
|
22
|
+
end
|
23
|
+
|
24
|
+
def build_prompt
|
25
|
+
"Tell me a joke"
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_system_prompt
|
29
|
+
super + "\nYou are also good at telling jokes. Your response should be a JSON object with the following keys: joke, answer."
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Raif::TestHtmlTask < Raif::Task
|
34
|
+
llm_response_format :html
|
35
|
+
llm_response_allowed_tags %w[p b i u s a]
|
36
|
+
llm_response_allowed_attributes %w[style href]
|
37
|
+
|
38
|
+
def build_prompt
|
39
|
+
"Tell me a joke"
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_system_prompt
|
43
|
+
super + "\nYou are also good at telling jokes. Your response should be an HTML snippet that is formatted with basic HTML tags."
|
44
|
+
end
|
45
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: raif
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.1.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Roesch
|
@@ -315,6 +315,13 @@ files:
|
|
315
315
|
- lib/raif/utils/readable_content_extractor.rb
|
316
316
|
- lib/raif/version.rb
|
317
317
|
- lib/tasks/raif_tasks.rake
|
318
|
+
- spec/support/complex_test_tool.rb
|
319
|
+
- spec/support/rspec_helpers.rb
|
320
|
+
- spec/support/test_conversation.rb
|
321
|
+
- spec/support/test_embedding_model.rb
|
322
|
+
- spec/support/test_llm.rb
|
323
|
+
- spec/support/test_model_tool.rb
|
324
|
+
- spec/support/test_task.rb
|
318
325
|
homepage: https://github.com/cultivatelabs/raif
|
319
326
|
licenses:
|
320
327
|
- MIT
|