appchat 0.0.5 → 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 +30 -11
- data/lib/generators/appchat/templates/get_ai_response_job.rb +27 -5
- data/lib/generators/appchat/templates/messages/message.html.erb +3 -1
- 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 +8 -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,17 +9,24 @@ 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
|
|
@@ -30,7 +37,9 @@ class AppchatGenerator < Rails::Generators::Base
|
|
30
37
|
|
31
38
|
def generate_models
|
32
39
|
generate "model", "Chat context:text"
|
33
|
-
generate "model", "Message chat:references content:text role:integer"
|
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"
|
34
43
|
end
|
35
44
|
|
36
45
|
def create_controllers
|
@@ -60,14 +69,16 @@ class AppchatGenerator < Rails::Generators::Base
|
|
60
69
|
def copy_models
|
61
70
|
copy_file "models/message.rb", "app/models/message.rb", force: true
|
62
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
|
63
73
|
end
|
64
74
|
|
65
|
-
def
|
66
|
-
|
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
|
67
78
|
end
|
68
79
|
|
69
|
-
def
|
70
|
-
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"
|
71
82
|
end
|
72
83
|
|
73
84
|
def run_migrations
|
@@ -88,6 +99,14 @@ class AppchatGenerator < Rails::Generators::Base
|
|
88
99
|
end
|
89
100
|
end
|
90
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"
|
108
|
+
end
|
109
|
+
|
91
110
|
def show_art
|
92
111
|
puts <<-ART
|
93
112
|
|
@@ -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
|
@@ -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' %>
|
@@ -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: []
|
@@ -86,8 +86,12 @@ files:
|
|
86
86
|
- lib/generators/appchat/templates/messages/message.html.erb
|
87
87
|
- lib/generators/appchat/templates/messages/new.html.erb
|
88
88
|
- lib/generators/appchat/templates/messages_controller.rb
|
89
|
+
- lib/generators/appchat/templates/models/appchat_function.rb
|
89
90
|
- lib/generators/appchat/templates/models/chat.rb
|
90
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
|
91
95
|
homepage: https://rubygems.org/gems/appchat
|
92
96
|
licenses:
|
93
97
|
- MIT
|
@@ -111,5 +115,5 @@ requirements: []
|
|
111
115
|
rubygems_version: 3.5.17
|
112
116
|
signing_key:
|
113
117
|
specification_version: 4
|
114
|
-
summary: Appchat makes it easy to add
|
118
|
+
summary: Appchat makes it easy to add an AI chat to your app
|
115
119
|
test_files: []
|