-
Notifications
You must be signed in to change notification settings - Fork 3.5k
[AI-3] List the cached models on the connection page #24883
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
tangopium
wants to merge
12
commits into
feature/66020-pr04-model-records-sync-capabilities
from
feature/66020-pr05-model-list-ui
Open
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
d7b4be0
[AI-3] List the cached models on the connection page
tangopium f02ba13
[AI-3] Move the model list to its own AI models page
tangopium 3470717
[AI-3] Warn when the model list predates the current settings
tangopium 0df514a
[AI-3] Truncate long model names with the full text on hover
tangopium a80511a
[AI-3] Show every model as either a chat or an embedding model
tangopium 3d87351
[AI-3] Drop the forward-looking comment above disconnect
tangopium f995eed
[AI-3] Stop the dialog axe checks from failing on Primer colours
tangopium 367d8df
[AI-3] Show the models as a tab of the LLM settings page
tangopium a8fc459
[AI-3] Keep the source label inside its column
tangopium cdb14a6
[AI-3] Point at the LLMs tab only once it is offered
tangopium 386b860
[AI-3] Say why a direct visit lands on the settings
tangopium 9a59eaf
[AI-3] Follow the settings switch through the models tab
tangopium File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
app/components/llm_connections/models/index_component.html.erb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| <%= component_wrapper do %> | ||
| <%= render(LlmConnections::ModelsTableComponent.new(rows:, connection:)) %> | ||
| <% end %> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| #-- copyright | ||
| # OpenProject is an open source project management software. | ||
| # Copyright (C) the OpenProject GmbH | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License version 3. | ||
| # | ||
| # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: | ||
| # Copyright (C) 2006-2013 Jean-Philippe Lang | ||
| # Copyright (C) 2010-2013 the ChiliProject Team | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License | ||
| # as published by the Free Software Foundation; either version 2 | ||
| # of the License, or (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program; if not, write to the Free Software | ||
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| # | ||
| # See COPYRIGHT and LICENSE files for more details. | ||
| #++ | ||
|
|
||
| module LlmConnections | ||
| module Models | ||
| # A turbo-frame around the model table, so filtering can replace just the | ||
| # table rather than reloading the settings page. | ||
| class IndexComponent < ApplicationComponent | ||
| include OpTurbo::Streamable | ||
| include OpPrimer::ComponentHelpers | ||
|
|
||
| alias_method :rows, :model | ||
|
|
||
| def initialize(rows, connection:) | ||
| super(rows) | ||
| @connection = connection | ||
| end | ||
|
|
||
| attr_reader :connection | ||
| end | ||
| end | ||
| end |
25 changes: 25 additions & 0 deletions
25
app/components/llm_connections/models/sub_header_component.html.erb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| <%= render(Primer::OpenProject::SubHeader.new(collapsed_search: false, data: sub_header_data_attributes)) do |subheader| %> | ||
| <% subheader.with_filter_input( | ||
| name: "name", | ||
| label: t("admin.llm_models.index.filter_label"), | ||
| visually_hide_label: true, | ||
| value: filter_input_value, | ||
| placeholder: t("admin.llm_models.index.filter_label"), | ||
| leading_visual: { icon: :search, size: :small }, | ||
| show_clear_button: true, | ||
| clear_button_id:, | ||
| data: filter_input_data_attributes | ||
| ) %> | ||
|
|
||
| <% subheader.with_action_button( | ||
| scheme: :default, | ||
| label: t("admin.llm_models.index.refresh"), | ||
| mobile_label: t("admin.llm_models.index.refresh"), | ||
| mobile_icon: :sync, | ||
| leading_icon: :sync, | ||
| tag: :a, | ||
| href: helpers.refresh_llm_models_path, | ||
| data: { turbo_method: :post, controller: "disable-when-clicked" }, | ||
| test_selector: "llm-model--refresh-button" | ||
| ) { t("admin.llm_models.index.refresh") } %> | ||
| <% end %> |
65 changes: 65 additions & 0 deletions
65
app/components/llm_connections/models/sub_header_component.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| #-- copyright | ||
| # OpenProject is an open source project management software. | ||
| # Copyright (C) the OpenProject GmbH | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License version 3. | ||
| # | ||
| # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: | ||
| # Copyright (C) 2006-2013 Jean-Philippe Lang | ||
| # Copyright (C) 2010-2013 the ChiliProject Team | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License | ||
| # as published by the Free Software Foundation; either version 2 | ||
| # of the License, or (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program; if not, write to the Free Software | ||
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| # | ||
| # See COPYRIGHT and LICENSE files for more details. | ||
| #++ | ||
|
|
||
| module LlmConnections | ||
| module Models | ||
| # The filter bar above the model list. | ||
| class SubHeaderComponent < ApplicationComponent | ||
| include OpPrimer::ComponentHelpers | ||
|
|
||
| alias_method :query, :model | ||
|
|
||
| def filter_input_value | ||
| query.find_active_filter(:name)&.values&.first | ||
| end | ||
|
|
||
| def clear_button_id = "llm-models-filter-clear" | ||
|
|
||
| def sub_header_data_attributes | ||
| { | ||
| controller: "filter--filters-form", | ||
| "filter--filters-form-perform-turbo-requests-value": true, | ||
| "filter--filters-form-output-format-value": "json", | ||
| "filter--filters-form-url-path-name-value": helpers.search_llm_models_path, | ||
| "filter--filters-form-clear-button-id-value": clear_button_id | ||
| } | ||
| end | ||
|
|
||
| def filter_input_data_attributes | ||
| { | ||
| "filter-name": "name", | ||
| "filter-type": "string", | ||
| "filter-operator": "~", | ||
| "filter--filters-form-target": "simpleFilter filterValueContainer simpleValue" | ||
| } | ||
| end | ||
| end | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| #-- copyright | ||
| # OpenProject is an open source project management software. | ||
| # Copyright (C) the OpenProject GmbH | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License version 3. | ||
| # | ||
| # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: | ||
| # Copyright (C) 2006-2013 Jean-Philippe Lang | ||
| # Copyright (C) 2010-2013 the ChiliProject Team | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License | ||
| # as published by the Free Software Foundation; either version 2 | ||
| # of the License, or (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program; if not, write to the Free Software | ||
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| # | ||
| # See COPYRIGHT and LICENSE files for more details. | ||
| #++ | ||
|
|
||
| module LlmConnections | ||
| # A row of the model list. Note +model+ is the row's record (an LlmModel), | ||
| # not the connection -- aliased to avoid confusion with either. | ||
| class ModelsRowComponent < OpPrimer::BorderBoxRowComponent | ||
| alias_method :llm_model, :model | ||
|
|
||
| def column_css_class(column) | ||
| return "#{super} -no-ellipsis" if column == :source | ||
|
|
||
| super | ||
| end | ||
|
|
||
| def identifier | ||
| render(Primer::Beta::Truncate.new(font_weight: :bold)) do |truncate| | ||
| truncate.with_item(expandable: true, max_width: 320, title: llm_model.name) { llm_model.name } | ||
| end | ||
| end | ||
|
|
||
| # vLLM and SGLang report the operator's real --max-model-len here, which is | ||
| # more trustworthy than any published figure for the model. Servers that do | ||
| # not report it simply show nothing rather than a guess. | ||
| def context_window | ||
| window = llm_model.context_window | ||
| return render(Primer::Beta::Text.new(color: :muted)) { "—" } if window.blank? | ||
|
|
||
| number_with_delimiter(window) | ||
| end | ||
|
|
||
| # Derived from the embeddings verdict rather than stored separately: a model | ||
| # that produces vectors is an embedding model, and everything else is a chat | ||
| # model. | ||
| def kind | ||
| if table.embeddings_states[llm_model.external_id] == "supported" | ||
| render(Primer::Beta::Label.new(scheme: :success)) { I18n.t("llm.model_kinds.embedding") } | ||
| else | ||
| render(Primer::Beta::Label.new(scheme: :secondary)) { I18n.t("llm.model_kinds.chat") } | ||
| end | ||
| end | ||
|
|
||
| def source | ||
| scheme, key = source_label | ||
|
|
||
| render(Primer::Beta::Label.new(scheme:, title: I18n.t("admin.llm_models.index.#{key}_description"))) do | ||
| I18n.t("admin.llm_models.index.#{key}") | ||
| end | ||
| end | ||
|
|
||
| def source_label | ||
| return %i[accent source_manual] if llm_model.manual? | ||
| return %i[attention source_withdrawn] if llm_model.withdrawn? | ||
|
|
||
| %i[secondary source_discovered] | ||
| end | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| #-- copyright | ||
| # OpenProject is an open source project management software. | ||
| # Copyright (C) the OpenProject GmbH | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License version 3. | ||
| # | ||
| # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: | ||
| # Copyright (C) 2006-2013 Jean-Philippe Lang | ||
| # Copyright (C) 2010-2013 the ChiliProject Team | ||
| # | ||
| # This program is free software; you can redistribute it and/or | ||
| # modify it under the terms of the GNU General Public License | ||
| # as published by the Free Software Foundation; either version 2 | ||
| # of the License, or (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program; if not, write to the Free Software | ||
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| # | ||
| # See COPYRIGHT and LICENSE files for more details. | ||
| #++ | ||
|
|
||
| module LlmConnections | ||
| # Lists the models the remote server reported, read from the cached catalogue. | ||
| # | ||
| # Rendering never issues an HTTP request: the catalogue is refreshed explicitly | ||
| # through the "Refresh models" action. | ||
| class ModelsTableComponent < OpPrimer::BorderBoxTableComponent | ||
| columns :identifier, :kind, :context_window, :source | ||
|
|
||
| main_column :identifier | ||
|
|
||
| mobile_columns :identifier | ||
|
|
||
| # The connection is passed in rather than derived from the first row: with a | ||
| # paginated, filtered list a page can legitimately be empty. | ||
| def initialize(connection:, **) | ||
| super(**) | ||
| @connection = connection | ||
| end | ||
|
|
||
| attr_reader :connection | ||
|
|
||
| def initial_sort = %i[identifier asc] | ||
|
|
||
| def has_footer? = false | ||
|
|
||
| def mobile_title = I18n.t("admin.llm_connections.tabs.models") | ||
|
|
||
| # The row class is otherwise derived by convention as LlmConnections::RowComponent. | ||
| def row_class = ModelsRowComponent | ||
|
|
||
| def headers | ||
| [ | ||
| [:identifier, { caption: I18n.t("admin.llm_models.index.identifier") }], | ||
| [:kind, { caption: I18n.t("admin.llm_models.index.kind") }], | ||
| [:context_window, { caption: I18n.t("admin.llm_models.index.context_window") }], | ||
| [:source, { caption: I18n.t("admin.llm_models.index.source") }] | ||
| ] | ||
| end | ||
|
|
||
| # Built once for the whole table so each row does not query for its own | ||
| # verdict. Rows reach this through their +table+ accessor. | ||
| def embeddings_states | ||
| @embeddings_states ||= load_embeddings_states | ||
| end | ||
|
|
||
| def load_embeddings_states | ||
| connection.capability_verdicts.for_capability(:embeddings).pluck(:model_id, :state).to_h | ||
| end | ||
|
|
||
| def blank_title = I18n.t("admin.llm_models.index.blank_title") | ||
|
|
||
| def blank_description = I18n.t("admin.llm_models.index.blank_description") | ||
|
|
||
| def blank_icon = :sparkle | ||
| end | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.