raif 1.2.1 → 1.3.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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +29 -935
  3. data/app/assets/builds/raif_admin.css +5 -1
  4. data/app/assets/images/raif-logo-white.svg +8 -0
  5. data/app/assets/stylesheets/raif_admin.scss +4 -0
  6. data/app/jobs/raif/conversation_entry_job.rb +1 -1
  7. data/app/models/raif/agents/re_act_step.rb +1 -2
  8. data/app/models/raif/concerns/has_llm.rb +1 -1
  9. data/app/models/raif/concerns/task_run_args.rb +62 -0
  10. data/app/models/raif/conversation.rb +8 -0
  11. data/app/models/raif/conversation_entry.rb +6 -9
  12. data/app/models/raif/llm.rb +1 -1
  13. data/app/models/raif/llms/open_router.rb +47 -4
  14. data/app/models/raif/task.rb +22 -9
  15. data/app/views/layouts/raif/admin.html.erb +3 -1
  16. data/app/views/raif/conversation_entries/_form.html.erb +1 -1
  17. data/app/views/raif/conversations/_full_conversation.html.erb +3 -6
  18. data/app/views/raif/conversations/_initial_chat_message.html.erb +5 -0
  19. data/config/locales/en.yml +8 -0
  20. data/db/migrate/20250804013843_add_task_run_args_to_raif_tasks.rb +13 -0
  21. data/db/migrate/20250811171150_make_raif_task_creator_optional.rb +8 -0
  22. data/exe/raif +7 -0
  23. data/lib/generators/raif/agent/agent_generator.rb +22 -7
  24. data/lib/generators/raif/agent/templates/agent.rb.tt +20 -24
  25. data/lib/generators/raif/agent/templates/agent_eval_set.rb.tt +48 -0
  26. data/lib/generators/raif/agent/templates/application_agent.rb.tt +0 -2
  27. data/lib/generators/raif/base_generator.rb +19 -0
  28. data/lib/generators/raif/conversation/conversation_generator.rb +21 -2
  29. data/lib/generators/raif/conversation/templates/application_conversation.rb.tt +0 -2
  30. data/lib/generators/raif/conversation/templates/conversation.rb.tt +29 -33
  31. data/lib/generators/raif/conversation/templates/conversation_eval_set.rb.tt +70 -0
  32. data/lib/generators/raif/eval_set/eval_set_generator.rb +28 -0
  33. data/lib/generators/raif/eval_set/templates/eval_set.rb.tt +21 -0
  34. data/lib/generators/raif/evals/setup/setup_generator.rb +47 -0
  35. data/lib/generators/raif/install/install_generator.rb +15 -0
  36. data/lib/generators/raif/install/templates/initializer.rb +14 -3
  37. data/lib/generators/raif/model_tool/model_tool_generator.rb +5 -2
  38. data/lib/generators/raif/model_tool/templates/model_tool.rb.tt +78 -76
  39. data/lib/generators/raif/model_tool/templates/model_tool_invocation_partial.html.erb.tt +10 -0
  40. data/lib/generators/raif/task/task_generator.rb +22 -3
  41. data/lib/generators/raif/task/templates/application_task.rb.tt +0 -2
  42. data/lib/generators/raif/task/templates/task.rb.tt +55 -59
  43. data/lib/generators/raif/task/templates/task_eval_set.rb.tt +54 -0
  44. data/lib/raif/cli/base.rb +39 -0
  45. data/lib/raif/cli/evals.rb +47 -0
  46. data/lib/raif/cli/evals_setup.rb +27 -0
  47. data/lib/raif/cli.rb +67 -0
  48. data/lib/raif/configuration.rb +23 -9
  49. data/lib/raif/engine.rb +2 -1
  50. data/lib/raif/evals/eval.rb +30 -0
  51. data/lib/raif/evals/eval_set.rb +111 -0
  52. data/lib/raif/evals/eval_sets/expectations.rb +53 -0
  53. data/lib/raif/evals/eval_sets/llm_judge_expectations.rb +255 -0
  54. data/lib/raif/evals/expectation_result.rb +39 -0
  55. data/lib/raif/evals/llm_judge.rb +32 -0
  56. data/lib/raif/evals/llm_judges/binary.rb +94 -0
  57. data/lib/raif/evals/llm_judges/comparative.rb +89 -0
  58. data/lib/raif/evals/llm_judges/scored.rb +63 -0
  59. data/lib/raif/evals/llm_judges/summarization.rb +166 -0
  60. data/lib/raif/evals/run.rb +201 -0
  61. data/lib/raif/evals/scoring_rubric.rb +174 -0
  62. data/lib/raif/evals.rb +26 -0
  63. data/lib/raif/llm_registry.rb +33 -0
  64. data/lib/raif/migration_checker.rb +3 -3
  65. data/lib/raif/utils/colors.rb +23 -0
  66. data/lib/raif/utils.rb +1 -0
  67. data/lib/raif/version.rb +1 -1
  68. data/lib/raif.rb +4 -0
  69. data/spec/support/current_temperature_test_tool.rb +34 -0
  70. data/spec/support/test_conversation.rb +1 -1
  71. metadata +37 -3
