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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3071421a8c3c49e11f5c5186b9f17a00bd37d45229d0d95a3b466ae9b9032fee
4
- data.tar.gz: 79284185b56a3efaeb60e1ec1ad144ada532fd499e686b4604db7a29aabccd16
3
+ metadata.gz: 4aca406ace060a7c7b9af445e20d5be1f3605614109320792523cba09b7f92fe
4
+ data.tar.gz: 8c1e290b46dfca74c66c94748d305386d67703291e455c066aed1df0d1dc5cc8
5
5
  SHA512:
6
- metadata.gz: 9e642582fe817de68a171c14b272a0b0ceb2257b96021ae9a51e630c5ef562ed982c452fdc9694c2d6e6c81378720f636e0e8218f51d1704134d383602cf2df7
7
- data.tar.gz: a71996c59642154416cb3178b9406d44242b5626ab7069aeb6341a15e1062d6c297131dfd19de2455c7e59be0cbf8af8c43e6daa0879afb269c001a88723f0a3
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
- unless gem_exists?('ollama-ai')
13
- append_to_file 'Gemfile', "\ngem 'ollama-ai'\n"
14
- end
15
-
16
- unless gem_exists?('tailwindcss-rails')
17
- append_to_file 'Gemfile', "\ngem 'tailwindcss-rails'\n"
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 serialize_context
66
- inject_into_class 'app/models/chat.rb', 'Chat', "serialize :context, coder:JSON, type: Array\n"
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 set_associations
70
- inject_into_class 'app/models/chat.rb', 'Chat', " has_many :messages, dependent: :destroy\n"
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 call_ollama
15
- client = Ollama.new(
17
+ def new_client
18
+ Ollama.new(
16
19
  credentials: { address: 'http://localhost:11434' },
17
20
  options: { server_sent_events: true }
18
21
  )
19
- message = chat.messages.create(role: 'assistant')
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: user_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.5
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-08-29 00:00:00.000000000 Z
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 a chat to your app
118
+ summary: Appchat makes it easy to add an AI chat to your app
115
119
  test_files: []