solid_agent 0.0.0 → 0.1.1

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +130 -0
  3. data/Rakefile +12 -0
  4. data/lib/generators/solid_agent/agent/agent_generator.rb +95 -0
  5. data/lib/generators/solid_agent/agent/templates/action.text.erb +10 -0
  6. data/lib/generators/solid_agent/agent/templates/agent.rb.erb +93 -0
  7. data/lib/generators/solid_agent/context/context_generator.rb +124 -0
  8. data/lib/generators/solid_agent/context/templates/context_model.rb.erb +100 -0
  9. data/lib/generators/solid_agent/context/templates/create_context.rb.erb +32 -0
  10. data/lib/generators/solid_agent/context/templates/create_generations.rb.erb +38 -0
  11. data/lib/generators/solid_agent/context/templates/create_messages.rb.erb +33 -0
  12. data/lib/generators/solid_agent/context/templates/generation_model.rb.erb +40 -0
  13. data/lib/generators/solid_agent/context/templates/message_model.rb.erb +47 -0
  14. data/lib/generators/solid_agent/install/install_generator.rb +83 -0
  15. data/lib/generators/solid_agent/install/templates/agent_context.rb.erb +128 -0
  16. data/lib/generators/solid_agent/install/templates/agent_generation.rb.erb +59 -0
  17. data/lib/generators/solid_agent/install/templates/agent_message.rb.erb +76 -0
  18. data/lib/generators/solid_agent/install/templates/create_agent_contexts.rb.erb +32 -0
  19. data/lib/generators/solid_agent/install/templates/create_agent_generations.rb.erb +38 -0
  20. data/lib/generators/solid_agent/install/templates/create_agent_messages.rb.erb +33 -0
  21. data/lib/generators/solid_agent/install/templates/initializer.rb.erb +51 -0
  22. data/lib/generators/solid_agent/tool/templates/tool.json.erb +19 -0
  23. data/lib/generators/solid_agent/tool/tool_generator.rb +117 -0
  24. data/lib/solid_agent/engine.rb +16 -0
  25. data/lib/solid_agent/has_context.rb +449 -0
  26. data/lib/solid_agent/has_tools.rb +257 -0
  27. data/lib/solid_agent/streams_tool_updates.rb +178 -0
  28. data/lib/solid_agent/version.rb +5 -0
  29. data/lib/solid_agent.rb +28 -0
  30. data/sig/solid_agent.rbs +4 -0
  31. metadata +88 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2538e4679d79c86ca07874c50eeff13dfa542019af76c9b9b247e68a6527281b
4
- data.tar.gz: b883e74f4e2c870b451bc8fc206c21c3639681abb99877b09a702d717385742c
3
+ metadata.gz: 52f173d6c45fc416105fb4a93760dc7aacc81061b75bada9820fce835e9652dd
4
+ data.tar.gz: '07348df42b87220dea84691d61a11cac6e6576cebbcbe2272c702a0bed6f539c'
5
5
  SHA512:
