Compare commits

...
Author SHA1 Message Date
Keegan George a84e253c5b Revert "FEATURE: smooth streaming animation for summarization (#778)"
This reverts commit fdadfa029e.
2024-08-29 15:24:22 -07:00
Keegan George fdadfa029e FEATURE: smooth streaming animation for summarization (#778) 2024-08-29 15:07:07 -07:00
Keegan George 94f6c632bf DEV: Publish AI Bot PM title update to message bus channel (#781) 2024-08-29 14:48:44 -07:00
Sam 41054c4fb8 FEATURE: improve site setting search (#780)
This improves the site setting search so it performs a somewhat
fuzzy match.

Previously it did not handle seperators such as "space" and a
term such as "min_post_length" would not find "min_first_post_length"

A more liberal search algorithm makes it easier to the AI to
navigate settings.

* Minor fix, {{and parameter.enum parameter.enum.length}} is non
obviously broken.


If parameter.enum is a tracked array it will return the object
cause embers and helper implementation.

This corrects an issue where enum keeps on selecting itself by
mistake.
2024-08-29 16:05:38 +10:00
Keegan George 943504049c FIX: Prevent proofreading when there is no content (#779) 2024-08-28 12:21:34 -07:00
Rafael dos Santos Silva a08d168740 FEATURE: Initial support for seeded LLMs (#756) 2024-08-28 15:57:58 -03:00
Sam 0687ec75c3 FEATURE: allow embedding based search without hyde (#777)
This allows callers of embedding based search to bypass hyde.

Hyde will expand the search term using an LLM, but if an LLM is
performing the search we can skip this expansion.

It also introduced some tests for the controller which we did not have
2024-08-28 14:17:34 +10:00
Roman Rizzi 72607c3560 Revert "FEATURE: Triage rule can skip posts created via email (#775)" (#776)
This reverts commit 6af415f7f0.
2024-08-27 15:45:55 -03:00
Roman Rizzi 6af415f7f0 FEATURE: Triage rule can skip posts created via email (#775) 2024-08-27 12:04:55 -03:00
Jarek Radosz 1b209f5fb1 DEV: Update ace-editor usage (#773)
AceEditor is now a glimmer component (see: https://github.com/discourse/discourse/pull/28492) and it follows the "data down, actions up" pattern.
2024-08-27 13:52:55 +02:00
Keegan Georgeandchapoi 50c2d56aff UX: Topic summary UI improvements due to DMenu changes (#772)
Co-authored-by: chapoi <[email protected]>
2024-08-26 08:32:39 -07:00
SamandMartin Brennan f148452f4c FEATURE: single click proofreading (#769)
Previously there was too much work proofreading text, new implementation
provides a single shortcut and easy way of proofreading text.


Co-authored-by: Martin Brennan <[email protected]>
2024-08-26 15:43:40 +10:00
Roman Rizzi c6aeabbfc0 FIX: Malformed message in systemless + inline img scenario (#771) 2024-08-23 16:41:57 -03:00
Roman Rizzi eac83eb619 FIX: Triage's search_for_text should be case-insensitive (#767) 2024-08-22 18:32:42 -03:00
Roman Rizzi 9019e90b87 FIX: urlEditable must be true for all providers except Bedrock (#766) 2024-08-22 11:31:28 -03:00
David Taylor 3c7bd9bbd3 FIX: Tool editing code editor resetting on every keypress (#765)
`withEventValue` is not needed here, because the `onChange` event comes from ace, not a normal DOM event. But even with that fix, it seems AceEditor doesn't yet work well with the DDAU pattern. On every keypress, the editor re-renders and puts the cursor back at the beginning.

For now, this commit removes the `@onChange` hook, so we go back to relying on the two-way binding of `@content`.

Followup to a5a39dd2ee
2024-08-22 10:18:16 +01:00
chapoi 324af23b72 UX: add dark-light mixin for better visibility in dark themes (#764) 2024-08-22 01:24:18 +02:00
Keegan George da9e192949 FIX: Mobile composer helper trigger should work on Android and iOS (#763) 2024-08-21 16:19:25 -07:00
chapoiandKeegan George 10dae65740 UX: style & position AI helper in composer (#758)
Co-authored-by: Keegan George <[email protected]>
2024-08-21 11:01:03 -07:00
Roman Rizzi 64641b6175 FEATURE: LLM Triage support for systemless models. (#757)
* FEATURE: LLM Triage support for systemless models.

This change adds support for OSS models without support for system messages. LlmTriage's system message field is no longer mandatory. We now send the post contents in a separate user message.

* Models using Ollama can also disable system prompts
2024-08-21 11:41:55 -03:00
Sam 97fc822cb6 FEATURE: Allow specific groups access to summary feature on PMs (#760)
New `ai_pm_summarization_allowed_groups` can be used to allow
visibility of the summarization feature on PMs.

This can be useful on forums where a lot of communication happens
inside PMs.
2024-08-21 07:58:24 +10:00
Keegan George c0328267be FIX: Add original upload markdown on caption failure (#762) 2024-08-20 12:53:51 -07:00
Discourse Translator Bot dd63ade348 Update translations (#761) 2024-08-20 10:58:36 -03:00
Keegan George 4a40531143 FIX: Clicking on summary links breaks summary (#759) 2024-08-20 10:57:23 -03:00
Keegan George bfe3b1c3b8 FIX: Modals in composer helper menu not working (#755) 2024-08-16 10:08:58 -07:00
Roman Rizzi f789d3ee96 FIX: Triage-flagged posts didn't have a score. (#752)
The score will contain the LLM result, and make sure the flag isn't displayed when a minimum score threshold is present.
2024-08-14 15:54:09 -03:00
Keegan George 867cd54556 DEV: Reduce megapixel requirement for auto image captioning (#754) 2024-08-14 11:26:56 -07:00
Keegan George 23b88537d9 FIX: Prevent AI caption setting from showing unless all criteria is met (#753) 2024-08-14 10:17:36 -07:00
Discourse Translator Bot 72980a3d84 Update translations (#751) 2024-08-13 16:33:25 +02:00
Sam 14443bf890 FIX: more robust summary implementation (#750)
When navigating between topic we were not correctly resetting
internal state for summarization. This leads to a situation where
incorrect summaries can be displayed to users and wrong summaries
can be displayed.

Additionally our controller for grabbing summaries was always
streaming results via message bus, which could be delayed when
sidekiq is overloaded. We now will return the cached summary
right away if it is available direct from REST endpoint.
2024-08-13 08:47:47 -03:00
Keegan George f72ab12761 DEV: Clearly separate post/composer helper settings (#747) 2024-08-12 15:40:23 -07:00
Keegan George a1eb1ba180 FIX: AI helper not being shown on mobile view (#749) 2024-08-12 15:32:49 -07:00
Sam cf1e15ef12 FIX: gemini 0801 tool calls (#748)
Gemini experimental model requires tool_config.

Previously defaults would apply.

This corrects prompts containing multiple tools on gemini.
2024-08-12 16:10:16 +10:00
Discourse Translator Bot a7c0ddba97 Update translations (#741) 2024-08-08 18:12:35 -03:00
Keegan George 0be292f247 FIX: auto_image_caption not always present for current user. (#746) 2024-08-08 13:37:26 -07:00
Keegan George 1d6a6c9f8f FEATURE: Stream other post helper options (#745) 2024-08-08 11:32:39 -07:00
Rafael dos Santos Silva 1686a8a683 DEV: Move to single table per embeddings type (#561)
Also move us to halfvecs for speed and disk usage gains
2024-08-08 11:55:20 -03:00
Roman Rizzi 20efc9285e FIX: Correctly save provider-specific params for new models. (#744)
Creating a new model, either manually or from presets, doesn't initialize the `provider_params` object, meaning their custom params won't persist.

Additionally, this change adds some validations for Bedrock params, which are mandatory, and a clear message when a completion fails because we cannot build the URL.
2024-08-07 16:08:56 -03:00
Rafael dos Santos Silva 3b5245dc54 FIX: Handle nil reply_to_post in AI Bot event handler (#743)
Somehow it's possible to have a nil `post.reply_to_post` with
a non-nil `post.reply_to_post_number`.
2024-08-07 14:22:32 -03:00
Keegan George 1254d7c7d0 REFACTOR: AI Composer Helper Menu (#715) 2024-08-06 10:57:39 -07:00
Roman Rizzi 7b4c099673 FIX: LlmModel validations. (#742)
- Validate fields to reduce the chance of breaking features by a misconfigured model.
- Fixed a bug where the URL might get deleted during an update.
- Display a warning when a model is currently in use.
2024-08-06 14:35:35 -03:00
chapoiandRafael Silva d17bbc2dc4 UX: summary fixed positioning (#740)
Co-authored-by: Rafael Silva <[email protected]>
2024-08-05 12:39:08 -03:00
dependabot[bot] 5c9f610fa4 Build(deps-dev): Bump rexml from 3.3.1 to 3.3.3 (#738)
Bumps [rexml](https://github.com/ruby/rexml) from 3.3.1 to 3.3.3.
- [Release notes](https://github.com/ruby/rexml/releases)
- [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md)
- [Commits](https://github.com/ruby/rexml/compare/v3.3.1...v3.3.3)

---
updated-dependencies:
- dependency-name: rexml
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 13:10:20 +02:00
Keegan George 2ff3fe3a9f UX: Use stacked line chart for post sentiment (#737) 2024-08-02 14:23:29 -07:00
Roman Rizzi acc3f817e1 UX: Clarify purpose of OpenAI API key setting (#739) 2024-08-02 15:57:40 -03:00
Sam 948cf893a9 FIX: Add tool support to open ai compatible dialect and vllm (#734)
* FIX: Add tool support to open ai compatible dialect and vllm

Automatic tools are in progress in vllm see: https://github.com/vllm-project/vllm/pull/5649

Even when they are supported, initial support will be uneven, only some models have native tool support
notably mistral which has some special tokens for tool support.

After the above PR lands in vllm we will still need to swap to
XML based tools on models without native tool support.

* fix specs
2024-08-02 09:52:33 -03:00
Natalie Tay b7ac229547 DEV: Pin plugin for Discourse < 3.4.0.beta1-dev (#735) 2024-08-02 17:03:27 +08:00
Rafael dos Santos Silva 7dc0a2a20a DEV: Log error responses on Gemini / Cloudflare embeddings requests (#736) 2024-08-01 17:25:24 -03:00
Rafael dos Santos Silva 9d887ad4ac FIX: Use correct date for cached summary (#733) 2024-07-31 15:21:26 -03:00
Rafael dos Santos Silva d4c90bc2ba FEATURE: Make emotion analysis enabled by default (#732) 2024-07-31 12:00:00 -03:00
Sam c16c622b53 FIX: properly pass errors to client (#731)
render_json_error expects a AR model not a serializer, using a
serializer eats up the error message
2024-07-31 17:53:18 +10:00
Roman Rizzi bed044448c DEV: Remove old code now that features rely on LlmModels. (#729)
* DEV: Remove old code now that features rely on LlmModels.

* Hide old settings and migrate persona llm overrides

* Remove shadowing special URL + seeding code. Use srv:// prefix instead.
2024-07-30 13:44:57 -03:00
Discourse Translator Bot 73a2b15e91 Update translations (#730) 2024-07-30 11:08:03 -03:00
Natalie Tay 7cd7f71857 DEV: Promote historical post-deploy migrations (#728) 2024-07-30 01:44:57 +08:00
Rafael dos Santos Silva 665637fbad FIX: Properly fix ai_summaries table sequence (#727)
* FIX: Properly fix ai_summaries table sequence

Previous attempt at 3815360 could fail due to a race introduced in 1b0ba91 where summaries are migrated to core in a post_migrate erroneously.
2024-07-26 14:45:01 -03:00
Jan Cernik d58913b227 FIX: Collapse Summarize button on mobile (#726) 2024-07-25 16:10:53 -03:00
Jan Cernik d9dad56c6a UX: Use DMenu for topic summarization (#724) 2024-07-25 10:47:18 -03:00
Roman Rizzi 5c196bca89 FEATURE: Track if a model can do vision in the llm_models table (#725)
* FEATURE: Track if a model can do vision in the llm_models table

* Data migration
2024-07-24 16:29:47 -03:00
Discourse Translator Bot 06e239321b Update translations (#723) 2024-07-23 15:23:51 +02:00
Jan Cernik 738cd99ad6 FIX: Add bottom spacing for the new topic map (#722) 2024-07-22 22:21:23 -03:00
Rafael dos Santos Silva 3502f0f1cd FEATURE: GPT4o Tokenizer (#721) 2024-07-22 15:26:14 -03:00
Roman Rizzi 7f2c3a1ab9 DEV: Pin plugin for v3.2 to guarantee Ruby 3.3 compat (#719) 2024-07-17 13:16:14 -03:00
Joffrey JAFFEUX e4145ad6a6 FIX: ace-editor is now in discourse bundle, not admin (#717) 2024-07-17 11:58:50 +02:00
Discourse Translator Bot 4d8090002f Update translations (#716) 2024-07-16 22:14:09 +02:00
Roman Rizzi f328b81c78 FIX: Make sure custom tool enums follow json-schema. (#718)
Enums didn't work as expected because we the dialect couldn't translate
them correctly. It doesn't understand what "enum_values" is.
2024-07-16 14:23:17 -03:00
Roman Rizzi 0a8195242b FIX: Limit system message size to 60% of available tokens. (#714)
Using RAG fragments can lead to considerably big system messages, which becomes problematic when models have a smaller context window.

Before this change, we only look at the rest of the conversation to make sure we don't surpass the limit, which could lead to two unwanted scenarios when having large system messages:

All other messages are excluded due to size.
The system message already exceeds the limit.

As a result, I'm putting a hard-limit of 60% of available tokens. We don't want to aggresively truncate because if rag fragments are included, the system message contains a lot of context to improve the model response, but we also want to make room for the recent messages in the conversation.
2024-07-12 15:09:01 -03:00
Martin Brennan 5c1ab85583 DEV: More topic title prompt tweaks (#712)
Followup 8d4a67fbe2

The prompt worked better, but it took the instructions
about never using lowercase a little too literally,
it wasn't using it for things like LLM or Discourse,
also it was almost always framing the title as questions
so now I asked it for a mix of questions and statements
because that's less ambiguous.
2024-07-11 10:14:53 +10:00
Keegan George 08355ea5d8 FEATURE: Show post helper as bottom modal on mobile (#704) 2024-07-10 11:01:05 -07:00
Martin Brennan 8d4a67fbe2 DEV: Tweak topic title generator prompt (#710)
Changes the title generator prompt to avoid clickbait-y
titles and also try to avoid AI's favourite title format,
which is "Some Thing: Other Thing"

Leaving the chat thread title generator for now, that's
not as important, the bizarre titles add to the experience there.
2024-07-10 14:31:59 +10:00
Martin Brennan da6d70da8f FEATURE: Add breadcrumbs to LLMs and Persona admin pages (#666)
Followup to https://github.com/discourse/discourse-ai/pull/656,
adding these back in with the new core component.
2024-07-10 10:56:13 +10:00
Discourse Translator Bot 84b1c9af71 Update translations (#709) 2024-07-09 23:12:11 +02:00
Roman Rizzi 5cb91217bd FIX: Flaky SRV-backed model seeding. (#708)
* Seeding the SRV-backed model should happen inside an initializer.
* Keep the model up to date when the hidden setting changes.
* Use the correct Mixtral model name and fix previous data migration.
* URL validation should trigger only when we attempt to update it.
2024-07-08 18:47:10 -03:00
PangBo 4ebbdc043e fix: locale handling in assistant.rb (#705) 2024-07-05 11:16:09 +02:00
Régis Hanol d555f18c6f FIX: don't reset open state on details tag when morphing (#702) 2024-07-05 08:07:28 +10:00
Roman Rizzi 442681a3d3 FIX: Mixtral models have system role support. (#703)
Using assistant role for system produces an error because
they expect alternating roles like user/assistant/user and so on.
Prompts cannot start with the assistant role.
2024-07-04 13:23:03 -03:00
Keegan George eab2f74b58 DEV: Use site locale for composer helper translations (#698) 2024-07-04 08:23:37 -07:00
Sam 38153608f8 FIX: repair id sequence identity on summary table (#701)
1. Repairs the identity on the summary table, we migrated data without resetting it.
2. Adds an index into ai_summary table to match expected retrieval pattern
2024-07-04 12:23:46 +10:00
SamandRoman Rizzi 1320eed9b2 FEATURE: move summary to use llm_model (#699)
This allows summary to use the new LLM models and migrates of API key based model selection

Claude 3.5 etc... all work now. 

---------

Co-authored-by: Roman Rizzi <[email protected]>
2024-07-04 10:48:18 +10:00
Roman Rizzi fc081d9da6 FIX: Restore ability to fold summaries, which was accidentally removed (#700) 2024-07-03 18:10:31 -03:00
Keegan George ef4b3559cd FIX: Disabled chat breaks ai-chat-summarization (#697) 2024-07-02 11:10:31 -07:00
Keegan George 1b0ba9197c DEV: Add summarization logic from core (#658) 2024-07-02 08:51:59 -07:00
Discourse Translator Bot c352cc5ba3 Update translations (#695) 2024-07-02 16:42:52 +02:00
Sam b671ffe7fa FIX: info not working, not suppressing hidden tags from report (#696)
2 small fixes

1. The info button was not properly working post refactor
2. Suppress any secured tags from report input
2024-07-02 16:38:33 +10:00
Jarek Radosz a5a39dd2ee DEV: Clean up after #677 (#694)
Follow up to b863ddc94b

Ruby:
* Validate `summary` (the column is `not null`)
* Fix `name` validation (the column has `max_length` 100)
* Fix table annotations
* Accept missing `parameter` attributes (`required, `enum`, `enum_values`)

JS:
* Use native classes
* Don't use ember's array extensions
* Add explicit service injections
* Correct class names
* Use `||=` operator
* Use `store` service to create records
* Remove unused service injections
* Extract consts
* Group actions together
* Use `async`/`await`
* Use `withEventValue`
* Sort html attributes
* Use DButtons `@label` arg
* Use `input` elements instead of Ember's `Input` component (same w/ textarea)
* Remove `btn-default` class (automatically applied by DButton)
* Don't mix `I18n.t` and `i18n` in the same template
* Don't track props that aren't used in a template
* Correct invalid `target.value` code
* Remove unused/invalid `this.parameter`/`onChange` code
* Whitespace
* Use the new service import `inject as service` -> `service`
* Use `Object.entries()`
* Add missing i18n strings
* Fix an error in `addEnumValue` (calling `pushObject` on `undefined`)
* Use `TrackedArray`/`TrackedObject`
* Transform tool `parameters` keys (`enumValues` -> `enum_values`)
2024-06-28 08:59:51 +10:00
Rafael dos Santos Silva a708d4dfa2 FIX: Use base64 encoded images in AI Image Caption via LLaVa (#693)
* FIX: Use base64 encoded images in AI Image Caption via LLaVa

This fixed a regression introduced in #646 where we started sending
schemaless URLs for our LLaVa service, which doesn't handle it well.

Moving to base64 encoded images solves:

- The service needing to download images
  Now the service running LLaVa doesn't need internet access

- Secure uploads compat
  Every image is treated the same, less branching for secure uploads

- Image Size problems
  Discourse is now responsible for ensure a max size for images

- Troublesome dev env
  Previously to this commit you would need a dev env that was internet
acessible to use llava image captions
2024-06-27 16:24:44 -03:00
Jan Cernik 8e83c091a2 DEV: Use explicit serializers for all models (#691) 2024-06-27 10:43:00 -03:00
SamandMartin Brennan b863ddc94b FEATURE: custom user defined tools (#677)
Introduces custom AI tools functionality. 

1. Why it was added:
   The PR adds the ability to create, manage, and use custom AI tools within the Discourse AI system. This feature allows for more flexibility and extensibility in the AI capabilities of the platform.

2. What it does:
   - Introduces a new `AiTool` model for storing custom AI tools
   - Adds CRUD (Create, Read, Update, Delete) operations for AI tools
   - Implements a tool runner system for executing custom tool scripts
   - Integrates custom tools with existing AI personas
   - Provides a user interface for managing custom tools in the admin panel

3. Possible use cases:
   - Creating custom tools for specific tasks or integrations (stock quotes, currency conversion etc...)
   - Allowing administrators to add new functionalities to AI assistants without modifying core code
   - Implementing domain-specific tools for particular communities or industries

4. Code structure:
   The PR introduces several new files and modifies existing ones:

   a. Models:
      - `app/models/ai_tool.rb`: Defines the AiTool model
      - `app/serializers/ai_custom_tool_serializer.rb`: Serializer for AI tools

   b. Controllers:
      - `app/controllers/discourse_ai/admin/ai_tools_controller.rb`: Handles CRUD operations for AI tools

   c. Views and Components:
      - New Ember.js components for tool management in the admin interface
      - Updates to existing AI persona management components to support custom tools 

   d. Core functionality:
      - `lib/ai_bot/tool_runner.rb`: Implements the custom tool execution system
      - `lib/ai_bot/tools/custom.rb`: Defines the custom tool class

   e. Routes and configurations:
      - Updates to route configurations to include new AI tool management pages

   f. Migrations:
      - `db/migrate/20240618080148_create_ai_tools.rb`: Creates the ai_tools table

   g. Tests:
      - New test files for AI tool functionality and integration

The PR integrates the custom tools system with the existing AI persona framework, allowing personas to use both built-in and custom tools. It also includes safety measures such as timeouts and HTTP request limits to prevent misuse of custom tools.

Overall, this PR significantly enhances the flexibility and extensibility of the Discourse AI system by allowing administrators to create and manage custom AI tools tailored to their specific needs.

Co-authored-by: Martin Brennan <[email protected]>
2024-06-27 17:27:40 +10:00
Sam af4f871096 FIX: never provide tools with invalid UTF-8 strings (#692)
Previous to this change, on truncation we could return invalid
UTF-8 strings to caller

This also allows tools to read up to 30 megs vs the old 4 megs.
2024-06-27 14:06:52 +10:00
Loïc Guitaut e26c5986f2 DEV: Use Rails 7.0 instead of 7.1 in post-migrations 2024-06-26 18:41:38 +02:00
Loïc Guitaut 6f5873b072 DEV: Use Rails 7.0 instead of 7.1 in migrations 2024-06-26 18:32:11 +02:00
Roman Rizzi e39e0bdb4a FIX: Move the bot user toggling to the controller. (#688)
Having this as a callback prevents deploys of sites with a vLLM SRV configured and pending migrations. Additionally, this fixes a bug where we didn't delete/deactivate the companion user after deleting an LLM.
2024-06-25 12:45:19 -03:00
Discourse Translator Bot b6f0ad157c Update translations (#687) 2024-06-25 15:40:17 +02:00
Roman Rizzi f622e2644f FEATURE: Store provider-specific parameters. (#686)
Previously, we stored request parameters like the OpenAI organization and Bedrock's access key and region as site settings. This change stores them in the `llm_models` table instead, letting us drop more settings while also becoming more flexible.
2024-06-25 08:26:30 +10:00
Sam 1d5fa0ce6c FIX: when creating an llm we were not creating user (#685)
This meant that if you toggle ai user early it surprisingly did
not work.

Also remove safety settings from gemini, it is overly cautious
2024-06-24 09:59:42 +10:00
dependabot[bot] 346dd734d1 Build(deps): Bump braces from 3.0.2 to 3.0.3 (#683)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-22 16:50:25 +02:00
Roman Rizzi 091dc626e8 FIX: Make sure LlmEnumerator always return value hashes using symbols (#684) 2024-06-21 16:35:31 -03:00
Sam 3a2339902e FEATURE: allow select claude-3-5 for native tools (#682) 2024-06-21 18:17:36 +10:00
Sam e04a7be122 FEATURE: LLM presets for model creation (#681)
* FEATURE: LLM presets for model creation

Previous to this users needed to look up complicated settings
when setting up models.

This introduces and extensible preset system with Google/OpenAI/Anthropic
presets.

This will cover all the most common LLMs, we can always add more as
we go.

Additionally:

- Proper support for Anthropic Claude Sonnet 3.5
- Stop blurring api keys when navigating away - this made it very complex to reuse keys
2024-06-21 17:32:15 +10:00
Roman Rizzi 558574fa87 DEV: Use LlmModels as options in automation rules (#676) 2024-06-21 08:07:17 +10:00
Rafael dos Santos Silva 714caf34fe FEATURE: Support for Claude 3.5 Sonnet via AWS Bedrock (#680) 2024-06-20 17:51:46 -03:00
Rafael dos Santos Silva b23a3a86a6 FIX: SRV warning should not trigger on brand new LLM (#679) 2024-06-20 15:37:14 -03:00
Kris 1a8532b91b UX: improve composer suggestion button layout (#678) 2024-06-20 13:32:14 -04:00
Kris 59ed6d7b3e UX: show image caption button on image hover (#670) 2024-06-20 08:58:19 +10:00
Roman Rizzi 8849caf136 DEV: Transition "Select model" settings to only use LlmModels (#675)
We no longer support the "provider:model" format in the "ai_helper_model" and
"ai_embeddings_semantic_search_hyde_model" settings. We'll migrate existing
values and work with our new data-driven LLM configs from now on.
2024-06-19 18:01:35 -03:00
Roman Rizzi ed3d5521a8 UX: QoL impromevements to the admin LLM models page. (#674)
API Key value is secret by default, and we include a link to the AI bot user.
2024-06-19 11:21:21 -03:00
Roman Rizzi c4d9fab8e7 FIX: typo when excluding personas from enabled bot list (#671) 2024-06-19 10:16:00 -03:00
Sam 0d6d9a6ef5 FEATURE: allow access to private topics if tool permits (#673)
Previously read tool only had access to public topics, this allows
access to all topics user has access to, if admin opts for the option
Also

- Fixes VLLM migration
- Display which llms have bot enabled
2024-06-19 15:49:36 +10:00
Krzysztof Kotlarek 3c45335936 DEV: disable flaky playground_spec (#672) 2024-06-19 10:56:15 +10:00
Roman Rizzi 8d5f901a67 DEV: Rewire AI bot internals to use LlmModel (#638)
* DRAFT: Create AI Bot users dynamically and support custom LlmModels

* Get user associated to llm_model

* Track enabled bots with attribute

* Don't store bot username. Minor touches to migrate default values in settings

* Handle scenario where vLLM uses a SRV record

* Made 3.5-turbo-16k the default version so we can remove hack
2024-06-18 14:32:14 -03:00
Discourse Translator Bot cc0b222faa Update translations (#669) 2024-06-18 15:39:41 +02:00
Sam 460f5c4553 FIX: display search correctly, bug when stripping XML (#668)
- Display filtered search correctly, so it is not confusing
- When XML stripping, if a chunk was `<` it would crash
- SQL Helper improved to be better aware of Data Explorer
2024-06-14 15:28:40 +10:00
Sam f642a27f11 FIX: Dall E / Artist broken when tool_details is disabled (#667)
We were missing logic to handle custom_html from tools

This also fixes image generation in chat
2024-06-12 17:58:28 +10:00
Kris 8a0c2020f2 UX: improve appearance of attributed quote when sharing (#665) 2024-06-11 12:06:57 -04:00
Discourse Translator Bot effa6cc59f Update translations (#664) 2024-06-11 17:20:49 +02:00
Sam 5abf80cb4e FIX: do not mark column read only so certain deployments work (#663)
In some case we may be deploying migrations, seeding and then
running post migrations, we need this to work so we give up
on this small window of protection
2024-06-11 21:32:49 +10:00
SamandMartin Brennan 52a7dd2a4b FEATURE: optional tool detail blocks (#662)
This is a rather huge refactor with 1 new feature (tool details can
be suppressed)

Previously we use the name "Command" to describe "Tools", this unifies
all the internal language and simplifies the code.

We also amended the persona UI to use less DToggles which aligns
with our design guidelines.

Co-authored-by: Martin Brennan <[email protected]>
2024-06-11 18:14:14 +10:00
Sam 875bb04467 FIX: summarize is not working remove for now (#661)
Summarize is not working right, remove it for now.

Also it was specified twice breaking tool calls.
2024-06-08 12:25:30 +10:00
Roman Rizzi 212ee23698 FIX: Use new report color keys defined in discourse/discourse#27240 (#660) 2024-06-07 15:08:06 -03:00
Sam 8b81ff45b8 FIX: switch off native tools on Anthropic Claude Opus (#659)
Native tools do not work well on Opus.

Chain of Thought prompting means it consumes enormous amounts of
tokens and has poor latency.

This commit introduce and XML stripper to remove various chain of
thought XML islands from anthropic prompts when tools are involved.

This mean Opus native tools is now functions (albeit slowly)

From local testing XML just works better now.

Also fixes enum support in Anthropic native tools
2024-06-07 10:52:01 -03:00
Martin Brennan 7a64699314 DEV: Remove breadcrumbs for now (#656)
This core PR will change the component API a bit,
so it's easier to remove these for now and add them
back after:

https://github.com/discourse/discourse/pull/27365
2024-06-07 10:37:53 +10:00
Rafael dos Santos Silva 92108452f2 FIX: AI Bot Shared Conversation didn't respect CDN / Subfolder (#657) 2024-06-06 15:13:51 -03:00
Sam 3993c685e1 FEATURE: anthropic function calling (#654)
Adds support for native tool calling (both streaming and non streaming) for Anthropic.

This improves general tool support on the Anthropic models.
2024-06-06 08:34:23 +10:00
Sam 564d2de534 FEATURE: Add native Cohere tool support (#655)
Add native Cohere tool support

- Introduce CohereTools class for tool translation and result processing
- Update Command dialect to integrate with CohereTools
- Modify Cohere endpoint to support passing tools and processing tool calls
- Add spec for testing tool triggering with Cohere endpoint
2024-06-04 08:59:15 +10:00
Discourse Translator Bot 97afda278b Update translations (#653) 2024-05-31 12:27:35 +02:00
Keegan George 02a50a29f8 DEV: Conditionally show AI results toggle based on sort order (#652) 2024-05-29 18:18:22 -07:00
Sam 834fea672f FEATURE: improved tooling (#651)
1. New tool to easily find files (and default branch) in a Github repo
2. Improved read tool with clearer params and larger context

* limit can totally mess up the richness semantic search adds, so include the results unconditionally.
2024-05-30 06:33:50 +10:00
Martin Brennan d812ecf5da FIX: Remove doubled up breadcrumb in persona UI (#650)
Followup to 06137ac706
2024-05-29 11:37:01 +10:00
Sam 13840f68b3 FEATURE: restrict public sharing on login required sites (#649)
Initial implementation allowed internet wide sharing of
AI conversations, on sites that require login.

This feature can be an anti feature for private sites cause they
can not share conversations internally.

For now we are removing support for public sharing on login required
sites, if the community need the feature we can consider adding a
setting.
2024-05-29 11:04:47 +10:00
Roman Rizzi bd1490a536 FIX: include_usage is not available in the Azure API. (#648)
Follow-up #618
2024-05-28 16:55:43 -03:00
Sam b487de933d FEATURE: add support for all vision models (#646)
Previoulsy on GPT-4-vision was supported, change introduces support
for Google/Anthropic and new OpenAI models

Additionally this makes vision work properly in dev environments
cause we sent the encoded payload via prompt vs sending urls
2024-05-28 10:31:15 -03:00
Loïc Guitaut dd4e305ff7 DEV: Update rubocop-discourse to version 3.8.0 (#641) 2024-05-28 11:15:42 +02:00
Sam 309280cbb6 FEATURE: add aspect ratio support to DallE 3 (#647)
DallE 3 supports tall/square and wide images.

This adds support to the 3 variants. (wide / tall / square)
2024-05-28 16:21:40 +10:00
Keegan George dae9d6f14e FIX: Move image caption group check logic to server side (#645)
* FIX: User groups error before initialization
* DEV: Move group check logic to server side
2024-05-28 10:29:11 +10:00
Keegan George 71affe75bf UX: Hide AI preferences page completely if no settings for user (#644) 2024-05-27 13:27:45 -07:00
Roman Rizzi 333b331eb9 FEATURE: Allow deleting custom LLMs. (#643)
This change allows us to delete custom models. It checks if there is no module using them.

It also fixes a bug where the after-create transition wasn't working. While this prevents a model from being saved multiple times, endpoint validations are still needed (will be added in a separate PR).:
2024-05-27 16:44:08 -03:00
Keegan George 90c5e4bb0e FIX: Reply broken when auto caption is enabled (#642) 2024-05-27 12:17:35 -07:00
Keegan George a1c649965f FEATURE: Auto image captions (#637) 2024-05-27 10:49:24 -07:00
Sam baf88e7cfc FEATURE: improve logging by including llm name (#640)
Log the language model name when logging api requests
2024-05-27 16:46:01 +10:00
Ted Johansson d8a0f44fed FIX: Amend incorrect translation keys (#639)
I am enabling config.i18n.raise_on_missing_translations in core. This revealed a couple of broken translations in the plugin.
2024-05-24 20:00:36 +08:00
Sam f8381c0e8a UX: suppress "this is a warning" (#636)
When triggering a PM from new-message route, we still had the UI
for "this is an official warning"

This removes that UI from bot messages, which is all clutter.
2024-05-23 12:55:33 +08:00
Sam d5c23f01ff FIX: correct gemini streaming implementation (#632)
This also implements image support and gemini-flash support
2024-05-22 16:35:29 +10:00
Martin Brennan 06137ac706 FEATURE: Use DBreadcrumbsItem in admin UI (#631)
This commit uses the new DBreadcrumbsItem and DBreadcrumbsContainer
from core to show a path back to the admin plugins list.
2024-05-22 09:19:53 +10:00
Keegan George 59e63a2da9 FIX: Unresponsive post buttons due to Ask AI highlight (#635) 2024-05-21 13:58:37 -07:00
Roman Rizzi 3a9080dd14 FEATURE: Test LLM configuration (#634) 2024-05-21 13:35:50 -03:00
Discourse Translator Bot 2b473dd4a5 Update translations (#633) 2024-05-21 17:41:00 +02:00
Sam d4116ecfac FEATURE: Add support for contextualizing a DM to a bot (#627)
This brings the context of the current topic on screen into chat
2024-05-21 17:17:02 +10:00
Sam 232f12eba6 FEATURE: JavaScript evaluation tool (#630)
This is similar to code interpreter by ChatGPT, except that it uses
JavaScript as the execution engine.

Safeguards were added to ensure memory is constrained and evaluation
times out.
2024-05-21 07:57:01 +10:00
dependabot[bot] 4d8d822351 Build(deps-dev): Bump rexml from 3.2.6 to 3.2.8 (#629)
Bumps [rexml](https://github.com/ruby/rexml) from 3.2.6 to 3.2.8.
- [Release notes](https://github.com/ruby/rexml/releases)
- [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md)
- [Commits](https://github.com/ruby/rexml/compare/v3.2.6...v3.2.8)

---
updated-dependencies:
- dependency-name: rexml
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-16 23:54:10 +02:00
Roman Rizzi d8ebed8fb5 UX: Follow plugin user interface UI guidelines. (#628) 2024-05-16 14:28:57 -03:00
Roman RizziandRafael Silva 1d786fbaaf FEATURE: Set endpoint credentials directly from LlmModel. (#625)
* FEATURE: Set endpoint credentials directly from LlmModel.

Drop Llama2Tokenizer since we no longer use it.

* Allow http for custom LLMs

---------

Co-authored-by: Rafael Silva <[email protected]>
2024-05-16 09:50:22 -03:00
Sam 255139056d FEATURE: safeguard to avoid over triage (#626)
- a post can be triaged a maximum of twice a minute
- system can run a total of 60 triages a minute

Low defaults were picked to safeguard against any possible loops

This can be amended if required via hidden site settings.
2024-05-16 16:49:44 +10:00
Régis Hanol d64cff7692 DEV: use sinon for safer stubbing (#624)
Follow up to https://github.com/discourse/discourse-ai/pull/622#discussion_r1601199538
2024-05-15 11:54:19 +02:00
Régis Hanol 1e6d9ed634 FIX: respect "external_links_in_new_tab" user preference... (#622)
... for AI conversations since they look & feel "external".

Internal ref - /t/128182
2024-05-15 10:06:38 +02:00
Sam 3db89bfdc8 FIX: incorrect description for LLM field (#623) 2024-05-14 16:55:25 -07:00
Discourse Translator Bot 28647b81fe Update translations (#620) 2024-05-14 16:19:53 +02:00
Bianca Nenciu d5e30592f3 FIX: Load categories from search response (#612)
When lazy load categories is enabled, the list of categories does not
have to fetched from the "site.json" endpoint because it is already
returned by "search.json".

This commit reverts commits 5056502 and 3e54697 because iterating over
all pages of categories is not really necessary.
2024-05-14 17:13:25 +03:00
Jarek Radosz fc73cce113 FIX: Invalid header button title (#621)
`[en.Start a PM with an AI bot]`
2024-05-14 15:30:03 +02:00
Sam cb23ae614f UX: Remove multi llm selector from header and move to composer (#619)
LLM selector control had no memory and was awkward to click.

Instead we now:

- Clearly display which llm you are talking to
- Allow you to change llm direct from composer
2024-05-14 17:54:54 +10:00
Sam 8eee6893d6 FEATURE: GPT4o support and better auditing (#618)
- Introduce new support for GPT4o (automation / bot / summary / helper)
- Properly account for token counts on OpenAI models
- Track feature that was used when generating AI completions
- Remove custom llm support for summarization as we need better interfaces to control registration and de-registration
2024-05-14 13:28:46 +10:00
Roman Rizzi 8b00c47087 FIX: dialects var was not defined in prod (#617) 2024-05-13 17:28:27 -03:00
Roman Rizzi e22194f321 HACK: Llama3 support for summarization/AI helper. (#616)
There are still some limitations to which models we can support with the `LlmModel` class. This will enable support for Llama3 while we sort those out.
2024-05-13 15:54:42 -03:00
Roman Rizzi 62fc7d6ed0 FEATURE: Configurable LLMs. (#606)
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
2024-05-13 12:46:42 -03:00
Rafael dos Santos Silva 5c02b885ea FEATURE: Llama 3 tokenizer (#615) 2024-05-13 12:45:52 -03:00
Bianca Nenciu 5861418e9d FIX: Load categories for AI search results (#614)
Categories should be preloaded when "lazy load categories" is enabled.
2024-05-13 16:47:37 +03:00
Sam 0069256efd FIX: improve function call parsing (#613)
- support " / ' wrapped values
- coerce integer to integer
- enforce enum at boundary
2024-05-13 19:40:11 +10:00
Joffrey JAFFEUX 64f41454ad FIX: uses getByIdentifier of d-menu (#611)
The menu service doesn’t implement an activeMenu property anymore as it can now support concurrent menus. The solution to this is to use `getByIdentifier`.
2024-05-10 12:01:02 +02:00
Sam 61890b667c FEATURE: search command now support searching in context of user (#610)
This optional feature allows search to be performed in the context
of the user that executed it.

By default we do not allow this behavior cause it means llm gets
access to potentially secure data.
2024-05-10 11:32:34 +10:00
Sam 514823daca FIX: streaming broken in bedrock when chunks are not aligned (#609)
Also

- Stop caching llm list - this cause llm list in persona to be incorrect
- Add more UI to debug screen so you can properly see raw response
2024-05-09 12:11:50 +10:00
Sam cf34838a09 FIX: context repairs for @mentioned bot (#608)
When the bot is @mentioned, we need to be a lot more careful
about constructing context otherwise bot gets ultra confused.

This changes multiple things:

1. We were omitting all thread first messages (fixed)
2. Include thread title (if available) in context
3. Construct context in a clearer way separating user request from data
2024-05-08 18:44:04 +10:00
Rafael dos Santos Silva ab4544d897 DEV: Mark hypothetical_post_from method public (#607) 2024-05-07 15:17:26 -03:00
Discourse Translator Bot 27827c0898 Update translations (#605) 2024-05-07 09:33:25 -04:00
Roman Rizzi 4f1a3effe0 REFACTOR: Migrate Vllm/TGI-served models to the OpenAI format. (#588)
Both endpoints provide OpenAI-compatible servers. The only difference is that Vllm doesn't support passing tools as a separate parameter. Even if the tool param is supported, it ultimately relies on the model's ability to handle native functions, which is not the case with the models we have today.

As a part of this change, we are dropping support for StableBeluga/Llama2 models. They don't have a chat_template, meaning the new API can translate them.

These changes let us remove some of our existing dialects and are a first step in our plan to support any LLM by defining them as data-driven concepts.

 I rewrote the "translate" method to use a template method and extracted the tool support strategies into its classes to simplify the code.

Finally, these changes bring support for Ollama when running in dev mode. It only works with Mistral for now, but it will change soon..
2024-05-07 10:02:16 -03:00
Joffrey JAFFEUX dacc1b9f28 DEV: updates spec following changes in chat (#604)
Also makes this spec to use the `update_message!` helper.
2024-05-07 15:01:06 +02:00
Sam ab78d9b597 REFACTOR: Simplify tool invocation by removing bot_user and llm parameters (#603)
* Well, it was quite a journey but now tools have "context" which
can be critical for the stuff they generate

This entire change was so Dall E and Artist generate images in the correct context

* FIX: improve error handling around image generation

- also corrects image markdown and clarifies code

* fix spec
2024-05-07 21:55:46 +10:00
Sam 88c7427fab FEATURE: allow @mentioning an ai bot in a channel (#602)
if a persona is mentionable and allows chat allow it to be mentioned in a chat channel
2024-05-07 10:30:39 +10:00
Sam 37a2db5223 FIX: uploader not removing app event (#601)
FIX: restricted to mentionabled vs allowed_chat by mistake
2024-05-06 14:42:55 +10:00
Sam e4b326c711 FEATURE: support Chat with AI Persona via a DM (#488)
Add support for chat with AI personas

- Allow enabling chat for AI personas that have an associated user
- Add new setting `allow_chat` to AI persona to enable/disable chat
- When a message is created in a DM channel with an allowed AI persona user, schedule a reply job
- AI replies to chat messages using the persona's `max_context_posts` setting to determine context
- Store tool calls and custom prompts used to generate a chat reply on the `ChatMessageCustomPrompt` table
- Add tests for AI chat replies with tools and context

At the moment unlike posts we do not carry tool calls in the context.

No @mention support yet for ai personas in channels, this is future work
2024-05-06 09:49:02 +10:00
Keegan George 8875830f6a FEATURE: Insert footnote from explained result (#591) 2024-05-03 11:53:17 -07:00
483 changed files with 432704 additions and 101838 deletions
+5 -6
View File
@@ -1,7 +1,6 @@
< 3.3.0.beta1-dev: a65c4076a63104b61d213534db7f89aa3c8acd9e
< 3.2.0.beta5-dev: c70f43f130fb98e4a1adb1afd9b621aaa49088e3
< 3.2.0.beta4-dev: 493b48477a9288f2f1b6f021954088705a6673d6
< 3.2.0.beta2-dev: 3bced1c6f525553fe2b5b79d29bd66c1cf2a47d5
< 3.4.0.beta1-dev: 9d887ad4ace8e33c3fe7dbb39237e882c08b4f0b
< 3.3.0.beta5-dev: 4d8090002f6dcd8e34d41033606bf131fa221475
< 3.3.0.beta2-dev: 61890b667c06299841ae88946f84a112f00060e1
< 3.3.0.beta1-dev: d94234d89f50733678a5c62809445198a9fda74b
3.2.999: d94234d89f50733678a5c62809445198a9fda74b
3.1.999: c0415bb7eb878b1b7abf112d65cba981030df8df
< 3.2.0.beta1-dev: 3e7c99de894b03e9161d9481da118fe893991a1d
3.1.0.beta3: 057fbe1ce6
+36 -25
View File
@@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (7.1.3.2)
activesupport (7.1.3.4)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
@@ -13,26 +13,28 @@ GEM
tzinfo (~> 2.0)
ast (2.4.2)
base64 (0.2.0)
bigdecimal (3.1.6)
concurrent-ruby (1.2.3)
bigdecimal (3.1.8)
concurrent-ruby (1.3.3)
connection_pool (2.4.1)
drb (2.2.1)
i18n (1.14.1)
i18n (1.14.5)
concurrent-ruby (~> 1.0)
json (2.7.1)
json (2.7.2)
language_server-protocol (3.17.0.3)
minitest (5.22.2)
minitest (5.24.1)
mutex_m (0.2.0)
parallel (1.24.0)
parser (3.3.0.5)
parallel (1.25.1)
parser (3.3.3.0)
ast (~> 2.4.1)
racc
prettier_print (1.2.1)
racc (1.7.3)
racc (1.8.0)
rack (3.1.6)
rainbow (3.1.1)
regexp_parser (2.9.0)
rexml (3.2.6)
rubocop (1.61.0)
regexp_parser (2.9.2)
rexml (3.3.3)
strscan
rubocop (1.64.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
@@ -40,26 +42,35 @@ GEM
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.30.0, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.31.1)
parser (>= 3.3.0.4)
rubocop-capybara (2.20.0)
rubocop-ast (1.31.3)
parser (>= 3.3.1.0)
rubocop-capybara (2.21.0)
rubocop (~> 1.41)
rubocop-discourse (3.7.1)
rubocop-discourse (3.8.1)
activesupport (>= 6.1)
rubocop (>= 1.59.0)
rubocop-capybara (>= 2.0.0)
rubocop-factory_bot (>= 2.0.0)
rubocop-rspec (>= 2.25.0)
rubocop-factory_bot (2.25.1)
rubocop (~> 1.41)
rubocop-rspec (2.27.1)
rubocop (~> 1.40)
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
rubocop-rails (>= 2.25.0)
rubocop-rspec (>= 3.0.1)
rubocop-rspec_rails (>= 2.30.0)
rubocop-factory_bot (2.26.1)
rubocop (~> 1.61)
rubocop-rails (2.25.1)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rspec (3.0.2)
rubocop (~> 1.61)
rubocop-rspec_rails (2.30.0)
rubocop (~> 1.61)
rubocop-rspec (~> 3, >= 3.0.1)
ruby-progressbar (1.13.0)
strscan (3.1.0)
syntax_tree (6.2.0)
prettier_print (>= 1.2.0)
tzinfo (2.0.6)
@@ -74,4 +85,4 @@ DEPENDENCIES
syntax_tree
BUNDLED WITH
2.5.4
2.5.10
@@ -0,0 +1,17 @@
import DiscourseRoute from "discourse/routes/discourse";
export default DiscourseRoute.extend({
async model() {
const record = this.store.createRecord("ai-llm");
record.provider_params = {};
return record;
},
setupController(controller, model) {
this._super(controller, model);
controller.set(
"allLlms",
this.modelFor("adminPlugins.show.discourse-ai-llms")
);
},
});
@@ -0,0 +1,19 @@
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);
const record = allLlms.findBy("id", id);
record.provider_params = record.provider_params || {};
return record;
},
setupController(controller, model) {
this._super(controller, model);
controller.set(
"allLlms",
this.modelFor("adminPlugins.show.discourse-ai-llms")
);
},
});
@@ -0,0 +1,7 @@
import DiscourseRoute from "discourse/routes/discourse";
export default class DiscourseAiAiLlmsRoute extends DiscourseRoute {
model() {
return this.store.findAll("ai-llm");
}
}
@@ -0,0 +1,15 @@
import DiscourseRoute from "discourse/routes/discourse";
export default class DiscourseAiToolsNewRoute extends DiscourseRoute {
async model() {
return this.store.createRecord("ai-tool");
}
setupController(controller) {
super.setupController(...arguments);
const toolsModel = this.modelFor("adminPlugins.show.discourse-ai-tools");
controller.set("allTools", toolsModel);
controller.set("presets", toolsModel.resultSetMeta.presets);
}
}
@@ -0,0 +1,18 @@
import DiscourseRoute from "discourse/routes/discourse";
export default class DiscourseAiToolsShowRoute extends DiscourseRoute {
async model(params) {
const allTools = this.modelFor("adminPlugins.show.discourse-ai-tools");
const id = parseInt(params.id, 10);
return allTools.find((tool) => tool.id === id);
}
setupController(controller) {
super.setupController(...arguments);
const toolsModel = this.modelFor("adminPlugins.show.discourse-ai-tools");
controller.set("allTools", toolsModel);
controller.set("presets", toolsModel.resultSetMeta.presets);
}
}
@@ -0,0 +1,10 @@
import { service } from "@ember/service";
import DiscourseRoute from "discourse/routes/discourse";
export default class DiscourseAiToolsRoute extends DiscourseRoute {
@service store;
model() {
return this.store.findAll("ai-tool");
}
}
@@ -0,0 +1 @@
<AiLlmsListEditor @llms={{this.model}} />
@@ -0,0 +1 @@
<AiLlmsListEditor @llms={{this.allLlms}} @currentLlm={{this.model}} />
@@ -0,0 +1 @@
<AiLlmsListEditor @llms={{this.allLlms}} @currentLlm={{this.model}} />
@@ -0,0 +1 @@
<AiToolListEditor @tools={{this.model}} />
@@ -0,0 +1,5 @@
<AiToolEditor
@tools={{this.allTools}}
@model={{this.model}}
@presets={{this.presets}}
/>
@@ -0,0 +1,5 @@
<AiToolEditor
@tools={{this.allTools}}
@model={{this.model}}
@presets={{this.presets}}
/>
@@ -0,0 +1,148 @@
# frozen_string_literal: true
module DiscourseAi
module Admin
class AiLlmsController < ::Admin::AdminController
requires_plugin ::DiscourseAi::PLUGIN_NAME
def index
llms = LlmModel.all.order(:display_name)
render json: {
ai_llms:
ActiveModel::ArraySerializer.new(
llms,
each_serializer: LlmModelSerializer,
root: false,
).as_json,
meta: {
provider_params: LlmModel.provider_params,
presets: DiscourseAi::Completions::Llm.presets,
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
llm_model = LlmModel.new(ai_llm_params)
if llm_model.save
llm_model.toggle_companion_user
render json: { ai_persona: LlmModelSerializer.new(llm_model) }, status: :created
else
render_json_error llm_model
end
end
def update
llm_model = LlmModel.find(params[:id])
if llm_model.seeded?
return render_json_error(I18n.t("discourse_ai.llm.cannot_edit_builtin"), status: 403)
end
if llm_model.update(ai_llm_params(updating: llm_model))
llm_model.toggle_companion_user
render json: LlmModelSerializer.new(llm_model)
else
render_json_error llm_model
end
end
def destroy
llm_model = LlmModel.find(params[:id])
if llm_model.seeded?
return render_json_error(I18n.t("discourse_ai.llm.cannot_delete_builtin"), status: 403)
end
in_use_by = DiscourseAi::Configuration::LlmValidator.new.modules_using(llm_model)
if !in_use_by.empty?
return(
render_json_error(
I18n.t(
"discourse_ai.llm.delete_failed",
settings: in_use_by.join(", "),
count: in_use_by.length,
),
status: 409,
)
)
end
# Clean up companion users
llm_model.enabled_chat_bot = false
llm_model.toggle_companion_user
if llm_model.destroy
head :no_content
else
render_json_error llm_model
end
end
def test
RateLimiter.new(current_user, "llm_test_#{current_user.id}", 3, 1.minute).performed!
llm_model = LlmModel.new(ai_llm_params)
DiscourseAi::Configuration::LlmValidator.new.run_test(llm_model)
render json: { success: true }
rescue DiscourseAi::Completions::Endpoints::Base::CompletionFailed => e
render json: { success: false, error: e.message }
end
private
def ai_llm_params(updating: nil)
return {} if params[:ai_llm].blank?
permitted =
params.require(:ai_llm).permit(
:display_name,
:name,
:provider,
:tokenizer,
:max_prompt_tokens,
:api_key,
:enabled_chat_bot,
:vision_enabled,
)
provider = updating ? updating.provider : permitted[:provider]
permit_url = provider != LlmModel::BEDROCK_PROVIDER_NAME
new_url = params.dig(:ai_llm, :url)
permitted[:url] = new_url if permit_url && new_url
extra_field_names = LlmModel.provider_params.dig(provider&.to_sym)
if extra_field_names.present?
received_prov_params =
params.dig(:ai_llm, :provider_params)&.slice(*extra_field_names.keys)
if received_prov_params.present?
received_prov_params.each do |pname, value|
if extra_field_names[pname.to_sym] == :checkbox
received_prov_params[pname] = ActiveModel::Type::Boolean.new.cast(value)
end
end
permitted[:provider_params] = received_prov_params.permit!
end
end
permitted
end
end
end
end
@@ -19,11 +19,19 @@ module DiscourseAi
DiscourseAi::AiBot::Personas::Persona.all_available_tools.map do |tool|
AiToolSerializer.new(tool, root: false)
end
AiTool
.where(enabled: true)
.each do |tool|
tools << {
id: "custom-#{tool.id}",
name: I18n.t("discourse_ai.tools.custom_name", name: tool.name.capitalize),
}
end
llms =
DiscourseAi::Configuration::LlmEnumerator.values.map do |hash|
{ id: hash[:value], name: hash[:name] }
end
render json: { ai_personas: ai_personas, meta: { commands: tools, llms: llms } }
render json: { ai_personas: ai_personas, meta: { tools: tools, llms: llms } }
end
def show
@@ -35,7 +43,10 @@ module DiscourseAi
if ai_persona.save
RagDocumentFragment.link_persona_and_uploads(ai_persona, attached_upload_ids)
render json: { ai_persona: ai_persona }, status: :created
render json: {
ai_persona: LocalizedAiPersonaSerializer.new(ai_persona, root: false),
},
status: :created
else
render_json_error ai_persona
end
@@ -50,7 +61,7 @@ module DiscourseAi
if @ai_persona.update(ai_persona_params.except(:rag_uploads))
RagDocumentFragment.update_persona_uploads(@ai_persona, attached_upload_ids)
render json: @ai_persona
render json: LocalizedAiPersonaSerializer.new(@ai_persona, root: false)
else
render_json_error @ai_persona
end
@@ -125,28 +136,30 @@ module DiscourseAi
:rag_chunk_overlap_tokens,
:rag_conversation_chunks,
:question_consolidator_llm,
:allow_chat,
:tool_details,
allowed_group_ids: [],
rag_uploads: [:id],
)
if commands = params.dig(:ai_persona, :commands)
permitted[:commands] = permit_commands(commands)
if tools = params.dig(:ai_persona, :tools)
permitted[:tools] = permit_tools(tools)
end
permitted
end
def permit_commands(commands)
return [] if !commands.is_a?(Array)
def permit_tools(tools)
return [] if !tools.is_a?(Array)
commands.filter_map do |command, options|
break nil if !command.is_a?(String)
tools.filter_map do |tool, options|
break nil if !tool.is_a?(String)
options&.permit! if options && options.is_a?(ActionController::Parameters)
if options
[command, options]
[tool, options]
else
command
tool
end
end
end
@@ -0,0 +1,89 @@
# frozen_string_literal: true
module DiscourseAi
module Admin
class AiToolsController < ::Admin::AdminController
requires_plugin ::DiscourseAi::PLUGIN_NAME
before_action :find_ai_tool, only: %i[show update destroy]
def index
ai_tools = AiTool.all
render_serialized({ ai_tools: ai_tools }, AiCustomToolListSerializer, root: false)
end
def show
render_serialized(@ai_tool, AiCustomToolSerializer)
end
def create
ai_tool = AiTool.new(ai_tool_params)
ai_tool.created_by_id = current_user.id
if ai_tool.save
render_serialized(ai_tool, AiCustomToolSerializer, status: :created)
else
render_json_error ai_tool
end
end
def update
if @ai_tool.update(ai_tool_params)
render_serialized(@ai_tool, AiCustomToolSerializer)
else
render_json_error @ai_tool
end
end
def destroy
if @ai_tool.destroy
head :no_content
else
render_json_error @ai_tool
end
end
def test
if params[:id].present?
ai_tool = AiTool.find(params[:id])
else
ai_tool = AiTool.new(ai_tool_params)
end
parameters = params[:parameters].to_unsafe_h
# we need an llm so we have a tokenizer
# but will do without if none is available
llm = LlmModel.first&.to_llm
runner = ai_tool.runner(parameters, llm: llm, bot_user: current_user, context: {})
result = runner.invoke
if result.is_a?(Hash) && result[:error]
render_json_error result[:error]
else
render json: { output: result }
end
rescue ActiveRecord::RecordNotFound => e
render_json_error e.message, status: 400
rescue => e
render_json_error "Error executing the tool: #{e.message}", status: 400
end
private
def find_ai_tool
@ai_tool = AiTool.find(params[:id])
end
def ai_tool_params
params.require(:ai_tool).permit(
:name,
:description,
:script,
:summary,
parameters: [:name, :type, :description, :required, enum: []],
)
end
end
end
end
@@ -18,7 +18,7 @@ module DiscourseAi
debug_info = AiApiAuditLog.where(post: posts).order(created_at: :desc).first
render json: debug_info, status: 200
render json: AiApiAuditLogSerializer.new(debug_info, root: false), status: 200
end
def stop_streaming_response
@@ -31,12 +31,10 @@ module DiscourseAi
end
def show_bot_username
bot_user_id = DiscourseAi::AiBot::EntryPoint.map_bot_model_to_user_id(params[:username])
raise Discourse::InvalidParameters.new(:username) if !bot_user_id
bot_user = DiscourseAi::AiBot::EntryPoint.find_user_from_model(params[:username])
raise Discourse::InvalidParameters.new(:username) if !bot_user
bot_username_lower = User.find(bot_user_id).username_lower
render json: { bot_username: bot_username_lower }, status: 200
render json: { bot_username: bot_user.username_lower }, status: 200
end
end
end
@@ -7,7 +7,7 @@ module DiscourseAi
requires_login only: %i[create update destroy]
before_action :require_site_settings!
skip_before_action :preload_json, :check_xhr, :redirect_to_login_if_required, only: %i[show]
skip_before_action :preload_json, :check_xhr, only: %i[show]
def create
ensure_allowed_create!
@@ -19,7 +19,8 @@ module DiscourseAi
if shared_conversation.persisted?
render json: success_json.merge(share_key: shared_conversation.share_key)
else
render json: failed_json.merge(error: I18n.t("discourse_ai.share_ai.failed_to_share")),
render json:
failed_json.merge(error: I18n.t("discourse_ai.share_ai.errors.failed_to_share")),
status: :unprocessable_entity
end
end
@@ -30,7 +31,9 @@ module DiscourseAi
SharedAiConversation.destroy_conversation(@shared_conversation)
render json:
success_json.merge(message: I18n.t("discourse_ai.share_ai.conversation_deleted"))
success_json.merge(
message: I18n.t("discourse_ai.share_ai.errors.conversation_deleted"),
)
end
def show
@@ -12,6 +12,7 @@ module DiscourseAi
def suggest
input = get_text_param!
force_default_locale = params[:force_default_locale] || false
prompt = CompletionPrompt.find_by(id: params[:mode])
@@ -31,6 +32,7 @@ module DiscourseAi
prompt,
input,
current_user,
force_default_locale,
),
status: 200
end
@@ -87,18 +89,26 @@ module DiscourseAi
end
end
def explain
def stream_suggestion
post_id = get_post_param!
term_to_explain = get_text_param!
text = get_text_param!
post = Post.includes(:topic).find_by(id: post_id)
prompt = CompletionPrompt.find_by(id: params[:mode])
raise Discourse::InvalidParameters.new(:mode) if !prompt || !prompt.enabled?
raise Discourse::InvalidParameters.new(:post_id) unless post
if prompt.id == CompletionPrompt::CUSTOM_PROMPT
raise Discourse::InvalidParameters.new(:custom_prompt) if params[:custom_prompt].blank?
end
Jobs.enqueue(
:stream_post_helper,
post_id: post.id,
user_id: current_user.id,
term_to_explain: term_to_explain,
text: text,
prompt: prompt.name,
custom_prompt: params[:custom_prompt],
)
render json: { success: true }, status: 200
@@ -109,18 +119,26 @@ module DiscourseAi
def caption_image
image_url = params[:image_url]
image_url_type = params[:image_url_type]
raise Discourse::InvalidParameters.new(:image_url) if !image_url
raise Discourse::InvalidParameters.new(:image_url) if !image_url_type
if image_url_type == "short_path"
image = Upload.find_by(sha1: Upload.sha1_from_short_path(image_url))
elsif image_url_type == "short_url"
image = Upload.find_by(sha1: Upload.sha1_from_short_url(image_url))
else
image = upload_from_full_url(image_url)
end
image = upload_from_full_url(image_url)
raise Discourse::NotFound if image.blank?
final_image_url = get_caption_url(image, image_url)
check_secure_upload_permission(image) if image.secure?
user = current_user
hijack do
caption =
DiscourseAi::AiHelper::Assistant.new.generate_image_caption(
final_image_url,
current_user,
)
caption = DiscourseAi::AiHelper::Assistant.new.generate_image_caption(image, user)
render json: {
caption:
"#{caption} (#{I18n.t("discourse_ai.ai_helper.image_caption.attribution")})",
@@ -147,18 +165,13 @@ module DiscourseAi
end
def ensure_can_request_suggestions
if !current_user.in_any_groups?(SiteSetting.ai_helper_allowed_groups_map)
raise Discourse::InvalidAccess
end
end
allowed_groups =
(
SiteSetting.composer_ai_helper_allowed_groups_map |
SiteSetting.post_ai_helper_allowed_groups_map
)
def get_caption_url(image_upload, image_url)
if image_upload.secure?
check_secure_upload_permission(image_upload)
return Discourse.store.url_for(image_upload)
end
UrlHelper.absolute(image_url)
raise Discourse::InvalidAccess if !current_user.in_any_groups?(allowed_groups)
end
end
end
@@ -9,6 +9,7 @@ module DiscourseAi
def search
query = params[:q].to_s
skip_hyde = params[:hyde].downcase.to_s == "false" || params[:hyde].to_s == "0"
if query.length < SiteSetting.min_search_term_length
raise Discourse::InvalidParameters.new(:q)
@@ -20,6 +21,7 @@ module DiscourseAi
term: query,
search_context: guardian,
use_pg_headlines_for_excerpt: false,
can_lazy_load_categories: guardian.can_lazy_load_categories?,
)
semantic_search = DiscourseAi::Embeddings::SemanticSearch.new(guardian)
@@ -30,7 +32,7 @@ module DiscourseAi
hijack do
semantic_search
.search_for_topics(query)
.search_for_topics(query, _page = 1, hyde: !skip_hyde)
.each { |topic_post| grouped_results.add(topic_post) }
render_serialized(grouped_results, GroupedSearchResultSerializer, result: grouped_results)
@@ -38,6 +40,9 @@ module DiscourseAi
end
def quick_search
# this search function searches posts (vs: topics)
# it requires post embeddings and a reranker
# it will not perform a hyde expantion
query = params[:q].to_s
if query.length < SiteSetting.min_search_term_length
@@ -0,0 +1,48 @@
# frozen_string_literal: true
module DiscourseAi
module Summarization
class ChatSummaryController < ::Chat::ApiController
requires_plugin ::DiscourseAi::PLUGIN_NAME
requires_plugin ::Chat::PLUGIN_NAME
VALID_SINCE_VALUES = [1, 3, 6, 12, 24, 72, 168]
def show
since = params[:since].to_i
raise Discourse::InvalidParameters.new(:since) if !VALID_SINCE_VALUES.include?(since)
channel = ::Chat::Channel.find(params[:channel_id])
guardian.ensure_can_join_chat_channel!(channel)
strategy = DiscourseAi::Summarization.default_strategy
raise Discourse::NotFound.new unless strategy
guardian.ensure_can_request_summary!
RateLimiter.new(current_user, "channel_summary", 6, 5.minutes).performed!
hijack do
content = { content_title: channel.name }
content[:contents] = channel
.chat_messages
.where("chat_messages.created_at > ?", since.hours.ago)
.includes(:user)
.order(created_at: :asc)
.pluck(:id, :username_lower, :message)
.map { { id: _1, poster: _2, text: _3 } }
summarized_text =
if content[:contents].empty?
I18n.t("discourse_ai.summarization.chat.no_targets")
else
strategy.summarize(content, current_user).dig(:summary)
end
render json: { summary: summarized_text }
end
end
end
end
end
@@ -0,0 +1,49 @@
# frozen_string_literal: true
module DiscourseAi
module Summarization
class SummaryController < ::ApplicationController
requires_plugin ::DiscourseAi::PLUGIN_NAME
def show
topic = Topic.find(params[:topic_id])
guardian.ensure_can_see!(topic)
raise Discourse::NotFound if !guardian.can_see_summary?(topic)
RateLimiter.new(current_user, "summary", 6, 5.minutes).performed! if current_user
opts = params.permit(:skip_age_check)
skip_age_check = opts[:skip_age_check] == "true"
if params[:stream] && current_user
cached_summary = DiscourseAi::TopicSummarization.cached_summary(topic, current_user)
if cached_summary && !skip_age_check
render_serialized(cached_summary, AiTopicSummarySerializer)
return
end
Jobs.enqueue(
:stream_topic_ai_summary,
topic_id: topic.id,
user_id: current_user.id,
skip_age_check: skip_age_check,
)
render json: success_json
else
hijack do
summary =
DiscourseAi::TopicSummarization.summarize(
topic,
current_user,
skip_age_check: skip_age_check,
)
render_serialized(summary, AiTopicSummarySerializer)
end
end
end
end
end
end
@@ -8,7 +8,7 @@ module DiscourseAi
VERSION = "1"
def share_asset_url(short_path)
::UrlHelper.local_cdn_url("/plugins/discourse-ai/ai-share/#{short_path}?#{VERSION}")
::UrlHelper.absolute("/plugins/discourse-ai/ai-share/#{short_path}?#{VERSION}")
end
end
end
+28
View File
@@ -0,0 +1,28 @@
# frozen_string_literal: true
module ::Jobs
class CreateAiChatReply < ::Jobs::Base
sidekiq_options retry: false
def execute(args)
channel = ::Chat::Channel.find_by(id: args[:channel_id])
return if channel.blank?
message = ::Chat::Message.find_by(id: args[:message_id])
return if message.blank?
personaClass =
DiscourseAi::AiBot::Personas::Persona.find_by(id: args[:persona_id], user: message.user)
return if personaClass.blank?
user = User.find_by(id: personaClass.user_id)
bot = DiscourseAi::AiBot::Bot.as(user, persona: personaClass.new)
DiscourseAi::AiBot::Playground.new(bot).reply_to_chat_message(
message,
channel,
args[:context_post_ids],
)
end
end
end
+1 -1
View File
@@ -12,7 +12,7 @@ module Jobs
topic = target.is_a?(Topic) ? target : target.topic
post = target.is_a?(Post) ? target : target.first_post
return unless topic.present? && post.present?
return if topic.blank? || post.blank?
return if topic.private_message? && !SiteSetting.ai_embeddings_generate_for_pms
return if post.raw.blank?
+13 -5
View File
@@ -7,27 +7,35 @@ module Jobs
def execute(args)
return unless post = Post.includes(:topic).find_by(id: args[:post_id])
return unless user = User.find_by(id: args[:user_id])
return unless args[:term_to_explain]
return unless args[:text]
topic = post.topic
reply_to = post.reply_to_post
return unless user.guardian.can_see?(post)
prompt = CompletionPrompt.enabled_by_name("explain")
prompt = CompletionPrompt.enabled_by_name(args[:prompt])
input = <<~TEXT
<term>#{args[:term_to_explain]}</term>
if prompt.id == CompletionPrompt::CUSTOM_PROMPT
prompt.custom_instruction = args[:custom_prompt]
end
if prompt.name == "explain"
input = <<~TEXT
<term>#{args[:text]}</term>
<context>#{post.raw}</context>
<topic>#{topic.title}</topic>
#{reply_to ? "<replyTo>#{reply_to.raw}</replyTo>" : nil}
TEXT
else
input = args[:text]
end
DiscourseAi::AiHelper::Assistant.new.stream_prompt(
prompt,
input,
user,
"/discourse-ai/ai-helper/explain/#{post.id}",
"/discourse-ai/ai-helper/stream_suggestion/#{post.id}",
)
end
end
@@ -0,0 +1,50 @@
# frozen_string_literal: true
module Jobs
class StreamTopicAiSummary < ::Jobs::Base
sidekiq_options retry: false
def execute(args)
return unless topic = Topic.find_by(id: args[:topic_id])
return unless user = User.find_by(id: args[:user_id])
strategy = DiscourseAi::Summarization.default_strategy
return if strategy.nil? || !Guardian.new(user).can_see_summary?(topic)
guardian = Guardian.new(user)
return unless guardian.can_see?(topic)
skip_age_check = !!args[:skip_age_check]
streamed_summary = +""
start = Time.now
summary =
DiscourseAi::TopicSummarization
.new(strategy, topic, user)
.summarize(skip_age_check: skip_age_check) do |partial_summary|
streamed_summary << partial_summary
# Throttle updates.
if (Time.now - start > 0.5) || Rails.env.test?
payload = { done: false, ai_topic_summary: { summarized_text: streamed_summary } }
publish_update(topic, user, payload)
start = Time.now
end
end
publish_update(
topic,
user,
AiTopicSummarySerializer.new(summary, { scope: guardian }).as_json.merge(done: true),
)
end
private
def publish_update(topic, user, payload)
MessageBus.publish("/discourse-ai/summaries/topic/#{topic.id}", payload, user_ids: [user.id])
end
end
end
@@ -12,6 +12,13 @@ module ::Jobs
return unless post.topic.custom_fields[DiscourseAi::AiBot::EntryPoint::REQUIRE_TITLE_UPDATE]
DiscourseAi::AiBot::Playground.new(bot).title_playground(post)
publish_update(post.topic, { title: post.topic.title })
end
def publish_update(topic, payload)
allowed_users = topic.topic_allowed_users.pluck(:user_id)
MessageBus.publish("/discourse-ai/ai-bot/topic/#{topic.id}", payload, user_ids: allowed_users)
end
end
end
+5 -2
View File
@@ -11,6 +11,7 @@ class AiApiAuditLog < ActiveRecord::Base
Gemini = 4
Vllm = 5
Cohere = 6
Ollama = 7
end
end
@@ -21,12 +22,14 @@ end
# id :bigint not null, primary key
# provider_id :integer not null
# user_id :integer
# topic_id :integer
# post_id :integer
# request_tokens :integer
# response_tokens :integer
# raw_request_payload :string
# raw_response_payload :string
# created_at :datetime not null
# updated_at :datetime not null
# topic_id :integer
# post_id :integer
# feature_name :string(255)
# language_model :string(255)
#

Some files were not shown because too many files have changed in this diff Show More