appchat 0.0.4 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/appchat/appchat_generator.rb +56 -16
- data/lib/generators/appchat/templates/assets/appchat.tailwind.css +14 -0
- data/lib/generators/appchat/templates/chats/chat.html.erb +3 -3
- data/lib/generators/appchat/templates/chats/index.html.erb +18 -15
- data/lib/generators/appchat/templates/chats_controller.rb +1 -1
- data/lib/generators/appchat/templates/get_ai_response_job.rb +27 -5
- data/lib/generators/appchat/templates/javascript/speech_to_text_controller.js +65 -0
- data/lib/generators/appchat/templates/messages/message.html.erb +3 -1
- data/lib/generators/appchat/templates/messages/new.html.erb +35 -12
- data/lib/generators/appchat/templates/models/appchat_function.rb +18 -0
- data/lib/generators/appchat/templates/services/appchat_function_service.rb +47 -0
- data/lib/generators/appchat/templates/services/web_search_service.rb +23 -0
- data/lib/generators/appchat/templates/tasks/create_appchat_function.rake +13 -0
- metadata +9 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4aca406ace060a7c7b9af445e20d5be1f3605614109320792523cba09b7f92fe
|
4
|
+
data.tar.gz: 8c1e290b46dfca74c66c94748d305386d67703291e455c066aed1df0d1dc5cc8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5aaa00c36101e08cb562924a7a3a0cca7da5ef49680eb6f9f202a8d17ebe0cbbe852fe86ef4481cb65ef8c2811d9933731074363875930ccb916e92d9fdd66d4
|
7
|
+
data.tar.gz: 876249a02dbec15d9d3ee4c9dbabcfbcc843970f8d85d7b4e9fad563a97af8a86c1a3600c7383ef3dea9fc2bfffef4c19f3fef70cb7009f0fc0a12401efbb462
|
@@ -9,28 +9,45 @@ class AppchatGenerator < Rails::Generators::Base
|
|
9
9
|
source_root File.expand_path('templates', __dir__)
|
10
10
|
|
11
11
|
def add_gems
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
gems = %w(
|
13
|
+
ollama-ai
|
14
|
+
tailwindcss-rails
|
15
|
+
watir
|
16
|
+
)
|
17
|
+
|
18
|
+
gems.each do |gem|
|
19
|
+
unless gem_exists?(gem)
|
20
|
+
append_to_file 'Gemfile', "\ngem '#{gem}'\n"
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
24
|
Bundler.with_unbundled_env do
|
21
25
|
run 'bundle install'
|
22
26
|
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def install_tailwind
|
23
30
|
run 'rails tailwindcss:install'
|
24
31
|
end
|
25
32
|
|
26
|
-
def
|
27
|
-
generate "model", "Chat context:text"
|
33
|
+
def set_routes
|
28
34
|
route "resources :chats"
|
29
|
-
|
35
|
+
route "resources :messages"
|
30
36
|
end
|
31
37
|
|
32
|
-
def
|
38
|
+
def generate_models
|
39
|
+
generate "model", "Chat context:text"
|
40
|
+
generate "model", "Message chat:references content:text role:integer status:string"
|
41
|
+
generate "model", "AppchatFunction name:string description:text class_name:string"
|
42
|
+
generate "model", "FunctionParameter appchat_function:references name:string example_value:string"
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_controllers
|
33
46
|
copy_file "chats_controller.rb", "app/controllers/chats_controller.rb"
|
47
|
+
copy_file "messages_controller.rb", "app/controllers/messages_controller.rb", force: true
|
48
|
+
end
|
49
|
+
|
50
|
+
def create_views
|
34
51
|
copy_file "chats/chat.html.erb", "app/views/chats/_chat.html.erb", force: true
|
35
52
|
copy_file "chats/index.html.erb", "app/views/chats/index.html.erb", force: true
|
36
53
|
copy_file "chats/show.html.erb", "app/views/chats/show.html.erb", force: true
|
@@ -38,21 +55,30 @@ class AppchatGenerator < Rails::Generators::Base
|
|
38
55
|
copy_file "messages/new.html.erb", "app/views/messages/new.html.erb", force: true
|
39
56
|
copy_file "messages/message.html.erb", "app/views/messages/_message.html.erb", force: true
|
40
57
|
copy_file "messages/_typing_bubbles.html.erb", "app/views/messages/_typing_bubbles.html.erb", force: true
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_stylesheets
|
41
61
|
copy_file "assets/appchat.tailwind.css", "app/assets/stylesheets/appchat.tailwind.css", force: true
|
62
|
+
end
|
63
|
+
|
64
|
+
def create_stimulus_controllers
|
42
65
|
copy_file "javascript/chat_message_controller.js", "app/javascript/controllers/chat_message_controller.js"
|
66
|
+
copy_file "javascript/speech_to_text_controller.js", "app/javascript/controllers/speech_to_text_controller.js"
|
43
67
|
end
|
44
68
|
|
45
69
|
def copy_models
|
46
70
|
copy_file "models/message.rb", "app/models/message.rb", force: true
|
47
71
|
copy_file "models/chat.rb", "app/models/chat.rb", force: true
|
72
|
+
copy_file "models/appchat_function.rb", "app/models/appchat_function.rb", force: true
|
48
73
|
end
|
49
74
|
|
50
|
-
def
|
51
|
-
|
75
|
+
def copy_services
|
76
|
+
copy_file "services/appchat_function_service.rb", "app/services/appchat_function_service.rb", force: true
|
77
|
+
copy_file "services/web_search_service.rb", "app/services/web_search_service.rb", force: true
|
52
78
|
end
|
53
79
|
|
54
|
-
def
|
55
|
-
inject_into_class 'app/models/chat.rb', 'Chat', "
|
80
|
+
def serialize_context
|
81
|
+
inject_into_class 'app/models/chat.rb', 'Chat', "serialize :context, coder:JSON, type: Array\n"
|
56
82
|
end
|
57
83
|
|
58
84
|
def run_migrations
|
@@ -63,8 +89,22 @@ class AppchatGenerator < Rails::Generators::Base
|
|
63
89
|
copy_file "get_ai_response_job.rb", "app/jobs/get_ai_response_job.rb"
|
64
90
|
end
|
65
91
|
|
66
|
-
def
|
67
|
-
|
92
|
+
def swap_class_in_layout
|
93
|
+
layout_file = "app/views/layouts/application.html.erb"
|
94
|
+
|
95
|
+
if File.exist?(layout_file)
|
96
|
+
gsub_file layout_file, /\bmt-28\b/, "mt-10"
|
97
|
+
else
|
98
|
+
say "Layout file not found. No changes were made.", :red
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def copy_rake_tasks
|
103
|
+
copy_file "tasks/create_appchat_function.rake", "lib/tasks/create_appchat_function.rake"
|
104
|
+
end
|
105
|
+
|
106
|
+
def create_functions
|
107
|
+
rake "appchat_function:create_web_search"
|
68
108
|
end
|
69
109
|
|
70
110
|
def show_art
|
@@ -59,4 +59,18 @@
|
|
59
59
|
}
|
60
60
|
}
|
61
61
|
|
62
|
+
#microphone-button.recording {
|
63
|
+
animation: flash 1s infinite;
|
64
|
+
}
|
65
|
+
|
66
|
+
@keyframes flash {
|
67
|
+
0%, 100% {
|
68
|
+
opacity: 80;
|
69
|
+
background-color: #EE4B2B; /* Ensure the color remains during the flash */
|
70
|
+
}
|
71
|
+
50% {
|
72
|
+
opacity: 0.5;
|
73
|
+
background-color: #EE4B2B; /* Ensure the color remains during the flash */
|
74
|
+
}
|
75
|
+
}
|
62
76
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<%= link_to chat, data: { turbo_frame: :chat } do %>
|
2
|
-
<div class="w-full text-gray-100 hover:text-green-500">
|
3
|
-
|
4
|
-
</div>
|
2
|
+
<div class="w-full text-gray-100 hover:text-green-500">
|
3
|
+
<%= chat&.messages&.first&.content&.truncate(45) || "New AI chat conversation" %>
|
4
|
+
</div>
|
5
5
|
<% end %>
|
@@ -3,20 +3,23 @@
|
|
3
3
|
background-color: black;
|
4
4
|
}
|
5
5
|
</style>
|
6
|
+
|
6
7
|
<div class="flex w-full">
|
7
|
-
<%= turbo_stream_from :chats %>
|
8
|
-
<div class="max-w-sm w-full h-full flex flex-col gap-4 p-2 w-full flex-shrink-0 bg-[#2e2e2e] rounded-lg" id="chats">
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
8
|
+
<%= turbo_stream_from :chats %>
|
9
|
+
<div class="max-w-sm w-full h-full flex flex-col gap-4 p-2 w-full flex-shrink-0 bg-[#2e2e2e] rounded-lg" id="chats">
|
10
|
+
<%= link_to chats_path, data: { turbo_method: :post, turbo_frame: :chat }, class: "flex gap-2 justify-center items-center bg-gray-500 text-gray-100 rounded-lg p-2 text-center mr-auto" do %>
|
11
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
12
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
13
|
+
</svg>
|
14
|
+
<span> Create Chat </span>
|
15
|
+
<% end %>
|
16
|
+
<div class="h-[70vh] overflow-y-hidden overflow-y-scroll">
|
17
|
+
<% @chats.each do |chat| %>
|
18
|
+
<%= render "chat", chat: chat %>
|
19
|
+
<% end %>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
<div class="w-full ml-4">
|
23
|
+
<%= turbo_frame_tag :chat, src: ( chat_path(@chats.first) if @chats.any? ) %>
|
24
|
+
</div>
|
22
25
|
</div>
|
@@ -1,27 +1,33 @@
|
|
1
1
|
class GetAiResponseJob < ApplicationJob
|
2
2
|
queue_as :default
|
3
3
|
|
4
|
-
attr_reader :chat, :user_prompt
|
4
|
+
attr_reader :client, :chat, :user_prompt, :informed_prompt, :message
|
5
5
|
|
6
6
|
def perform(chat_id, user_prompt)
|
7
|
+
@client = new_client
|
7
8
|
@chat = Chat.find(chat_id)
|
8
9
|
@user_prompt = user_prompt
|
10
|
+
@message = chat.messages.create(role: 'assistant')
|
11
|
+
appchat_functions
|
9
12
|
call_ollama
|
10
13
|
end
|
11
14
|
|
12
15
|
private
|
13
16
|
|
14
|
-
def
|
15
|
-
|
17
|
+
def new_client
|
18
|
+
Ollama.new(
|
16
19
|
credentials: { address: 'http://localhost:11434' },
|
17
20
|
options: { server_sent_events: true }
|
18
21
|
)
|
19
|
-
|
22
|
+
end
|
23
|
+
|
24
|
+
def call_ollama
|
25
|
+
prompt = informed_prompt || user_prompt
|
20
26
|
|
21
27
|
response = client.generate(
|
22
28
|
{
|
23
29
|
model: 'llama3.1',
|
24
|
-
prompt:
|
30
|
+
prompt: prompt,
|
25
31
|
context: chat.context,
|
26
32
|
stream: true,
|
27
33
|
}
|
@@ -35,4 +41,20 @@ class GetAiResponseJob < ApplicationJob
|
|
35
41
|
end
|
36
42
|
chat.update(context: response.last["context"])
|
37
43
|
end
|
44
|
+
|
45
|
+
def appchat_functions
|
46
|
+
appchat_function_service = AppchatFunctionService.new(chat.id, user_prompt).run
|
47
|
+
if appchat_function_service["match"] == "true" && appchat_function_service["appchat_function"].in?(AppchatFunction.pluck(:class_name))
|
48
|
+
puts "Function Match Found! --> #{appchat_function_service}"
|
49
|
+
function_class = appchat_function_service["appchat_function"].constantize
|
50
|
+
function_response = function_class.new(appchat_function_service["parameters"]).run do |status|
|
51
|
+
message.update(status: status)
|
52
|
+
end
|
53
|
+
|
54
|
+
return if function_response.nil?
|
55
|
+
@informed_prompt = "#{ user_prompt }, base your response on this data:
|
56
|
+
#{ appchat_function_service["name"] } responded with: #{ function_response },
|
57
|
+
:current_time => #{Date.current}"
|
58
|
+
end
|
59
|
+
end
|
38
60
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
// Connects to data-controller="speech-to-text"
|
4
|
+
export default class extends Controller {
|
5
|
+
static targets = ["microphoneButton", "chatInput"]
|
6
|
+
|
7
|
+
connect() {
|
8
|
+
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
9
|
+
if (SpeechRecognition) {
|
10
|
+
this.microphoneButtonTarget.classList.remove("hidden")
|
11
|
+
} else {
|
12
|
+
return
|
13
|
+
}
|
14
|
+
|
15
|
+
this.recognition = new SpeechRecognition();
|
16
|
+
this.recognition.continuous = false;
|
17
|
+
this.recognition.lang = 'en-US';
|
18
|
+
this.recognition.interimResults = false;
|
19
|
+
this.recognition.maxAlternatives = 1;
|
20
|
+
|
21
|
+
this.recognition.onresult = this.handleResult.bind(this);
|
22
|
+
this.recognition.onerror = this.handleError.bind(this);
|
23
|
+
this.recognition.onstart = this.handleStart.bind(this);
|
24
|
+
this.recognition.onend = this.handleEnd.bind(this);
|
25
|
+
|
26
|
+
this.isRecognizing = false; // Track if recognition is active
|
27
|
+
}
|
28
|
+
|
29
|
+
startRecognition(event) {
|
30
|
+
event.preventDefault();
|
31
|
+
if (this.isRecognizing) {
|
32
|
+
console.log("Recognition already started");
|
33
|
+
return;
|
34
|
+
}
|
35
|
+
|
36
|
+
try {
|
37
|
+
this.recognition.start();
|
38
|
+
this.isRecognizing = true;
|
39
|
+
} catch (error) {
|
40
|
+
console.error("Recognition start failed:", error);
|
41
|
+
this.isRecognizing = false;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
handleStart() {
|
46
|
+
this.microphoneButtonTarget.classList.add("recording");
|
47
|
+
}
|
48
|
+
|
49
|
+
handleEnd() {
|
50
|
+
this.microphoneButtonTarget.classList.remove("recording");
|
51
|
+
this.isRecognizing = false;
|
52
|
+
}
|
53
|
+
|
54
|
+
handleResult(event) {
|
55
|
+
const transcript = event.results[0][0].transcript;
|
56
|
+
this.chatInputTarget.value += ` ${transcript}`;
|
57
|
+
this.recognition.stop();
|
58
|
+
}
|
59
|
+
|
60
|
+
handleError(event) {
|
61
|
+
console.error(`Error occurred in recognition: ${event.error}`);
|
62
|
+
this.isRecognizing = false;
|
63
|
+
alert(`Speech recognition error: ${event.error}`);
|
64
|
+
}
|
65
|
+
}
|
@@ -1,5 +1,7 @@
|
|
1
1
|
<div data-chat-message-target="message" id="<%= dom_id(message) %>" class="bg-gray-200 text-gray-900 shadow-lg p-4 rounded-lg">
|
2
|
-
<% if message.content? %>
|
2
|
+
<% if message.status? && !message.content? %>
|
3
|
+
<%= message.status %>
|
4
|
+
<% elsif message.content? %>
|
3
5
|
<%= message.content %>
|
4
6
|
<% else %>
|
5
7
|
<%= render 'messages/typing_bubbles' %>
|
@@ -1,16 +1,39 @@
|
|
1
1
|
<%= turbo_frame_tag :new_message do %>
|
2
2
|
<%= form_with model: @chat.messages.new, url: messages_path(chat_id: @chat.id) do |f| %>
|
3
|
-
<div class="w-full flex items-center gap-2">
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
3
|
+
<div class="w-full flex items-center gap-2 mt-4">
|
4
|
+
<div class="flex flex-col w-full" data-controller="speech-to-text">
|
5
|
+
<div class="relative w-full">
|
6
|
+
<%= f.text_area :content, class: "w-full pr-32 rounded-lg", "data-speech-to-text-target":"chatInput" %>
|
7
|
+
<button id="microphone-button"
|
8
|
+
data-speech-to-text-target="microphoneButton"
|
9
|
+
data-speech-to-text-recording-class="recording"
|
10
|
+
data-action="click->speech-to-text#startRecognition"
|
11
|
+
class="absolute right-14 top-1/2 transform -translate-y-1/2 p-1 mr-2 bg-gray-200 rounded-full hidden">
|
12
|
+
<svg
|
13
|
+
fill="#000000"
|
14
|
+
height="40px"
|
15
|
+
width="40px"
|
16
|
+
version="1.1"
|
17
|
+
xmlns="http://www.w3.org/2000/svg"
|
18
|
+
viewBox="0 0 512 512"
|
19
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
20
|
+
enable-background="new 0 0 512 512">
|
21
|
+
<g>
|
22
|
+
<g>
|
23
|
+
<path d="m439.5,236c0-11.3-9.1-20.4-20.4-20.4s-20.4,9.1-20.4,20.4c0,70-64,126.9-142.7,126.9-78.7,0-142.7-56.9-142.7-126.9 0-11.3-9.1-20.4-20.4-20.4s-20.4,9.1-20.4,20.4c0,86.2 71.5,157.4 163.1,166.7v57.5h-23.6c-11.3,0-20.4,9.1-20.4,20.4 0,11.3 9.1,20.4 20.4,20.4h88c11.3,0 20.4-9.1 20.4-20.4 0-11.3-9.1-20.4-20.4-20.4h-23.6v-57.5c91.6-9.3 163.1-80.5 163.1-166.7z"/>
|
24
|
+
<path d="m256,323.5c51,0 92.3-41.3 92.3-92.3v-127.9c0-51-41.3-92.3-92.3-92.3s-92.3,41.3-92.3,92.3v127.9c0,51 41.3,92.3 92.3,92.3zm-52.3-220.2c0-28.8 23.5-52.3 52.3-52.3s52.3,23.5 52.3,52.3v127.9c0,28.8-23.5,52.3-52.3,52.3s-52.3-23.5-52.3-52.3v-127.9z"/>
|
25
|
+
</g>
|
26
|
+
</g>
|
27
|
+
</svg>
|
28
|
+
</button>
|
29
|
+
<%= button_tag class: "absolute top-1 right-0 flex p-2 rounded-lg text-gray-900 font-semibold" do %>
|
30
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-10">
|
31
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M6 12 3.269 3.125A59.769 59.769 0 0 1 21.485 12 59.768 59.768 0 0 1 3.27 20.875L5.999 12Zm0 0h7.5" />
|
32
|
+
</svg>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
|
37
|
+
</div>
|
15
38
|
<% end %>
|
16
39
|
<% end %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class AppchatFunction < ApplicationRecord
|
2
|
+
has_many :function_parameters, dependent: :destroy
|
3
|
+
|
4
|
+
def to_prompt_hash
|
5
|
+
{
|
6
|
+
name: name,
|
7
|
+
description: description,
|
8
|
+
class_name: class_name,
|
9
|
+
parameters: function_parameters.each_with_object({}) do |param, hash|
|
10
|
+
hash[param.name] = param.example_value
|
11
|
+
end
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.all_to_prompt_json
|
16
|
+
all.includes(:function_parameters).map(&:to_prompt_hash).to_json
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class AppchatFunctionService
|
2
|
+
attr_reader :chat, :user_prompt, :response_json
|
3
|
+
|
4
|
+
def initialize(chat_id, user_prompt)
|
5
|
+
@chat = Chat.find(chat_id)
|
6
|
+
@user_prompt = user_prompt
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
call_ollama
|
11
|
+
response_json
|
12
|
+
end
|
13
|
+
|
14
|
+
def call_ollama
|
15
|
+
client = Ollama.new(
|
16
|
+
credentials: { address: 'http://localhost:11434' },
|
17
|
+
options: { server_sent_events: true }
|
18
|
+
)
|
19
|
+
response = client.generate(
|
20
|
+
{
|
21
|
+
model: 'llama3.1',
|
22
|
+
prompt: prompt,
|
23
|
+
context: chat.context,
|
24
|
+
"format": "json"
|
25
|
+
}
|
26
|
+
)
|
27
|
+
@response_json = JSON.parse(response.map { |r| r["response"] }.join)
|
28
|
+
end
|
29
|
+
def prompt
|
30
|
+
<<-PROMPT
|
31
|
+
Evaluate the following user prompt in chat context.
|
32
|
+
Your goal is to determine whether the user prompt is requesting or requires additional information from any of the available services.
|
33
|
+
- If the prompt is a general greeting, casual remark, or something that can be answered without further information, respond with JSON { 'match' => 'false' }.
|
34
|
+
- If the prompt asks for specific information, requires a search, or needs a service to generate the correct response, then respond with JSON like this example:
|
35
|
+
{
|
36
|
+
'match' => 'true',
|
37
|
+
'appchat_function' => 'WebSearchService',
|
38
|
+
'parameters' => {
|
39
|
+
'query' => 'a query based on the prompt and context'
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
Here is the user's prompt: #{user_prompt}
|
44
|
+
Here are the Available Services: #{ AppchatFunction.all_to_prompt_json }
|
45
|
+
PROMPT
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'watir'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
class WebSearchService
|
5
|
+
attr_reader :query
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
@query = args["query"]
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
yield("Searching Google for #{@query}") if block_given?
|
13
|
+
|
14
|
+
browser = Watir::Browser.new :chrome, headless: true
|
15
|
+
search_url = "https://www.google.com/search?q=#{CGI.escape(query)}"
|
16
|
+
browser.goto(search_url)
|
17
|
+
response = "browser_text: #{browser.text} browser_links: #{browser.links}"
|
18
|
+
browser.close
|
19
|
+
response
|
20
|
+
rescue Selenium::WebDriver::Error::UnknownError => e
|
21
|
+
"Error: #{e.message}"
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# lib/tasks/create_appchat_function.rake
|
2
|
+
namespace :appchat_function do
|
3
|
+
desc "Create an AppchatFunction with name 'Web Search', class name 'WebSearchService', and a description"
|
4
|
+
task create_web_search: :environment do
|
5
|
+
af = AppchatFunction.create!(
|
6
|
+
name: "Web Search",
|
7
|
+
class_name: "WebSearchService",
|
8
|
+
description: "Searches Google with a user's query and returns the page text and links"
|
9
|
+
)
|
10
|
+
puts "AppchatFunction 'Web Search' created successfully."
|
11
|
+
af.function_parameters.create(name: 'query', example_value: 'Dog friendly vegan resturants in Austin, TX')
|
12
|
+
end
|
13
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appchat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- hackliteracy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,7 +66,7 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 1.3.0
|
69
|
-
description: The best and easiest framework for adding chats
|
69
|
+
description: The best and easiest framework for adding AI chats
|
70
70
|
email: hackliteracy@gmail.com
|
71
71
|
executables: []
|
72
72
|
extensions: []
|
@@ -80,13 +80,18 @@ files:
|
|
80
80
|
- lib/generators/appchat/templates/chats_controller.rb
|
81
81
|
- lib/generators/appchat/templates/get_ai_response_job.rb
|
82
82
|
- lib/generators/appchat/templates/javascript/chat_message_controller.js
|
83
|
+
- lib/generators/appchat/templates/javascript/speech_to_text_controller.js
|
83
84
|
- lib/generators/appchat/templates/messages/_typing_bubbles.html.erb
|
84
85
|
- lib/generators/appchat/templates/messages/index.html.erb
|
85
86
|
- lib/generators/appchat/templates/messages/message.html.erb
|
86
87
|
- lib/generators/appchat/templates/messages/new.html.erb
|
87
88
|
- lib/generators/appchat/templates/messages_controller.rb
|
89
|
+
- lib/generators/appchat/templates/models/appchat_function.rb
|
88
90
|
- lib/generators/appchat/templates/models/chat.rb
|
89
91
|
- lib/generators/appchat/templates/models/message.rb
|
92
|
+
- lib/generators/appchat/templates/services/appchat_function_service.rb
|
93
|
+
- lib/generators/appchat/templates/services/web_search_service.rb
|
94
|
+
- lib/generators/appchat/templates/tasks/create_appchat_function.rake
|
90
95
|
homepage: https://rubygems.org/gems/appchat
|
91
96
|
licenses:
|
92
97
|
- MIT
|
@@ -110,5 +115,5 @@ requirements: []
|
|
110
115
|
rubygems_version: 3.5.17
|
111
116
|
signing_key:
|
112
117
|
specification_version: 4
|
113
|
-
summary: Appchat makes it easy to add
|
118
|
+
summary: Appchat makes it easy to add an AI chat to your app
|
114
119
|
test_files: []
|