raif 1.1.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 +150 -4
- data/app/assets/builds/raif.css +26 -1
- data/app/assets/stylesheets/raif/loader.scss +27 -1
- data/app/models/raif/concerns/llm_response_parsing.rb +22 -16
- data/app/models/raif/concerns/llms/anthropic/tool_formatting.rb +56 -0
- data/app/models/raif/concerns/llms/{bedrock_claude → bedrock}/message_formatting.rb +4 -4
- data/app/models/raif/concerns/llms/bedrock/tool_formatting.rb +37 -0
- data/app/models/raif/concerns/llms/message_formatting.rb +7 -6
- data/app/models/raif/concerns/llms/open_ai/json_schema_validation.rb +138 -0
- data/app/models/raif/concerns/llms/{open_ai → open_ai_completions}/message_formatting.rb +1 -1
- data/app/models/raif/concerns/llms/open_ai_completions/tool_formatting.rb +26 -0
- data/app/models/raif/concerns/llms/open_ai_responses/message_formatting.rb +43 -0
- data/app/models/raif/concerns/llms/open_ai_responses/tool_formatting.rb +42 -0
- data/app/models/raif/conversation.rb +17 -4
- data/app/models/raif/conversation_entry.rb +18 -2
- data/app/models/raif/embedding_models/{bedrock_titan.rb → bedrock.rb} +2 -2
- data/app/models/raif/llm.rb +73 -7
- data/app/models/raif/llms/anthropic.rb +56 -36
- data/app/models/raif/llms/{bedrock_claude.rb → bedrock.rb} +62 -45
- data/app/models/raif/llms/open_ai_base.rb +66 -0
- data/app/models/raif/llms/open_ai_completions.rb +100 -0
- data/app/models/raif/llms/open_ai_responses.rb +144 -0
- data/app/models/raif/llms/open_router.rb +44 -44
- data/app/models/raif/model_completion.rb +2 -0
- data/app/models/raif/model_tool.rb +4 -0
- data/app/models/raif/model_tools/provider_managed/base.rb +9 -0
- data/app/models/raif/model_tools/provider_managed/code_execution.rb +5 -0
- data/app/models/raif/model_tools/provider_managed/image_generation.rb +5 -0
- data/app/models/raif/model_tools/provider_managed/web_search.rb +5 -0
- data/app/models/raif/streaming_responses/anthropic.rb +63 -0
- data/app/models/raif/streaming_responses/bedrock.rb +89 -0
- data/app/models/raif/streaming_responses/open_ai_completions.rb +76 -0
- data/app/models/raif/streaming_responses/open_ai_responses.rb +54 -0
- data/app/views/raif/admin/conversations/_conversation_entry.html.erb +48 -0
- data/app/views/raif/admin/conversations/show.html.erb +1 -1
- data/app/views/raif/admin/model_completions/_model_completion.html.erb +7 -0
- data/app/views/raif/admin/model_completions/index.html.erb +1 -0
- data/app/views/raif/admin/model_completions/show.html.erb +28 -0
- data/app/views/raif/conversation_entries/_citations.html.erb +9 -0
- data/app/views/raif/conversation_entries/_conversation_entry.html.erb +5 -1
- data/app/views/raif/conversation_entries/_message.html.erb +4 -0
- data/config/locales/admin.en.yml +2 -0
- data/config/locales/en.yml +24 -0
- data/db/migrate/20250224234252_create_raif_tables.rb +1 -1
- data/db/migrate/20250421202149_add_response_format_to_raif_conversations.rb +1 -1
- data/db/migrate/20250424200755_add_cost_columns_to_raif_model_completions.rb +1 -1
- data/db/migrate/20250424232946_add_created_at_indexes.rb +1 -1
- data/db/migrate/20250502155330_add_status_indexes_to_raif_tasks.rb +1 -1
- data/db/migrate/20250527213016_add_response_id_and_response_array_to_model_completions.rb +14 -0
- data/db/migrate/20250603140622_add_citations_to_raif_model_completions.rb +13 -0
- data/db/migrate/20250603202013_add_stream_response_to_raif_model_completions.rb +7 -0
- data/lib/generators/raif/conversation/templates/conversation.rb.tt +3 -3
- data/lib/generators/raif/install/templates/initializer.rb +14 -2
- data/lib/raif/configuration.rb +27 -5
- data/lib/raif/embedding_model_registry.rb +1 -1
- data/lib/raif/engine.rb +25 -9
- data/lib/raif/errors/streaming_error.rb +18 -0
- data/lib/raif/errors.rb +1 -0
- data/lib/raif/llm_registry.rb +169 -47
- data/lib/raif/migration_checker.rb +74 -0
- data/lib/raif/utils/html_fragment_processor.rb +170 -0
- data/lib/raif/utils.rb +1 -0
- data/lib/raif/version.rb +1 -1
- data/lib/raif.rb +2 -0
- 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 +52 -8
- data/app/models/raif/llms/open_ai.rb +0 -256
@@ -0,0 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Utility class for processing HTML fragments with various cleaning and transformation operations.
|
4
|
+
#
|
5
|
+
# This class provides methods for sanitizing HTML content, converting markdown links to HTML,
|
6
|
+
# processing existing HTML links (adding target="_blank", stripping tracking parameters),
|
7
|
+
# and removing tracking parameters from URLs.
|
8
|
+
class Raif::Utils::HtmlFragmentProcessor
|
9
|
+
# List of common tracking parameters to remove from URLs
|
10
|
+
TRACKING_PARAMS = %w[
|
11
|
+
utm_source
|
12
|
+
utm_medium
|
13
|
+
utm_campaign
|
14
|
+
utm_term
|
15
|
+
utm_content
|
16
|
+
utm_id
|
17
|
+
]
|
18
|
+
|
19
|
+
class << self
|
20
|
+
# Cleans and sanitizes an HTML fragment by removing empty text nodes and dangerous content.
|
21
|
+
#
|
22
|
+
# @param html [String, Nokogiri::HTML::DocumentFragment] The HTML content to clean
|
23
|
+
# @param allowed_tags [Array<String>, nil] Array of allowed HTML tags. Defaults to Rails HTML5 safe list
|
24
|
+
# @param allowed_attributes [Array<String>, nil] Array of allowed HTML attributes. Defaults to Rails HTML5 safe list
|
25
|
+
# @return [String] Cleaned and sanitized HTML string
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# clean_html_fragment("<script>alert('xss')</script><p>Safe content</p>")
|
29
|
+
# # => "<p>Safe content</p>"
|
30
|
+
#
|
31
|
+
# @example With custom allowed tags
|
32
|
+
# clean_html_fragment("<p>Para</p><div>Div</div>", allowed_tags: %w[p])
|
33
|
+
# # => "<p>Para</p>Div"
|
34
|
+
def clean_html_fragment(html, allowed_tags: nil, allowed_attributes: nil)
|
35
|
+
fragment = html.is_a?(Nokogiri::HTML::DocumentFragment) ? html : Nokogiri::HTML.fragment(html)
|
36
|
+
|
37
|
+
fragment.traverse do |node|
|
38
|
+
if node.text? && node.text.strip.empty?
|
39
|
+
node.remove
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
allowed_tags = allowed_tags.presence || Rails::HTML5::SafeListSanitizer.allowed_tags
|
44
|
+
allowed_attributes = allowed_attributes.presence || Rails::HTML5::SafeListSanitizer.allowed_attributes
|
45
|
+
|
46
|
+
ActionController::Base.helpers.sanitize(fragment.to_html, tags: allowed_tags, attributes: allowed_attributes).strip
|
47
|
+
end
|
48
|
+
|
49
|
+
# Converts markdown-style links to HTML anchor tags with target="_blank" and rel="noopener".
|
50
|
+
#
|
51
|
+
# Converts [text](url) format to <a href="url" target="_blank" rel="noopener">text</a>.
|
52
|
+
# Also strips tracking parameters from the URLs.
|
53
|
+
#
|
54
|
+
# @param text [String] The text content that may contain markdown links
|
55
|
+
# @return [String] HTML with markdown links converted to anchor tags
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# convert_markdown_links_to_html("Check out [Google](https://google.com) for search.")
|
59
|
+
# # => 'Check out <a href="https://google.com" target="_blank" rel="noopener">Google</a> for search.'
|
60
|
+
#
|
61
|
+
# @example With tracking parameters
|
62
|
+
# convert_markdown_links_to_html("[Example](https://example.com?utm_source=test¶m=keep)")
|
63
|
+
# # => '<a href="https://example.com?param=keep" target="_blank" rel="noopener">Example</a>'
|
64
|
+
def convert_markdown_links_to_html(text)
|
65
|
+
# Convert markdown links [text](url) to HTML links <a href="url" target="_blank" rel="noopener">text</a>
|
66
|
+
text.gsub(/\[([^\]]*)\]\(([^)]+)\)/) do |_match|
|
67
|
+
text = ::Regexp.last_match(1)
|
68
|
+
url = ::Regexp.last_match(2)
|
69
|
+
clean_url = strip_tracking_parameters(url)
|
70
|
+
%(<a href="#{CGI.escapeHTML(clean_url)}" target="_blank" rel="noopener">#{CGI.escapeHTML(text)}</a>)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Processes existing HTML links by optionally adding target="_blank" and stripping tracking parameters.
|
75
|
+
#
|
76
|
+
# This method provides fine-grained control over link processing with configurable options
|
77
|
+
# for both target="_blank" addition and tracking parameter removal.
|
78
|
+
#
|
79
|
+
# @param html [String, Nokogiri::HTML::DocumentFragment] The HTML content containing links to process
|
80
|
+
# @param add_target_blank [Boolean] Whether to add target="_blank" and rel="noopener" to links (required)
|
81
|
+
# @param strip_tracking_parameters [Boolean] Whether to remove tracking parameters from URLs (required)
|
82
|
+
# @return [String] Processed HTML with modified links
|
83
|
+
#
|
84
|
+
# @example Default behavior (adds target="_blank" and strips tracking params)
|
85
|
+
# process_links('<a href="https://example.com?utm_source=test">Link</a>', add_target_blank: true, strip_tracking_parameters: true)
|
86
|
+
# # => '<a href="https://example.com" target="_blank" rel="noopener">Link</a>'
|
87
|
+
#
|
88
|
+
# @example Only strip tracking parameters
|
89
|
+
# process_links(html, add_target_blank: false, strip_tracking_parameters: true)
|
90
|
+
# # => '<a href="https://example.com">Link</a>'
|
91
|
+
#
|
92
|
+
# @example Only add target="_blank"
|
93
|
+
# process_links(html, add_target_blank: true, strip_tracking_parameters: false)
|
94
|
+
# # => '<a href="https://example.com?utm_source=test" target="_blank" rel="noopener">Link</a>'
|
95
|
+
#
|
96
|
+
# @example No processing
|
97
|
+
# process_links(html, add_target_blank: false, strip_tracking_parameters: false)
|
98
|
+
# # => Original HTML unchanged
|
99
|
+
def process_links(html, add_target_blank:, strip_tracking_parameters:)
|
100
|
+
fragment = html.is_a?(Nokogiri::HTML::DocumentFragment) ? html : Nokogiri::HTML.fragment(html)
|
101
|
+
|
102
|
+
fragment.css("a").each do |link|
|
103
|
+
if add_target_blank
|
104
|
+
link["target"] = "_blank"
|
105
|
+
link["rel"] = "noopener"
|
106
|
+
end
|
107
|
+
|
108
|
+
if strip_tracking_parameters
|
109
|
+
link["href"] = strip_tracking_parameters(link["href"])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
fragment.to_html
|
114
|
+
end
|
115
|
+
|
116
|
+
# Removes tracking parameters (UTM parameters) from a URL.
|
117
|
+
#
|
118
|
+
# Preserves all non-tracking query parameters and handles various URL formats including
|
119
|
+
# relative URLs, absolute URLs, and malformed URLs gracefully.
|
120
|
+
#
|
121
|
+
# @param url [String] The URL to clean
|
122
|
+
# @return [String] URL with tracking parameters removed, or original URL if parsing fails
|
123
|
+
#
|
124
|
+
# @example
|
125
|
+
# strip_tracking_parameters("https://example.com?utm_source=google&page=1")
|
126
|
+
# # => "https://example.com?page=1"
|
127
|
+
#
|
128
|
+
# @example Removes all tracking parameters
|
129
|
+
# strip_tracking_parameters("https://example.com?utm_source=test&utm_medium=cpc")
|
130
|
+
# # => "https://example.com"
|
131
|
+
#
|
132
|
+
# @example Preserves fragments
|
133
|
+
# strip_tracking_parameters("https://example.com?utm_source=test&page=1#section")
|
134
|
+
# # => "https://example.com?page=1#section"
|
135
|
+
#
|
136
|
+
# @example Handles relative URLs
|
137
|
+
# strip_tracking_parameters("/path?utm_source=test¶m=keep")
|
138
|
+
# # => "/path?param=keep"
|
139
|
+
def strip_tracking_parameters(url)
|
140
|
+
return url if url.blank?
|
141
|
+
return url unless url.include?("?")
|
142
|
+
|
143
|
+
begin
|
144
|
+
uri = URI.parse(url)
|
145
|
+
return url unless uri.query
|
146
|
+
|
147
|
+
# Only process URLs that have a valid scheme and host, or are relative URLs
|
148
|
+
unless uri.scheme || url.start_with?("/", "#")
|
149
|
+
return url
|
150
|
+
end
|
151
|
+
|
152
|
+
# Parse query parameters and filter out tracking ones
|
153
|
+
params = URI.decode_www_form(uri.query)
|
154
|
+
clean_params = params.reject { |param, _| TRACKING_PARAMS.include?(param.downcase) }
|
155
|
+
|
156
|
+
# Rebuild the URL
|
157
|
+
uri.query = if clean_params.empty?
|
158
|
+
nil
|
159
|
+
else
|
160
|
+
URI.encode_www_form(clean_params)
|
161
|
+
end
|
162
|
+
|
163
|
+
uri.to_s
|
164
|
+
rescue URI::InvalidURIError
|
165
|
+
# If URL parsing fails, return the original URL
|
166
|
+
url
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
data/lib/raif/utils.rb
CHANGED
data/lib/raif/version.rb
CHANGED
data/lib/raif.rb
CHANGED
@@ -9,8 +9,10 @@ require "raif/utils"
|
|
9
9
|
require "raif/llm_registry"
|
10
10
|
require "raif/embedding_model_registry"
|
11
11
|
require "raif/json_schema_builder"
|
12
|
+
require "raif/migration_checker"
|
12
13
|
|
13
14
|
require "faraday"
|
15
|
+
require "event_stream_parser"
|
14
16
|
require "json-schema"
|
15
17
|
require "loofah"
|
16
18
|
require "pagy"
|
@@ -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,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: raif
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.2.1.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Roesch
|
8
8
|
- Brian Leslie
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-bedrockruntime
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: event_stream_parser
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: faraday
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -175,18 +189,26 @@ files:
|
|
175
189
|
- app/models/raif/concerns/llm_response_parsing.rb
|
176
190
|
- app/models/raif/concerns/llm_temperature.rb
|
177
191
|
- app/models/raif/concerns/llms/anthropic/message_formatting.rb
|
178
|
-
- app/models/raif/concerns/llms/
|
192
|
+
- app/models/raif/concerns/llms/anthropic/tool_formatting.rb
|
193
|
+
- app/models/raif/concerns/llms/bedrock/message_formatting.rb
|
194
|
+
- app/models/raif/concerns/llms/bedrock/tool_formatting.rb
|
179
195
|
- app/models/raif/concerns/llms/message_formatting.rb
|
180
|
-
- app/models/raif/concerns/llms/open_ai/
|
196
|
+
- app/models/raif/concerns/llms/open_ai/json_schema_validation.rb
|
197
|
+
- app/models/raif/concerns/llms/open_ai_completions/message_formatting.rb
|
198
|
+
- app/models/raif/concerns/llms/open_ai_completions/tool_formatting.rb
|
199
|
+
- app/models/raif/concerns/llms/open_ai_responses/message_formatting.rb
|
200
|
+
- app/models/raif/concerns/llms/open_ai_responses/tool_formatting.rb
|
181
201
|
- app/models/raif/conversation.rb
|
182
202
|
- app/models/raif/conversation_entry.rb
|
183
203
|
- app/models/raif/embedding_model.rb
|
184
|
-
- app/models/raif/embedding_models/
|
204
|
+
- app/models/raif/embedding_models/bedrock.rb
|
185
205
|
- app/models/raif/embedding_models/open_ai.rb
|
186
206
|
- app/models/raif/llm.rb
|
187
207
|
- app/models/raif/llms/anthropic.rb
|
188
|
-
- app/models/raif/llms/
|
189
|
-
- app/models/raif/llms/
|
208
|
+
- app/models/raif/llms/bedrock.rb
|
209
|
+
- app/models/raif/llms/open_ai_base.rb
|
210
|
+
- app/models/raif/llms/open_ai_completions.rb
|
211
|
+
- app/models/raif/llms/open_ai_responses.rb
|
190
212
|
- app/models/raif/llms/open_router.rb
|
191
213
|
- app/models/raif/model_completion.rb
|
192
214
|
- app/models/raif/model_file_input.rb
|
@@ -195,7 +217,15 @@ files:
|
|
195
217
|
- app/models/raif/model_tool_invocation.rb
|
196
218
|
- app/models/raif/model_tools/agent_final_answer.rb
|
197
219
|
- app/models/raif/model_tools/fetch_url.rb
|
220
|
+
- app/models/raif/model_tools/provider_managed/base.rb
|
221
|
+
- app/models/raif/model_tools/provider_managed/code_execution.rb
|
222
|
+
- app/models/raif/model_tools/provider_managed/image_generation.rb
|
223
|
+
- app/models/raif/model_tools/provider_managed/web_search.rb
|
198
224
|
- app/models/raif/model_tools/wikipedia_search.rb
|
225
|
+
- app/models/raif/streaming_responses/anthropic.rb
|
226
|
+
- app/models/raif/streaming_responses/bedrock.rb
|
227
|
+
- app/models/raif/streaming_responses/open_ai_completions.rb
|
228
|
+
- app/models/raif/streaming_responses/open_ai_responses.rb
|
199
229
|
- app/models/raif/task.rb
|
200
230
|
- app/models/raif/user_tool_invocation.rb
|
201
231
|
- app/views/layouts/raif/admin.html.erb
|
@@ -218,6 +248,7 @@ files:
|
|
218
248
|
- app/views/raif/admin/tasks/_task.html.erb
|
219
249
|
- app/views/raif/admin/tasks/index.html.erb
|
220
250
|
- app/views/raif/admin/tasks/show.html.erb
|
251
|
+
- app/views/raif/conversation_entries/_citations.html.erb
|
221
252
|
- app/views/raif/conversation_entries/_conversation_entry.html.erb
|
222
253
|
- app/views/raif/conversation_entries/_form.html.erb
|
223
254
|
- app/views/raif/conversation_entries/_form_with_available_tools.html.erb
|
@@ -242,6 +273,9 @@ files:
|
|
242
273
|
- db/migrate/20250424232946_add_created_at_indexes.rb
|
243
274
|
- db/migrate/20250502155330_add_status_indexes_to_raif_tasks.rb
|
244
275
|
- db/migrate/20250507155314_add_retry_count_to_raif_model_completions.rb
|
276
|
+
- db/migrate/20250527213016_add_response_id_and_response_array_to_model_completions.rb
|
277
|
+
- db/migrate/20250603140622_add_citations_to_raif_model_completions.rb
|
278
|
+
- db/migrate/20250603202013_add_stream_response_to_raif_model_completions.rb
|
245
279
|
- lib/generators/raif/agent/agent_generator.rb
|
246
280
|
- lib/generators/raif/agent/templates/agent.rb.tt
|
247
281
|
- lib/generators/raif/agent/templates/application_agent.rb.tt
|
@@ -268,16 +302,26 @@ files:
|
|
268
302
|
- lib/raif/errors/invalid_model_image_input_error.rb
|
269
303
|
- lib/raif/errors/invalid_user_tool_type_error.rb
|
270
304
|
- lib/raif/errors/open_ai/json_schema_error.rb
|
305
|
+
- lib/raif/errors/streaming_error.rb
|
271
306
|
- lib/raif/errors/unsupported_feature_error.rb
|
272
307
|
- lib/raif/json_schema_builder.rb
|
273
308
|
- lib/raif/languages.rb
|
274
309
|
- lib/raif/llm_registry.rb
|
310
|
+
- lib/raif/migration_checker.rb
|
275
311
|
- lib/raif/rspec.rb
|
276
312
|
- lib/raif/utils.rb
|
313
|
+
- lib/raif/utils/html_fragment_processor.rb
|
277
314
|
- lib/raif/utils/html_to_markdown_converter.rb
|
278
315
|
- lib/raif/utils/readable_content_extractor.rb
|
279
316
|
- lib/raif/version.rb
|
280
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
|
281
325
|
homepage: https://github.com/cultivatelabs/raif
|
282
326
|
licenses:
|
283
327
|
- MIT
|
@@ -300,7 +344,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
300
344
|
- !ruby/object:Gem::Version
|
301
345
|
version: '0'
|
302
346
|
requirements: []
|
303
|
-
rubygems_version: 3.6.
|
347
|
+
rubygems_version: 3.6.7
|
304
348
|
specification_version: 4
|
305
349
|
summary: Raif (Ruby AI Framework) is a Rails engine that helps you add AI-powered
|
306
350
|
features to your Rails apps, such as tasks, conversations, and agents.
|