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 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: []