From 7ca21cc3299c7501f8ad9cca2c10c12a55dbf3c4 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 10 Dec 2024 05:59:19 +1100 Subject: [PATCH] FEATURE: first class support for OpenRouter (#1011) * FEATURE: first class support for OpenRouter This new implementation supports picking quantization and provider pref Also: - Improve logging for summary generation - Improve error message when contacting LLMs fails * Better support for full screen artifacts on iPad Support back button to close full screen --- app/models/ai_api_audit_log.rb | 1 + app/models/llm_model.rb | 5 ++ .../discourse/components/ai-artifact.gjs | 46 ++++++++++-------- .../modules/ai-bot/common/ai-artifact.scss | 25 +++++----- config/locales/client.en.yml | 5 +- config/locales/server.en.yml | 1 + lib/ai_bot/playground.rb | 20 ++++++-- lib/completions/dialects/chat_gpt.rb | 3 +- lib/completions/endpoints/base.rb | 1 + lib/completions/endpoints/open_router.rb | 42 +++++++++++++++++ lib/completions/llm.rb | 19 ++++++++ .../strategies/hot_topic_gists.rb | 8 ++-- lib/summarization/strategies/topic_summary.rb | 4 +- spec/fabricators/llm_model_fabricator.rb | 9 ++++ .../completions/endpoints/open_router_spec.rb | 47 +++++++++++++++++++ 15 files changed, 192 insertions(+), 44 deletions(-) create mode 100644 lib/completions/endpoints/open_router.rb create mode 100644 spec/lib/completions/endpoints/open_router_spec.rb diff --git a/app/models/ai_api_audit_log.rb b/app/models/ai_api_audit_log.rb index ca33a2de..075f1444 100644 --- a/app/models/ai_api_audit_log.rb +++ b/app/models/ai_api_audit_log.rb @@ -15,6 +15,7 @@ class AiApiAuditLog < ActiveRecord::Base Ollama = 7 SambaNova = 8 Mistral = 9 + OpenRouter = 10 end def next_log_id diff --git a/app/models/llm_model.rb b/app/models/llm_model.rb index e8c21f21..98d39623 100644 --- a/app/models/llm_model.rb +++ b/app/models/llm_model.rb @@ -47,6 +47,11 @@ class LlmModel < ActiveRecord::Base disable_system_prompt: :checkbox, enable_native_tool: :checkbox, }, + open_router: { + disable_native_tools: :checkbox, + provider_order: :text, + provider_quantizations: :text, + }, } end diff --git a/assets/javascripts/discourse/components/ai-artifact.gjs b/assets/javascripts/discourse/components/ai-artifact.gjs index e8ba7090..e1958392 100644 --- a/assets/javascripts/discourse/components/ai-artifact.gjs +++ b/assets/javascripts/discourse/components/ai-artifact.gjs @@ -1,12 +1,16 @@ import Component from "@glimmer/component"; import { tracked } from "@glimmer/tracking"; -import { on } from "@ember/modifier"; import { action } from "@ember/object"; import { service } from "@ember/service"; import DButton from "discourse/components/d-button"; import htmlClass from "discourse/helpers/html-class"; import getURL from "discourse-common/lib/get-url"; +// note the panel for artifact full screen can not be at position 0,0 +// otherwise this hack will not activate. +// https://github.com/discourse/discourse/blob/b8325f2190a8c0a9022405c219faeac6f0f98ca5/app/assets/javascripts/discourse/app/components/scrolling-post-stream.js#L77-L77 +// this will cause post stream to navigate to a different post + export default class AiArtifactComponent extends Component { @service siteSettings; @tracked expanded = false; @@ -15,17 +19,29 @@ export default class AiArtifactComponent extends Component { constructor() { super(...arguments); this.keydownHandler = this.handleKeydown.bind(this); + this.popStateHandler = this.handlePopState.bind(this); + window.addEventListener("popstate", this.popStateHandler); } willDestroy() { super.willDestroy(...arguments); window.removeEventListener("keydown", this.keydownHandler); + window.removeEventListener("popstate", this.popStateHandler); } @action handleKeydown(event) { if (event.key === "Escape" || event.key === "Esc") { - this.expanded = false; + history.back(); + } + } + + @action + handlePopState(event) { + const state = event.state; + this.expanded = state?.artifactId === this.args.artifactId; + if (!this.expanded) { + window.removeEventListener("keydown", this.keydownHandler); } } @@ -52,12 +68,17 @@ export default class AiArtifactComponent extends Component { @action toggleView() { - this.expanded = !this.expanded; - if (this.expanded) { + if (!this.expanded) { + window.history.pushState( + { artifactId: this.args.artifactId }, + "", + window.location.href + "#artifact-fullscreen" + ); window.addEventListener("keydown", this.keydownHandler); } else { - window.removeEventListener("keydown", this.keydownHandler); + history.back(); } + this.expanded = !this.expanded; } get wrapperClasses() { @@ -66,25 +87,12 @@ export default class AiArtifactComponent extends Component { }`; } - @action - artifactPanelHover() { - // retrrigger animation - const panel = document.querySelector(".ai-artifact__panel"); - panel.style.animation = "none"; // Stop the animation - setTimeout(() => { - panel.style.animation = ""; // Re-trigger the animation by removing the none style - }, 0); - } -