llm_meta_client 0.1.0

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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +29 -0
  3. data/LICENSE +21 -0
  4. data/README.md +88 -0
  5. data/Rakefile +6 -0
  6. data/app/assets/stylesheets/llm_meta_client/application.css +15 -0
  7. data/app/controllers/llm_meta_client/application_controller.rb +4 -0
  8. data/app/helpers/llm_meta_client/application_helper.rb +4 -0
  9. data/app/jobs/llm_meta_client/application_job.rb +4 -0
  10. data/app/mailers/llm_meta_client/application_mailer.rb +6 -0
  11. data/app/models/llm_meta_client/application_record.rb +5 -0
  12. data/app/views/layouts/llm_meta_client/application.html.erb +17 -0
  13. data/config/routes.rb +2 -0
  14. data/lib/generators/llm_meta_client/authentication/authentication_generator.rb +74 -0
  15. data/lib/generators/llm_meta_client/authentication/templates/app/controllers/users/omniauth_callbacks_controller.rb +26 -0
  16. data/lib/generators/llm_meta_client/authentication/templates/app/controllers/users/sessions_controller.rb +13 -0
  17. data/lib/generators/llm_meta_client/authentication/templates/app/models/user.rb +50 -0
  18. data/lib/generators/llm_meta_client/authentication/templates/config/initializers/devise.rb +330 -0
  19. data/lib/generators/llm_meta_client/authentication/templates/config/initializers/omniauth.rb +4 -0
  20. data/lib/generators/llm_meta_client/authentication/templates/config/locales/devise.en.yml +65 -0
  21. data/lib/generators/llm_meta_client/authentication/templates/db/migrate/create_users.rb +14 -0
  22. data/lib/generators/llm_meta_client/scaffold/scaffold_generator.rb +97 -0
  23. data/lib/generators/llm_meta_client/scaffold/templates/app/controllers/chats_controller.rb +188 -0
  24. data/lib/generators/llm_meta_client/scaffold/templates/app/controllers/prompts_controller.rb +33 -0
  25. data/lib/generators/llm_meta_client/scaffold/templates/app/javascript/controllers/chat_title_edit_controller.js +99 -0
  26. data/lib/generators/llm_meta_client/scaffold/templates/app/javascript/controllers/chats_form_controller.js +95 -0
  27. data/lib/generators/llm_meta_client/scaffold/templates/app/javascript/controllers/llm_selector_controller.js +236 -0
  28. data/lib/generators/llm_meta_client/scaffold/templates/app/javascript/popover.js +34 -0
  29. data/lib/generators/llm_meta_client/scaffold/templates/app/models/chat.rb +154 -0
  30. data/lib/generators/llm_meta_client/scaffold/templates/app/models/message.rb +8 -0
  31. data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/_message.html.erb +16 -0
  32. data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/_messages_list.html.erb +9 -0
  33. data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/create.turbo_stream.erb +84 -0
  34. data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/edit.html.erb +74 -0
  35. data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/new.html.erb +62 -0
  36. data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/update.turbo_stream.erb +78 -0
  37. data/lib/generators/llm_meta_client/scaffold/templates/app/views/layouts/_header.html.erb +35 -0
  38. data/lib/generators/llm_meta_client/scaffold/templates/app/views/layouts/_sidebar.html.erb +27 -0
  39. data/lib/generators/llm_meta_client/scaffold/templates/app/views/layouts/application.html.erb +59 -0
  40. data/lib/generators/llm_meta_client/scaffold/templates/app/views/shared/_api_key_field.html.erb +15 -0
  41. data/lib/generators/llm_meta_client/scaffold/templates/app/views/shared/_family_field.html.erb +18 -0
  42. data/lib/generators/llm_meta_client/scaffold/templates/app/views/shared/_model_field.html.erb +12 -0
  43. data/lib/generators/llm_meta_client/scaffold/templates/config/initializers/llm_service.rb +10 -0
  44. data/lib/generators/llm_meta_client/scaffold/templates/db/migrate/create_chats.rb +13 -0
  45. data/lib/generators/llm_meta_client/scaffold/templates/db/migrate/create_messages.rb +12 -0
  46. data/lib/llm_meta_client/chat_manageable.rb +11 -0
  47. data/lib/llm_meta_client/engine.rb +11 -0
  48. data/lib/llm_meta_client/exceptions.rb +13 -0
  49. data/lib/llm_meta_client/helpers.rb +6 -0
  50. data/lib/llm_meta_client/history_manageable.rb +11 -0
  51. data/lib/llm_meta_client/server_query.rb +47 -0
  52. data/lib/llm_meta_client/server_resource.rb +127 -0
  53. data/lib/llm_meta_client/version.rb +3 -0
  54. data/lib/llm_meta_client.rb +13 -0
  55. data/lib/tasks/llm_meta_client_tasks.rake +4 -0
  56. metadata +162 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c97f3a183b70b928d55677581100d6774fbf6400140ff9604f883136f4c92c7e
