Compare commits

...
Author SHA1 Message Date
Roman RizziandRafael dos Santos Silva d94234d89f DEV: Update libraries for Ruby 3.3 compat (#477) (#720)
Co-authored-by: Rafael dos Santos Silva <[email protected]>
2024-07-17 13:04:13 -03:00
Roman Rizzi a65c4076a6 SECURITY: Place a SSRF protection when calling services from the plugin. (#485)
The Faraday adapter and `FinalDestionation::HTTP` will protect us from admin-initiated SSRF attacks when interacting with the external services powering this plugin features.:
2024-03-07 12:21:03 +00:00
Sam 1f74a77e17 DEV: correct flaky spec (#475)
We were not properly expiring prompt cache
2024-02-19 15:21:55 +11:00
Sam 0fb87b00e2 FEATURE: new Discourse Helper persona (#473)
This persona searches Discourse Meta for help with Discourse and
points users at relevant posts.

It is somewhat similar to using "Forum Helper" on meta, with the
notable difference that we can not lean on semantic search so using
some prompt engineering we try to keep it simple.
2024-02-19 14:52:12 +11:00
Krzysztof Kotlarek dd6b073fc3 DEV: Make more group-based settings client: false (#474)
Affects the following settings:

ai_toxicity_groups_bypass
ai_helper_allowed_groups
ai_helper_custom_prompts_allowed_groups
post_ai_helper_allowed_groups

This turns off client: true for these group-based settings,
because there is no guarantee that the current user gets all
their group memberships serialized to the client. Better to check
server-side first.
2024-02-19 13:26:24 +11:00
Keegan George d66915ecc1 DEV: Make prompts available on CurrentUserSerializer (#472) 2024-02-16 10:57:14 -08:00
SamandMartin Brennan 3a8d95f6b2 FEATURE: mentionable personas and random picker tool, context limits (#466)
1. Personas are now optionally mentionable, meaning that you can mention them either from public topics or PMs
       - Mentioning from PMs helps "switch" persona mid conversation, meaning if you want to look up sites setting you can invoke the site setting bot, or if you want to generate an image you can invoke dall e
        - Mentioning outside of PMs allows you to inject a bot reply in a topic trivially
     - We also add the support for max_context_posts this allow you to limit the amount of context you feed in, which can help control costs

2. Add support for a "random picker" tool that can be used to pick random numbers 

3. Clean up routing ai_personas -> ai-personas

4. Add Max Context Posts so users can control how much history a persona can consume (this is important for mentionable personas) 

Co-authored-by: Martin Brennan <[email protected]>
2024-02-15 16:37:59 +11:00
Rafael dos Santos Silva 33164a0fec FIX: Cleanup AI search results when a subsequent search happens (#469) 2024-02-14 11:08:41 +11:00
Discourse Translator Bot 2092ffd141 Update translations (#471) 2024-02-13 16:11:39 +01:00
Rafael dos Santos Silva 59fbbb156b DEV: Make indexing less frequent when related topics is disabled (#468) 2024-02-09 16:08:54 -03:00
Rafael dos Santos Silva 0dba6623a0 FIX: Better AI chat thread titles (#467)
* FIX: Better AI chat thread titles

- Fix quote removal when multi-line

- Use XML tags for better LLM output parsing

- Use stop_sequences for faster and less wasteful LLM calls

- Adds truncation as the last line of defense
2024-02-09 14:49:28 -03:00
Rafael dos Santos Silva 8b1f542238 UX: Add missing settings descriptions (#465) 2024-02-08 12:18:05 -03:00
Rafael dos Santos Silva bccb7efdd6 FIX: Use a dedicated prompt for thread titles (#464) 2024-02-07 15:05:50 -03:00
Roman Rizzi 0ff5c0c2c4 FIX: Explicit check for empty string in compat migration (#463) 2024-02-07 14:51:51 -03:00
Discourse Translator Bot 9168c75eb6 Update translations (#462) 2024-02-06 22:35:35 +01:00
Roman Rizzi 8bd8280427 FIX: Hide related topics when module is disabled (#461) 2024-02-05 11:45:24 -03:00
Sam ba3c3951cf FIX: typo causing text_embedding_3_large to fail (#460) 2024-02-05 11:16:36 +11:00
Sam a3c827efcc FEATURE: allow personas to supply top_p and temperature params (#459)
* FEATURE: allow personas to supply top_p and temperature params

Code assistance generally are more focused at a lower temperature
This amends it so SQL Helper runs at 0.2 temperature vs the more
common default across LLMs of 1.0.

Reduced temperature leads to more focused, concise and predictable
answers for the SQL Helper

* fix tests

* This is not perfect, but far better than what we do today

Instead of fishing for

1. Draft sequence
2. Draft body

We skip (2), this means the composer "only" needs 1 http request to
open, we also want to eliminate (1) but it is a bit of a trickier
core change, may figure out how to pull it off (defer it to first draft save)

Value of bot drafts < value of opening bot conversations really fast
2024-02-03 07:09:34 +11:00
Keegan George 944fd6569c DEV: Add granular control for AI composer helper features (#458) 2024-02-01 14:58:04 -08:00
Roman Rizzi fba9c1bf2c UX: Re-introduce embedding settings validations (#457)
* Revert "Revert "UX: Validate embeddings settings (#455)" (#456)"

This reverts commit 392e2e8aef.

* Resstore previous default
2024-02-01 16:54:09 -03:00
Roman Rizzi 392e2e8aef Revert "UX: Validate embeddings settings (#455)" (#456)
This reverts commit 85fca89e01.
2024-02-01 14:06:51 -03:00
Roman Rizzi 85fca89e01 UX: Validate embeddings settings (#455) 2024-02-01 13:05:38 -03:00
Sam cec4251b00 DEV: improve error bedrock error messages (#454)
When bedrock rate limits it returns a 200 BUT also returns a JSON
document with the error.

Previously we had no special case here so we complained about nil

New code properly logs the problem
2024-02-01 08:01:07 -03:00
Rafael dos Santos Silva fd6fcfdb61 DEV: Increase embeddings backfill job frequency (#453)
The idea is to increase the frequency so we can run with smaller batch sizes.
Big batches cause problems when running backups, so it's better to have shorter but
more frequent jobs.
2024-01-31 15:09:39 -03:00
Sam dcafc8032f FIX: improve embedding generation (#452)
1. on failure we were queuing a job to generate embeddings, it had the wrong params. This is both fixed and covered in a test.
2. backfill embedding in the order of bumped_at, so newest content is embedded first, cover with a test
3. add a safeguard for hidden site setting that only allows batches of 50k in an embedding job run

Previously old embeddings were updated in a random order, this changes it so we update in a consistent order
2024-01-31 10:38:47 -03:00
Sam abcf5ea94a FEATURE: fine tune llm report to follow instructions more closely (#451)
- Allow users to supply top_p and temperature values, which means people can fine tune randomness
- Fix bad localization string
- Fix bad remapping of max tokens in gemini
- Add support for top_p as a general param to llms
- Amend system prompt so persona stops treating a user as an adversary
2024-01-31 09:58:25 +11:00
Rafael dos Santos Silva 9543ded3ee DEV: Make per post embeddings a hidden setting (#450) 2024-01-30 15:51:54 -03:00
Rafael dos Santos Silva b41c5cc31c FIX: Add table name to remove ambiguous column reference in SQL (#449) 2024-01-30 15:50:26 -03:00
Discourse Translator Bot 57d350c913 Update translations (#448) 2024-01-30 17:03:35 +01:00
Sam ab7e9e31aa FEATURE: allow excluding tags and categories from LLM report (#447)
Also

- Better diagnostics, output model being used
- Prompt LLM that true content is being injected in <context> tag
2024-01-30 15:55:05 +11:00
Roman Rizzi bae71eb047 FIX: Include provider in automation models (#446) 2024-01-29 18:07:29 -03:00
Roman Rizzi 0634b85a81 UX: Validations to LLM-backed features (except AI Bot) (#436)
* UX: Validations to Llm-backed features (except AI Bot)

This change is part of an ongoing effort to prevent enabling a broken feature due to lack of configuration. We also want to explicit which provider we are going to use. For example, Claude models are available through AWS Bedrock and Anthropic, but the configuration differs.

Validations are:

* You must choose a model before enabling the feature.
* You must turn off the feature before setting the model to blank.
* You must configure each model settings before being able to select it.

* Add provider name to summarization options

* vLLM can technically support same models as HF

* Check we can talk to the selected model

* Check for Bedrock instead of anthropic as a site could have both creds setup
2024-01-29 16:04:25 -03:00
Sam b2b01185f2 FEATURE: add support for new OpenAI embedding models (#445)
* FEATURE: add support for new OpenAI embedding models

This adds support for just released text_embedding_3_small and large

Note, we have not yet implemented truncation support which is a
new API feature. (triggered using dimensions)

* Tiny side fix, recalc bots when ai is enabled or disabled

* FIX: downsample to 2000 items per vector which is a pgvector limitation
2024-01-29 13:24:30 -03:00
Keegan George 4c4b418cff DEV: Not necessary to show modal with errors (#444) 2024-01-26 09:54:43 -08:00
Sam 092da860e2 FEATURE: support gpt-4-0125 which was just released (#443)
The new model has better performance and is always preferable to
the old one which has unicode issues during function calls.
2024-01-26 09:08:02 +11:00
Roman Rizzi b461ebc4ca FIX: typo in Automation::AVAILABLE_MODELS (#442) 2024-01-25 11:56:28 -03:00
Rafael dos Santos Silva fa6bc7f409 FIX: Automatic embeddings index could fail if it existed in the backup schema (#441) 2024-01-24 15:57:26 -03:00
Rafael dos Santos Silva 16d666fe69 FIX: Misconfigured OpenAI API for embeddings shouldn't spam logs (#440) 2024-01-24 15:57:18 -03:00
Rafael dos Santos Silva 04bc402aae FEATURE: Setting to control per post embeddings (#439)
* FEATURE: Setting to control per post embeddings
2024-01-23 22:09:27 -03:00
Discourse Translator Bot 797f5971b6 Update translations (#438) 2024-01-23 18:29:44 +01:00
Kris 900df4e8c8 UX: start progress dot animation instantly if it's the only content (#437) 2024-01-22 13:10:51 -05:00
Jarek Radosz 4b4aedb50f DEV: Use the new controller/period component for the dashboard (#435) 2024-01-19 13:27:33 +01:00
Jarek Radosz 5802cd1a0c DEV: Fix various typos (#434) 2024-01-19 12:51:26 +01:00
Rafael dos Santos Silva d4e23e0df6 FIX: Don't try to generate embeddings of posts in deleted topics (#433) 2024-01-18 16:10:25 -03:00
Dax74 f65314bdab FIX: typo (#432) 2024-01-18 16:38:29 +01:00
Rafael dos Santos Silva c70f43f130 FIX: Truncate content for sentiment/toxicity classification (#431) 2024-01-17 15:17:58 -03:00
Roman Rizzi 5bdf3dc1f4 DEV: Stop using shared_examples for endpoint specs (#430) 2024-01-17 15:08:49 -03:00
Gerhard Schlager 8eb1e851fc DEV: Spec didn't work correctly with translations (#429) 2024-01-16 16:28:24 +01:00
Discourse Translator Bot 14020e7095 Update translations (#428) 2024-01-16 14:54:42 +01:00
Sam 370074ef21 FIX: always ensure #generate gets a valid input (#427)
We were not validating input for generate leading to 2 tests not
failing correctly despite functionality being broken.

This ensures that input is validated,and in turn fixes the broken
specs
2024-01-16 15:21:58 +11:00
Sam 05d8b021f1 FIX: scrub invalid prompts when truncating (#426)
When you trim a prompt we never want to have a state where there
is a "tool" reply without a corresponding tool call, it makes no
sense

Also

- GPT-4-Turbo is 128k, fix that
- Claude was not preserving username in prompt
- We were throwing away unicode usernames instead of adding to
message
2024-01-16 13:48:00 +11:00
Roman Rizzi ff4da6ace8 FIX: Clean unicode usernames when adding messages through prompt's contrstuctor (#425) 2024-01-15 12:01:40 -03:00
Ted Johansson 37e6ac169e DEV: Update test setup to work with auto groups (#424)
We're updating core to change TL based access settings to be group based. This requires some updates of tests to work correctly. (The existing test setup gives false positives.)
2024-01-15 20:18:56 +08:00
Sam 825f01cfb2 FEATURE: even smoother streaming (#420)
Account properly for function calls, don't stream through <details> blocks
- Rush cooked content back to client
- Wait longer (up to 60 seconds) before giving up on streaming
- Clean up message bus channels so we don't have leftover data
- Make ai streamer much more reusable and much easier to read
- If buffer grows quickly, rush update so you are not artificially waiting
- Refine prompt interface
- Fix lost system message when prompt gets long
2024-01-15 18:51:14 +11:00
Jarek RadoszandKeegan George 6b8a57d957 DEV: Update linting (#423)
Co-authored-by: Keegan George <[email protected]>
2024-01-13 00:28:06 +01:00
Keegan George 1748ebcb8c DEV: Prevent HyDE search from being called multiple times (#422) 2024-01-12 11:48:07 -08:00
Roman RizziandSam Saffron 04eae76f68 REFACTOR: Represent generic prompts with an Object. (#416)
* REFACTOR: Represent generic prompts with an Object.

* Adds a bit more validation for clarity

* Rewrite bot title prompt and fix quirk handling

---------

Co-authored-by: Sam Saffron <[email protected]>
2024-01-12 14:36:44 -03:00
Rafael dos Santos Silva 705ef986b4 FIX: Set ivfflat.probes using topic count, not post count (#421)
Fixes a regression from 140359c which caused we to set this globally based on post count, rendering the cost of an index scan on the topics table too high and making the planner, correctly, not use the index anymore.

Hopefully https://github.com/pgvector/pgvector/issues/235 lands soon.
2024-01-12 11:20:23 -03:00
Rafael dos Santos Silva 3be76ebd7a FEATURE: Move the default embeddings model to bge-large-en (#417) 2024-01-11 14:16:25 -03:00
SamandMartin Brennan 8df966e9c5 FEATURE: smooth streaming of AI responses on the client (#413)
This PR introduces 3 things:

1. Fake bot that can be used on local so you can test LLMs, to enable on dev use:

SiteSetting.ai_bot_enabled_chat_bots = "fake"

2. More elegant smooth streaming of progress on LLM completion

This leans on JavaScript to buffer and trickle llm results through. It also amends it so the progress dot is much 
more consistently rendered

3. It fixes the Claude dialect 

Claude needs newlines **exactly** at the right spot, amended so it is happy 

---------

Co-authored-by: Martin Brennan <[email protected]>
2024-01-11 15:56:40 +11:00
Martin Brennan 37b957dbbb DEV: Fix SemanticRelated module load error (#419)
Followup 2636efcd1b,
whenever ruby code was changed locally this would break
module loading, giving an "uninitialized constant
DiscourseAi::Embeddings::EntryPoint::SemanticRelated" error.
2024-01-11 13:52:50 +10:00
Keegan George 5c9b570562 FIX: Revert AI action not working in Firefox (#418)
* FIX: Revert AI action not working in Firefox

* Make it pretty 💄
2024-01-11 11:43:39 +11:00
Rafael dos Santos Silva 8fcba12fae FEATURE: Support for SRV records for Discourse services (#414)
This allows admins to configure services with multiple backends using DNS SRV records. This PR also adds support for shared secret auth via headers for TEI and vLLM endpoints, so they are inline with the other ones.
2024-01-10 19:23:07 -03:00
Keegan George 9d8bbe32a9 FIX: AI Explain copy button not working (#415) 2024-01-10 10:41:48 -08:00
Keegan George 726cffc8af FIX: New illustrate post suggestions should be auto tracked (#412) 2024-01-10 09:04:10 -08:00
Roman Rizzi abde82c1f3 FIX: Use claude-2.1 to enable system prompts (#411) 2024-01-09 14:10:20 -03:00
Discourse Translator Bot 0f4e7723d7 Update translations (#410) 2024-01-09 15:09:46 +01:00
Sam 05f7808057 FEATURE: more elegant progress (#409)
Previous to this change it was very hard to tell if completion was
stuck or not.

This introduces a "dot" that follows the completion and starts
flashing after 5 seconds.
2024-01-09 09:20:28 -03:00
SamandRoman Rizzi b0a0cbe3ca FIX: improve bot behavior (#408)
* FIX: improve bot behavior

- Provide more information to Gemini context post function execution
- Use system prompts for Claude (fixes Dall E)
- Ensure Assistant is properly separated
- Teach Claude to return arrays in JSON vs XML

Also refactors tests so we do not copy tool preamble everywhere

* System msg is claude-2 only. fix typo

---------

Co-authored-by: Roman Rizzi <[email protected]>
2024-01-08 10:28:03 -03:00
Roman Rizzi 6124f910c1 FIX: Bring back Azure support. (#407)
We thought Azure's latest API version didn't have tool support yet, but I didn't understand it was complaining about a required field in the tool call message.
2024-01-05 17:08:10 -03:00
SamandRoman Rizzi 17cc09ec9c FIX: don't include <details> in context (#406)
* FIX: don't include <details> in context

We need to be careful adding <details> into context of conversations
it can cause LLMs to hallucinate results

* Fix Gemini multi-turn ctx flattening

---------

Co-authored-by: Roman Rizzi <[email protected]>
2024-01-05 15:21:14 -03:00
Keegan George 7201d482d5 FEATURE: Add DallE support to AI helper's illustrate post (#404) 2024-01-05 09:03:23 -08:00
Rafael dos Santos Silva 23b2809638 FEATURE: Generate proper embeddings for posts/topics with embedded content (#401) 2024-01-05 10:27:45 -03:00
Rafael dos Santos Silva 6fc1c9f7a6 FEATURE: Try to automatically handle larger embedding indexes (#403)
* FEATURE: Try to automatically handle larger embedding indexes

* linteeeeeeeer
2024-01-05 09:56:28 -03:00
Sam dd42a4e47b FIX: array arguments not parsed correctly (#405)
DALL E command accepts an Array as a tool argument, this was not
parsed correctly by the invoker leading to errors generating
images with DALL E

Side quest ... don't use update! it calls validations and will now
fail due to email validation
2024-01-05 14:39:32 +11:00
Roman Rizzi 971e03bdf2 FEATURE: AI Bot Gemini support. (#402)
It also corrects the syntax around tool support, which was wrong.

Gemini doesn't want us to include messages about previous tool invocations, so I had to shuffle around some code to send the response it generated from those invocations instead. For this, I created the "multi_turn" context, which bundles all the context involved in the interaction.
2024-01-04 18:15:34 -03:00
Roman Rizzi aa56baad37 FEATURE: Add Mixtral support for AI Bot (#396) 2024-01-04 12:22:43 -03:00
Roman Rizzi e6422c542e FIX: Tools::DbSchema's tables parameter is a string (#400) 2024-01-04 11:50:26 -03:00
Roman Rizzi f9d7d7f5f0 DEV: AI bot migration to the Llm pattern. (#343)
* DEV: AI bot migration to the Llm pattern.

We added tool and conversation context support to the Llm service in discourse-ai#366, meaning we met all the conditions to migrate this module.

This PR migrates to the new pattern, meaning adding a new bot now requires minimal effort as long as the service supports it. On top of this, we introduce the concept of a "Playground" to separate the PM-specific bits from the completion, allowing us to use the bot in other contexts like chat in the future. Commands are called tools, and we simplified all the placeholder logic to perform updates in a single place, making the flow more one-wayish.

* Followup fixes based on testing

* Cleanup unused inference code

* FIX: text-based tools could be in the middle of a sentence

* GPT-4-turbo support

* Use new LLM API
2024-01-04 10:44:07 -03:00
Sam 03fc94684b FIX: AI helper not working correctly with mixtral (#399)
* FIX: AI helper not working correctly with mixtral

This PR introduces a new function on the generic llm called #generate

This will replace the implementation of completion!

#generate introduces a new way to pass temperature, max_tokens and stop_sequences

Then LLM implementers need to implement #normalize_model_params to
ensure the generic names match the LLM specific endpoint

This also adds temperature and stop_sequences to completion_prompts
this allows for much more robust completion prompts

* port everything over to #generate

* Fix translation

- On anthropic this no longer throws random "This is your translation:"
- On mixtral this actually works

* fix markdown table generation as well
2024-01-04 09:53:47 -03:00
Keegan George 0483e0bb88 UX: Add proper attribution to illustrate post images (#398) 2024-01-03 13:01:19 -08:00
Jan Cernik d9c052f8e7 FIX: 500 error when reviewable has a missing message (#397) 2024-01-03 11:49:47 -03:00
Keegan George 1a5985134a FIX: Show illustrate post only if stability API key present (#395) 2024-01-02 11:24:16 -08:00
Discourse Translator Bot f8fdb6db76 Update translations (#394) 2024-01-02 15:26:08 +01:00
Roman Rizzi 4182af230a FIX: Correctly translate and read tools for Claude and Chat GPT. (#393)
I tested against the live models for the AI bot migration. It ensures Open AI's tool syntax is correct and we can correctly read the replies.
:
2024-01-02 11:21:13 -03:00
Rafael dos Santos Silva cec9bb8910 FIX: Skip embeddings for blank content (#392) 2023-12-29 14:59:08 -03:00
Rafael dos Santos Silva c778592da4 FIX: Corner cases on post embedding and crawler related (#391)
* FIX: Simplify markup for crawler related

* FIX: Handle title-less topics when truncating for embeddings

* fix
2023-12-29 14:05:02 -03:00
Rafael dos Santos Silva 140359c2ef FEATURE: Per post embeddings (#387) 2023-12-29 12:28:45 -03:00
chapoi c3af27571b UX: copy change (#390) 2023-12-29 16:26:03 +01:00
Sam c9b109f9d4 FIX: use async versions of clipboardCopy (#389)
Chrome and Firefox work with standard clipboardCopy, but since
we are making an ajax call Safari fails cause there is too much
delay detected.

To avoid this issue we trade in promises which are acceptable and
work in iOS.
2023-12-29 22:50:10 +11:00
Natalie Tay 73c58155f8 FIX: Get topic's category instead of array's (#388)
Currently we're seeing 500s when related_topics are getting rendered. We should get the topic's category rather than on the array.
```
ActionView::Template::Error (undefined method `category' for [#<Topic id ... ]
```
2023-12-29 16:52:48 +08:00
SamandAlan Guo Xiang Tan 933784a873 FEATURE: allow easy sharing of bot conversations (#385)
* FEATURE: allow easy sharing of bot conversations

* Lean on new core API i

* Added system spec for copy functionality


* Update assets/javascripts/initializers/ai-bot-replies.js

Co-authored-by: Alan Guo Xiang Tan <[email protected]>

* discourse later insted of setTimeout

* Update spec/system/ai_bot/share_spec.rb

Co-authored-by: Alan Guo Xiang Tan <[email protected]>

* feedback from review

just check the whole payload

* remove uneeded code

* fix spec

---------

Co-authored-by: Alan Guo Xiang Tan <[email protected]>
2023-12-29 19:47:47 +11:00
Rafael dos Santos Silva 2636efcd1b FEATURE: Render Related Topics for Crawlers (#386) 2023-12-28 15:32:03 -03:00
Rafael dos Santos Silva 1287ef4428 FEATURE: Support for Gemini Embeddings (#382) 2023-12-28 10:28:01 -03:00
Keegan George 5f088b12a7 FIX: Prevent no results label from showing when untrue (#384) 2023-12-27 16:42:13 -08:00
Rafael dos Santos Silva 76f7940b55 Revert "FEATURE: User sentiment on profile summary page (#329)" (#383)
This reverts commit 71c5077228.
2023-12-28 11:01:57 +11:00
Discourse Translator Bot 714a33a59d Update translations (#379) 2023-12-27 16:44:48 -03:00
Rafael dos Santos Silva 20cb15ab5f FEATURE: Mixtral for summarization (#381) 2023-12-26 17:50:02 -03:00
Rafael dos Santos Silva 3c27cbfb9a FIX: Use vLLM if TGI is not configured for OSS LLM inference (#380) 2023-12-26 17:18:08 -03:00
Rafael dos Santos SilvaandRoman Rizzi 5db7bf6e68 Mixtral (#376)
Add both Mistral and Mixtral support. Also includes vLLM-openAI inference support.

Co-authored-by: Roman Rizzi <[email protected]>
2023-12-26 14:49:55 -03:00
Keegan George cb325bb883 UX: Menu placement bottom for AI (#378) 2023-12-22 11:53:41 -08:00
Sam a5d240991f FEATURE: allow sending AI based report to a topic (#377)
This makes the reporting far more flexible cause it can target a
far wider audience by pointing it at a topic in a secure category
or an existing PM
2023-12-22 11:46:23 +11:00
Sam 37dd98c937 FIX: exclude non visible topics from report context (#375)
Generally non visible topics are not that interesting, do not add
this noise to the report context
2023-12-21 19:08:36 +11:00
Keegan George 8feb3ad4db FIX: Context menu should not appear if selected text is deleted (#374) 2023-12-20 10:50:17 -08:00
Keegan George ea116f91fc FIX: Undo issue (#372) 2023-12-20 10:46:22 -08:00
Sam af2e692761 FIX: under certain conditions we would get duplicate data from llm (#373)
Previously endpoint/base would `+=` decoded_chunk to leftover

This could lead to cases where the leftover buffer had duplicate
previously processed data

Fix ensures we properly skip previously decoded data.
2023-12-20 14:28:05 -03:00
Discourse Translator Bot 09238d33fa Update translations (#370) 2023-12-19 22:45:37 +01:00
Sam 8664771b7f FIX: triage no longer working with claude (#369) 2023-12-20 07:58:38 +11:00
Keegan George 5a84969c96 FIX: Illustrate post icon and translation not appearing correctly (#371) 2023-12-19 12:55:43 -08:00
Keegan George 7b4710d5c9 FEATURE: Generate post illustrations (#367) 2023-12-19 11:17:34 -08:00
Sam 529703b5ec FEATURE: support sending AI report to an email address (#368)
Support emailing the AI report to any arbitrary email
2023-12-19 17:51:49 +11:00
Sam d0f54443ae FEATURE: LLM based peroidical summary report (#357)
Introduce a Discourse Automation based periodical report. Depends on Discourse Automation.

Report works best with very large context language models such as GPT-4-Turbo and Claude 2.

- Introduces final_insts to generic llm format, for claude to work best it is better to guide the last assistant message (we should add this to other spots as well)
- Adds GPT-4 turbo support to generic llm interface
2023-12-19 12:04:15 +11:00
Roman Rizzi e0bf6adb5b DEV: Tool support for the LLM service. (#366)
This PR adds tool support to available LLMs. We'll buffer tool invocations and return them instead of making users of this service parse the response.

It also adds support for conversation context in the generic prompt. It includes bot messages, user messages, and tool invocations, which we'll trim to make sure it doesn't exceed the prompt limit, then translate them to the correct dialect.

Finally, It adds some buffering when reading chunks to handle cases when streaming is extremely slow.:M
2023-12-18 18:06:01 -03:00
Roman Rizzi 203906be65 FIX: Bedrock was complaining input was too long (#365) 2023-12-18 16:06:06 -03:00
Rafael dos Santos Silva 4d7ccdda2f FEATURE: DNS SRV support for TEI (#363) 2023-12-18 13:21:21 -03:00
Keegan George ba09582d7c UX: Hide suggest edit button in fast edit when triggering from AI (#364) 2023-12-15 15:19:04 -08:00
Keegan George 19a2949914 UX: Improve focus state of post options item by making full width (#362) 2023-12-15 12:11:29 -08:00
Keegan George d674e47ca4 FEATURE: AI suggestion buttons in move-to-topic modal (#360) 2023-12-15 12:11:14 -08:00
Rafael dos Santos Silva 83744bf192 FEATURE: Support for Gemini in AiHelper / Search / Summarization (#358) 2023-12-15 14:32:01 -03:00
Osama Sayegh 831559662e UX: Automatically detect text direction for AI generated text (#361) 2023-12-15 13:51:47 -03:00
Keegan George 408d9f68eb FEATURE: Proofread with post AI helper (#359) 2023-12-14 19:30:52 -08:00
Keegan George 74a7ac4a3d FEATURE: Add custom prompts to post helper options (#355)
* FEATURE: Add custom prompts to post helper options

* 💄Make pretty

* 💄Make pretty!
2023-12-14 13:47:20 -03:00
Krzysztof Kotlarek 6de9b9c274 DEV: Replace deprecated min_trust_to_create_post (#356)
In https://github.com/discourse/discourse/pull/24740, `min_trust_to_create_topic` site setting was replaced by `create_topic_allowed_groups`. This PR replaces the former, deprecated one, with the latter.
2023-12-14 14:07:28 +11:00
Keegan George 64587967c9 DEV: Cook streamed suggestion (#354) 2023-12-13 12:24:22 -08:00
Roman Rizzi 031c2a6b46 Revert "FIX: Recover from Bedrock returning invalid base64 payloads during streaming (#352)" (#353)
This reverts commit ef7d4cc509.
2023-12-12 17:22:44 -03:00
Roman Rizzi ef7d4cc509 FIX: Recover from Bedrock returning invalid base64 payloads during streaming (#352) 2023-12-12 17:06:53 -03:00
6aaf1f002e FEATURE: Add streaming to post AI helper's explain option (#344)
Co-authored-by: Rafael dos Santos Silva <[email protected]>
Co-authored-by: Roman Rizzi <[email protected]>
2023-12-12 09:28:39 -08:00
Discourse Translator Bot d4357c29c7 Update translations (#350) 2023-12-12 14:24:01 +01:00
Sam 605445831f FEATURE: try including views/username/likes in search results (#349)
This is somewhat experimental, but the context of likes/view/username
can help the llm find out what content is more important or even
common users that produce great content

This inflates the amount of tokens somewhat, but given it is all numbers
and search columns titles are only included once this is not severe
2023-12-12 12:22:28 +11:00
Roman Rizzi 2798e4c86d FIX: Custom instructions where missing when generating custom prompt input (#348) 2023-12-11 19:26:56 -03:00
Keegan George a89549919d FIX: Missing closing > in input tag (#347) 2023-12-11 12:09:17 -08:00
Sam a66b1042cc FEATURE: scale up result count for search depending on model (#346)
We were limiting to 20 results unconditionally cause we had to make
sure search always fit in an 8k context window.

Models such as GPT 3.5 Turbo (16k) and GPT 4 Turbo / Claude 2.1 (over 150k)
allow us to return a lot more results.

This means we have a much richer understanding cause context is far
larger.

This also allows a persona to tweak this number, in some cases admin
may want to be conservative and save on tokens by limiting results

This also tweaks the `limit` param which GPT-4 liked to set to tell
model only to use it when it needs to (and describes default behavior)
2023-12-11 16:54:16 +11:00
SamandMartin Brennan 3c9901d43a FEATURE: implement GPT-4 turbo support (#345)
Keep in mind:

- GPT-4 is only going to be fully released next year - so this hardcodes preview model for now
- Fixes streaming bugs which became a big problem with GPT-4 turbo
- Adds Azure endpoing for turbo as well

Co-authored-by: Martin Brennan <[email protected]>
2023-12-11 14:59:57 +11:00
Sam 6380ebd829 FEATURE: allow personas to provide command options (#331)
Personas now support providing options for commands.

This PR introduces a single option "base_query" for the SearchCommand. When supplied all searches the persona will perform will also include the pre-supplied filter.

This can allow personas to search a subset of the forum (such as documentation)

This system is extensible we can add options to any command trivially.
2023-12-08 08:42:56 +11:00
Rafael dos Santos Silva 381b0d74ca FIX: Handle truncation in HyDE search (#342) 2023-12-07 10:36:56 -03:00
Roman Rizzi 450ec915d8 FIX: Make FoldContent strategy more resilient when using models with low token count. (#341)
We'll recursively summarize  the content into smaller chunks until we are sure we can concatenate
them without going over the token limit.
2023-12-06 19:00:24 -03:00
Rafael dos Santos Silva c8352f21ce FIX: Fallback to whole LLM response when XML fail (#340) 2023-12-06 18:58:26 -03:00
Rafael dos Santos Silva 7ac1dbb6b6 FEATURE: Llama2 support in AiHelper (#339) 2023-12-06 17:47:53 -03:00
Rafael dos Santos Silva 252efdf142 FIX: Don't echo prompt back on HF/TGI (#338)
* FIX: Don't echo prompt back on HF/TGI

* teeeeests
2023-12-06 16:06:26 -03:00
Rafael dos Santos Silva d8267d8da0 FIX: Many fixes for huggingface and llama2 inference (#335) 2023-12-06 11:22:42 -03:00
Martin Brennan 24370a9ca6 Revert "FIX: Use Guardian.basic_user instead of new (anon) (#332)" (#337)
This reverts commit a3a1285dc5.

c.f. https://github.com/discourse/discourse/pull/24742
2023-12-06 16:26:43 +10:00
Mal Curtis 525ba801ff s/Azuer/Azure (#336) 2023-12-06 17:00:10 +11:00
Martin Brennan a3a1285dc5 FIX: Use Guardian.basic_user instead of new (anon) (#332)
c.f. de983796e1b66aa2ab039a4fb6e32cec8a65a098

There will soon be additional login_required checks
for Guardian, and the intent of many checks by automated
systems is better fulfilled by using BasicUser, which
simulates a logged in TL0 forum user, rather than an
anon user.
2023-12-06 12:01:41 +10:00
Jordan Vidrine 92286ad0c4 UX: Add title to button (#334) 2023-12-05 14:33:10 -06:00
Discourse Translator Bot 5f464c7b1b Update translations (#333) 2023-12-05 14:39:18 +01:00
Rafael dos Santos SilvaandKeegan George 71c5077228 FEATURE: User sentiment on profile summary page (#329)
* FEATURE: User sentiment on profile summary page

This introduces a new user stat in a user profile summary page.

It will show either neutral/positive/negative according to the dominant
sentiment in the user last interactions.

The user-stat widget is only rendered for staff.


Co-authored-by: Keegan George <[email protected]>
2023-12-04 18:17:43 -03:00
Sam c8cd38cdda FIX: command selector behavior stopped working (#330)
When moving ai-command-selector from gjs to js the underlying
behavior changed, this corrects it and adds a test case.
2023-12-02 14:20:26 +11:00
Roman Rizzi 3bc010b686 FIX: call the right method to summarize with truncation (#328) 2023-12-01 10:17:24 -03:00
Sam 7f36175ad3 FIX: allow selection of persona when list gets too long (#327) 2023-12-01 08:29:10 +11:00
Jarek Radosz f973fafc33 DEV: Update linting (#326) 2023-11-29 23:01:48 +01:00
Discourse Translator Bot 4ccb98fdcd Update translations (#320) 2023-11-29 10:41:32 +01:00
Sam a0b9fb9721 FIX: explicitly load embedding strategies (#325)
If not, sometimes during tests these constants may not be loaded
leading to flaky tests
2023-11-29 16:36:56 +11:00
Sam 6ddc17fd61 DEV: port directory structure to Zeitwerk (#319)
Previous to this change we relied on explicit loading for a files in Discourse AI.

This had a few downsides:

- Busywork whenever you add a file (an extra require relative)
- We were not keeping to conventions internally ... some places were OpenAI others are OpenAi
- Autoloader did not work which lead to lots of full application broken reloads when developing.

This moves all of DiscourseAI into a Zeitwerk compatible structure.

It also leaves some minimal amount of manual loading (automation - which is loading into an existing namespace that may or may not be there)

To avoid needing /lib/discourse_ai/... we mount a namespace thus we are able to keep /lib pointed at ::DiscourseAi

Various files were renamed to get around zeitwerk rules and minimize usage of custom inflections

Though we can get custom inflections to work it is not worth it, will require a Discourse core patch which means we create a hard dependency.
2023-11-29 15:17:46 +11:00
Kris 0b9947771c FIX: adjust tag composer input to avoid wrapping (#324) 2023-11-28 17:38:29 -05:00
Rafael dos Santos Silva fd0fb58eca FEATURE: HuggingFace Text Embeddings Inference compatibility (#323)
* FEATURE: HuggingFace Text Embeddings Inference compatibility

* lint
2023-11-28 17:05:26 -03:00
Roman Rizzi f26adf2cf6 FIX: Use XML tags in generate_titles prompt. (#322)
We must ensure we can isolate titles, and the models sometimes ignore the example we give them.

Additionally, anons can generate HyDE posts, so we need to check if user is nil when attempting to log requests.
2023-11-28 12:52:22 -03:00
Rafael dos Santos Silva 11e531b099 FEATURE: Backfill task for sentiment module (#316)
* FEATURE: Backfill task for sentiment module

* fix join clause
2023-11-28 12:28:36 -03:00
Roman Rizzi 2e7c5f047d DEV: Don't attempt to update log if completion request fails. (#321)
We already log the request failure when we raise the exception.
2023-11-28 11:15:12 -03:00
Keegan George 02f7b368a1 FIX: Too many requests from single search (#318) 2023-11-27 17:51:42 -08:00
Keegan George c7665e891b A11Y: Add title attribute to sparkles icon for AI search results (#317) 2023-11-27 14:25:33 -08:00
Keegan George d493880c0a FIX: Incorrect sort order label appearing when it should not (#315) 2023-11-27 17:27:22 -03:00
Roman Rizzi 775610b1c2 FIX: Chat titler was still using the old code after LLM migration (#314) 2023-11-27 13:03:24 -03:00
Roman Rizzi 54a8dd9556 REFACTOR: Use LLM abstraction in the AI Helper. (#312)
It also removes the need for multiple versions of our seeded prompts per model, further simplifying the code.
2023-11-27 09:33:31 -03:00
Sam 5a4598a7b4 FEATURE: Azure OpenAI support for DALL*E 3 (#313)
* FEATURE: Azure OpenAI support for DALL*E 3

Previous to this there was no way to add an inference endpoint for
DALL*E on Azure cause it requires custom URLs

Also:

- On save, when editing a persona it would revert priority and enabled
- More forgiving parsing in command framework for array function calls
- By default generate HD images - they tend to be a bit better
- Improve DALL*E prompt which was getting very annoying and always echoing what it is about to do
- Add a bit of a sleep between retries on image generation
- Fix error handling in image_command
2023-11-27 13:01:05 +11:00
SamandMartin Brennan dff9f33a97 FEATURE: DALL-E-3 persona for image generation (#311)
* FIX: no selected persona should pick first prioritized one

Previously we were looking at `.personaId` but there is only an
id attribute so it failed

* FEATURE: new DALL-E-3 persona

This persona generates images using DALL-E-3 API and is enabled
by default

Keep in mind that we are still waiting on seeds/gen_id so we can
not retain style consistently between turns.

This will change as soon as a new Open AI API provides the missing
parameters

Co-authored-by: Martin Brennan <[email protected]>
2023-11-24 18:08:08 +11:00
Sam 6282b6d21f FIX: implement tools framework for Anthropic (#307)
Previous to this changeset we used a custom system for tools/command
support for Anthropic.

We defined commands by using !command as a signal to execute it

Following Anthropic Claude 2.1, there is an official supported syntax (beta)
for tools execution.

eg:

```
+      <function_calls>
+      <invoke>
+      <tool_name>image</tool_name>
+      <parameters>
+      <prompts>
+      [
+      "an oil painting",
+      "a cute fluffy orange",
+      "3 apple's",
+      "a cat"
+      ]
+      </prompts>
+      </parameters>
+      </invoke>
+      </function_calls>
```

This implements the spec per Anthropic, it should be stable enough
to also work on other LLMs.

Keep in mind that OpenAI is not impacted here at all, as it has its
own custom system for function calls.

Additionally:

- Fixes the title system prompt so it works with latest Anthropic
- Uses new spec for "system" messages by Anthropic
- Tweak forum helper persona to guide Anthropic a tiny be better

Overall results are pretty awesome and Anthropic Claude performs
really well now on Discourse
2023-11-24 06:39:56 +11:00
Roman Rizzi 419c43592a FIX: Make summaries more cohesive by tweaking prompt. (#310)
Other changes:

- Don't use Bedrock for non claude models if credentials are set.
- Remove extra sentence from HyDE prompt.
2023-11-23 16:33:37 -03:00
Keegan George df8804afcd DEV: Only allow semantic search on "Relevance" sort mode (#306) 2023-11-23 11:30:17 -08:00
Roman Rizzi 02efca162e FIX: Bedrock uses slightly different model names
* Revert "FIX: We don't need to prepend anthropic. to bedrock models (#308)"

This reverts commit 8a01751991.

* FIX: Bedrock uses slightly different model names
2023-11-23 15:49:24 -03:00
Roman Rizzi 8a01751991 FIX: We don't need to prepend anthropic. to bedrock models (#308) 2023-11-23 14:39:21 -03:00
Roman Rizzi 3064d4c288 REFACTOR: Summarization and HyDE now use an LLM abstraction. (#297)
* DEV: One LLM abstraction to rule them all

* REFACTOR: HyDE search uses new LLM abstraction

* REFACTOR: Summarization uses the LLM abstraction

* Updated documentation and made small fixes. Remove Bedrock claude-2 restriction
2023-11-23 12:58:54 -03:00
Jarek Radosz 53b7f031ba DEV: Remove gem "aws-eventstream" entry (#305)
Core already includes aws-eventstream through aws-sdk-s3 and aws-sdk-sns.

Specifying a dependency in a plugin was blocking an upgrade in core (https://github.com/discourse/discourse/pull/24518)
2023-11-23 10:00:25 +01:00
Keegan George 787cd1bf17 FIX: Error 500 from search with only filters (#304) 2023-11-22 11:05:56 -08:00
Keegan George 432590f8d9 DEV: Run AI search before submit if query params present (#303) 2023-11-21 16:51:08 -08:00
Keegan George c55014839f FIX: more results not appearing on scroll (#302) 2023-11-21 09:46:37 -08:00
Roman Rizzi e0691e70e8 DEV: Updates to the summarization strategy API (#301)
Introduced by discourse/discourse#24489

In the future, this change will let us log who requested the summary in the `AiApiAuditLog`.:
2023-11-21 13:27:35 -03:00
Discourse Translator Bot 493b48477a Update translations (#300) 2023-11-21 14:36:22 +01:00
SamandMartin Brennan 98c89953d3 FEATURE: remember previously selected persona (#299)
People tend to keep to 1 persona when working with the bot,
this adds local browser memory for the last persona you interacted
with so you do not need to select it over and over again.

This is per browser, not per user memory.

Also... clean up tests so they do not need to require stubs which
were breaking the build

---------

Co-authored-by: Martin Brennan <[email protected]>
2023-11-21 17:02:27 +10:00
5b5edb22c6 FEATURE: UI to update ai personas on admin page (#290)
Introduces a UI to manage customizable personas (admin only feature)

Part of the change was some extensive internal refactoring:

- AIBot now has a persona set in the constructor, once set it never changes
- Command now takes in bot as a constructor param, so it has the correct persona and is not generating AIBot objects on the fly
- Added a .prettierignore file, due to the way ALE is configured in nvim it is a pre-req for prettier to work
- Adds a bunch of validations on the AIPersona model, system personas (artist/creative etc...) are all seeded. We now ensure
- name uniqueness, and only allow certain properties to be touched for system personas.
- (JS note) the client side design takes advantage of nested routes, the parent route for personas gets all the personas via this.store.findAll("ai-persona") then child routes simply reach into this model to find a particular persona.
- (JS note) data is sideloaded into the ai-persona model the meta property supplied from the controller, resultSetMeta
- This removes ai_bot_enabled_personas and ai_bot_enabled_chat_commands, both should be controlled from the UI on a per persona basis
- Fixes a long standing bug in token accounting ... we were doing to_json.length instead of to_json.to_s.length
- Amended it so {commands} are always inserted at the end unconditionally, no need to add it to the template of the system message as it just confuses things
- Adds a concept of required_commands to stock personas, these are commands that must be configured for this stock persona to show up.
- Refactored tests so we stop requiring inference_stubs, it was very confusing to need it, added to plugin.rb for now which at least is clearer
- Migrates the persona selector to gjs

---------

Co-authored-by: Joffrey JAFFEUX <[email protected]>
Co-authored-by: Martin Brennan <[email protected]>
2023-11-21 16:56:43 +11:00
Keegan George 491111e5c8 FIX: Un-clickable AI Results (#298) 2023-11-20 11:37:00 -08:00
Keegan George f7277d244e DEV: Mix semantic search results with normal results (#278) 2023-11-17 12:46:59 -08:00
Keegan George d1f21c78f1 UX: Add copy button to generated suggestion (#296) 2023-11-17 09:25:41 -08:00
David Taylor e1a7d07a91 DEV: Update tests for core change (#293)
https://github.com/discourse/discourse/pull/23967
2023-11-15 09:23:27 +00:00
Discourse Translator Bot c6902c40ce Update translations (#294) 2023-11-14 14:30:22 +01:00
Roman Rizzi 1588ab99a6 FIX: ChatChannel no longer inherits from RestModel. (#292)
Trying to hydrate using the store will fail. We need to use the model create function instead.
2023-11-13 14:45:20 -03:00
Roman Rizzi 244a338558 UX: Graph surprise next to joy in post_emotion chart (#291) 2023-11-10 14:41:50 -03:00
Sam a4f419f54f FEATURE: basic infrastructure for custom personas (#288)
- New AiPersona model which can store custom personas
- Persona are restricted via group security
- They can contain custom system messages
- They can support a list of commands optionally

To avoid expensive DB calls in the serializer a Multisite friendly Hash was introduced (which can be expired on transaction commit)
2023-11-10 11:39:49 +11:00
Roman Rizzi d0198c5c5b FIX: Changes to the sentiment reports. (#289)
This PR aims to clarify sentiment reports by replacing averages with a count of posts that have one of their values above a threshold (60), meaning we have some level of confidence they are, in fact, positive or negative.

Same thing happen with post emotions, with the difference that a post can have multiple values above it (30). Additionally, we dropped the "Neutral" axis.

We also reworded the tooltip next to each report title, and added an early return to signal we have no data available instead of displaying an empty chart.
2023-11-09 17:23:25 -03:00
Martin Brennan 0c83963a28 DEV: Fix plugin.rb wording (#287)
This was not a complete sentence before
2023-11-09 11:23:43 +10:00
Roman Rizzi 458e66aef9 FIX: Filter classification type using the correct column (#286) 2023-11-08 14:58:35 -03:00
Roman Rizzi 231cf91cc2 FIX: Don't divide by zero if there is no emotion data for TL group (#285) 2023-11-08 13:05:36 -03:00
Roman Rizzi b172ef11c4 FEATURE: Expose sentiment classifications via the admin dashboard. (#284)
This PR adds new reports for displaying information about post sentiments grouped by date and emotions group by TL.

Depends on discourse/discourse#24274
2023-11-08 10:50:37 -03:00
Martin BrennanandAlexDev 30821badf2 Update plugin.rb metadata (#279)
Co-authored-by: AlexDev <[email protected]>
2023-11-07 15:45:02 -08:00
Ty Correll 3df6cb1ec9 UX: prevent suggestion button from wrapping (#282)
* UX: prevent suggestion button from wrapping

This PR addresses the visual bug where the suggestion button wraps on any screen size.

* UX: edge case screen size

This addition fixes screen size issues at specific screen sizes without affecting larger screen sizes, no media query necessary.
2023-11-04 12:52:51 -05:00
509 changed files with 114993 additions and 8979 deletions
+2
View File
@@ -1,3 +1,5 @@
< 3.2.0.beta5-dev: c70f43f130fb98e4a1adb1afd9b621aaa49088e3
< 3.2.0.beta4-dev: 493b48477a9288f2f1b6f021954088705a6673d6
< 3.2.0.beta2-dev: 3bced1c6f525553fe2b5b79d29bd66c1cf2a47d5
3.1.999: c0415bb7eb878b1b7abf112d65cba981030df8df
< 3.2.0.beta1-dev: 3e7c99de894b03e9161d9481da118fe893991a1d
+21
View File
@@ -0,0 +1,21 @@
app/assets/stylesheets/vendor/
documentation/
package.json
config/locales/**/*.yml
!config/locales/**/*.en*.yml
script/import_scripts/**/*.yml
plugins/**/lib/javascripts/locale
public/
!/app/assets/javascripts/discourse/public
vendor/
app/assets/javascripts/discourse/tests/fixtures
spec/
node_modules/
dist/
tmp/
**/*.rb
**/*.html
**/*.json
**/*.md
+15 -20
View File
@@ -2,18 +2,18 @@ GEM
remote: https://rubygems.org/
specs:
ast (2.4.2)
json (2.6.3)
json (2.7.1)
language_server-protocol (3.17.0.3)
parallel (1.23.0)
parser (3.2.2.4)
parallel (1.24.0)
parser (3.3.0.3)
ast (~> 2.4.1)
racc
prettier_print (1.2.1)
racc (1.7.2)
racc (1.7.3)
rainbow (3.1.1)
regexp_parser (2.8.2)
regexp_parser (2.9.0)
rexml (3.2.6)
rubocop (1.57.2)
rubocop (1.59.0)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
@@ -21,19 +21,19 @@ GEM
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.28.1, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.30.0)
parser (>= 3.2.1.0)
rubocop-capybara (2.19.0)
rubocop-capybara (2.20.0)
rubocop (~> 1.41)
rubocop-discourse (3.4.0)
rubocop (>= 1.1.0)
rubocop-rspec (>= 2.0.0)
rubocop-factory_bot (2.24.0)
rubocop (~> 1.33)
rubocop-rspec (2.25.0)
rubocop-discourse (3.6.0)
rubocop (>= 1.59.0)
rubocop-rspec (>= 2.25.0)
rubocop-factory_bot (2.25.1)
rubocop (~> 1.41)
rubocop-rspec (2.26.1)
rubocop (~> 1.40)
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
@@ -43,16 +43,11 @@ GEM
unicode-display_width (2.5.0)
PLATFORMS
arm64-darwin-20
ruby
x86_64-darwin-18
x86_64-darwin-19
x86_64-darwin-20
x86_64-linux
DEPENDENCIES
rubocop-discourse
syntax_tree
BUNDLED WITH
2.4.21
2.5.4
@@ -0,0 +1,106 @@
# frozen_string_literal: true
module DiscourseAi
module Admin
class AiPersonasController < ::Admin::AdminController
before_action :find_ai_persona, only: %i[show update destroy create_user]
def index
ai_personas =
AiPersona.ordered.map do |persona|
# we use a special serializer here cause names and descriptions are
# localized for system personas
LocalizedAiPersonaSerializer.new(persona, root: false)
end
tools =
DiscourseAi::AiBot::Personas::Persona.all_available_tools.map do |tool|
AiToolSerializer.new(tool, root: false)
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 } }
end
def show
render json: LocalizedAiPersonaSerializer.new(@ai_persona)
end
def create
ai_persona = AiPersona.new(ai_persona_params)
if ai_persona.save
render json: { ai_persona: ai_persona }, status: :created
else
render_json_error ai_persona
end
end
def create_user
user = @ai_persona.create_user!
render json: BasicUserSerializer.new(user, root: "user")
end
def update
if @ai_persona.update(ai_persona_params)
render json: @ai_persona
else
render_json_error @ai_persona
end
end
def destroy
if @ai_persona.destroy
head :no_content
else
render_json_error @ai_persona
end
end
private
def find_ai_persona
@ai_persona = AiPersona.find(params[:id])
end
def ai_persona_params
permitted =
params.require(:ai_persona).permit(
:name,
:description,
:enabled,
:system_prompt,
:priority,
:top_p,
:temperature,
:default_llm,
:user_id,
:mentionable,
:max_context_posts,
allowed_group_ids: [],
)
if commands = params.dig(:ai_persona, :commands)
permitted[:commands] = permit_commands(commands)
end
permitted
end
def permit_commands(commands)
return [] if !commands.is_a?(Array)
commands.filter_map do |command, options|
break nil if !command.is_a?(String)
options&.permit! if options && options.is_a?(ActionController::Parameters)
if options
[command, options]
else
command
end
end
end
end
end
end
@@ -0,0 +1,12 @@
# frozen_string_literal: true
module DiscourseAi
module Admin
class DashboardController < ::Admin::StaffController
requires_plugin DiscourseAi::PLUGIN_NAME
def sentiment
end
end
end
end
@@ -8,60 +8,51 @@ module DiscourseAi
before_action :ensure_can_request_suggestions
before_action :rate_limiter_performed!, except: %i[prompts]
def prompts
render json:
ActiveModel::ArraySerializer.new(
DiscourseAi::AiHelper::LlmPrompt.new.available_prompts,
root: false,
),
status: 200
end
def suggest
input = get_text_param!
prompt = CompletionPrompt.find_by(id: params[:mode])
raise Discourse::InvalidParameters.new(:mode) if !prompt || !prompt.enabled?
if prompt.prompt_type == "custom_prompt" && params[:custom_prompt].blank?
raise Discourse::InvalidParameters.new(:custom_prompt)
if prompt.id == CompletionPrompt::CUSTOM_PROMPT
raise Discourse::InvalidParameters.new(:custom_prompt) if params[:custom_prompt].blank?
prompt.custom_instruction = params[:custom_prompt]
end
suggest_thumbnails(input) if prompt.id == CompletionPrompt::ILLUSTRATE_POST
hijack do
render json:
DiscourseAi::AiHelper::LlmPrompt.new.generate_and_send_prompt(prompt, params),
DiscourseAi::AiHelper::Assistant.new.generate_and_send_prompt(
prompt,
input,
current_user,
),
status: 200
end
rescue ::DiscourseAi::Inference::OpenAiCompletions::CompletionFailed,
::DiscourseAi::Inference::HuggingFaceTextGeneration::CompletionFailed,
::DiscourseAi::Inference::AnthropicCompletions::CompletionFailed => e
rescue DiscourseAi::Completions::Endpoints::Base::CompletionFailed
render_json_error I18n.t("discourse_ai.ai_helper.errors.completion_request_failed"),
status: 502
end
def suggest_title
input = get_text_param!
input_hash = { text: input }
llm_prompt =
DiscourseAi::AiHelper::LlmPrompt
.new
.available_prompts(name_filter: "generate_titles")
.first
prompt = CompletionPrompt.find_by(id: llm_prompt[:id])
raise Discourse::InvalidParameters.new(:mode) if !prompt || !prompt.enabled?
prompt = CompletionPrompt.enabled_by_name("generate_titles")
raise Discourse::InvalidParameters.new(:mode) if !prompt
hijack do
render json:
DiscourseAi::AiHelper::LlmPrompt.new.generate_and_send_prompt(
DiscourseAi::AiHelper::Assistant.new.generate_and_send_prompt(
prompt,
input_hash,
input,
current_user,
),
status: 200
end
rescue ::DiscourseAi::Inference::OpenAiCompletions::CompletionFailed,
::DiscourseAi::Inference::HuggingFaceTextGeneration::CompletionFailed,
::DiscourseAi::Inference::AnthropicCompletions::CompletionFailed => e
rescue DiscourseAi::Completions::Endpoints::Base::CompletionFailed
render_json_error I18n.t("discourse_ai.ai_helper.errors.completion_request_failed"),
status: 502
end
@@ -86,9 +77,7 @@ module DiscourseAi
status: 200
end
def suggest_thumbnails
input = get_text_param!
def suggest_thumbnails(input)
hijack do
thumbnails = DiscourseAi::AiHelper::Painter.new.commission_thumbnails(input, current_user)
@@ -98,21 +87,20 @@ module DiscourseAi
def explain
post_id = get_post_param!
text = get_text_param!
post = Post.find_by(id: post_id)
term_to_explain = get_text_param!
post = Post.includes(:topic).find_by(id: post_id)
raise Discourse::InvalidParameters.new(:post_id) unless post
render json:
DiscourseAi::AiHelper::TopicHelper.new(
{ text: text },
current_user,
post: post,
).explain,
status: 200
rescue ::DiscourseAi::Inference::OpenAiCompletions::CompletionFailed,
::DiscourseAi::Inference::HuggingFaceTextGeneration::CompletionFailed,
::DiscourseAi::Inference::AnthropicCompletions::CompletionFailed => e
Jobs.enqueue(
:stream_post_helper,
post_id: post.id,
user_id: current_user.id,
term_to_explain: term_to_explain,
)
render json: { success: true }, status: 200
rescue DiscourseAi::Completions::Endpoints::Base::CompletionFailed
render_json_error I18n.t("discourse_ai.ai_helper.errors.completion_request_failed"),
status: 502
end
+26
View File
@@ -0,0 +1,26 @@
# frozen_string_literal: true
module ::Jobs
class CreateAiReply < ::Jobs::Base
sidekiq_options retry: false
def execute(args)
return unless bot_user = User.find_by(id: args[:bot_user_id])
return unless post = Post.includes(:topic).find_by(id: args[:post_id])
persona_id = args[:persona_id]
begin
persona = DiscourseAi::AiBot::Personas::Persona.find_by(user: post.user, id: persona_id)
raise DiscourseAi::AiBot::Bot::BOT_NOT_FOUND if persona.nil?
bot = DiscourseAi::AiBot::Bot.as(bot_user, persona: persona.new)
DiscourseAi::AiBot::Playground.new(bot).reply_to(post)
rescue DiscourseAi::AiBot::Bot::BOT_NOT_FOUND
Rails.logger.warn(
"Bot not found for post #{post.id} - perhaps persona was deleted or bot was disabled",
)
end
end
end
end
@@ -11,7 +11,7 @@ module Jobs
return if post.uploads.none? { |u| FileHelper.is_supported_image?(u.url) }
DiscourseAi::PostClassificator.new(DiscourseAi::NSFW::NSFWClassification.new).classify!(post)
DiscourseAi::PostClassificator.new(DiscourseAi::Nsfw::Classification.new).classify!(post)
end
end
end
+26
View File
@@ -0,0 +1,26 @@
# frozen_string_literal: true
module Jobs
class GenerateEmbeddings < ::Jobs::Base
sidekiq_options queue: "low"
def execute(args)
return unless SiteSetting.ai_embeddings_enabled
return if args[:target_type].blank? || args[:target_id].blank?
target = args[:target_type].constantize.find_by_id(args[:target_id])
return if target.nil? || target.deleted_at.present?
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.private_message? && !SiteSetting.ai_embeddings_generate_for_pms
return if post.raw.blank?
strategy = DiscourseAi::Embeddings::Strategies::Truncation.new
vector_rep =
DiscourseAi::Embeddings::VectorRepresentations::Base.current_representation(strategy)
vector_rep.generate_representation_from(target)
end
end
end
+35
View File
@@ -0,0 +1,35 @@
# frozen_string_literal: true
module Jobs
class StreamPostHelper < ::Jobs::Base
sidekiq_options retry: false
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]
topic = post.topic
reply_to = post.reply_to_post
guardian = Guardian.new(user)
return unless guardian.can_see?(post)
prompt = CompletionPrompt.enabled_by_name("explain")
input = <<~TEXT
<term>#{args[:term_to_explain]}</term>
<context>#{post.raw}</context>
<topic>#{topic.title}</topic>
#{reply_to ? "<replyTo>#{reply_to.raw}</replyTo>" : nil}
TEXT
DiscourseAi::AiHelper::Assistant.new.stream_prompt(
prompt,
input,
user,
"/discourse-ai/ai-helper/explain/#{post.id}",
)
end
end
end
@@ -11,7 +11,7 @@ module ::Jobs
return unless post.topic.custom_fields[DiscourseAi::AiBot::EntryPoint::REQUIRE_TITLE_UPDATE]
bot.update_pm_title(post)
DiscourseAi::AiBot::Playground.new(bot).title_playground(post)
end
end
end
+132
View File
@@ -0,0 +1,132 @@
# frozen_string_literal: true
module Jobs
class EmbeddingsBackfill < ::Jobs::Scheduled
every 5.minutes
sidekiq_options queue: "low"
cluster_concurrency 1
def execute(args)
return unless SiteSetting.ai_embeddings_enabled
limit = SiteSetting.ai_embeddings_backfill_batch_size
if limit > 50_000
limit = 50_000
Rails.logger.warn(
"Limiting backfill batch size to 50,000 to avoid OOM errors, reduce ai_embeddings_backfill_batch_size to avoid this warning",
)
end
rebaked = 0
strategy = DiscourseAi::Embeddings::Strategies::Truncation.new
vector_rep =
DiscourseAi::Embeddings::VectorRepresentations::Base.current_representation(strategy)
table_name = vector_rep.topic_table_name
topics =
Topic
.joins("LEFT JOIN #{table_name} ON #{table_name}.topic_id = topics.id")
.where(archetype: Archetype.default)
.where(deleted_at: nil)
.order("topics.bumped_at DESC")
.limit(limit - rebaked)
rebaked += populate_topic_embeddings(vector_rep, topics)
vector_rep.consider_indexing
return if rebaked >= limit
# Then, we'll try to backfill embeddings for topics that have outdated
# embeddings, be it model or strategy version
relation = topics.where(<<~SQL)
#{table_name}.model_version < #{vector_rep.version}
OR
#{table_name}.strategy_version < #{strategy.version}
SQL
rebaked += populate_topic_embeddings(vector_rep, relation)
return if rebaked >= limit
# Finally, we'll try to backfill embeddings for topics that have outdated
# embeddings due to edits or new replies. Here we only do 10% of the limit
relation =
topics.where("#{table_name}.updated_at < ?", 7.days.ago).limit((limit - rebaked) / 10)
populate_topic_embeddings(vector_rep, relation)
return if rebaked >= limit
return unless SiteSetting.ai_embeddings_per_post_enabled
# Now for posts
table_name = vector_rep.post_table_name
posts =
Post
.joins("LEFT JOIN #{table_name} ON #{table_name}.post_id = posts.id")
.where(deleted_at: nil)
.limit(limit - rebaked)
# First, we'll try to backfill embeddings for posts that have none
posts
.where("#{table_name}.post_id IS NULL")
.find_each do |t|
vector_rep.generate_representation_from(t)
rebaked += 1
end
vector_rep.consider_indexing
return if rebaked >= limit
# Then, we'll try to backfill embeddings for posts that have outdated
# embeddings, be it model or strategy version
posts
.where(<<~SQL)
#{table_name}.model_version < #{vector_rep.version}
OR
#{table_name}.strategy_version < #{strategy.version}
SQL
.find_each do |t|
vector_rep.generate_representation_from(t)
rebaked += 1
end
return if rebaked >= limit
# Finally, we'll try to backfill embeddings for posts that have outdated
# embeddings due to edits. Here we only do 10% of the limit
posts
.where("#{table_name}.updated_at < ?", 7.days.ago)
.order("random()")
.limit((limit - rebaked) / 10)
.pluck(:id)
.each do |id|
vector_rep.generate_representation_from(Post.find_by(id: id))
rebaked += 1
end
rebaked
end
private
def populate_topic_embeddings(vector_rep, topics)
done = 0
ids = topics.where("#{vector_rep.topic_table_name}.topic_id IS NULL").pluck("topics.id")
ids.each do |id|
topic = Topic.find_by(id: id)
if topic
vector_rep.generate_representation_from(topic)
done += 1
end
end
done
end
end
end
+9
View File
@@ -0,0 +1,9 @@
# frozen_string_literal: true
class AiReportMailer < ActionMailer::Base
include Email::BuildEmailHelper
def send_report(to_address, opts = {})
build_email(to_address, **opts)
end
end
+2
View File
@@ -5,6 +5,8 @@ class AiApiAuditLog < ActiveRecord::Base
OpenAI = 1
Anthropic = 2
HuggingFaceTextGeneration = 3
Gemini = 4
Vllm = 5
end
end
File diff suppressed because it is too large Load Diff
+41 -20
View File
@@ -1,40 +1,60 @@
# frozen_string_literal: true
class CompletionPrompt < ActiveRecord::Base
# TODO(roman): Remove sept 2023.
self.ignored_columns = ["value"]
# TODO(roman): Remove may 2024.
self.ignored_columns = ["provider"]
TRANSLATE = -301
GENERATE_TITLES = -307
PROOFREAD = -303
MARKDOWN_TABLE = -304
CUSTOM_PROMPT = -305
EXPLAIN = -306
ILLUSTRATE_POST = -308
enum :prompt_type, { text: 0, list: 1, diff: 2 }
validates :messages, length: { maximum: 20 }
validate :each_message_length
def messages_with_user_input(user_input)
return messages unless user_input.present?
after_commit { DiscourseAi::AiHelper::Assistant.clear_prompt_cache! }
if user_input[:custom_prompt].present?
case ::DiscourseAi::AiHelper::LlmPrompt.new.enabled_provider
when "huggingface"
self.messages.each { |msg| msg.sub!("{{custom_prompt}}", user_input[:custom_prompt]) }
def self.enabled_by_name(name)
where(enabled: true).find_by(name: name)
end
attr_accessor :custom_instruction
def messages_with_input(input)
return unless input
user_input =
if id == CUSTOM_PROMPT && custom_instruction.present?
"#{custom_instruction}:\n#{input}"
else
self.messages.each do |msg|
msg["content"].sub!("{{custom_prompt}}", user_input[:custom_prompt])
end
input
end
instructions = [messages_hash[:insts], messages_hash[:post_insts].to_s].join("\n")
prompt = DiscourseAi::Completions::Prompt.new(instructions)
messages_hash[:examples].to_a do |example_pair|
prompt.push(type: :user, content: example_pair.first)
prompt.push(type: :model, content: example_pair.second)
end
case ::DiscourseAi::AiHelper::LlmPrompt.new.enabled_provider
when "openai"
self.messages << { role: "user", content: user_input[:text] }
when "anthropic"
self.messages << { "role" => "Input", "content" => "<input>#{user_input[:text]}</input>" }
when "huggingface"
self.messages.first.sub("{{user_input}}", user_input[:text])
end
prompt.push(type: :user, content: "<input>#{user_input}</input>")
prompt
end
private
def messages_hash
@messages_hash ||= messages.symbolize_keys!
end
def each_message_length
messages.each_with_index do |msg, idx|
next if msg["content"].length <= 1000
@@ -56,7 +76,8 @@ end
# created_at :datetime not null
# updated_at :datetime not null
# messages :jsonb
# provider :text
# temperature :integer
# stop_sequences :string is an Array
#
# Indexes
#
+2 -1
View File
@@ -34,7 +34,8 @@ class ReviewableAiChatMessage < Reviewable
def build_actions(actions, guardian, args)
return unless pending?
return if chat_message.blank?
return build_action(actions, :ignore, icon: "external-link-alt") if chat_message.blank?
agree =
actions.add_bundle("#{id}-agree", icon: "thumbs-up", label: "reviewables.actions.agree.title")
+33
View File
@@ -0,0 +1,33 @@
# frozen_string_literal: true
class AiToolSerializer < ApplicationSerializer
attributes :options, :id, :name, :help
def include_options?
object.accepted_options.present?
end
def id
object.to_s.split("::").last
end
def name
object.name.humanize.titleize
end
def help
object.help
end
def options
options = {}
object.accepted_options.each do |option|
options[option.name] = {
name: option.localized_name,
description: option.localized_description,
type: option.type,
}
end
options
end
end
@@ -0,0 +1,31 @@
# frozen_string_literal: true
class LocalizedAiPersonaSerializer < ApplicationSerializer
root "ai_persona"
attributes :id,
:name,
:description,
:enabled,
:system,
:priority,
:commands,
:system_prompt,
:allowed_group_ids,
:temperature,
:top_p,
:mentionable,
:default_llm,
:user_id,
:max_context_posts
has_one :user, serializer: BasicUserSerializer, embed: :object
def name
object.class_instance.name
end
def description
object.class_instance.description
end
end
@@ -10,7 +10,7 @@ class ReviewableAiChatMessageSerializer < ReviewableSerializer
has_one :chat_channel, serializer: AiChatChannelSerializer, root: false, embed: :objects
def chat_channel
object.chat_message.chat_channel
object.chat_message&.chat_channel
end
def target_id
+58
View File
@@ -0,0 +1,58 @@
<div id="related-topics" class="more-topics__list " role="complementary" aria-labelledby="related-topics-title">
<h3 id="related-topics-title" class="more-topics__list-title">
<%= t 'js.discourse_ai.related_topics.title' %>
</h3>
<div class="topic-list-container" itemscope itemtype='http://schema.org/ItemList'>
<meta itemprop='itemListOrder' content='http://schema.org/ItemListOrderDescending'>
<table class='topic-list'>
<thead>
<tr>
<th><%= t 'js.topic.title' %></th>
<th></th>
<th class="replies"><%= t 'js.replies' %></th>
<th class="views"><%= t 'js.views' %></th>
<th><%= t 'js.activity' %></th>
</tr>
</thead>
<tbody>
<% @list.each_with_index do |topic, idx| %>
<tr class="topic-list-item" id="topic-list-item-<%= topic.id %>">
<td class="main-link" itemprop='itemListElement' itemscope itemtype='http://schema.org/ListItem'>
<meta itemprop='position' content='<%= idx + 1 %>'>
<span class="link-top-line">
<a itemprop='url' href='<%= topic.url %>' class='title raw-link raw-topic-link'><%= topic.title %></a>
</span>
<div class="link-bottom-line">
<% if (!@category || @category.has_children?) && topic.category && !topic.category.uncategorized? %>
<a href='<%= topic.category.url %>' class='badge-wrapper bullet'>
<span class='badge-category-bg' style='background-color: #<%= topic.category.color %>'></span>
<span class='badge-category clear-badge'>
<span class='category-name'><%= topic.category.name %></span>
</span>
</a>
<% end %>
<% if tags = topic.visible_tags(guardian) %>
<div class="discourse-tags">
<% tags.each_with_index do |tag, index| %>
<a href='<%= tag.full_url %>' class='discourse-tag'><%= tag.name %></a>
<% if index < tags.size - 1 %>,&nbsp;<% end %>
<% end %>
</div>
<% end %>
</div>
</td>
<td class="replies">
<span class='posts' title='<%= t 'posts' %>'><%= topic.posts_count - 1 %></span>
</td>
<td class="views">
<span class='views' title='<%= t 'views' %>'><%= topic.views %></span>
</td>
<td>
<%= I18n.l(topic.last_posted_at || topic.created_at, format: :date_only) %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
@@ -0,0 +1,14 @@
export default {
resource: "admin.adminPlugins",
path: "/plugins",
map() {
this.route("discourse-ai", function () {
this.route("ai-personas", function () {
this.route("new");
this.route("show", { path: "/:id" });
});
});
},
};
@@ -0,0 +1,10 @@
export default {
resource: "admin.dashboard",
path: "/dashboard",
map() {
this.route("admin.dashboardSentiment", {
path: "/dashboard/sentiment",
resetNamespace: true,
});
},
};
@@ -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-persona";
}
}
@@ -0,0 +1,115 @@
import { tracked } from "@glimmer/tracking";
import { ajax } from "discourse/lib/ajax";
import RestModel from "discourse/models/rest";
const ATTRIBUTES = [
"name",
"description",
"commands",
"system_prompt",
"allowed_group_ids",
"enabled",
"system",
"priority",
"top_p",
"temperature",
"user_id",
"mentionable",
"default_llm",
"user",
"max_context_posts",
];
class CommandOption {
@tracked value = null;
}
export default class AiPersona extends RestModel {
// this code is here to convert the wire schema to easier to work with object
// on the wire we pass in/out commands as an Array.
// [[CommandName, {option1: value, option2: value}], CommandName2, CommandName3]
// So we rework this into a "commands" property and nested commandOptions
init(properties) {
if (properties.commands) {
properties.commands = properties.commands.map((command) => {
if (typeof command === "string") {
return command;
} else {
let [commandId, options] = command;
for (let optionId in options) {
if (!options.hasOwnProperty(optionId)) {
continue;
}
this.getCommandOption(commandId, optionId).value =
options[optionId];
}
return commandId;
}
});
}
super.init(properties);
this.commands = properties.commands;
}
async createUser() {
const result = await ajax(
`/admin/plugins/discourse-ai/ai-personas/${this.id}/create-user.json`,
{
type: "POST",
}
);
this.user = result.user;
this.user_id = this.user.id;
return this.user;
}
getCommandOption(commandId, optionId) {
this.commandOptions ||= {};
this.commandOptions[commandId] ||= {};
return (this.commandOptions[commandId][optionId] ||= new CommandOption());
}
populateCommandOptions(attrs) {
if (!attrs.commands) {
return;
}
let commandsWithOptions = [];
attrs.commands.forEach((commandId) => {
if (typeof commandId !== "string") {
commandId = commandId[0];
}
if (this.commandOptions && this.commandOptions[commandId]) {
let options = this.commandOptions[commandId];
let optionsWithValues = {};
for (let optionId in options) {
if (!options.hasOwnProperty(optionId)) {
continue;
}
let option = options[optionId];
optionsWithValues[optionId] = option.value;
}
commandsWithOptions.push([commandId, optionsWithValues]);
} else {
commandsWithOptions.push(commandId);
}
});
attrs.commands = commandsWithOptions;
}
updateProperties() {
let attrs = this.getProperties(ATTRIBUTES);
attrs.id = this.id;
this.populateCommandOptions(attrs);
return attrs;
}
createProperties() {
let attrs = this.getProperties(ATTRIBUTES);
this.populateCommandOptions(attrs);
return attrs;
}
workingCopy() {
return AiPersona.create(this.createProperties());
}
}

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