@@ -1,87 +1,89 @@
1
- # frozen_string_literal: true
1
+ <% raif_module_namespacing(["ModelTools"]) do -%>
2
+ class <%= class_name.demodulize %> < Raif::ModelTool
3
+ # For example tool implementations, see:
4
+ # Wikipedia Search Tool: https://github.com/CultivateLabs/raif/blob/main/app/models/raif/model_tools/wikipedia_search.rb
5
+ # Fetch URL Tool: https://github.com/CultivateLabs/raif/blob/main/app/models/raif/model_tools/fetch_url.rb
2
6
 
3
- class Raif::ModelTools::<%= class_name %> < Raif::ModelTool
4
- # For example tool implementations, see:
5
- # Wikipedia Search Tool: https://github.com/CultivateLabs/raif/blob/main/app/models/raif/model_tools/wikipedia_search.rb
6
- # Fetch URL Tool: https://github.com/CultivateLabs/raif/blob/main/app/models/raif/model_tools/fetch_url.rb
7
-
8
- tool_description do
9
- "Description of your tool that will be provided to the LLM so it knows when to invoke it"
10
- end
7
+ tool_description do
8
+ "Description of your tool that will be provided to the LLM so it knows when to invoke it"
9
+ end
11
10
 
12
- # Define the schema for the arguments that the LLM should use when invoking your tool.
13
- # It should be a valid JSON schema. When the model invokes your tool,
14
- # the arguments it provides will be validated against this schema using JSON::Validator from the json-schema gem.
15
- #
16
- # All attributes will be required and additionalProperties will be set to false.
17
- tool_arguments_schema do
18
- # string :title, description: "The title of the operation", minLength: 3
11
+ # Define the schema for the arguments that the LLM should use when invoking your tool.
12
+ # It should be a valid JSON schema. When the model invokes your tool,
13
+ # the arguments it provides will be validated against this schema using JSON::Validator from the json-schema gem.
19
14
  #
20
- # object :widget, description: "A widget's description" do
21
- # boolean :is_red, description: "Whether the widget is red"
22
- # integer :rating, description: "A rating of the widget from 1 to 10", minimum: 1, maximum: 10
23
- # array :tags, description: "Associated tags" do
24
- # items type: "string"
25
- # end
26
- # end
15
+ # All attributes will be required and additionalProperties will be set to false.
27
16
  #
