# Discourse AI - Sentiment

**URL:** https://meta.discourse.org/t/discourse-ai-sentiment/259599
**Category:** Site Management
**Tags:** how-to, ai, ai-sentiment, content
**Created:** [April 24, 2023, 7:40pm UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599 "2023-04-24T19:40:01Z")
**Posts on this page:** 15
**Page:** 1

<div class="post-metadata">

### Author: ![Discourse](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/discourse/32/148734_2.png) [@Discourse](https://meta.discourse.org/u/Discourse)
#### Post date: [April 24, 2023, 7:40pm UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/1 "2023-04-24T19:40:01Z")

</div>

> 🔖 This topic covers the configuration of the Sentiment feature of the [Discourse AI](https://meta.discourse.org/t/discourse-ai/259214?slient=true) plugin.
> 
> 🙋 Required user level: Moderator

Sentiment keeps tabs on your community by analyzing posts and providing sentiment and emotional scores to give you an overall sense of your community for any period of time. These insights can be helpful in determining the type of users posting within your community and interacting with one another.

## Features

- Overall sentiment: compares the number of posts classified as either positive or negative
- Bar graph showcasing toggleable numerical value for positive, negative, and overall scores
- Emotion: number of topics and posts classified by multiple emotions, grouped by time frame
  - Today
  - Yesterday
  - Last 7 days
  - Last 30 days

- Reports for any period of time that can be accessed via settings
  - Yearly
  - Quarterly
  - Monthly
  - Weekly
  - Custom range

- Accessible to all staff users (admins and moderators)

## Enabling Sentiment

### Configuration

> Sentiment is enabled by default for hosted customers. For manual steps see below

1. Go to `Admin` settings-\> `Plugins` → search or find `discourse-ai` and make sure its enabled
2. Enable `ai_sentiment_enabled` for Sentiment Analysis
3. Head over to `/admin/dashboard/sentiment` to see their respective reports

> ℹ Once enabled, Sentiment will automatically classify all new posts and backfill posts from the last 60 days via a scheduled job that runs every 5 minutes. To backfill posts older than 60 days, increase the `ai_sentiment_backfill_post_max_age_days` site setting.
> 
> :discourse2: **Hosted by us?**
> 
> Contact us at [team@discourse.org](mailto:team@discourse.org) if you need help adjusting backfill settings.
> 
> 🧑‍🔧 **Self-hosted?**
> 
> Increase `ai_sentiment_backfill_post_max_age_days` in your site settings to cover the desired time range. The scheduled backfill job will automatically process the older posts. For details on setting up the required model endpoints, see [Self-Hosting Sentiment and Emotion for DiscourseAI](https://meta.discourse.org/t/self-hosting-sentiment-and-emotion-for-discourseai/334451).

## Technical FAQ

**How is topic/post data processed? How are scores assigned?**

- Sentiment has a “per post” fidelity. For each post we are able to tell sentiment and then cut that data in many shapes (per tag / category / time etc… ). It compares the number of posts classified as either positive or negative. These are calculated when positive or negative scores exceed a fixed threshold of 0.6 (not currently configurable).

**Are there any plans to add support for other languages?**

- In the future Yes! both by adding multilingual simple Machine Learning (ML) models and by using multilingual Large Language Models (LLMs) to classify the data, instead of dedicated models.

**What models are used to power Sentiment?**

- Currently we are using [SamLowe/roberta-base-go\_emotions · Hugging Face](https://huggingface.co/SamLowe/roberta-base-go_emotions) and [cardiffnlp/twitter-roberta-base-sentiment-latest · Hugging Face](https://huggingface.co/cardiffnlp/twitter-roberta-base-sentiment-latest).

## Caveats

- Posts classified as neutral (neither positive or negative) are not shown
- Private messages (PMs) are excluded from calculations

> Last edited by @keegan 2025-10-23T22:28:36Z
> 
> Last checked by @hugh 2024-08-06T05:27:46Z
> 
> > **Check document**
> >
> > Perform check on document:

---

<div class="post-metadata">

### Author: ![JammyDodger](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/jammydodger/32/254611_2.png) [@JammyDodger](https://meta.discourse.org/u/JammyDodger)
#### Post date: [May 24, 2024, 9:29am UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/32 "2024-05-24T09:29:49Z")

</div>

A post was merged into an existing topic: [Problems with Sentiment Backfill](https://meta.discourse.org/t/problems-with-sentiment-backfill/309270/10)

---

<div class="post-metadata">

### Author: ![JammyDodger](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/jammydodger/32/254611_2.png) [@JammyDodger](https://meta.discourse.org/u/JammyDodger)
#### Post date: [June 1, 2024, 5:39pm UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/35 "2024-06-01T17:39:41Z")

</div>

A post was merged into an existing topic: [Problems with Sentiment Backfill](https://meta.discourse.org/t/problems-with-sentiment-backfill/309270/14)

---

<div class="post-metadata">

### Author: ![Saif](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/saif/32/318253_2.png) [@Saif](https://meta.discourse.org/u/Saif)
#### Post date: [February 9, 2025, 8:01am UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/38 "2025-02-09T08:01:33Z")

</div>

The OP has been updated with a new video showcasing the updated features of Sentiment including a ton more emotions and understanding which topics/posts are associated with each emotion

---

<div class="post-metadata">

### Author: ![markschmucker](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/markschmucker/32/141599_2.png) [@markschmucker](https://meta.discourse.org/u/markschmucker)
#### Post date: [February 9, 2025, 8:11pm UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/39 "2025-02-09T20:11:31Z")

</div>

I configured the sentiment model config with a model\_name, endpoint, and api\_key copied from the LLM settings, where it passes the test, but I get the error below in /logs.

(But maybe I don’t understand correctly, because why doesn’t sentiment use one of the configured LLMs?)

Using claude-3-5-sonnet.

```plaintext

{"type":"error","error":{"type":"invalid_request_error","message":"anthropic-version: header is required"}} (Net::HTTPBadResponse)
/var/www/discourse/plugins/discourse-ai/lib/inference/hugging_face_text_embeddings.rb:71:in `classify'
/var/www/discourse/plugins/discourse-ai/lib/sentiment/post_classification.rb:142:in `request_with'
/var/www/discourse/plugins/discourse-ai/lib/sentiment/post_classification.rb:78:in `block (4 levels) in bulk_classify!'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promises.rb:1593:in `evaluate_to'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promises.rb:1776:in `block in on_resolvable'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:359:in `run_task'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:350:in `block (3 levels) in create_worker'
<internal:kernel>:187:in `loop'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:341:in `block (2 levels) in create_worker'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:340:in `catch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:340:in `block in create_worker'
Status: 400

{"type":"error","error":{"type":"invalid_request_error","message":"anthropic-version: header is required"}} (Net::HTTPBadResponse)
/var/www/discourse/plugins/discourse-ai/lib/inference/hugging_face_text_embeddings.rb:71:in `classify'
/var/www/discourse/plugins/discourse-ai/lib/sentiment/post_classification.rb:142:in `request_wit...

```

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [February 9, 2025, 9:19pm UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/40 "2025-02-09T21:19:33Z")

</div>

> [@markschmucker](#):
>
> Using claude-3-5-sonnet.

The sentiment module doesn’t use general LLMs, but models specifically fine tuned to sentiment classification. If you want to run those models on your own that is documented at [Self-Hosting Sentiment and Emotion for DiscourseAI](https://meta.discourse.org/t/self-hosting-sentiment-and-emotion-for-discourseai/334451)

---

<div class="post-metadata">

### Author: ![RBoy](https://avatars.discourse-cdn.com/v4/letter/r/2bfe46/32.png) [@RBoy](https://meta.discourse.org/u/RBoy)
#### Post date: [March 25, 2025, 11:56pm UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/41 "2025-03-25T23:56:20Z")

</div>

@Falco I just noticed the sentiment has stopped running as of Jan 2025. My guess that there’s this new setting `ai_sentiment_model` which as the above link explains if for running your own dedicated sentiment model/image. I noticed that after updating discourse now `ai_sentiment_model_configs` is all blank (should it be blank?).

When I try to run a backfill rake it gives me an error:

```plaintext
rake ai:sentiment:backfill
rake aborted!
ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: syntax error at or near ")" (ActiveRecord::StatementInvalid)
LINE 1: ...e_upload_id", "posts"."outbound_message_id" FROM () as posts...
                                                             ^
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rack-mini-profiler-3.3.1/lib/patches/db/pg.rb:69:in `exec_params'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rack-mini-profiler-3.3.1/lib/patches/db/pg.rb:69:in `exec_params'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:894:in `block (2 levels) in exec_no_cache'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract_adapter.rb:1004:in `block in with_raw_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.2.1/lib/active_support/concurrency/null_lock.rb:9:in `synchronize'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract_adapter.rb:976:in `with_raw_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:893:in `block in exec_no_cache'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.2.1/lib/active_support/notifications/instrumenter.rb:58:in `instrument'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract_adapter.rb:1119:in `log'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:892:in `exec_no_cache'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:872:in `execute_and_clear'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:66:in `internal_exec_query'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/database_statements.rb:647:in `select'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/database_statements.rb:73:in `select_all'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/query_cache.rb:251:in `select_all'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/querying.rb:70:in `_query_by_sql'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1431:in `block (2 levels) in exec_main_query'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:415:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_handling.rb:296:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1430:in `block in exec_main_query'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/query_cache.rb:143:in `disable_query_cache'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/query_cache.rb:30:in `uncached'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/delegation.rb:78:in `block in uncached'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1355:in `_scoping'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:541:in `scoping'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/delegation.rb:78:in `uncached'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1450:in `skip_query_cache_if_necessary'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1414:in `exec_main_query'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1392:in `block in exec_queries'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/query_cache.rb:143:in `disable_query_cache'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/query_cache.rb:30:in `uncached'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/delegation.rb:120:in `public_send'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/delegation.rb:120:in `block in method_missing'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1355:in `_scoping'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:541:in `scoping'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/delegation.rb:120:in `method_missing'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1450:in `skip_query_cache_if_necessary'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1386:in `exec_queries'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1167:in `load'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:336:in `records'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/batches.rb:380:in `block in batch_on_unloaded_relation'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/batches.rb:378:in `batch_on_unloaded_relation'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/batches.rb:269:in `in_batches'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/batches.rb:157:in `find_in_batches'
/var/www/discourse/plugins/discourse-ai/lib/tasks/modules/sentiment/backfill.rake:7:in `block in <main>'
/usr/local/bin/bundle:25:in `load'
/usr/local/bin/bundle:25:in `<main>'

Caused by:
PG::SyntaxError: ERROR: syntax error at or near ")" (PG::SyntaxError)
LINE 1: ...e_upload_id", "posts"."outbound_message_id" FROM () as posts...
                                                             ^
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rack-mini-profiler-3.3.1/lib/patches/db/pg.rb:69:in `exec_params'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rack-mini-profiler-3.3.1/lib/patches/db/pg.rb:69:in `exec_params'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:894:in `block (2 levels) in exec_no_cache'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract_adapter.rb:1004:in `block in with_raw_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.2.1/lib/active_support/concurrency/null_lock.rb:9:in `synchronize'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract_adapter.rb:976:in `with_raw_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:893:in `block in exec_no_cache'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.2.1/lib/active_support/notifications/instrumenter.rb:58:in `instrument'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract_adapter.rb:1119:in `log'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:892:in `exec_no_cache'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:872:in `execute_and_clear'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:66:in `internal_exec_query'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/database_statements.rb:647:in `select'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/database_statements.rb:73:in `select_all'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/query_cache.rb:251:in `select_all'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/querying.rb:70:in `_query_by_sql'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1431:in `block (2 levels) in exec_main_query'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:415:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_handling.rb:296:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1430:in `block in exec_main_query'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/query_cache.rb:143:in `disable_query_cache'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/query_cache.rb:30:in `uncached'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/delegation.rb:78:in `block in uncached'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1355:in `_scoping'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:541:in `scoping'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/delegation.rb:78:in `uncached'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1450:in `skip_query_cache_if_necessary'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1414:in `exec_main_query'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1392:in `block in exec_queries'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/query_cache.rb:143:in `disable_query_cache'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/query_cache.rb:30:in `uncached'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/delegation.rb:120:in `public_send'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/delegation.rb:120:in `block in method_missing'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1355:in `_scoping'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:541:in `scoping'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/delegation.rb:120:in `method_missing'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1450:in `skip_query_cache_if_necessary'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1386:in `exec_queries'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1167:in `load'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:336:in `records'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/batches.rb:380:in `block in batch_on_unloaded_relation'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/batches.rb:378:in `batch_on_unloaded_relation'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/batches.rb:269:in `in_batches'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/batches.rb:157:in `find_in_batches'
/var/www/discourse/plugins/discourse-ai/lib/tasks/modules/sentiment/backfill.rake:7:in `block in <main>'
/usr/local/bin/bundle:25:in `load'
/usr/local/bin/bundle:25:in `<main>'
Tasks: TOP => ai:sentiment:backfill
(See full trace by running task with --trace)

```

My question is, this was working fine until Nov/Dec 2024. How was it working earlier without a dedicated sentiment model/server? Is there a way to get it working again using a generic public service or built in service without having to run a dedicated server for it? I really like discourse is a self contained setup which makes it easy for simple deployment. Having to do custom deployments increases the complexity and maintenance costs which is troublesome for small deployments. Is there way to get back to the pre 2025 setup where sentiment worked out of the box?

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [March 26, 2025, 12:10am UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/42 "2025-03-26T00:10:29Z")

</div>

> [@RBoy](#):
>
> How was it working earlier without a dedicated sentiment model/server?

The plugin defaulted to a server located in DigitalOcean which I put together to make testing easier.

I have since changed the plugin defaults to a clean slate and people who want AI classified need to run servers following the documentation here on Meta.

> [@RBoy](#):
>
> Having to do custom deployments increases the complexity and maintenance costs which is troublesome

Indeed, but we were paying that cost for testing purposes. It’s not sustainable to offer that for every self hoster.

Worth mentioning, that we do offer this classification service on GPU accelerated servers as part of our hosting service.

---

<div class="post-metadata">

### Author: ![Anri](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/anri/32/505186_2.png) [@Anri](https://meta.discourse.org/u/Anri)
#### Post date: [May 29, 2025, 7:17pm UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/43 "2025-05-29T19:17:30Z")

</div>

**Discourse AI Sentiment Analysis Issues: Hugging Face Model Format & Azure Endpoint Failure**

* * *

Hi Discourse Community and Developers,

I’m encountering significant issues when attempting to configure and use the Sentiment Analysis feature within the Discourse AI plugin on my forum. It appears there are two distinct problems preventing it from working correctly.

* * *

### **Issue 1: Hugging Face Model Response Format Mismatch**

I’ve configured the `cardiffnlp/twitter-roberta-base-sentiment` model from Hugging Face for sentiment analysis. While my API key is valid and I can successfully call the API from my Discourse instance using `curl`, the Discourse AI plugin seems to be parsing the response incorrectly due to a change in the Hugging Face model’s output format.

**My `curl` command (confirming valid API key and new format):**

`Bashcurl -X POST https://api-inference.huggingface.co/models/cardiffnlp/twitter-roberta-base-sentiment \ -H "Authorization: Bearer hf_xxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d "{\"inputs\": \"I love Discourse!\"}"`

**Output from `curl` (showing the new nested array format):**

`[[{"label":"LABEL_2","score":0.9891520738601685},{"label":"LABEL_1","score":0.009014752693474293},{"label":"LABEL_0","score":0.0018332178005948663}]]`

**Problem:** The `twitter-roberta-base-sentiment model` _used to_ return a single array of label-score hashes: `[{"label": "LABEL_2", "score": 0.98}, ...]`. However, it now returns a nested array: `[[{"label": "LABEL_2", "score": 0.98}, ...]]`.

The Discourse AI plugin’s hardcoded parsing logic (specifically, `classification["label"][/\d+/].to_i` as indicated by the backtrace) does not account for this outer array layer. This leads to a `TypeError` when it tries to access a Symbol as an Integer.

**Error Message (from Job Exception):**

no implicit conversion of Symbol into Integer (TypeError)  
/var/www/discourse/plugins/discourse-ai/lib/sentiment/post\_classification.rb:163:in block in transform\_result’  
/var/www/discourse/plugins/discourse-ai/lib/sentiment/post\_classification.rb:163:in each’  
/var/www/discourse/plugin…`

**Full Backtrace for Hugging Face Issue:**

```plaintext
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promises.rb:1268:in `raise'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promises.rb:1268:in `wait_until_resolved!'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promises.rb:998:in `value!'
/var/www/discourse/plugins/discourse-ai/lib/sentiment/post_classification.rb:93:in `bulk_classify!'
/var/www/discourse/plugins/discourse-ai/app/jobs/scheduled/sentiment_backfill.rb:27:in `execute'
/var/www/discourse/app/jobs/base.rb:316:in `block (2 levels) in perform'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-6.1.0/lib/rails_multisite/connection_management/null_instance.rb:49:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-6.1.0/lib/rails_multisite/connection_management.rb:21:in `with_connection'
/var/www/discourse/app/jobs/base.rb:303:in `block in perform'
/var/www/discourse/app/jobs/base.rb:299:in `each'
/var/www/discourse/app/jobs/base.rb:299:in `perform'
/var/www/discourse/app/jobs/base.rb:379:in `perform'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_scheduler-0.18.0/lib/mini_scheduler/manager.rb:137:in `process_queue'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_scheduler-0.18.0/lib/mini_scheduler/manager.rb:77:in `worker_loop'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mini_scheduler-0.18.0/lib/mini_scheduler/manager.rb:63:in `block (2 levels) in ensure_worker_threads'
<internal:kernel>:187:in `loop'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:341:in `block (2 levels) in create_worker'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:340:in `catch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:340:in `block in create_worker'
no implicit conversion of Symbol into Integer (TypeError)
/var/www/discourse/plugins/discourse-ai/lib/sentiment/post_classification.rb:163:in `block in transform_result'
/var/www/discourse/plugins/discourse-ai/lib/sentiment/post_classification.rb:163:in `each'
/var/www/discourse/plugin...

```

* * *

### **Issue 2: Microsoft Azure Model Configuration Leads to Hugging Face Error**

When I attempted to switch to the Microsoft Text Analytics model within the Discourse AI settings, I encountered a `404 Resource not found` error, and surprisingly, the backtrace still points to `hugging_face_text_embeddings.rb`.

**Error Message (from Job Exception):**

```plaintext
Job exception: 416 errors
{"error":{"code":"404","message": "Resource not found"}} (Net::HTTPBadResponse)

```

**Relevant Backtrace Snippet (pointing to Hugging Face despite Microsoft model selected):**

```plaintext
/var/www/discourse/plugins/discourse-ai/lib/inference/hugging_face_text_embeddings.rb:76:in `do_request!'
/var/www/discourse/plugins/discourse-ai/lib/inference/hugging_face_text_embeddings.rb:51:in `classify_by_sentiment!'
/var/www/discourse/plugins/discourse-ai/lib/sentiment/post_classification.rb:156:in `request_with'

```

**Observation:** This indicates that even when I select and configure the Microsoft model’s endpoint and API key, the Discourse AI plugin appears to be hardcoded or incorrectly routing the sentiment analysis requests through Hugging Face-specific logic or endpoints. This prevents the Microsoft model from being used at all.

* * *

### **Configuration Screenshots:**

I’ve attached screenshot of my Discourse AI settings to show the configuration:

- Detailed configuration for AI sentiment models (showing both Hugging Face and Microsoft models) - I tested with just Hugging Face or just Microsoft model configs with the same result  
 ![AI Sentiment Model Config](https://global.discourse-cdn.com/meta/original/4X/3/1/d/31d712366bf054122ca629ea2af23c54005da854.png)

These issues make the sentiment analysis feature effectively unusable. It seems the plugin requires an update to handle the new Hugging Face response format and to correctly route requests when different sentiment providers are configured.

Any assistance or guidance on these issues would be greatly appreciated.

Thank you!

---

<div class="post-metadata">

### Author: ![Anri](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/anri/32/505186_2.png) [@Anri](https://meta.discourse.org/u/Anri)
#### Post date: [July 29, 2025, 10:48am UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/44 "2025-07-29T10:48:44Z")

</div>

I am curious if sentiment reporting is functioning for others, or if I have misconfigured something. I would like to know what else I need to check or configure to enable sentiment reporting, as I am still experiencing the same issue.

---

<div class="post-metadata">

### Author: ![Neil\_Evans2](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/neil_evans2/32/492791_2.png) [@Neil\_Evans2](https://meta.discourse.org/u/Neil_Evans2)
#### Post date: [August 27, 2025, 12:23pm UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/45 "2025-08-27T12:23:52Z")

</div>

We are trying to use this feature with Azure AI Language (from our self-hosted Discourse instance) - as we are already using our Azure subscription to integrate GPT-4.5 with Discourse (for summarization and chat-bot functionality):

> **[What is Azure Language in Foundry Tools - Foundry Tools](https://learn.microsoft.com/en-us/azure/ai-services/language-service/overview)**
>
> Learn how to integrate AI into your applications that can extract information and understand written language.

…but we are getting no data in the the sentiment dashboard, and can see these errors in the logs:

`Discourse AI: Errors during bulk classification: Failed to classify 208 posts (example ids: 2256, 909, 2270, 2260, 2797) : JSON::ParserError : An empty string is not a valid JSON string.`

The backtrace shows that Discourse might be trying to use HuggingFace - are these the only models supported at the moment?

Thanks,

N

---

<div class="post-metadata">

### Author: ![dylanb](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/dylanb/32/139102_2.png) [@dylanb](https://meta.discourse.org/u/dylanb)
#### Post date: [September 2, 2025, 6:07pm UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/46 "2025-09-02T18:07:37Z")

</div>

So, is the only way to use this feature to setup your own instances of the models (which need resource heavy GPU instances that would be costly)? This feature looks very useful, but it seems like it would cost me more to set up than my actual Discourse hosting.

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [September 3, 2025, 8:08am UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/47 "2025-09-03T08:08:57Z")

</div>

> [@Neil\_Evans2](#):
>
> The backtrace shows that Discourse might be trying to use HuggingFace - are these the only models supported at the moment?

Yes, the supported models are the ones listed in the OP.

We will eventually add support for classifying using LLMs for people whose cost isn’t an issue.

> [@dylanb](#):
>
> but it seems like it would cost me more to set up than my actual Discourse hosting.

Well, the whole feature is build around classifying posts using ML models, so yes, you need somewhere to run those.

And since Discourse can run in the very cheapest VPS out there, running ML models is indeed more expensive. If you wanna have the feature on the cheapest way possible, it is doable to run it on a server with just a handful of CPU cores, as long as you have enough RAM to load the models.

---

<div class="post-metadata">

### Author: ![s42](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/s42/32/525981_2.png) [@s42](https://meta.discourse.org/u/s42)
#### Post date: [October 18, 2025, 8:36am UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/48 "2025-10-18T08:36:23Z")

</div>

> [@Discourse](#):
>
> It compares the number of posts classified as either positive or negative. These are calculated when positive or negative scores \> the set threshold score.

Sorry if this question has already been asked before, but I was unable to find a reference to where exactly the threshold score can be configured 😅

---

<div class="post-metadata">

### Author: ![keegan](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/keegan/32/383395_2.png) [@keegan](https://meta.discourse.org/u/keegan)
#### Post date: [October 24, 2025, 1:31pm UTC](https://meta.discourse.org/t/discourse-ai-sentiment/259599/49 "2025-10-24T13:31:34Z")

</div>

> [@s42](#):
>
> where exactly the threshold score can be configured 😅

Unfortunately, the threshold score is not something that is configurable by users or admins. It’s a specific value set in the code base.
