Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/assets/config/active_prompt_manifest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
//= link_directory ../javascripts/active_prompt .js
//= link_directory ../stylesheets/active_prompt .css
8 changes: 8 additions & 0 deletions app/assets/javascripts/active_prompt/application.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// This file is compiled into the host app's asset pipeline as active_prompt/application.js
// Add engine-specific JS here.
//= require_self

(function () {
// Namespace guard
window.ActivePrompt = window.ActivePrompt || {};
})();
6 changes: 6 additions & 0 deletions app/assets/stylesheets/active_prompt/application.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
*= require_self
*/

/* Add engine-specific styles here */
.active-prompt--hidden { display: none; }
7 changes: 7 additions & 0 deletions app/controllers/active_prompt/application_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module ActivePrompt
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
end
end
6 changes: 6 additions & 0 deletions app/helpers/active_prompt/application_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

module ActivePrompt
module ApplicationHelper
end
end
10 changes: 10 additions & 0 deletions app/models/active_prompt/action.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true
module ActivePrompt
class Action < ApplicationRecord
self.table_name = "active_prompt_actions"

belongs_to :prompt, class_name: "ActivePrompt::Prompt", inverse_of: :actions

validates :name, presence: true
end
end
13 changes: 13 additions & 0 deletions app/models/active_prompt/context.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true
module ActivePrompt
# Polymorphic join: attach prompts to any agent record
class Context < ApplicationRecord
self.table_name = "active_prompt_contexts"

belongs_to :agent, polymorphic: true, inverse_of: :prompt_contexts
belongs_to :prompt, class_name: "ActivePrompt::Prompt", inverse_of: :contexts

validates :agent, :prompt, presence: true
validates :label, length: { maximum: 255 }, allow_nil: true
end
end
11 changes: 11 additions & 0 deletions app/models/active_prompt/message.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true
module ActivePrompt
class Message < ApplicationRecord
self.table_name = "active_prompt_messages"

belongs_to :prompt, class_name: "ActivePrompt::Prompt", inverse_of: :messages

enum :role, { system: "system", user: "user", assistant: "assistant", tool: "tool" }, prefix: true
validates :role, :content, presence: true
end
end
25 changes: 25 additions & 0 deletions app/models/active_prompt/prompt.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# frozen_string_literal: true
module ActivePrompt
class Prompt < ApplicationRecord
self.table_name = "active_prompt_prompts"

has_many :messages, class_name: "ActivePrompt::Message", dependent: :destroy, inverse_of: :prompt
has_many :actions, class_name: "ActivePrompt::Action", dependent: :destroy, inverse_of: :prompt

has_many :contexts, class_name: "ActivePrompt::Context", dependent: :destroy, inverse_of: :prompt
has_many :agents, through: :contexts, source: :agent

validates :name, presence: true

def to_runtime
{
name: name,
description: description,
template: template,
messages: messages.order(:position).map(&:attributes),
actions: actions.map(&:attributes),
metadata: metadata || {}
}
end
end
end
5 changes: 5 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

ActivePrompt::Engine.routes.draw do
get "health", to: proc { [200, { "Content-Type" => "text/plain" }, ["ok"]] }, as: :health
end
44 changes: 44 additions & 0 deletions db/migrate/20251127000000_create_active_prompt_core.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true
class CreateActivePromptCore < ActiveRecord::Migration[7.0]
def change
create_table :active_prompt_prompts do |t|
t.string :name, null: false
t.text :description
t.json :metadata, null: false, default: {}
t.text :template
t.timestamps
end
add_index :active_prompt_prompts, :name

create_table :active_prompt_messages do |t|
t.references :prompt, null: false, foreign_key: { to_table: :active_prompt_prompts }
t.string :role, null: false
t.text :content, null: false
t.integer :position
t.json :metadata, null: false, default: {}
t.timestamps
end
add_index :active_prompt_messages, [:prompt_id, :position]

create_table :active_prompt_actions do |t|
t.references :prompt, null: false, foreign_key: { to_table: :active_prompt_prompts }
t.string :name, null: false
t.string :tool_name
t.json :parameters, null: false, default: {}
t.json :result, null: false, default: {}
t.string :status
t.timestamps
end
add_index :active_prompt_actions, [:prompt_id, :name]

