deepagents_rails 0.1.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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +309 -0
- data/lib/deepagents_rails/service.rb +93 -0
- data/lib/deepagents_rails/version.rb +3 -0
- data/lib/deepagents_rails.rb +31 -0
- data/lib/generators/deepagents/agent/agent_generator.rb +55 -0
- data/lib/generators/deepagents/agent/templates/agent.rb +99 -0
- data/lib/generators/deepagents/agent/templates/agent_spec.rb +33 -0
- data/lib/generators/deepagents/agent/templates/controller.rb +44 -0
- data/lib/generators/deepagents/agent/templates/index.html.erb +178 -0
- data/lib/generators/deepagents/agent/templates/show.html.erb +131 -0
- data/lib/generators/deepagents/controller/controller_generator.rb +51 -0
- data/lib/generators/deepagents/controller/templates/api_controller.rb +87 -0
- data/lib/generators/deepagents/controller/templates/serializer.rb +22 -0
- data/lib/generators/deepagents/install/install_generator.rb +41 -0
- data/lib/generators/deepagents/install/templates/deepagents.yml +38 -0
- data/lib/generators/deepagents/install/templates/initializer.rb +35 -0
- data/lib/generators/deepagents/model/model_generator.rb +43 -0
- data/lib/generators/deepagents/model/templates/conversation.rb +73 -0
- data/lib/generators/deepagents/model/templates/create_conversations_migration.rb +16 -0
- data/lib/generators/deepagents/model/templates/create_files_migration.rb +17 -0
- data/lib/generators/deepagents/model/templates/create_messages_migration.rb +17 -0
- data/lib/generators/deepagents/model/templates/file.rb +96 -0
- data/lib/generators/deepagents/model/templates/message.rb +30 -0
- data/lib/generators/deepagents/tool/templates/tool.rb +27 -0
- data/lib/generators/deepagents/tool/templates/tool_spec.rb +36 -0
- data/lib/generators/deepagents/tool/tool_generator.rb +58 -0
- data/lib/generators/deepagents/view/templates/_conversation.html.erb +10 -0
- data/lib/generators/deepagents/view/templates/_form.html.erb +33 -0
- data/lib/generators/deepagents/view/templates/_message.html.erb +31 -0
- data/lib/generators/deepagents/view/templates/index.html.erb +35 -0
- data/lib/generators/deepagents/view/templates/javascript.js +199 -0
- data/lib/generators/deepagents/view/templates/new.html.erb +10 -0
- data/lib/generators/deepagents/view/templates/show.html.erb +54 -0
- data/lib/generators/deepagents/view/templates/stylesheet.css +397 -0
- data/lib/generators/deepagents/view/view_generator.rb +50 -0
- metadata +121 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<%# DeepAgents <%= class_name %> Agent Interface %>
|
|
2
|
+
|
|
3
|
+
<div class="deepagents-container">
|
|
4
|
+
<div class="deepagents-header">
|
|
5
|
+
<h1><%= class_name %> Agent</h1>
|
|
6
|
+
<p>Ask a question or provide instructions to interact with the agent.</p>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div class="deepagents-conversation" id="conversation">
|
|
10
|
+
<% if @messages.present? %>
|
|
11
|
+
<% @messages.each do |message| %>
|
|
12
|
+
<div class="message <%= message[:role] %>">
|
|
13
|
+
<div class="message-content">
|
|
14
|
+
<%= simple_format(message[:content]) %>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="message-timestamp">
|
|
17
|
+
<%= message[:timestamp].strftime("%H:%M") %>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
<% end %>
|
|
21
|
+
<% else %>
|
|
22
|
+
<div class="empty-state">
|
|
23
|
+
<p>No messages yet. Start a conversation with the agent.</p>
|
|
24
|
+
</div>
|
|
25
|
+
<% end %>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<%= form_with url: deepagents_<%= file_name %>_index_path, method: :post, class: "deepagents-input-form", id: "agent-form", data: { remote: true } do |f| %>
|
|
29
|
+
<div class="input-container">
|
|
30
|
+
<%= f.text_area :input, placeholder: "Type your message here...", rows: 3, class: "deepagents-input", id: "agent-input" %>
|
|
31
|
+
|
|
32
|
+
<div class="file-upload-container">
|
|
33
|
+
<%= f.file_field :files, multiple: true, class: "file-input", id: "file-upload" %>
|
|
34
|
+
<label for="file-upload" class="file-upload-button">
|
|
35
|
+
<i class="fa fa-paperclip"></i> Attach Files
|
|
36
|
+
</label>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<%= f.submit "Send", class: "send-button", id: "send-button" %>
|
|
40
|
+
</div>
|
|
41
|
+
<% end %>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<style>
|
|
45
|
+
.deepagents-container {
|
|
46
|
+
max-width: 800px;
|
|
47
|
+
margin: 0 auto;
|
|
48
|
+
padding: 20px;
|
|
49
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.deepagents-header {
|
|
53
|
+
margin-bottom: 20px;
|
|
54
|
+
text-align: center;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.deepagents-conversation {
|
|
58
|
+
border: 1px solid #e0e0e0;
|
|
59
|
+
border-radius: 8px;
|
|
60
|
+
padding: 15px;
|
|
61
|
+
height: 400px;
|
|
62
|
+
overflow-y: auto;
|
|
63
|
+
margin-bottom: 20px;
|
|
64
|
+
background-color: #f9f9f9;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.message {
|
|
68
|
+
margin-bottom: 15px;
|
|
69
|
+
padding: 10px 15px;
|
|
70
|
+
border-radius: 8px;
|
|
71
|
+
max-width: 80%;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.message.user {
|
|
75
|
+
background-color: #e1f5fe;
|
|
76
|
+
margin-left: auto;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.message.assistant {
|
|
80
|
+
background-color: #f0f0f0;
|
|
81
|
+
margin-right: auto;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.message-timestamp {
|
|
85
|
+
font-size: 0.8em;
|
|
86
|
+
color: #888;
|
|
87
|
+
text-align: right;
|
|
88
|
+
margin-top: 5px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.input-container {
|
|
92
|
+
display: flex;
|
|
93
|
+
flex-direction: column;
|
|
94
|
+
gap: 10px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.deepagents-input {
|
|
98
|
+
width: 100%;
|
|
99
|
+
padding: 10px;
|
|
100
|
+
border: 1px solid #ddd;
|
|
101
|
+
border-radius: 8px;
|
|
102
|
+
resize: none;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.file-upload-container {
|
|
106
|
+
display: flex;
|
|
107
|
+
align-items: center;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.file-input {
|
|
111
|
+
display: none;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.file-upload-button {
|
|
115
|
+
display: inline-block;
|
|
116
|
+
padding: 8px 12px;
|
|
117
|
+
background-color: #f0f0f0;
|
|
118
|
+
border-radius: 4px;
|
|
119
|
+
cursor: pointer;
|
|
120
|
+
font-size: 0.9em;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.send-button {
|
|
124
|
+
padding: 10px 20px;
|
|
125
|
+
background-color: #2196f3;
|
|
126
|
+
color: white;
|
|
127
|
+
border: none;
|
|
128
|
+
border-radius: 8px;
|
|
129
|
+
cursor: pointer;
|
|
130
|
+
font-weight: bold;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.send-button:hover {
|
|
134
|
+
background-color: #1976d2;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.empty-state {
|
|
138
|
+
text-align: center;
|
|
139
|
+
color: #888;
|
|
140
|
+
padding: 40px 0;
|
|
141
|
+
}
|
|
142
|
+
</style>
|
|
143
|
+
|
|
144
|
+
<script>
|
|
145
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
146
|
+
const form = document.getElementById('agent-form');
|
|
147
|
+
const input = document.getElementById('agent-input');
|
|
148
|
+
const conversation = document.getElementById('conversation');
|
|
149
|
+
|
|
150
|
+
form.addEventListener('ajax:success', function(event) {
|
|
151
|
+
const [data, status, xhr] = event.detail;
|
|
152
|
+
|
|
153
|
+
// Add user message
|
|
154
|
+
const userMessage = document.createElement('div');
|
|
155
|
+
userMessage.className = 'message user';
|
|
156
|
+
userMessage.innerHTML = `
|
|
157
|
+
<div class="message-content">${input.value.replace(/\n/g, '<br>')}</div>
|
|
158
|
+
<div class="message-timestamp">${new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</div>
|
|
159
|
+
`;
|
|
160
|
+
conversation.appendChild(userMessage);
|
|
161
|
+
|
|
162
|
+
// Add assistant message
|
|
163
|
+
const assistantMessage = document.createElement('div');
|
|
164
|
+
assistantMessage.className = 'message assistant';
|
|
165
|
+
assistantMessage.innerHTML = `
|
|
166
|
+
<div class="message-content">${data.response.replace(/\n/g, '<br>')}</div>
|
|
167
|
+
<div class="message-timestamp">${new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</div>
|
|
168
|
+
`;
|
|
169
|
+
conversation.appendChild(assistantMessage);
|
|
170
|
+
|
|
171
|
+
// Clear input
|
|
172
|
+
input.value = '';
|
|
173
|
+
|
|
174
|
+
// Scroll to bottom
|
|
175
|
+
conversation.scrollTop = conversation.scrollHeight;
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
</script>
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
<%# DeepAgents <%= class_name %> Agent Conversation View %>
|
|
2
|
+
|
|
3
|
+
<div class="deepagents-container">
|
|
4
|
+
<div class="deepagents-header">
|
|
5
|
+
<h1>Conversation #<%= @conversation.id %></h1>
|
|
6
|
+
<p><%= @conversation.created_at.strftime("%B %d, %Y at %H:%M") %></p>
|
|
7
|
+
<%= link_to "Back to All Conversations", deepagents_<%= file_name %>_index_path, class: "back-link" %>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<div class="deepagents-conversation" id="conversation">
|
|
11
|
+
<% @conversation.messages.each do |message| %>
|
|
12
|
+
<div class="message <%= message.role %>">
|
|
13
|
+
<div class="message-content">
|
|
14
|
+
<%= simple_format(message.content) %>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="message-timestamp">
|
|
17
|
+
<%= message.created_at.strftime("%H:%M") %>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
<% end %>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<% if @conversation.files.any? %>
|
|
24
|
+
<div class="files-section">
|
|
25
|
+
<h3>Files</h3>
|
|
26
|
+
<div class="files-container">
|
|
27
|
+
<% @conversation.files.each do |file| %>
|
|
28
|
+
<div class="file-item">
|
|
29
|
+
<div class="file-name"><%= file.filename %></div>
|
|
30
|
+
<div class="file-actions">
|
|
31
|
+
<%= link_to "Download", file.url, class: "file-download" %>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<% end %>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
<% end %>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<style>
|
|
41
|
+
.deepagents-container {
|
|
42
|
+
max-width: 800px;
|
|
43
|
+
margin: 0 auto;
|
|
44
|
+
padding: 20px;
|
|
45
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.deepagents-header {
|
|
49
|
+
margin-bottom: 20px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.back-link {
|
|
53
|
+
display: inline-block;
|
|
54
|
+
margin-top: 10px;
|
|
55
|
+
color: #2196f3;
|
|
56
|
+
text-decoration: none;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.back-link:hover {
|
|
60
|
+
text-decoration: underline;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.deepagents-conversation {
|
|
64
|
+
border: 1px solid #e0e0e0;
|
|
65
|
+
border-radius: 8px;
|
|
66
|
+
padding: 15px;
|
|
67
|
+
max-height: 600px;
|
|
68
|
+
overflow-y: auto;
|
|
69
|
+
margin-bottom: 20px;
|
|
70
|
+
background-color: #f9f9f9;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.message {
|
|
74
|
+
margin-bottom: 15px;
|
|
75
|
+
padding: 10px 15px;
|
|
76
|
+
border-radius: 8px;
|
|
77
|
+
max-width: 80%;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.message.user {
|
|
81
|
+
background-color: #e1f5fe;
|
|
82
|
+
margin-left: auto;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.message.assistant {
|
|
86
|
+
background-color: #f0f0f0;
|
|
87
|
+
margin-right: auto;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.message-timestamp {
|
|
91
|
+
font-size: 0.8em;
|
|
92
|
+
color: #888;
|
|
93
|
+
text-align: right;
|
|
94
|
+
margin-top: 5px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.files-section {
|
|
98
|
+
margin-top: 30px;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.files-container {
|
|
102
|
+
display: flex;
|
|
103
|
+
flex-wrap: wrap;
|
|
104
|
+
gap: 10px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.file-item {
|
|
108
|
+
border: 1px solid #e0e0e0;
|
|
109
|
+
border-radius: 4px;
|
|
110
|
+
padding: 10px;
|
|
111
|
+
width: calc(50% - 5px);
|
|
112
|
+
display: flex;
|
|
113
|
+
justify-content: space-between;
|
|
114
|
+
align-items: center;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.file-name {
|
|
118
|
+
font-size: 0.9em;
|
|
119
|
+
word-break: break-all;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.file-download {
|
|
123
|
+
color: #2196f3;
|
|
124
|
+
text-decoration: none;
|
|
125
|
+
font-size: 0.9em;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.file-download:hover {
|
|
129
|
+
text-decoration: underline;
|
|
130
|
+
}
|
|
131
|
+
</style>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Deepagents
|
|
2
|
+
module Generators
|
|
3
|
+
class ControllerGenerator < Rails::Generators::NamedBase
|
|
4
|
+
source_root File.expand_path('templates', __dir__)
|
|
5
|
+
|
|
6
|
+
desc "Creates DeepAgents API controllers for your Rails application"
|
|
7
|
+
|
|
8
|
+
class_option :skip_routes, type: :boolean, default: false, desc: "Skip routes generation"
|
|
9
|
+
class_option :api_version, type: :string, default: "v1", desc: "API version"
|
|
10
|
+
|
|
11
|
+
def create_controller_file
|
|
12
|
+
template "api_controller.rb", "app/controllers/api/#{options[:api_version]}/#{file_name}_controller.rb"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def create_serializer_file
|
|
16
|
+
template "serializer.rb", "app/serializers/#{file_name}_serializer.rb"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def add_routes
|
|
20
|
+
return if options[:skip_routes]
|
|
21
|
+
|
|
22
|
+
route_file = "config/routes.rb"
|
|
23
|
+
route_content = <<~ROUTE
|
|
24
|
+
namespace :api do
|
|
25
|
+
namespace :#{options[:api_version]} do
|
|
26
|
+
resources :#{plural_name}, only: [:index, :show, :create] do
|
|
27
|
+
member do
|
|
28
|
+
post :run
|
|
29
|
+
post :upload
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
ROUTE
|
|
35
|
+
|
|
36
|
+
inject_into_file route_file, route_content, after: "Rails.application.routes.draw do\n"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def display_next_steps
|
|
40
|
+
say "\n"
|
|
41
|
+
say "DeepAgents API controller for #{file_name} has been created! 🎮", :green
|
|
42
|
+
say "\n"
|
|
43
|
+
say "Next steps:", :yellow
|
|
44
|
+
say " 1. Ensure you have the required models (use the model generator if needed)"
|
|
45
|
+
say " 2. Add any custom API endpoints to your controller"
|
|
46
|
+
say " 3. Test your API endpoints with curl or Postman"
|
|
47
|
+
say "\n"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Api
|
|
4
|
+
module <%= options[:api_version].camelize %>
|
|
5
|
+
class <%= class_name %>Controller < ApplicationController
|
|
6
|
+
before_action :set_conversation, only: [:show, :run, :upload]
|
|
7
|
+
|
|
8
|
+
# GET /api/<%= options[:api_version] %>/<%= plural_name %>
|
|
9
|
+
def index
|
|
10
|
+
@conversations = Deepagents::<%= class_name %>Conversation.recent.limit(20)
|
|
11
|
+
|
|
12
|
+
render json: @conversations, each_serializer: <%= class_name %>Serializer
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# GET /api/<%= options[:api_version] %>/<%= plural_name %>/:id
|
|
16
|
+
def show
|
|
17
|
+
render json: @conversation, serializer: <%= class_name %>Serializer, include: ['messages', 'files']
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# POST /api/<%= options[:api_version] %>/<%= plural_name %>
|
|
21
|
+
def create
|
|
22
|
+
@conversation = Deepagents::<%= class_name %>Conversation.new(conversation_params)
|
|
23
|
+
|
|
24
|
+
if @conversation.save
|
|
25
|
+
# Add a system message if provided
|
|
26
|
+
if params[:system_message].present?
|
|
27
|
+
@conversation.<%= file_name %>_messages.create!(
|
|
28
|
+
role: 'system',
|
|
29
|
+
content: params[:system_message]
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
render json: @conversation, serializer: <%= class_name %>Serializer, status: :created
|
|
34
|
+
else
|
|
35
|
+
render json: { errors: @conversation.errors }, status: :unprocessable_entity
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# POST /api/<%= options[:api_version] %>/<%= plural_name %>/:id/run
|
|
40
|
+
def run
|
|
41
|
+
input = params[:input]
|
|
42
|
+
agent_name = params[:agent_name]
|
|
43
|
+
|
|
44
|
+
if input.blank?
|
|
45
|
+
render json: { error: 'Input is required' }, status: :unprocessable_entity
|
|
46
|
+
return
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
result = @conversation.run_agent(input, agent_name)
|
|
50
|
+
|
|
51
|
+
render json: {
|
|
52
|
+
message: result[:message],
|
|
53
|
+
files: result[:files]
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# POST /api/<%= options[:api_version] %>/<%= plural_name %>/:id/upload
|
|
58
|
+
def upload
|
|
59
|
+
if params[:file].blank?
|
|
60
|
+
render json: { error: 'File is required' }, status: :unprocessable_entity
|
|
61
|
+
return
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
file = params[:file]
|
|
65
|
+
|
|
66
|
+
@file = @conversation.<%= file_name %>_files.create!(
|
|
67
|
+
filename: file.original_filename,
|
|
68
|
+
content: file.read
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
render json: @file
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def set_conversation
|
|
77
|
+
@conversation = Deepagents::<%= class_name %>Conversation.find(params[:id])
|
|
78
|
+
rescue ActiveRecord::RecordNotFound
|
|
79
|
+
render json: { error: 'Conversation not found' }, status: :not_found
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def conversation_params
|
|
83
|
+
params.require(:<%= file_name %>).permit(:title, :agent_name, metadata: {})
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= class_name %>Serializer < ActiveModel::Serializer
|
|
4
|
+
attributes :id, :title, :agent_name, :metadata, :created_at, :updated_at
|
|
5
|
+
|
|
6
|
+
has_many :<%= file_name %>_messages, serializer: <%= class_name %>MessageSerializer
|
|
7
|
+
has_many :<%= file_name %>_files, serializer: <%= class_name %>FileSerializer
|
|
8
|
+
|
|
9
|
+
# Add any custom serialization logic here
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class <%= class_name %>MessageSerializer < ActiveModel::Serializer
|
|
13
|
+
attributes :id, :role, :content, :metadata, :created_at
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class <%= class_name %>FileSerializer < ActiveModel::Serializer
|
|
17
|
+
attributes :id, :filename, :content, :metadata, :created_at, :url
|
|
18
|
+
|
|
19
|
+
def url
|
|
20
|
+
object.url if object.respond_to?(:url)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Deepagents
|
|
2
|
+
module Generators
|
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
|
4
|
+
source_root File.expand_path('templates', __dir__)
|
|
5
|
+
|
|
6
|
+
desc "Creates a DeepAgents initializer and necessary files for your Rails application"
|
|
7
|
+
|
|
8
|
+
def create_initializer_file
|
|
9
|
+
template "initializer.rb", "config/initializers/deepagents.rb"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def create_configuration_file
|
|
13
|
+
template "deepagents.yml", "config/deepagents.yml"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def mount_engine
|
|
17
|
+
route "mount DeepagentsRails::Engine => '/deepagents'"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def add_environment_variables
|
|
21
|
+
append_to_file '.env.example', <<~ENV
|
|
22
|
+
|
|
23
|
+
# DeepAgents configuration
|
|
24
|
+
# DEEPAGENTS_API_KEY=your_api_key_here
|
|
25
|
+
# DEEPAGENTS_PROVIDER=claude # or openai
|
|
26
|
+
ENV
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def display_post_install_message
|
|
30
|
+
say "\n"
|
|
31
|
+
say "DeepAgents Rails has been installed! 🎉", :green
|
|
32
|
+
say "\n"
|
|
33
|
+
say "Next steps:", :yellow
|
|
34
|
+
say " 1. Configure your API keys in config/deepagents.yml or through environment variables"
|
|
35
|
+
say " 2. Generate your first agent with: rails g deepagents:agent NAME"
|
|
36
|
+
say " 3. Check out the documentation at https://github.com/cdaviis/deepagents_rails"
|
|
37
|
+
say "\n"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# DeepAgents Rails Configuration
|
|
2
|
+
|
|
3
|
+
default: &default
|
|
4
|
+
# API key for your LLM provider (Claude or OpenAI)
|
|
5
|
+
# You can also set this via environment variable DEEPAGENTS_API_KEY
|
|
6
|
+
api_key: <%= ENV.fetch('DEEPAGENTS_API_KEY', nil) %>
|
|
7
|
+
|
|
8
|
+
# LLM provider to use (claude or openai)
|
|
9
|
+
provider: <%= ENV.fetch('DEEPAGENTS_PROVIDER', 'claude') %>
|
|
10
|
+
|
|
11
|
+
# Default model to use
|
|
12
|
+
model: <%= ENV.fetch('DEEPAGENTS_MODEL', 'claude-3-sonnet-20240229') %>
|
|
13
|
+
|
|
14
|
+
# Default temperature for LLM requests (0.0 to 1.0)
|
|
15
|
+
temperature: <%= ENV.fetch('DEEPAGENTS_TEMPERATURE', '0.7') %>
|
|
16
|
+
|
|
17
|
+
# Maximum tokens to generate in responses
|
|
18
|
+
max_tokens: <%= ENV.fetch('DEEPAGENTS_MAX_TOKENS', '4096') %>
|
|
19
|
+
|
|
20
|
+
# Default tools available to all agents
|
|
21
|
+
default_tools:
|
|
22
|
+
- calculator
|
|
23
|
+
- file_system
|
|
24
|
+
- planning
|
|
25
|
+
|
|
26
|
+
development:
|
|
27
|
+
<<: *default
|
|
28
|
+
log_requests: true
|
|
29
|
+
|
|
30
|
+
test:
|
|
31
|
+
<<: *default
|
|
32
|
+
# Use mock model for testing
|
|
33
|
+
provider: mock
|
|
34
|
+
model: mock-model
|
|
35
|
+
|
|
36
|
+
production:
|
|
37
|
+
<<: *default
|
|
38
|
+
log_requests: false
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# DeepAgents Rails configuration
|
|
4
|
+
DeepagentsRails::Engine.config.deepagents.tap do |config|
|
|
5
|
+
# API key for your LLM provider (Claude or OpenAI)
|
|
6
|
+
# You can set this via environment variable DEEPAGENTS_API_KEY
|
|
7
|
+
config.api_key = ENV.fetch('DEEPAGENTS_API_KEY', nil)
|
|
8
|
+
|
|
9
|
+
# LLM provider to use (:claude or :openai)
|
|
10
|
+
# You can set this via environment variable DEEPAGENTS_PROVIDER
|
|
11
|
+
config.provider = ENV.fetch('DEEPAGENTS_PROVIDER', 'claude').to_sym
|
|
12
|
+
|
|
13
|
+
# Default model to use
|
|
14
|
+
# For Claude: "claude-3-sonnet-20240229", "claude-3-opus-20240229", etc.
|
|
15
|
+
# For OpenAI: "gpt-4o", "gpt-4-turbo", etc.
|
|
16
|
+
config.model = ENV.fetch('DEEPAGENTS_MODEL', 'claude-3-sonnet-20240229')
|
|
17
|
+
|
|
18
|
+
# Default temperature for LLM requests (0.0 to 1.0)
|
|
19
|
+
config.temperature = ENV.fetch('DEEPAGENTS_TEMPERATURE', '0.7').to_f
|
|
20
|
+
|
|
21
|
+
# Maximum tokens to generate in responses
|
|
22
|
+
config.max_tokens = ENV.fetch('DEEPAGENTS_MAX_TOKENS', '4096').to_i
|
|
23
|
+
|
|
24
|
+
# Configure default tools available to all agents
|
|
25
|
+
# config.default_tools = [:calculator, :web_search]
|
|
26
|
+
|
|
27
|
+
# Configure logging
|
|
28
|
+
config.log_requests = Rails.env.development?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Load custom tools
|
|
32
|
+
# Dir[Rails.root.join('app/deepagents/tools/**/*.rb')].each { |file| require file }
|
|
33
|
+
|
|
34
|
+
# Load custom agents
|
|
35
|
+
# Dir[Rails.root.join('app/deepagents/agents/**/*.rb')].each { |file| require file }
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Deepagents
|
|
2
|
+
module Generators
|
|
3
|
+
class ModelGenerator < Rails::Generators::NamedBase
|
|
4
|
+
source_root File.expand_path('templates', __dir__)
|
|
5
|
+
|
|
6
|
+
desc "Creates DeepAgents models for your Rails application"
|
|
7
|
+
|
|
8
|
+
def create_conversation_model
|
|
9
|
+
template "conversation.rb", "app/models/deepagents/#{file_name}_conversation.rb"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def create_message_model
|
|
13
|
+
template "message.rb", "app/models/deepagents/#{file_name}_message.rb"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def create_file_model
|
|
17
|
+
template "file.rb", "app/models/deepagents/#{file_name}_file.rb"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def create_migrations
|
|
21
|
+
template "create_conversations_migration.rb", "db/migrate/#{timestamp}_create_deepagents_#{file_name}_conversations.rb"
|
|
22
|
+
template "create_messages_migration.rb", "db/migrate/#{timestamp(1)}_create_deepagents_#{file_name}_messages.rb"
|
|
23
|
+
template "create_files_migration.rb", "db/migrate/#{timestamp(2)}_create_deepagents_#{file_name}_files.rb"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def display_next_steps
|
|
27
|
+
say "\n"
|
|
28
|
+
say "DeepAgents models for #{file_name} have been created! 📚", :green
|
|
29
|
+
say "\n"
|
|
30
|
+
say "Next steps:", :yellow
|
|
31
|
+
say " 1. Run migrations with: rails db:migrate"
|
|
32
|
+
say " 2. Use the models in your agents and controllers"
|
|
33
|
+
say "\n"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def timestamp(offset = 0)
|
|
39
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S").to_i + offset
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|