appchat 0.0.5 → 0.0.6
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 +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: []
|