28
- # array :products, description: "List of products" do
29
- # object do
30
- # integer :id, description: "Product identifier"
31
- # string :name, description: "Product name"
32
- # number :price, description: "Product price", minimum: 0
33
- # end
34
- # end
35
- end
17
+ # See https://docs.raif.ai/learn_more/json_schemas for more information about defining JSON schemas.
18
+ tool_arguments_schema do
19
+ # string :title, description: "The title of the operation", minLength: 3
20
+ #
21
+ # object :widget, description: "A widget's description" do
22
+ # boolean :is_red, description: "Whether the widget is red"
23
+ # integer :rating, description: "A rating of the widget from 1 to 10", minimum: 1, maximum: 10
24
+ # array :tags, description: "Associated tags" do
25
+ # items type: "string"
26
+ # end
27
+ # end
28
+ #
29
+ # array :products, description: "List of products" do
30
+ # object do
31
+ # integer :id, description: "Product identifier"
32
+ # string :name, description: "Product name"
33
+ # number :price, description: "Product price", minimum: 0
34
+ # end
35
+ # end
36
+ end
36
37
 
37
- # An example of how the LLM should invoke your tool. This should return a hash with name and arguments keys.
38
- # `to_json` will be called on it and provided to the LLM as an example of how to invoke your tool.
39
- example_model_invocation do
40
- {
41
- "name": tool_name,
42
- "arguments": { }
43
- }
44
- end
38
+ # An example of how the LLM should invoke your tool. This should return a hash with name and arguments keys.
39
+ # `to_json` will be called on it and provided to the LLM as an example of how to invoke your tool.
40
+ example_model_invocation do
41
+ {
42
+ "name": tool_name,
43
+ "arguments": {}
44
+ }
45
+ end
45
46
 
46
- class << self
47
- # When your tool is invoked by the LLM in a Raif::Agent loop,
48
- # the results of the tool invocation are provided back to the LLM as an observation.
49
- # This method should return whatever you want provided to the LLM.
50
- # For example, if you were implementing a GoogleSearch tool, this might return a JSON
51
- # object containing search results for the query.
52
- def observation_for_invocation(tool_invocation)
53
- return "No results found" unless tool_invocation.result.present?
47
+ class << self
48
+ # When your tool is invoked by the LLM in a Raif::Agent loop,
49
+ # the results of the tool invocation are provided back to the LLM as an observation.
50
+ # This method should return whatever you want provided to the LLM.
51
+ # For example, if you were implementing a GoogleSearch tool, this might return a JSON
52
+ # object containing search results for the query.
53
+ def observation_for_invocation(tool_invocation)
54
+ return "No results found" unless tool_invocation.result.present?
54
55
 
55
- JSON.pretty_generate(tool_invocation.result)
56
- end
56
+ JSON.pretty_generate(tool_invocation.result)
57
+ end
57
58
 
58
- # When your tool is invoked in a Raif::Conversation, should the result be automatically provided back to the model?
59
- # When true, observation_for_invocation will be used to produce the observation provided to the model
60
- def triggers_observation_to_model?
61
- false
62
- end
59
+ # When your tool is invoked in a Raif::Conversation, should the result be automatically provided back to the model?
60
+ # When true, observation_for_invocation will be used to produce the observation provided to the model
61
+ def triggers_observation_to_model?
62
+ false
63
+ end
64
+
65
+ # When the LLM invokes your tool, this method will be called with a `Raif::ModelToolInvocation` record as an argument.
66
+ # It should handle the actual execution of the tool.
67
+ # For example, if you are implementing a GoogleSearch tool, this method should run the actual search
68
+ # and store the results in the tool_invocation's result JSON column.
69
+ def process_invocation(tool_invocation)
70
+ # Extract arguments from tool_invocation.tool_arguments
71
+ # query = tool_invocation.tool_arguments["query"]
72
+ #
73
+ # Process the invocation and perform the desired action
74
+ # ...
75
+ #
76
+ # Store the results in the tool_invocation
77
+ # tool_invocation.update!(
78
+ # result: {
79
+ # # Your result data structure
80
+ # }
81
+ # )
82
+ #
83
+ # Return the result
84
+ # tool_invocation.result
85
+ end
63
86
 
