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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f741156bd18664bfb66a4700b633dad255bc2c5e41cacc6b297acbc6d67492b8
4
- data.tar.gz: d4612a8a1ee88a6caf7bc4e039cf74fa44b369fe79b5d595b0e9544329749819
3
+ metadata.gz: 4aca406ace060a7c7b9af445e20d5be1f3605614109320792523cba09b7f92fe
4
+ data.tar.gz: 8c1e290b46dfca74c66c94748d305386d67703291e455c066aed1df0d1dc5cc8
5
5
  SHA512:
6
- metadata.gz: '0959e29a0ed53649f7f51523e5b4c6f4089b05449f8649165ed7bf7505bdb111774e0c24661e5344f0752df1fa08a38b250a3fe911543530adb7f8a3020f2bd6'
7
- data.tar.gz: '08f3d14d68ea6c2cf9c50da6842051b78bafa250bf58fc77475e09c32090344d06fe4d98205294aa08e9331bf1817e8d5b23213da001b136dd1fb2101c98aca8'
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
- 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
 
26
- def generate_scaffolds
27
- generate "model", "Chat context:text"
33
+ def set_routes
28
34
  route "resources :chats"
29
- generate "scaffold", "Message chat:references content:text role:integer"
35
+ route "resources :messages"
30
36
  end
31
37
 
32
- def create_views
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 serialize_context
51
- 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
52
78
  end
53
79
 
54
- def set_associations
55
- 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"
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 create_messages_controller
67
- copy_file "messages_controller.rb", "app/controllers/messages_controller.rb", force: true
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
- <%= chat&.messages&.first&.content || "New AI chat conversation" %>
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
- <%= 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 %>
10
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
11
- <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
12
- </svg>
13
- <span> Create Chat </span>
14
- <% end %>
15
- <% @chats.each do |chat| %>
16
- <%= render "chat", chat: chat %>
17
- <% end %>
18
- </div>
19
- <div class="w-full ml-4">
20
- <%= turbo_frame_tag :chat, src: ( chat_path(@chats.first) if @chats.any? ) %>
21
- </div>
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>
@@ -3,7 +3,7 @@ class ChatsController < ApplicationController
3
3
  @chat = Chat.find(params[:id])
4
4
  end
5
5
  def index
6
- @chats = Chat.all
6
+ @chats = Chat.all.reverse
7
7
  end
8
8
  def create
9
9
  @chat = Chat.create
@@ -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
@@ -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
- <div class="flex flex-col w-full">
5
- <%= f.label :content, "What do you want to ask?" %>
6
- <%= f.text_area :content, class: "w-full" %>
7
- </div>
8
- <%= button_tag class: "mt-4 flex p-2 rounded-lg bg-gradient-to-r from-indigo-500 to-blue-500 text-blue-100 font-semibold" do %>
9
- Send Message
10
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
11
- <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" />
12
- </svg>
13
- <% end %>
14
- </div>
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
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-26 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: []
@@ -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 a chat to your app
118
+ summary: Appchat makes it easy to add an AI chat to your app
114
119
  test_files: []