create_table :active_prompt_contexts do |t|
t.string :agent_type, null: false
t.bigint :agent_id, null: false
t.references :prompt, null: false, foreign_key: { to_table: :active_prompt_prompts }
t.string :label
t.json :metadata, null: false, default: {}
t.timestamps
end
add_index :active_prompt_contexts, [:agent_type, :agent_id, :prompt_id], unique: true, name: "idx_ap_contexts_agent_prompt"
end
end
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
<!-- Generated from structured_output_json_parsing_test.rb:69 -->
[activeagent/test/integration/structured_output_json_parsing_test.rb:69](vscode://file//Users/justinbowen/Documents/GitHub/claude-could/activeagent/test/integration/structured_output_json_parsing_test.rb:69)
[activeagent/test/integration/structured_output_json_parsing_test.rb:69](vscode://file//Users/sarbadajaiswal/development/Justin/activeagents/activeagent/test/integration/structured_output_json_parsing_test.rb:69)
<!-- Test: test-structured-output-sets-content-type-to-application/json-and-auto-parses-JSON -->

```ruby
# Response object
#<ActiveAgent::GenerationProvider::Response:0x36b0
@message=#<ActiveAgent::ActionPrompt::Message:0x36c4
#<ActiveAgent::GenerationProvider::Response:0x20b0
@message=#<ActiveAgent::ActionPrompt::Message:0x20b8
@action_id=nil,
@action_name=nil,
@action_requested=false,
@charset="UTF-8",
@content={"name"=>"John Doe", "age"=>30, "email"=>"john@example.com"},
@content={"name" => "John Doe", "age" => 30, "email" => "john@example.com"},
@role=:assistant>
@prompt=#<ActiveAgent::ActionPrompt::Prompt:0x36d8 ...>
@prompt=#<ActiveAgent::ActionPrompt::Prompt:0x20c0 ...>
@content_type="application/json"
@raw_response={...}>

# Message content
response.message.content # => {"name"=>"John Doe", "age"=>30, "email"=>"john@example.com"}
response.message.content # => {"name" => "John Doe", "age" => 30, "email" => "john@example.com"}
```
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<!-- Generated from structured_output_json_parsing_test.rb:151 -->
[activeagent/test/integration/structured_output_json_parsing_test.rb:151](vscode://file//Users/justinbowen/Documents/GitHub/claude-could/activeagent/test/integration/structured_output_json_parsing_test.rb:151)
[activeagent/test/integration/structured_output_json_parsing_test.rb:151](vscode://file//Users/sarbadajaiswal/development/Justin/activeagents/activeagent/test/integration/structured_output_json_parsing_test.rb:151)
<!-- Test: test-without-structured-output-uses-text/plain-content-type -->

```ruby
# Response object
#<ActiveAgent::GenerationProvider::Response:0x35fc
@message=#<ActiveAgent::ActionPrompt::Message:0x3610
#<ActiveAgent::GenerationProvider::Response:0x20d0
@message=#<ActiveAgent::ActionPrompt::Message:0x20d8
@action_id=nil,
@action_name=nil,
@action_requested=false,
@charset="UTF-8",
@content="The capital of France is Paris.",
@role=:assistant>
@prompt=#<ActiveAgent::ActionPrompt::Prompt:0x3624 ...>
@prompt=#<ActiveAgent::ActionPrompt::Prompt:0x20e0 ...>
@content_type="text/plain"
@raw_response={...}>

Expand Down
34 changes: 34 additions & 0 deletions lib/active_agent/has_context.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true
module ActiveAgent
module HasContext
extend ActiveSupport::Concern

class_methods do
# Example:
# has_context prompts: :prompts, messages: :messages, tools: :actions
#
# Associations added:
# has_many :prompt_contexts (ActivePrompt::Context, as: :agent)
# has_many :prompts, :messages, :actions (through prompt_contexts/prompts)
def has_context(prompts: :prompts, messages: :messages, tools: :actions)
has_many :prompt_contexts,
class_name: "ActivePrompt::Context",
as: :agent,
dependent: :destroy,
inverse_of: :agent

has_many prompts, through: :prompt_contexts, source: :prompt
has_many messages, through: prompts, source: :messages
has_many tools, through: prompts, source: :actions

define_method :add_prompt do |prompt, label: nil, metadata: {}|
ActivePrompt::Context.create!(agent: self, prompt:, label:, metadata:)
end

define_method :remove_prompt do |prompt|
prompt_contexts.where(prompt:).destroy_all
end
end
end
end
end
131 changes: 9 additions & 122 deletions lib/active_agent/test_case.rb
Original file line number Diff line number Diff line change
@@ -1,125 +1,12 @@
# # frozen_string_literal: true
# frozen_string_literal: true

# require_relative "test_helper"
# require "active_support/test_case"
# require "rails-dom-testing"
require "active_support/test_case"

# module ActiveAgent
# class NonInferrableAgentError < ::StandardError
# def initialize(name)
# super("Unable to determine the agent to test from #{name}. " \
# "You'll need to specify it using tests YourAgent in your " \
# "test case definition")
# end
# end
module ActiveAgent
class TestCase < ActiveSupport::TestCase
# minimal base to satisfy Zeitwerk
end
end

# class TestCase < ActiveSupport::TestCase
# module ClearTestDeliveries
# extend ActiveSupport::Concern

# included do
# setup :clear_test_generations
# teardown :clear_test_generations
# end

# private

# def clear_test_generations
# if ActiveAgent::Base.generation_method == :test
# ActiveAgent::Base.generations.clear
# end
# end
# end

# module Behavior
# extend ActiveSupport::Concern

# include ActiveSupport::Testing::ConstantLookup
# include TestHelper
# include Rails::Dom::Testing::Assertions::SelectorAssertions
# include Rails::Dom::Testing::Assertions::DomAssertions

# included do
# class_attribute :_agent_class
# setup :initialize_test_generations
# setup :set_expected_prompt
# teardown :restore_test_generations
# ActiveSupport.run_load_hooks(:active_agent_test_case, self)
# end

# module ClassMethods
# def tests(agent)
# case agent
# when String, Symbol
# self._agent_class = agent.to_s.camelize.constantize
# when Module
# self._agent_class = agent
# else
# raise NonInferrableAgentError.new(agent)
# end
# end

# def agent_class
# if agent = _agent_class
# agent
# else
# tests determine_default_agent(name)
# end
# end

# def determine_default_agent(name)
# agent = determine_constant_from_test_name(name) do |constant|
# Class === constant && constant < ActiveAgent::Base
# end
# raise NonInferrableAgentError.new(name) if agent.nil?
# agent
# end
# end

# # Reads the fixture file for the given agent.
# #
# # This is useful when testing agents by being able to write the body of
# # an promt inside a fixture. See the testing guide for a concrete example:
# # https://guides.rubyonrails.org/testing.html#revenge-of-the-fixtures
# def read_fixture(action)
# IO.readlines(File.join(Rails.root, "test", "fixtures", self.class.agent_class.name.underscore, action))
# end

# private

# def initialize_test_generations
# set_generation_method :test
# @old_perform_generations = ActiveAgent::Base.perform_generations
# ActiveAgent::Base.perform_generations = true
# ActiveAgent::Base.generations.clear
# end

# def restore_test_generations
# restore_generation_method
# ActiveAgent::Base.perform_generations = @old_perform_generations
# end

# def set_generation_method(method)
# @old_generation_method = ActiveAgent::Base.generation_method
# ActiveAgent::Base.generation_method = method
# end

# def restore_generation_method
# ActiveAgent::Base.generations.clear
# ActiveAgent::Base.generation_method = @old_generation_method
# end

# def set_expected_prompt
# @expected = ActiveAgent::ActionPrompt::Prompt.new
# @expected.content_type ["text", "plain", {"charset" => charset}]
# @expected.mime_version = "1.0"
# end

# def charset
# "UTF-8"
# end
# end

# include Behavior
# end
# end
# Back-compat for any existing tests
ActiveAgentTestCase = ActiveAgent::TestCase unless defined?(ActiveAgentTestCase)
7 changes: 7 additions & 0 deletions lib/active_prompt.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

require "active_prompt/version"
require "active_prompt/engine" if defined?(Rails)

module ActivePrompt
end
Loading