64
- # When the LLM invokes your tool, this method will be called with a `Raif::ModelToolInvocation` record as an argument.
65
- # It should handle the actual execution of the tool.
66
- # For example, if you are implementing a GoogleSearch tool, this method should run the actual search
67
- # and store the results in the tool_invocation's result JSON column.
68
- def process_invocation(tool_invocation)
69
- # Extract arguments from tool_invocation.tool_arguments
70
- # query = tool_invocation.tool_arguments["query"]
71
- #
72
- # Process the invocation and perform the desired action
73
- # ...
74
- #
75
- # Store the results in the tool_invocation
76
- # tool_invocation.update!(
77
- # result: {
78
- # # Your result data structure
79
- # }
80
- # )
81
- #
82
- # Return the result
83
- # tool_invocation.result
84
87
  end
85
88
  end
86
-
87
- end
89
+ <% end -%>
@@ -0,0 +1,10 @@
1
+ <%%#
2
+ This partial is used to render a model tool invocation to the user in the conversation interface.
3
+ If you don't want the tool invocation to be displayed to the user, you can override the `renderable?` method in your model tool class to return false
4
+ %>
5
+
6
+ <div class="raif-model-tool-invocation">
7
+ <h5><%%= <%= file_name %>.tool_type.demodulize.titleize %> Result</h5>
8
+ <pre><%%= JSON.pretty_generate(<%= file_name %>.result || {}) %></pre>
9
+ <p>Edit this file in <code><%%= __FILE__ %></code> to customize the display of the tool invocation.</p>
10
+ </div>
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../base_generator"
4
+
3
5
  module Raif
4
6
  module Generators
5
- class TaskGenerator < Rails::Generators::NamedBase
7
+ class TaskGenerator < BaseGenerator
6
8
  source_root File.expand_path("templates", __dir__)
7
9
 
8
10
  class_option :response_format,
@@ -10,6 +12,11 @@ module Raif
10
12
  default: "text",
11
13
  desc: "Response format for the task (text, html, or json)"
12
14
 
15
+ class_option :skip_eval_set,
16
+ type: :boolean,
17
+ default: false,
18
+ desc: "Skip generating the corresponding eval set"
19
+
13
20
  def create_application_task
14
21
  template "application_task.rb.tt", "app/models/raif/application_task.rb" unless File.exist?("app/models/raif/application_task.rb")
15
22
  end
@@ -18,11 +25,23 @@ module Raif
18
25
  template "task.rb.tt", File.join("app/models/raif/tasks", class_path, "#{file_name}.rb")
19
26
  end
20
27
 
28
+ def create_eval_set
29
+ return if options[:skip_eval_set]
30
+
31
+ template "task_eval_set.rb.tt", eval_set_file_path
32
+ end
33
+
34
+ def show_instructions
35
+ say "\nTask created!"
36
+ say ""
37
+ end
38
+
21
39
  private
22
40
 
23
- def task_class_name
24
- class_name
41
+ def eval_set_file_path
42
+ File.join("raif_evals", "eval_sets", "tasks", class_path, "#{file_name}_eval_set.rb")
25
43
  end
44
+
26
45
  end
27
46
  end
28
47
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Raif
4
2
  class ApplicationTask < Raif::Task
5
3
  # Add any shared task behavior here
