generated from discourse/discourse-plugin-skeleton
-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This PR introduces the concept of "LlmModel" as a new way to quickly add new LLM models without making any code changes. We are releasing this first version and will add incremental improvements, so expect changes. The AI Bot can't fully take advantage of this feature as users are hard-coded. We'll fix this in a separate PR.s
- Loading branch information
1 parent
95b915f
commit 39ea4a6
Showing
38 changed files
with
675 additions
and
413 deletions.
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
admin/assets/javascripts/discourse/routes/admin-plugins-show-discourse-ai-llms-new.js
This file contains 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,16 @@ | ||
import DiscourseRoute from "discourse/routes/discourse"; | ||
|
||
export default DiscourseRoute.extend({ | ||
async model() { | ||
const record = this.store.createRecord("ai-llm"); | ||
return record; | ||
}, | ||
|
||
setupController(controller, model) { | ||
this._super(controller, model); | ||
controller.set( | ||
"allLlms", | ||
this.modelFor("adminPlugins.show.discourse-ai-llms") | ||
); | ||
}, | ||
}); |
17 changes: 17 additions & 0 deletions
17
admin/assets/javascripts/discourse/routes/admin-plugins-show-discourse-ai-llms-show.js
This file contains 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,17 @@ | ||
import DiscourseRoute from "discourse/routes/discourse"; | ||
|
||
export default DiscourseRoute.extend({ | ||
async model(params) { | ||
const allLlms = this.modelFor("adminPlugins.show.discourse-ai-llms"); | ||
const id = parseInt(params.id, 10); | ||
return allLlms.findBy("id", id); | ||
}, | ||
|
||
setupController(controller, model) { | ||
this._super(controller, model); | ||
controller.set( | ||
"allLlms", | ||
this.modelFor("adminPlugins.show.discourse-ai-llms") | ||
); | ||
}, | ||
}); |
7 changes: 7 additions & 0 deletions
7
admin/assets/javascripts/discourse/routes/admin-plugins-show-discourse-ai-llms.js
This file contains 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,7 @@ | ||
import DiscourseRoute from "discourse/routes/discourse"; | ||
|
||
export default class DiscourseAiAiLlmsRoute extends DiscourseRoute { | ||
model() { | ||
return this.store.findAll("ai-llm"); | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
admin/assets/javascripts/discourse/templates/admin-plugins/show/discourse-ai-llms/index.hbs
This file contains 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 @@ | ||
<AiLlmsListEditor @llms={{this.model}} /> |
1 change: 1 addition & 0 deletions
1
admin/assets/javascripts/discourse/templates/admin-plugins/show/discourse-ai-llms/new.hbs
This file contains 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 @@ | ||
<AiLlmsListEditor @llms={{this.allLlms}} @currentLlm={{this.model}} /> |
1 change: 1 addition & 0 deletions
1
admin/assets/javascripts/discourse/templates/admin-plugins/show/discourse-ai-llms/show.hbs
This file contains 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 @@ | ||
<AiLlmsListEditor @llms={{this.allLlms}} @currentLlm={{this.model}} /> |
This file contains 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,64 @@ | ||
# frozen_string_literal: true | ||
|
||
module DiscourseAi | ||
module Admin | ||
class AiLlmsController < ::Admin::AdminController | ||
requires_plugin ::DiscourseAi::PLUGIN_NAME | ||
|
||
def index | ||
llms = LlmModel.all | ||
|
||
render json: { | ||
ai_llms: | ||
ActiveModel::ArraySerializer.new( | ||
llms, | ||
each_serializer: LlmModelSerializer, | ||
root: false, | ||
).as_json, | ||
meta: { | ||
providers: DiscourseAi::Completions::Llm.provider_names, | ||
tokenizers: | ||
DiscourseAi::Completions::Llm.tokenizer_names.map { |tn| | ||
{ id: tn, name: tn.split("::").last } | ||
}, | ||
}, | ||
} | ||
end | ||
|
||
def show | ||
llm_model = LlmModel.find(params[:id]) | ||
render json: LlmModelSerializer.new(llm_model) | ||
end | ||
|
||
def create | ||
if llm_model = LlmModel.new(ai_llm_params).save | ||
render json: { ai_persona: llm_model }, status: :created | ||
else | ||
render_json_error llm_model | ||
end | ||
end | ||
|
||
def update | ||
llm_model = LlmModel.find(params[:id]) | ||
|
||
if llm_model.update(ai_llm_params) | ||
render json: llm_model | ||
else | ||
render_json_error llm_model | ||
end | ||
end | ||
|
||
private | ||
|
||
def ai_llm_params | ||
params.require(:ai_llm).permit( | ||
:display_name, | ||
:name, | ||
:provider, | ||
:tokenizer, | ||
:max_prompt_tokens, | ||
) | ||
end | ||
end | ||
end | ||
end |
This file contains 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,7 @@ | ||
# frozen_string_literal: true | ||
|
||
class LlmModel < ActiveRecord::Base | ||
def tokenizer_class | ||
tokenizer.constantize | ||
end | ||
end |
This file contains 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,7 @@ | ||
# frozen_string_literal: true | ||
|
||
class LlmModelSerializer < ApplicationSerializer | ||
root "llm" | ||
|
||
attributes :id, :display_name, :name, :provider, :max_prompt_tokens, :tokenizer | ||
end |
This file contains 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
This file contains 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,21 @@ | ||
import RestAdapter from "discourse/adapters/rest"; | ||
|
||
export default class Adapter extends RestAdapter { | ||
jsonMode = true; | ||
|
||
basePath() { | ||
return "/admin/plugins/discourse-ai/"; | ||
} | ||
|
||
pathFor(store, type, findArgs) { | ||
// removes underscores which are implemented in base | ||
let path = | ||
this.basePath(store, type, findArgs) + | ||
store.pluralize(this.apiNameFor(type)); | ||
return this.appendQueryParams(path, findArgs); | ||
} | ||
|
||
apiNameFor() { | ||
return "ai-llm"; | ||
} | ||
} |
This file contains 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,20 @@ | ||
import RestModel from "discourse/models/rest"; | ||
|
||
export default class AiLlm extends RestModel { | ||
createProperties() { | ||
return this.getProperties( | ||
"display_name", | ||
"name", | ||
"provider", | ||
"tokenizer", | ||
"max_prompt_tokens" | ||
); | ||
} | ||
|
||
updateProperties() { | ||
const attrs = this.createProperties(); | ||
attrs.id = this.id; | ||
|
||
return attrs; | ||
} | ||
} |
123 changes: 123 additions & 0 deletions
123
assets/javascripts/discourse/components/ai-llm-editor.gjs
This file contains 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,123 @@ | ||
import Component from "@glimmer/component"; | ||
import { tracked } from "@glimmer/tracking"; | ||
import { Input } from "@ember/component"; | ||
import { action } from "@ember/object"; | ||
import { later } from "@ember/runloop"; | ||
import { inject as service } from "@ember/service"; | ||
import DButton from "discourse/components/d-button"; | ||
import { popupAjaxError } from "discourse/lib/ajax-error"; | ||
import i18n from "discourse-common/helpers/i18n"; | ||
import I18n from "discourse-i18n"; | ||
import ComboBox from "select-kit/components/combo-box"; | ||
import DTooltip from "float-kit/components/d-tooltip"; | ||
|
||
export default class AiLlmEditor extends Component { | ||
@service toasts; | ||
@service router; | ||
|
||
@tracked isSaving = false; | ||
|
||
get selectedProviders() { | ||
const t = (provName) => { | ||
return I18n.t(`discourse_ai.llms.providers.${provName}`); | ||
}; | ||
|
||
return this.args.llms.resultSetMeta.providers.map((prov) => { | ||
return { id: prov, name: t(prov) }; | ||
}); | ||
} | ||
|
||
@action | ||
async save() { | ||
this.isSaving = true; | ||
const isNew = this.args.model.isNew; | ||
|
||
debugger; | ||
try { | ||
await this.args.model.save(); | ||
|
||
if (isNew) { | ||
this.args.llms.addObject(this.args.model); | ||
this.router.transitionTo( | ||
"adminPlugins.show.discourse-ai-llms.show", | ||
this.args.model | ||
); | ||
} else { | ||
this.toasts.success({ | ||
data: { message: I18n.t("discourse_ai.llms.saved") }, | ||
duration: 2000, | ||
}); | ||
} | ||
} catch (e) { | ||
popupAjaxError(e); | ||
} finally { | ||
later(() => { | ||
this.isSaving = false; | ||
}, 1000); | ||
} | ||
} | ||
|
||
<template> | ||
<form class="form-horizontal ai-llm-editor"> | ||
<div class="control-group"> | ||
<label>{{i18n "discourse_ai.llms.display_name"}}</label> | ||
<Input | ||
class="ai-llm-editor__display-name" | ||
@type="text" | ||
@value={{@model.display_name}} | ||
/> | ||
</div> | ||
<div class="control-group"> | ||
<label>{{i18n "discourse_ai.llms.name"}}</label> | ||
<Input | ||
class="ai-llm-editor__name" | ||
@type="text" | ||
@value={{@model.name}} | ||
/> | ||
<DTooltip | ||
@icon="question-circle" | ||
@content={{I18n.t "discourse_ai.llms.hints.name"}} | ||
/> | ||
</div> | ||
<div class="control-group"> | ||
<label>{{I18n.t "discourse_ai.llms.provider"}}</label> | ||
<ComboBox | ||
@value={{@model.provider}} | ||
@content={{this.selectedProviders}} | ||
/> | ||
</div> | ||
<div class="control-group"> | ||
<label>{{I18n.t "discourse_ai.llms.tokenizer"}}</label> | ||
<ComboBox | ||
@value={{@model.tokenizer}} | ||
@content={{@llms.resultSetMeta.tokenizers}} | ||
/> | ||
</div> | ||
<div class="control-group"> | ||
<label>{{i18n "discourse_ai.llms.max_prompt_tokens"}}</label> | ||
<Input | ||
@type="number" | ||
class="ai-llm-editor__max-prompt-tokens" | ||
step="any" | ||
min="0" | ||
lang="en" | ||
@value={{@model.max_prompt_tokens}} | ||
/> | ||
<DTooltip | ||
@icon="question-circle" | ||
@content={{I18n.t "discourse_ai.llms.hints.max_prompt_tokens"}} | ||
/> | ||
</div> | ||
|
||
<div class="control-group ai-llm-editor__action_panel"> | ||
<DButton | ||
class="btn-primary ai-llm-editor__save" | ||
@action={{this.save}} | ||
@disabled={{this.isSaving}} | ||
> | ||
{{I18n.t "discourse_ai.llms.save"}} | ||
</DButton> | ||
</div> | ||
</form> | ||
</template> | ||
} |
61 changes: 61 additions & 0 deletions
61
assets/javascripts/discourse/components/ai-llms-list-editor.gjs
This file contains 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,61 @@ | ||
import Component from "@glimmer/component"; | ||
import { LinkTo } from "@ember/routing"; | ||
import icon from "discourse-common/helpers/d-icon"; | ||
import i18n from "discourse-common/helpers/i18n"; | ||
import I18n from "discourse-i18n"; | ||
import AiLlmEditor from "./ai-llm-editor"; | ||
|
||
export default class AiLlmsListEditor extends Component { | ||
get hasNoLLMElements() { | ||
this.args.llms.length !== 0; | ||
} | ||
|
||
<template> | ||
<section class="ai-llms-list-editor admin-detail pull-left"> | ||
|
||
<div class="ai-llms-list-editor__header"> | ||
<h3>{{i18n "discourse_ai.llms.short_title"}}</h3> | ||
{{#unless @currentLlm.isNew}} | ||
<LinkTo | ||
@route="adminPlugins.show.discourse-ai-llms.new" | ||
class="btn btn-small btn-primary" | ||
> | ||
{{icon "plus"}} | ||
<span>{{I18n.t "discourse_ai.llms.new"}}</span> | ||
</LinkTo> | ||
{{/unless}} | ||
</div> | ||
|
||
<div class="ai-llms-list-editor__container"> | ||
{{#if this.hasNoLLMElements}} | ||
<div class="ai-llms-list-editor__empty_list"> | ||
{{icon "robot"}} | ||
{{i18n "discourse_ai.llms.no_llms"}} | ||
</div> | ||
{{else}} | ||
<div class="content-list ai-llms-list-editor__content_list"> | ||
<ul> | ||
{{#each @llms as |llm|}} | ||
<li> | ||
<LinkTo | ||
@route="adminPlugins.show.discourse-ai-llms.show" | ||
current-when="true" | ||
@model={{llm}} | ||
> | ||
{{llm.display_name}} | ||
</LinkTo> | ||
</li> | ||
{{/each}} | ||
</ul> | ||
</div> | ||
{{/if}} | ||
|
||
<div class="ai-llms-list-editor__current"> | ||
{{#if @currentLlm}} | ||
<AiLlmEditor @model={{@currentLlm}} @llms={{@llms}} /> | ||
{{/if}} | ||
</div> | ||
</div> | ||
</section> | ||
</template> | ||
} |
This file contains 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.