6
- metadata.gz: 41868b5e2dcbdd9b7ee9a50d0479e02cab65cab421097bf7608725eb430bc15b2d86c63434d12aa29d3dc85c447e2f9230e0bb50ecee788e9b7c0bf82a5db06f
7
- data.tar.gz: '09b3bebf383a8a01617ad1b160bc4ca3362299b6f9e6c5e760790b5f669c4b4ac78cafce0bad4d1214914dc4e89d5ada45817c9c3a58c727c432a07ce7fde428'
6
+ metadata.gz: ffd6386b0d71a95df1ac198c9590bbf337bf7e1a37ccc19e17eb57afbbcf544260fe7e2e552ba8470d4091d636a5b7b074aa5e7745d9d17021af839704b81f46
7
+ data.tar.gz: bcb89c355432829006b38157c727c271b4b63c86aa09a67d14611b16896f515406dfbf5d84f45c1b2b53a9e4adae6d4cfa250397271f3bb0f8e029f15417d5b4
data/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # SolidAgent
2
+
3
+ SolidAgent extends the [ActiveAgent](https://github.com/activeagents/activeagent) framework with enterprise-grade features for building robust AI agents in Rails applications. It provides three core concerns that add database-backed persistence, declarative tool schemas, and real-time streaming capabilities to your agents.
4
+
5
+ ## Features
6
+
7
+ - **HasContext** - Database-backed prompt context management for maintaining conversation history and agent state
8
+ - **HasTools** - Declarative, schema-based tool definitions compatible with LLM function-calling APIs
9
+ - **StreamsToolUpdates** - Real-time UI feedback during tool execution via ActionCable
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem "solid_agent"
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ ```bash
22
+ $ bundle install
23
+ ```
24
+
25
+ Or install it yourself as:
26
+
27
+ ```bash
28
+ $ gem install solid_agent
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ### Quick Start
34
+
35
+ Generate a new agent with context support:
36
+
37
+ ```bash
38
+ $ rails generate solid_agent:agent WritingAssistant --context --context_name conversation --contextable user
39
+ ```
40
+
41
+ ### HasContext - Persistent Conversation History
42
+
43
+ Add database-backed context management to your agents:
44
+
45
+ ```ruby
46
+ class WritingAssistantAgent < ApplicationAgent
47
+ include SolidAgent::HasContext
48
+
49
+ has_context :conversation, contextable: :user
50
+
51
+ def improve
52
+ load_conversation(contextable: current_user)
53
+ add_conversation_user_message(params[:message])
54
+ prompt messages: conversation_messages
55
+ end
56
+ end
57
+ ```
58
+
59
+ This generates helper methods like:
60
+ - `load_conversation(contextable:)` - Load or create a context
61
+ - `conversation_messages` - Get formatted message history
62
+ - `add_conversation_user_message(content)` - Add a user message
63
+ - `add_conversation_assistant_message(content)` - Add an AI response
64
+ - `conversation_result` - Get the last assistant message
65
+
66
+ ### HasTools - Declarative Tool Schemas
67
+
68
+ Define tools inline with a clean DSL:
69
+
70
+ ```ruby
71
+ class ResearchAgent < ApplicationAgent
72
+ include SolidAgent::HasTools
73
+
74
+ tool :search do
75
+ description "Search for information"
76
+ parameter :query, type: :string, required: true
77
+ parameter :limit, type: :integer, default: 10
78
+ end
79
+
80
+ def research
81
+ prompt tools: tools
82
+ end
83
+
84
+ def search(query:, limit: 10)
85
+ # Tool implementation
86
+ end
87
+ end
88
+ ```
89
+
90
+ Or use JSON templates in `app/views/research_agent/tools/search.json.erb`.
91
+
92
+ ### StreamsToolUpdates - Real-Time Feedback
93
+
94
+ Broadcast tool execution status to your UI:
95
+
96
+ ```ruby
97
+ class BrowserAgent < ApplicationAgent
98
+ include SolidAgent::HasTools
99
+ include SolidAgent::StreamsToolUpdates
100
+
101
+ has_tools :navigate, :click
102
+ tool_description :navigate, ->(args) { "Visiting #{args[:url]}..." }
103
+ end
104
+ ```
105
+
106
+ ### Generators
107
+
108
+ ```bash
109
+ # Generate a new agent
110
+ $ rails generate solid_agent:agent MyAgent
111
+
112
+ # Generate with context support
113
+ $ rails generate solid_agent:agent MyAgent --context --context_name session
114
+
115
+ # Generate a tool template
116
+ $ rails generate solid_agent:tool search MyAgent --parameters query:string:required
117
+
118
+ # Generate context models
119
+ $ rails generate solid_agent:context conversation
120
+ ```
121
+
122
+ ## Development
123
+
124
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
125
+
126
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
127
+
128
+ ## Contributing
129
+
130
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/solid_agent.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: :test
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module SolidAgent
6
+ module Generators
7
+ class AgentGenerator < Rails::Generators::NamedBase
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ desc "Generates a new ActiveAgent agent with SolidAgent concerns"
11
+
12
+ class_option :context, type: :boolean, default: true,
13
+ desc: "Include HasContext concern"
14
+
15
+ class_option :context_name, type: :string, default: nil,
16
+ desc: "Custom context name (e.g., 'conversation', 'research_session')"
17
+
18
+ class_option :contextable, type: :string, default: nil,
19
+ desc: "Param key for auto-context (e.g., 'user', 'document')"
20
+
21
+ class_option :tools, type: :boolean, default: false,
22
+ desc: "Include HasTools concern"
23
+
24
+ class_option :streaming, type: :boolean, default: false,
25
+ desc: "Include StreamsToolUpdates concern"
26
+
27
+ class_option :actions, type: :array, default: ["perform"],
28
+ desc: "Agent actions to generate"
29
+
30
+ class_option :parent, type: :string, default: "ApplicationAgent",
31
+ desc: "Parent class for the agent"
32
+
33
+ def create_agent_file
34
+ @parent_class = options[:parent]
35
+ @include_context = options[:context]
36
+ @context_name = options[:context_name]
37
+ @contextable = options[:contextable]
38
+ @include_tools = options[:tools]
39
+ @include_streaming = options[:streaming]
40
+ @actions = options[:actions]
41
+
42
+ template "agent.rb.erb", "app/agents/#{file_name}_agent.rb"
43
+ end
44
+
45
+ def create_view_directory
46
+ empty_directory "app/views/#{file_name}_agent"
47
+
48
+ @actions.each do |action|
49
+ template "action.text.erb", "app/views/#{file_name}_agent/#{action}.text.erb",
50
+ action_name: action
51
+ end
52
+ end
53
+
54
+ def create_tools_directory
55
+ return unless @include_tools
56
+
57
+ empty_directory "app/views/#{file_name}_agent/tools"
58
+ end
59
+
60
+ def show_next_steps
61
+ say ""
62
+ say "Agent created successfully!", :green
63
+ say ""
64
+ say "Files generated:"
65
+ say " app/agents/#{file_name}_agent.rb"
66
+ say " app/views/#{file_name}_agent/"
67
+ @actions.each do |action|
68
+ say " app/views/#{file_name}_agent/#{action}.text.erb"
69
+ end
70
+ say " app/views/#{file_name}_agent/tools/" if @include_tools
71
+ say ""
72
+
73
+ if @include_tools
74
+ say "To add tools, run:", :yellow
75
+ say " rails g solid_agent:tool search #{class_name}Agent --parameters query:string:required --description \"Search for content\""
76
+ say ""
77
+ end
78
+
79
+ say "Example usage:", :yellow
80
+ say " #{class_name}Agent.with(content: \"Hello\").#{@actions.first}.generate_now"
81
+ say ""
82
+ end
83
+
84
+ private
85
+
86
+ def file_name
87
+ name.underscore
88
+ end
89
+
90
+ def class_name
91
+ name.camelize
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,10 @@
1
+ <%% # Action: <%= config[:action_name] %> %>
2
+ <%% # This template is rendered as the user message for the prompt %>
3
+
4
+ <%% if @content.present? %>
5
+ Please process the following content:
6
+
7
+ <%%= @content %>
8
+ <%% else %>
9
+ Please help me with my request.
10
+ <%% end %>
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= class_name %>Agent < <%= @parent_class %>
4
+ <%- if @include_context || @include_tools || @include_streaming -%>
5
+ # SolidAgent concerns
6
+ <%- end -%>
7
+ <%- if @include_context -%>
8
+ include SolidAgent::HasContext
9
+ <%- end -%>
10
+ <%- if @include_tools -%>
11
+ include SolidAgent::HasTools
12
+ <%- end -%>
13
+ <%- if @include_streaming -%>
14
+ include SolidAgent::StreamsToolUpdates
15
+ <%- end -%>
16
+
17
+ # Configure the generation provider and model
18
+ generate_with :openai, model: "gpt-4o"
19
+
20
+ <%- if @include_context -%>
21
+ # Enable database-backed context persistence
22
+ # Context is auto-created from params[:<%= @contextable || 'contextable' %>]
23
+ <%- if @context_name -%>
24
+ has_context :<%= @context_name %>, contextable: :<%= @contextable || 'contextable' %>
25
+ <%- else -%>
26
+ has_context contextable: :<%= @contextable || 'contextable' %>
27
+ <%- end -%>
28
+ <%- end -%>
29
+
30
+ <%- if @include_tools -%>
31
+ # Declare tools (auto-discover from app/views/<%= file_name %>_agent/tools/*.json.erb)
32
+ # has_tools
33
+ #
34
+ # Or declare specific tools:
35
+ # has_tools :search, :analyze
36
+ #
37
+ # Or define inline:
38
+ # tool :example do
39
+ # description "An example tool"
40
+ # parameter :input, type: :string, required: true
41
+ # end
42
+ <%- end -%>
43
+
44
+ <%- if @include_streaming -%>
45
+ # Streaming callbacks for real-time updates
46
+ on_stream :broadcast_chunk
47
+ on_stream_close :broadcast_complete
48
+
49
+ # Tool descriptions for UI feedback
50
+ # tool_description :search, ->(args) { "Searching for '#{args[:query]}'..." }
51
+ <%- end -%>
52
+
53
+ <%- @actions.each do |action| -%>
54
+ # Action: <%= action %>
55
+ def <%= action %>
56
+ <%- if @include_tools -%>
57
+ # Include tools in the prompt (context auto-created)
58
+ prompt(tools: tools, tool_choice: "auto")
59
+ <%- else -%>
60
+ # Render the action template (context auto-created)
61
+ prompt
62
+ <%- end -%>
63
+ end
64
+
65
+ <%- end -%>
66
+ <%- if @include_streaming -%>
67
+ private
68
+
69
+ def broadcast_chunk(chunk)
70
+ return unless chunk.delta
71
+ return unless params[:stream_id]
72
+
73
+ ActionCable.server.broadcast(params[:stream_id], { content: chunk.delta })
74
+ end
75
+
76
+ def broadcast_complete(chunk)
77
+ return unless params[:stream_id]
78
+
79
+ ActionCable.server.broadcast(params[:stream_id], { done: true })
80
+ end
81
+ <%- end -%>
82
+ <%- if @include_tools && !@include_streaming -%>
83
+ private
84
+
85
+ # Tool implementations go here
86
+ # def search(query:)
87
+ # Rails.logger.info "[<%= class_name %>Agent] Tool called: search(#{query})"
88
+ # { success: true, results: [] }
89
+ # rescue => e
90
+ # { success: false, error: e.message }
91
+ # end
92
+ <%- end -%>
93
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/active_record"
5
+
6
+ module SolidAgent
7
+ module Generators
8
+ class ContextGenerator < Rails::Generators::NamedBase
9
+ include ActiveRecord::Generators::Migration
10
+
11
+ source_root File.expand_path("templates", __dir__)
12
+
13
+ desc "Generates a custom context model with migrations for domain-specific agent contexts"
14
+
15
+ argument :agent_name, type: :string, required: false,
16
+ desc: "The agent class name that will use this context (optional)"
17
+
18
+ class_option :skip_migrations, type: :boolean, default: false,
19
+ desc: "Skip generating migrations"
20
+
21
+ def create_migrations
22
+ return if options[:skip_migrations]
23
+
24
+ migration_template "create_context.rb.erb",
25
+ "db/migrate/create_#{table_name}.rb"
26
+
27
+ migration_template "create_messages.rb.erb",
28
+ "db/migrate/create_#{message_table_name}.rb"
29
+
30
+ migration_template "create_generations.rb.erb",
31
+ "db/migrate/create_#{generation_table_name}.rb"
32
+ end
33
+
34
+ def create_models
35
+ template "context_model.rb.erb", "app/models/#{file_name}.rb"
36
+ template "message_model.rb.erb", "app/models/#{message_file_name}.rb"
37
+ template "generation_model.rb.erb", "app/models/#{generation_file_name}.rb"
38
+ end
39
+
40
+ def show_usage
41
+ say ""
42
+ say "Context models created!", :green
43
+ say ""
44
+ say "Files generated:"
45
+ say " app/models/#{file_name}.rb"
46
+ say " app/models/#{message_file_name}.rb"
47
+ say " app/models/#{generation_file_name}.rb"
48
+ say " db/migrate/*_create_#{table_name}.rb"
49
+ say " db/migrate/*_create_#{message_table_name}.rb"
50
+ say " db/migrate/*_create_#{generation_table_name}.rb"
51
+ say ""
52
+ say "Next steps:", :yellow
53
+ say " 1. Run migrations: rails db:migrate"
54
+ say ""
55
+ say " 2. Add to your agent:"
56
+ say " class #{agent_class_name} < ApplicationAgent"
57
+ say " include SolidAgent::HasContext"
58
+ say " has_context :#{context_name}"
59
+ say ""
60
+ say " def perform"
61
+ say " create_#{context_name}(contextable: params[:user])"
62
+ say " prompt"
63
+ say " end"
64
+ say " end"
65
+ say ""
66
+ end
67
+
68
+ private
69
+
70
+ def context_name
71
+ name.underscore.singularize
72
+ end
73
+
74
+ def class_name
75
+ name.camelize
76
+ end
77
+
78
+ def file_name
79
+ name.underscore
80
+ end
81
+
82
+ def table_name
83
+ name.underscore.pluralize
84
+ end
85
+
86
+ def message_class_name
87
+ "#{class_name}Message"
88
+ end
89
+
90
+ def message_file_name
91
+ "#{file_name}_message"
92
+ end
93
+
94
+ def message_table_name
95
+ "#{file_name}_messages"
96
+ end
97
+
98
+ def generation_class_name
99
+ "#{class_name}Generation"
100
+ end
101
+
102
+ def generation_file_name
103
+ "#{file_name}_generation"
104
+ end
105
+
106
+ def generation_table_name
107
+ "#{file_name}_generations"
108
+ end
109
+
110
+ def agent_class_name
111
+ agent_name&.camelize || "MyAgent"
112
+ end
113
+
114
+ def migration_version
115
+ "[#{ActiveRecord::Migration.current_version}]"
116
+ end
117
+
118
+ def self.next_migration_number(dirname)
119
+ next_migration_number = current_migration_number(dirname) + 1
120
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ # <%= class_name %> stores context for <%= context_name %>-based agent interactions.
4
+ #
5
+ # @example Creating a <%= context_name %> for a user
6
+ # <%= context_name %> = <%= class_name %>.create!(
7
+ # contextable: current_user,
8
+ # agent_name: "MyAgent",
9
+ # action_name: "perform"
10
+ # )
11
+ #
12
+ # @example Using with has_context
13
+ # class MyAgent < ApplicationAgent
14
+ # include SolidAgent::HasContext
15
+ # has_context :<%= context_name %>
16
+ #
17
+ # def perform
18
+ # create_<%= context_name %>(contextable: params[:user])
19
+ # prompt
20
+ # end
21
+ # end
22
+ #
23
+ class <%= class_name %> < ApplicationRecord
24
+ # Associations
25
+ belongs_to :contextable, polymorphic: true, optional: true
26
+ has_many :messages, class_name: "<%= message_class_name %>", dependent: :destroy
27
+ has_many :generations, class_name: "<%= generation_class_name %>", dependent: :destroy
28
+
29
+ # Validations
30
+ validates :agent_name, presence: true
31
+ validates :action_name, presence: true
32
+
33
+ # Scopes
34
+ scope :recent, -> { order(created_at: :desc) }
35
+ scope :for_agent, ->(name) { where(agent_name: name) }
36
+ scope :for_action, ->(name) { where(action_name: name) }
37
+
38
+ # Convenience method to get input_params from options
39
+ def input_params
40
+ options&.dig("input_params") || options&.dig(:input_params) || {}
41
+ end
42
+
43
+ # Records a generation response and updates token counts
44
+ def record_generation!(response)
45
+ generation = generations.create!(
46
+ content: response.message&.content,
47
+ model: response.model,
48
+ provider: response.provider,
49
+ finish_reason: response.finish_reason,
50
+ input_tokens: response.usage&.input_tokens || 0,
51
+ output_tokens: response.usage&.output_tokens || 0,
52
+ tool_calls: extract_tool_calls(response),
53
+ raw_response: response.raw_response,
54
+ duration_seconds: response.duration
55
+ )
56
+
57
+ increment!(:total_input_tokens, generation.input_tokens)
58
+ increment!(:total_output_tokens, generation.output_tokens)
59
+
60
+ add_assistant_message(response.message&.content, tool_calls: generation.tool_calls)
61
+
62
+ generation
63
+ end
64
+
65
+ def add_user_message(content, **attributes)
66
+ messages.create!(role: "user", content: content, **attributes)
67
+ end
68
+
69
+ def add_assistant_message(content, **attributes)
70
+ messages.create!(role: "assistant", content: content, **attributes)
71
+ end
72
+
73
+ def add_system_message(content)
74
+ messages.create!(role: "system", content: content)
75
+ end
76
+
77
+ def add_tool_message(tool_call_id:, tool_name:, result:)
78
+ messages.create!(
79
+ role: "tool",
80
+ tool_call_id: tool_call_id,
81
+ tool_name: tool_name,
82
+ tool_result: result,
83
+ content: result.is_a?(String) ? result : result.to_json
84
+ )
85
+ end
86
+
87
+ def total_tokens
88
+ total_input_tokens + total_output_tokens
89
+ end
90
+
91
+ private
92
+
93
+ def extract_tool_calls(response)
94
+ return [] unless response.message&.tool_calls.present?
95
+
96
+ response.message.tool_calls.map do |tc|
97
+ { id: tc.id, name: tc.name, arguments: tc.arguments }
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Create<%= class_name.pluralize %> < ActiveRecord::Migration<%= migration_version %>
4
+ def change
5
+ create_table :<%= table_name %> do |t|
6
+ # Polymorphic association to any model (User, Document, Project, etc.)
7
+ t.references :contextable, polymorphic: true, index: true
8
+
9
+ # Agent identification
10
+ t.string :agent_name, null: false
11
+ t.string :action_name, null: false
12
+
13
+ # System instructions for the conversation
14
+ t.text :instructions
15
+
16
+ # Flexible options storage (input_params, model settings, etc.)
17
+ t.jsonb :options, default: {}
18
+
19
+ # Tracing/debugging
20
+ t.string :trace_id, index: true
21
+
22
+ # Token usage tracking
23
+ t.integer :total_input_tokens, default: 0
24
+ t.integer :total_output_tokens, default: 0
25
+
26
+ t.timestamps
27
+ end
28
+
29
+ add_index :<%= table_name %>, [:agent_name, :action_name]
30
+ add_index :<%= table_name %>, :created_at
31
+ end
32
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Create<%= generation_class_name.pluralize %> < ActiveRecord::Migration<%= migration_version %>
4
+ def change
5
+ create_table :<%= generation_table_name %> do |t|
6
+ t.references :<%= file_name %>, null: false, foreign_key: true, index: true
7
+
8
+ # The assistant's response content
9
+ t.text :content
10
+
11
+ # Model information
12
+ t.string :model
13
+ t.string :provider
14
+
15
+ # Finish reason: stop, tool_calls, length, etc.
16
+ t.string :finish_reason
17
+
18
+ # Token usage for this generation
19
+ t.integer :input_tokens, default: 0
20
+ t.integer :output_tokens, default: 0
21
+
22
+ # Tool calls made in this generation
23
+ t.jsonb :tool_calls, default: []
24
+
25
+ # Raw response from the provider
26
+ t.jsonb :raw_response
27
+
28
+ # Timing
29
+ t.float :duration_seconds
30
+
31
+ t.timestamps
32
+ end
33
+
34
+ add_index :<%= generation_table_name %>, :model
35
+ add_index :<%= generation_table_name %>, :finish_reason
36
+ add_index :<%= generation_table_name %>, [:<%= file_name %>_id, :created_at]
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Create<%= message_class_name.pluralize %> < ActiveRecord::Migration<%= migration_version %>
4
+ def change
5
+ create_table :<%= message_table_name %> do |t|
6
+ t.references :<%= file_name %>, null: false, foreign_key: true, index: true
7
+
8
+ # Message role: user, assistant, system, tool
9
+ t.string :role, null: false
10
+
11
+ # Message content
12
+ t.text :content
13
+
14
+ # For tool calls and results
15
+ t.string :tool_call_id
16
+ t.string :tool_name
17
+ t.jsonb :tool_arguments, default: {}
18
+ t.jsonb :tool_result
19
+
20
+ # For multimodal content
21
+ t.jsonb :attachments, default: []
22
+
23
+ # Metadata
24
+ t.jsonb :metadata, default: {}
25
+
26
+ t.timestamps
27
+ end
28
+
29
+ add_index :<%= message_table_name %>, :role
30
+ add_index :<%= message_table_name %>, :tool_call_id
31
+ add_index :<%= message_table_name %>, [:<%= file_name %>_id, :created_at]
32
+ end
33
+ end