@@ -1,63 +1,59 @@
1
- # frozen_string_literal: true
2
-
3
- module Raif
4
- module Tasks
5
- class <%= task_class_name %> < Raif::ApplicationTask
6
- # Set the response format for the task. Options are :html, :text, or :json.
7
- llm_response_format :<%= options[:response_format] %>
8
-
9
- # Set the temperature for the task
10
- # llm_temperature 0.7
11
-
12
- # Optional: Set the allowed tags for the task. Only relevant if response_format is :html.
13
- # Defaults to Rails::HTML5::SafeListSanitizer.allowed_tags
14
- # llm_response_allowed_tags %w[p b i div strong]
15
-
16
- # Optional: Set the allowed attributes for the task. Only relevant if response_format is :html.
17
- # Defaults to Rails::HTML5::SafeListSanitizer.allowed_attributes
18
- # llm_response_allowed_attributes %w[style]
19
-
20
- # Define any attributes that are needed for the task.
21
- # You can then pass them when running the task and they will be available in build_prompt:
22
- # Raif::Tasks::<%= task_class_name %>.run(your_attribute: "some value")
23
- # attr_accessor :your_attribute
24
-
25
- <%- if options[:response_format] == "json" -%>
26
- # Define a JSON schema that the model's response should adhere to
1
+ <% raif_module_namespacing(["Tasks"]) do -%>
2
+ class <%= class_name.demodulize %> < Raif::ApplicationTask
3
+ # Set the response format for the task. Options are :html, :text, or :json.
4
+ llm_response_format :<%= options[:response_format] %>
5
+
6
+ # Set the temperature for the task
7
+ # llm_temperature 0.7
8
+
9
+ # Optional: Set the allowed tags for the task. Only relevant if response_format is :html.
10
+ # Defaults to Rails::HTML5::SafeListSanitizer.allowed_tags
11
+ # llm_response_allowed_tags %w[p b i div strong]
12
+
13
+ # Optional: Set the allowed attributes for the task. Only relevant if response_format is :html.
14
+ # Defaults to Rails::HTML5::SafeListSanitizer.allowed_attributes
15
+ # llm_response_allowed_attributes %w[style]
16
+
17
+ # Define any attributes that are needed for the task.
18
+ # You can then pass them when running the task and they will be available in build_prompt:
19
+ # Raif::Tasks::<%= class_name %>.run(your_attribute: "some value")
20
+ # task_run_arg :your_attribute
21
+ <%- if options[:response_format] == "json" -%>
22
+
23
+ # Define a JSON schema that the model's response should adhere to
24
+ #
25
+ # All attributes will be required and additionalProperties will be set to false.
26
+ json_response_schema do
27
+ # string :title, description: "The title of the operation", minLength: 3
27
28
  #
28
- # All attributes will be required and additionalProperties will be set to false.
29
- json_response_schema do
30
- # string :title, description: "The title of the operation", minLength: 3
31
- #
32
- # object :widget, description: "A widget's description" do
33
- # boolean :is_red, description: "Whether the widget is red"
34
- # integer :rating, description: "A rating of the widget from 1 to 10", minimum: 1, maximum: 10
35
- # array :tags, description: "Associated tags" do
36
- # items type: "string"
37
- # end
38
- # end
39
- #
40
- # array :products, description: "List of products" do
41
- # object do
42
- # integer :id, description: "Product identifier"
43
- # string :name, description: "Product name"
44
- # number :price, description: "Product price", minimum: 0
45
- # end
46
- # end
47
- end
48
- <%- end -%>
49
-
50
- def build_prompt
51
- # Implement the LLM prompt for this task.
52
- raise NotImplementedError, "Implement #build_prompt in #{self.class.name}"
53
- end
54
-
55
- # Optional: Override build_system_prompt if you need custom system instructions.
56
- # The default implementation, which you'll get if you call super, will use Raif.config.task_system_prompt_intro
57
- # and append the system_prompt_language_preference if the task's requested_language_key is set.
58
- # def build_system_prompt
59
- # super + "\nAdditional system instructions..."
29
+ # object :widget, description: "A widget's description" do
30
+ # boolean :is_red, description: "Whether the widget is red"
31
+ # integer :rating, description: "A rating of the widget from 1 to 10", minimum: 1, maximum: 10
32
+ # array :tags, description: "Associated tags" do
33
+ # items type: "string"
34
+ # end
35
+ # end
36
+ #
37
+ # array :products, description: "List of products" do
38
+ # object do
39
+ # integer :id, description: "Product identifier"
40
+ # string :name, description: "Product name"
41
+ # number :price, description: "Product price", minimum: 0
42
+ # end
60
43
  # end