4
+ data.tar.gz: 5d6078eabeb97d52b07980cb452cdbbf64ce65599d494c662e875249cd6e6f39
5
+ SHA512:
6
+ metadata.gz: 268562c7e43e59fd50e3cf38dc06d0164e461f98b3e650df580443de556681573dccdb5db5d82963237d230e150bd238ad20a3d03d21d58337f95c2a00247cd1
7
+ data.tar.gz: 6c7baa9d2c77789861727acbd5f1d40ea391a3045df9b3422a780fbe7978d60473db22d6a2d69bc81b7cb48b207e2a664ba2d31f05b6d4372d802c985860e879
data/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-02-27
9
+
10
+ ### Added
11
+
12
+ - Rails Engine with core modules:
13
+ - `ServerQuery` for making HTTP requests to LLM services
14
+ - `ServerResource` for fetching available LLM options (OpenAI, Anthropic, Google, Ollama)
15
+ - `Helpers` module integrating PromptNavigator and ChatManager helpers
16
+ - `ChatManageable` and `HistoryManageable` concerns
17
+ - Custom exceptions (`OllamaUnavailableError`, `ServerError`, `InvalidResponseError`, `EmptyResponseError`)
18
+ - Scaffold generator (`rails generate llm_meta_client:scaffold`):
19
+ - Chat and Message models with migrations
20
+ - ChatsController and PromptsController
21
+ - Chat views with Turbo Stream support
22
+ - Stimulus JavaScript controllers (llm_selector, chats_form, chat_title_edit)
23
+ - LLM service initializer with configurable environment variables
24
+ - Routes and importmap configuration
25
+ - Authentication generator (`rails generate llm_meta_client:authentication`):
26
+ - User model with Devise and OmniAuth integration
27
+ - Google OAuth2 sign-in support
28
+ - OmniAuth callbacks and sessions controllers
29
+ - Devise initializer and locale files
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DBCLS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # llm_meta_client
2
+
3
+ A Rails Engine for integrating multiple LLM providers into your application. Provides scaffold and authentication generators to quickly build LLM-powered chat applications with support for OpenAI, Anthropic, Google, and Ollama.
4
+
5
+ ## Features
6
+
7
+ - **Scaffold generator** — Generates models, controllers, views, and JavaScript controllers for a full chat UI with Turbo Stream support
8
+ - **Authentication generator** — Sets up Devise with Google OAuth2 for user authentication
9
+ - **Core modules** — `ServerQuery`, `ServerResource`, `Helpers`, `ChatManageable`, `HistoryManageable`, and custom exception classes
10
+
11
+ ## Requirements
12
+
13
+ - Ruby >= 3.4
14
+ - Rails >= 8.1.1
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem "llm_meta_client"
22
+ ```
23
+
24
+ And then execute:
25
+
26
+ ```bash
27
+ $ bundle install
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ### Scaffold Generator
33
+
34
+ Generates a complete chat interface with LLM integration:
35
+
36
+ ```bash
37
+ $ rails generate llm_meta_client:scaffold
38
+ ```
39
+
40
+ This creates:
41
+
42
+ - **Models:** `Chat`, `Message`
43
+ - **Controllers:** `ChatsController`, `PromptsController`
44
+ - **Views:** Chat views (`new`, `edit`, Turbo Stream responses), message partials, shared form fields (`_family_field`, `_api_key_field`, `_model_field`), layout with header and sidebar
45
+ - **JavaScript:** Stimulus controllers (`llm_selector`, `chats_form`, `chat_title_edit`), popover
46
+ - **Initializer:** `config/initializers/llm_service.rb`
47
+ - **Migrations:** `create_chats`, `create_messages`
48
+ - **Routes:** Chats resource with `clear`, `start_new`, `download_all_csv`, `download_csv`, `update_title` actions
49
+
50
+ ### Authentication Generator
51
+
52
+ Sets up user authentication with Devise and Google OAuth2:
53
+
54
+ ```bash
55
+ $ rails generate llm_meta_client:authentication
56
+ ```
57
+
58
+ This creates:
59
+
60
+ - **Model:** `User`
61
+ - **Controllers:** `Users::OmniauthCallbacksController`, `Users::SessionsController`
62
+ - **Initializers:** `config/initializers/devise.rb`, `config/initializers/omniauth.rb`
63
+ - **Locale:** `config/locales/devise.en.yml`
64
+ - **Migration:** `create_users`
65
+ - **Routes:** Devise routes with OmniAuth callbacks
66
+
67
+ ## Configuration
68
+
69
+ The following environment variables are used:
70
+
71
+ | Variable | Description | Default |
72
+ |---|---|---|
73
+ | `LLM_SERVICE_BASE_URL` | Base URL for the external LLM service | `http://localhost:3000` |
74
+ | `SUMMARIZE_CONVERSATION_COUNT` | Number of messages to include in conversation context | `10` |
75
+ | `GOOGLE_CLIENT_ID` | Google OAuth2 client ID (authentication generator) | — |
76
+ | `GOOGLE_CLIENT_SECRET` | Google OAuth2 client secret (authentication generator) | — |
77
+
78
+ ## Contributing
79
+
80
+ 1. Fork the repository
81
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
82
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
83
+ 4. Push to the branch (`git push origin my-new-feature`)
84
+ 5. Create a Pull Request
85
+
86
+ ## License
87
+
88
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ require "bundler/gem_tasks"
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,4 @@
1
+ module LlmMetaClient
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module LlmMetaClient
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module LlmMetaClient
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module LlmMetaClient
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module LlmMetaClient
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Llm mcp meta client</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= yield :head %>
9
+
10
+ <%= stylesheet_link_tag "llm_meta_client/application", media: "all" %>
11
+ </head>
12
+ <body>
13
+
14
+ <%= yield %>
15
+
16
+ </body>
17
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ LlmMetaClient::Engine.routes.draw do
2
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LlmMetaClient
4
+ module Generators
5
+ class AuthenticationGenerator < Rails::Generators::Base
6
+ include Rails::Generators::Migration
7
+
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+
11
+ def self.next_migration_number(dirname)
12
+ next_migration_number = current_migration_number(dirname) + 1
13
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
14
+ end
15
+
16
+ def enable_devise
17
+ enable_gem "devise"
18
+ end
19
+
20
+ def enable_omniauth
21
+ enable_gem "omniauth"
22
+ end
23
+
24
+ def enable_omniauth_google_oauth2
25
+ enable_gem "omniauth-google-oauth2"
26
+ end
27
+
28
+ def enable_omniauth_rails_csrf_protection
29
+ enable_gem "omniauth-rails_csrf_protection"
30
+ end
31
+
32
+
33
+ def create_authentication_file
34
+ template "app/models/user.rb"
35
+
36
+ template "app/controllers/users/omniauth_callbacks_controller.rb"
37
+ template "app/controllers/users/sessions_controller.rb"
38
+
39
+ template "config/initializers/devise.rb"
40
+ template "config/initializers/omniauth.rb"
41
+ template "config/locales/devise.en.yml"
42
+ end
43
+
44
+ def configure_authentication_routes
45
+ route <<-RUBY
46
+ devise_for :users, controllers: {
47
+ omniauth_callbacks: "users/omniauth_callbacks",
48
+ sessions: "users/sessions"
49
+ }
50
+
51
+ devise_scope :user do
52
+ delete "/logout", to: "users/sessions#destroy", as: :user_logout
53
+ post "/logout", to: "users/sessions#destroy"
54
+ end
55
+ RUBY
56
+ end
57
+
58
+ def add_migrations
59
+ migration_template "db/migrate/create_users.rb", "db/migrate/create_users.rb"
60
+ end
61
+
62
+ private
63
+
64
+ def enable_gem(gem_name)
65
+ if File.read("Gemfile").include?("gem \"#{gem_name}\"")
66
+ uncomment_lines "Gemfile", /gem "#{gem_name}"/
67
+ else
68
+ gem gem_name
69
+ end
70
+ run "bundle install --quiet"
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,26 @@
1
+ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
2
+ def google_oauth2
3
+ @user = User.from_omniauth(request.env["omniauth.auth"])
4
+
5
+ # Check if the user was successfully saved to the database
6
+ if @user.persisted?
7
+ # Authentication successful: Set flash message and sign in with redirect
8
+ flash[:notice] = I18n.t "devise.omniauth_callbacks.success", kind: "Google"
9
+ sign_in_and_redirect @user, event: :authentication
10
+ else
11
+ # Authentication failed: User creation/validation errors occurred
12
+ # This happens when:
13
+ # - Email validation fails
14
+ # - Required fields are missing
15
+ # - Database constraints are violated
16
+ # - User model validations fail
17
+
18
+ # Redirect to home with error messages
19
+ redirect_to root_path, alert: @user.errors.full_messages.join("\n")
20
+ end
21
+ end
22
+
23
+ def failure
24
+ redirect_to root_path, alert: "Failed to authentication. Please try again."
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ class Users::SessionsController < Devise::SessionsController
2
+ # Delete web application session and redirect to sign out action
3
+ def destroy
4
+ # Delete session
5
+ if current_user
6
+ sign_out current_user
7
+ reset_session
8
+ end
9
+
10
+ flash[:notice] = "You have successfully signed out."
11
+ redirect_to root_path
12
+ end
13
+ end
@@ -0,0 +1,50 @@
1
+ class User < ApplicationRecord
2
+ devise :omniauthable, omniauth_providers: %i[google_oauth2]
3
+
4
+ has_many :chats, dependent: :destroy
5
+
6
+ validates :email, presence: true, uniqueness: true
7
+ validates :google_id, presence: true, uniqueness: true
8
+ validates :id_token, presence: true
9
+
10
+ def self.from_omniauth(auth)
11
+ user = where(email: auth.info.email).first_or_initialize(
12
+ google_id: auth.uid
13
+ )
14
+
15
+ token = auth.extra&.id_token
16
+ if token.blank?
17
+ user.errors.add(:id_token, "is missing from provider response")
18
+ return user
19
+ end
20
+
21
+ user.id_token = token
22
+ user.save
23
+ user
24
+ end
25
+
26
+ # Return Google ID token (OpenID Connect)
27
+ # Validate expiration timestamp when using the token
28
+ def jwt_token
29
+ id_token if id_token.present? && token_valid?
30
+ end
31
+
32
+ private
33
+
34
+ # Check whether the ID token is valid
35
+ def token_valid?
36
+ return false if id_token.blank?
37
+
38
+ begin
39
+ # Attempt to decode JWT (no signature verification; only checks expiration)
40
+ decoded_token = JWT.decode(id_token, nil, false)
41
+ payload = decoded_token.first
42
+ exp = payload["exp"] if payload
43
+
44
+ # Valid if current time is before expiration
45
+ exp && Time.now.to_i < exp
46
+ rescue JWT::DecodeError, JWT::ExpiredSignature
47
+ false
48
+ end
49
+ end
50
+ end