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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +29 -0
- data/LICENSE +21 -0
- data/README.md +88 -0
- data/Rakefile +6 -0
- data/app/assets/stylesheets/llm_meta_client/application.css +15 -0
- data/app/controllers/llm_meta_client/application_controller.rb +4 -0
- data/app/helpers/llm_meta_client/application_helper.rb +4 -0
- data/app/jobs/llm_meta_client/application_job.rb +4 -0
- data/app/mailers/llm_meta_client/application_mailer.rb +6 -0
- data/app/models/llm_meta_client/application_record.rb +5 -0
- data/app/views/layouts/llm_meta_client/application.html.erb +17 -0
- data/config/routes.rb +2 -0
- data/lib/generators/llm_meta_client/authentication/authentication_generator.rb +74 -0
- data/lib/generators/llm_meta_client/authentication/templates/app/controllers/users/omniauth_callbacks_controller.rb +26 -0
- data/lib/generators/llm_meta_client/authentication/templates/app/controllers/users/sessions_controller.rb +13 -0
- data/lib/generators/llm_meta_client/authentication/templates/app/models/user.rb +50 -0
- data/lib/generators/llm_meta_client/authentication/templates/config/initializers/devise.rb +330 -0
- data/lib/generators/llm_meta_client/authentication/templates/config/initializers/omniauth.rb +4 -0
- data/lib/generators/llm_meta_client/authentication/templates/config/locales/devise.en.yml +65 -0
- data/lib/generators/llm_meta_client/authentication/templates/db/migrate/create_users.rb +14 -0
- data/lib/generators/llm_meta_client/scaffold/scaffold_generator.rb +97 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/controllers/chats_controller.rb +188 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/controllers/prompts_controller.rb +33 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/javascript/controllers/chat_title_edit_controller.js +99 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/javascript/controllers/chats_form_controller.js +95 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/javascript/controllers/llm_selector_controller.js +236 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/javascript/popover.js +34 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/models/chat.rb +154 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/models/message.rb +8 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/_message.html.erb +16 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/_messages_list.html.erb +9 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/create.turbo_stream.erb +84 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/edit.html.erb +74 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/new.html.erb +62 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/update.turbo_stream.erb +78 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/layouts/_header.html.erb +35 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/layouts/_sidebar.html.erb +27 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/layouts/application.html.erb +59 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/shared/_api_key_field.html.erb +15 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/shared/_family_field.html.erb +18 -0
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/shared/_model_field.html.erb +12 -0
- data/lib/generators/llm_meta_client/scaffold/templates/config/initializers/llm_service.rb +10 -0
- data/lib/generators/llm_meta_client/scaffold/templates/db/migrate/create_chats.rb +13 -0
- data/lib/generators/llm_meta_client/scaffold/templates/db/migrate/create_messages.rb +12 -0
- data/lib/llm_meta_client/chat_manageable.rb +11 -0
- data/lib/llm_meta_client/engine.rb +11 -0
- data/lib/llm_meta_client/exceptions.rb +13 -0
- data/lib/llm_meta_client/helpers.rb +6 -0
- data/lib/llm_meta_client/history_manageable.rb +11 -0
- data/lib/llm_meta_client/server_query.rb +47 -0
- data/lib/llm_meta_client/server_resource.rb +127 -0
- data/lib/llm_meta_client/version.rb +3 -0
- data/lib/llm_meta_client.rb +13 -0
- data/lib/tasks/llm_meta_client_tasks.rake +4 -0
- 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,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,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,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
|