61
44
  end
45
+ <%- end -%>
46
+
47
+ def build_prompt
48
+ # Implement the LLM prompt for this task.
49
+ raise NotImplementedError, "Implement #build_prompt in #{self.class.name}"
50
+ end
51
+
52
+ # Optional: Override build_system_prompt if you need custom system instructions.
53
+ # The default implementation, which you'll get if you call super, will use Raif.config.task_system_prompt_intro
54
+ # and append the system_prompt_language_preference if the task's requested_language_key is set.
55
+ # def build_system_prompt
56
+ # super + "\nAdditional system instructions..."
57
+ # end
62
58
  end
63
- end
59
+ <% end -%>
@@ -0,0 +1,54 @@
1
+ <% raif_module_namespacing(["Evals", "Tasks"]) do -%>
2
+ class <%= class_name.demodulize %>EvalSet < Raif::Evals::EvalSet
3
+ # Run this eval set with:
4
+ # bundle exec raif evals ./<%= eval_set_file_path %>
5
+
6
+ # Setup method runs before each eval
7
+ setup do
8
+ # Common setup code
9
+ end
10
+
11
+ # Teardown runs after each eval
12
+ teardown do
13
+ # Cleanup code
14
+ end
15
+
16
+ eval "<%= class_name %> produces expected output" do
17
+ # task = Raif::Tasks::<%= class_name %>.run(
18
+ # Add your task parameters here that produce the expected output
19
+ # )
20
+
21
+ # The return value of the block determines if the expectation passes or fails
22
+ # expect "task completes successfully" do
23
+ # task.completed?
24
+ # end
25
+
26
+ # expect "contains the word 'hello' in the output" do
27
+ # task.parsed_response.include?("hello")
28
+ # end
29
+
30
+ # Add more specific expectations based on your task's behavior
31
+ end
32
+
33
+ eval "properly handles refusals" do
34
+ # task = Raif::Tasks::<%= class_name %>.run(
35
+ # Add your task parameters here to trigger a refusal
36
+ # )
37
+
38
+ # expect "returns exactly the text 'I'm sorry, I can't do that.'" do
39
+ # task.parsed_response == "I'm sorry, I can't do that."
40
+ # end
41
+ end
42
+
43
+ eval "<%= class_name %> uses appropriate LLM tools" do
44
+ # Test that the task uses the expected tools if applicable
45
+ # task = Raif::Tasks::<%= class_name %>.run(
46
+ # Add parameters that trigger the use of the expected tools
47
+ # )
48
+
49
+ # Example tool invocation expectations (if your task uses tools):
50
+ # expect_tool_invocation(task, "tool_name", with: { param: "value" })
51
+ # expect_no_tool_invocation(task, "unwanted_tool")
52
+ end
53
+ end
54
+ <% end -%>
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raif
4
+ module CLI
5
+ class Base
6
+ attr_reader :args, :options
7
+
8
+ def initialize(args = [])
9
+ @args = args
10
+ @options = {}
11
+ end
12
+
13
+ protected
14
+
15
+ def find_rails_root
16
+ current = Dir.pwd
17
+
18
+ until File.exist?(File.join(current, "config", "environment.rb"))
19
+ parent = File.dirname(current)
20
+ if parent == current
21
+ puts "Error: Could not find Rails application root"
22
+ puts "Please run this command from within a Rails application directory"
23
+ exit 1
24
+ end
25
+
26
+ current = parent
27
+ end
28
+
29
+ current
30
+ end
31
+
32
+ def load_rails_application
33
+ rails_root = find_rails_root
34
+ Dir.chdir(rails_root)
35
+ require File.join(rails_root, "config", "environment")
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require_relative "base"
5
+
6
+ module Raif
7
+ module CLI
8
+ class Evals < Base
9
+ def run
10
+ # Set test environment by default for evals
11
+ ENV["RAILS_ENV"] ||= "test"
12
+ ENV["RAIF_RUNNING_EVALS"] = "true"
13
+
14
+ OptionParser.new do |opts|
15
+ opts.banner = "Usage: raif evals [options] [FILE_PATHS]"
16
+
17
+ opts.on("-e", "--environment ENV", "Rails environment (default: test)") do |env|
18
+ ENV["RAILS_ENV"] = env
19
+ end
20
+
21
+ opts.on("-h", "--help", "Show this help message") do
22
+ puts opts
23
+ exit
24
+ end
25
+ end.parse!(args)
26
+
27
+ # Parse file paths with optional line numbers
28
+ file_paths = args.map do |arg|
29
+ if arg.include?(":")
30
+ file_path, line_number = arg.split(":", 2)
31
+ { file_path: file_path, line_number: line_number.to_i }
32
+ else
33
+ { file_path: arg, line_number: nil }
34
+ end
35
+ end if args.any?
36
+
37
+ # Find and load Rails application
38
+ load_rails_application
39
+
40
+ require "raif/evals"
41
+
42
+ run = Raif::Evals::Run.new(file_paths: file_paths)
43
+ run.execute
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require_relative "base"
5
+
6
+ module Raif
7
+ module CLI
8
+ class EvalsSetup < Base
9
+ def run
10
+ OptionParser.new do |opts|
11
+ opts.banner = "Usage: raif evals:setup [options]"
12
+ opts.on("-h", "--help", "Show this help message") do
13
+ puts opts
14
+ exit
15
+ end
16
+ end.parse!(args)
17
+
18
+ # Load Rails application to use generators
19
+ load_rails_application
20
+
21
+ # Invoke the Rails generator
22
+ require "rails/generators"
23
+ Rails::Generators.invoke("raif:evals:setup", args)
24
+ end
25
+ end
26
+ end
27
+ end
data/lib/raif/cli.rb ADDED
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "cli/base"
4
+ require_relative "cli/evals"
5
+ require_relative "cli/evals_setup"
6
+
7
+ module Raif
8
+ module CLI
9
+ COMMANDS = {
10
+ "evals" => "Run Raif evaluation sets",
11
+ "evals:setup" => "Setup Raif evals directory structure",
12
+ "version" => "Show Raif version",
13
+ "help" => "Show this help message"
14
+ }.freeze
15
+
16
+ class Runner
17
+ def initialize(args)
18
+ @args = args
19
+ @command = args.shift
20
+ end
21
+
22
+ def run
23
+ case @command
24
+ when "evals"
25
+ Evals.new(@args).run
26
+ when "evals:setup"
27
+ EvalsSetup.new(@args).run
28
+ when "version", "--version", "-v"
29
+ show_version
30
+ when "help", "--help", "-h", nil
31
+ show_help
32
+ else
33
+ puts "Unknown command: #{@command}"
34
+ puts ""
35
+ show_help
36
+ exit 1
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def show_version
43
+ require_relative "../raif/version"
44
+ puts "Raif #{Raif::VERSION}"
45
+ end
46
+
47
+ def show_help
48
+ puts "Usage: raif COMMAND [options]"
49
+ puts ""
50
+ puts "Commands:"
51
+ COMMANDS.each do |command, description|
52
+ puts format(" %-12s %s", command, description)
53
+ end
54
+ puts ""
55
+ puts "For help on a specific command:"
56
+ puts " raif COMMAND --help"
57
+ puts ""
58
+ puts "Examples:"
59
+ puts " raif evals:setup # Setup eval directory structure"
60
+ puts " raif evals # Run all eval sets in test environment"
61
+ puts " raif evals CustomerSupportEvalSet # Run specific eval set"
62
+ puts " raif evals -e development # Run evals in development environment"
63
+ puts " raif version # Show Raif version"
64
+ end
65
+ end
66
+ end
67
+ end