惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

腾讯CDC
N
Netflix TechBlog - Medium
Google DeepMind News
Google DeepMind News
Scott Helme
Scott Helme
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
小众软件
小众软件
月光博客
月光博客
有赞技术团队
有赞技术团队
Microsoft Security Blog
Microsoft Security Blog
爱范儿
爱范儿
WordPress大学
WordPress大学
Jina AI
Jina AI
M
MIT News - Artificial intelligence
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
阮一峰的网络日志
阮一峰的网络日志
B
Blog RSS Feed
P
Proofpoint News Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
F
Fortinet All Blogs
Y
Y Combinator Blog
Microsoft Azure Blog
Microsoft Azure Blog
云风的 BLOG
云风的 BLOG
Hugging Face - Blog
Hugging Face - Blog
MongoDB | Blog
MongoDB | Blog
I
InfoQ
Vercel News
Vercel News
C
Check Point Blog
美团技术团队
V
V2EX
量子位
博客园 - 三生石上(FineUI控件)
D
DataBreaches.Net
G
Google Developers Blog
博客园_首页
J
Java Code Geeks
Recent Announcements
Recent Announcements
人人都是产品经理
人人都是产品经理
H
Help Net Security
博客园 - Franky
The GitHub Blog
The GitHub Blog
V
Visual Studio Blog
T
Tailwind CSS Blog
IT之家
IT之家
S
SegmentFault 最新的问题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
雷峰网
雷峰网
L
LangChain Blog
博客园 - 司徒正美
T
The Blog of Author Tim Ferriss
H
Hackread – Cybersecurity News, Data Breaches, AI and More

Discourse Meta - Latest posts

Horizon: High Context Topic Cards I'd like to ask what this file /var/www/discourse/vendor/data/RT_sRGB.icm is used for? Discourse to Markdown Plugin How to verify a subdomain (mysite.discourse.group) in Google Search Console? Reader Mode Publishing WordPress gallery posts on Discourse Discourse User Feedback New user filter by custom field and contact Interact with discourse from Python? Why we can't type the assignee in the search of a post to assign - home screen app Redis Flushall Category-toggled one-touch move post Some sort of variable to eliminate duplicate text? Why did this plugin replacement fail? Add user to list modal often appears above the mobile viewport sceen Creating/Editing a post on mobile: let's discuss the 2026 Discourse experience Wp-discourse feature request - do not show comments with specific tag? Are there any good Chinese plugins? Discourse ID and 2FA Discourse MCP is here! Sample Forum to See Admin Features? I am unable to register for a free Discourse site Wikipedia Lookup Add text for /login (like js.create_account.disclaimer for /signup) How can i set disable Markdown & Default to Rich Text What's the background image size for the Welcome Banner? How do I enable Associated Accounts with 2FA? Hamburger toggle Support <span data-attribute> in the rich editor Cannot disable read-only mode on Free Plan Coping with deprecated columns in Data Explorer Seeking advice on MaxMind error log issues Site setting menu limited by results page size Change default landing page for Groups (Members → Activity/Messages?) Email Notifications for Forum Posts or Replies Buffer -- streamline sharing topics on social media Moving a post between topics does not load the post on existing clients Diff is too complex to display Side bar footer container background color bug visible in RTL Vote display dimensions issue on the new foundation theme Icon is incredibly large when quoted Easily create Ideas categories using the Topic Voting plugin Modernizing the Foundation theme 429 error after Split Posts Error when switching locale: "topic doesn't exist" message appears on all topics Group Review Queue Items SSO Provider URL contains symbol # cause issue Migrate from wpForo to Discourse Users are unable to see invite details Disable initial page that re-directs to /login Discourse Livestream Discourse Dynamic Sidebar Icon Unable to save category settings made by plugins Post not being detected as German Misleading free plan shutdown timeline New posts/replies do not appear in real-time; manual refresh required Do Free forums not support the use of tags? Left side hamburger menu on mobile Inconsistent permission to use the same tag in the same category /upcoming-events intermittent drop on 2GB vRAM server 2 emails rejected by 2026.4.0-latest ( 5f0d95e1f1 ) Backups page Don't remove all push subscriptions on logoff Translated text on Linux Debian has weird formatting Remote images are always downloaded Can no longer create global banners from restricted categories Where did the you are replying to an old topic warning go? FKB Pro - Social theme Chat link missing in post event widget after to Rollup Whisper Warning Experiment: Collaborative content gardening hub for meta Topic Template Placeholder Text theme component Embedded comments dates are still not localized ProxyTracer: VPN & Proxy Blocker Automatically apply grids to image uploads What's causing the login issue? Single click activation of the Create Event modal Error in browser console when using hashtag autocomplete in the composer: undefined generateIconHTML How can badges and groups be localized (multilingual)? Exclude staged users from Watching? Shared Draft as default New Topic Allow AI Persona/Agent to respond only to @mentions, not to replies to its posts Adding a configurable delay to AI Agent responses Bidirectional sync between Discourse Topics/Categories and GitHub/Gitlab Issue Trackers "Key" and "Value" from the Wrap Modal are not translatable Deprecating .hbs file extension in themes and plugins New topic title input (#reply-title) remains LTR in RTL sites — fix still not effective (2026) Opening forum goes to topics 1 day ago, not recent topics Missing field 'text' (in 'mainEntity.suggestedAnswer') Current Projects - March 2026 Error loading route Caused by plugin 'discourse-subscriptions' User-specific 502 errors after login — traced to DiscourseUpdates.has_unseen_features? Force Mobile Homepage Documentation Plugin - Index Topic not working after Restore Returning bumping after editing last post Move 'whisper' functionality Any updates on the best way to create a HTML archive of a static site? Connect Discourse to WeChat Mini Program Send email notification only when @mentioned Include the Signatures plugin on more hosted plans
Add webhook / event support for Discourse AI artifact key-value updates (or allow admins to disable sandboxing)
MachineScholar · 2026-05-27 · via Discourse Meta - Latest posts

I’d like to propose allowing AI artifacts to send webhooks whenever the stored data in an AI artifact is updated. There are many reasons why this would be advantageous, but I’ll try to keep my case short.

Many organization who make use of Discourse also make use of various automations, and the tools for automations are becoming increasingly popular, easy to set up, and beginner friendly. AI artifacts can contain precious data which can be used very effectively in such automations with the added bonus that it’s already JSON!

In my case, I use n8n in a Docker Compose setup next to my Discourse container and I already use it to automate a wide variety of things, including things on my Discourse instances via a Docker network. However, one of the Discourse instances I maintain is for an educational organization/business and AI artifacts are used to track learner notes, lesson journals, and so on. This kind of data would be really beneficial for improving teachers’ workflows, making summaries, and so on via an automation tool such as n8n.

It would be technically possible to poll artifacts for updates, but that could easily begin to start straining system resources and it would be a waste of resources at best, as well as require extensive technical skills to set up which many admins don’t have.

Needless to say, this would also be highly beneficial for Discourse’s enterprise clients.

A workaround might be for the artifact’s JavaScript to call an external webhook after calling window.discourseArtifact.set(...).

A server-side event/webhook would be much more reliable and secure.

I'm not a Ruby developer, so I have been chatting with GPT-5.5 about this and it has offered some interesting insight as well…

Based on Discourse’s current webhook architecture, the clean implementation is probably not a bespoke “call this URL” feature inside AI artifacts. It should be implemented as a normal Discourse webhook event type, backed by a normal internal DiscourseEvent.

Best implementation shape

I’d suggest the Discourse devs implement it in two layers:

  1. Internal events emitted when artifact KV records change.

  2. Webhook event types that subscribe to those internal events and use the existing webhook delivery system.

That keeps it consistent with the rest of Discourse. Existing webhooks already work by mapping internal DiscourseEvents to WebHook.enqueue_* calls in config/initializers/012-web_hook_events.rb; for example, topic, post, user, category, tag, reviewable, notification, and like events are wired this way.

The artifact storage path is simple enough: ArtifactKeyValuesController#set finds or initializes a key-value record, assigns key/value/public, and saves it; destroy finds the current user’s record by key and destroys it. The model itself is AiArtifactKeyValue, belongs to an artifact and a user, has key, value, and public, and enforces uniqueness by artifact/user/key.

Proposed event names

I’d probably use three specific webhook events:

ai_artifact_key_value_created
ai_artifact_key_value_updated
ai_artifact_key_value_deleted

Alternatively, a single event could work:

ai_artifact_key_value_changed

…but three events fit Discourse’s existing style better. Discourse already has separate webhook event names like post_created, post_edited, post_destroyed, calendar_event_created, calendar_event_updated, and so on.

Files/classes they would likely touch

1. Add new webhook event types

WebHookEventType currently defines numeric constants, a group enum, and a TYPES hash of event names to IDs.

They could add something like:

AI_ARTIFACT = 20

enum :group,
  {
    # existing groups...
    ai_artifact: 18,
  },
  scopes: false

TYPES = {
  # existing types...
  ai_artifact_key_value_created: 2001,
  ai_artifact_key_value_updated: 2002,
  ai_artifact_key_value_deleted: 2003,
}

The exact IDs would be up to Discourse maintainers; they just need to avoid conflicts.

They would also add seed entries in db/fixtures/007_web_hook_event_types.rb, because existing webhook event types are seeded there with IDs, names, and groups.

Example:

WebHookEventType.seed do |b|
  b.id = WebHookEventType::TYPES[:ai_artifact_key_value_created]
  b.name = "ai_artifact_key_value_created"
  b.group = WebHookEventType.groups[:ai_artifact]
end

WebHookEventType.seed do |b|
  b.id = WebHookEventType::TYPES[:ai_artifact_key_value_updated]
  b.name = "ai_artifact_key_value_updated"
  b.group = WebHookEventType.groups[:ai_artifact]
end

WebHookEventType.seed do |b|
  b.id = WebHookEventType::TYPES[:ai_artifact_key_value_deleted]
  b.name = "ai_artifact_key_value_deleted"
  b.group = WebHookEventType.groups[:ai_artifact]
end

The admin webhook UI should then pick these up automatically, because Admin::WebHooksController#index already serializes grouped active event types for the UI. The event type serializer already exposes id, name, and group.

2. Hide the events when Discourse AI is disabled

WebHookEventType.active already hides plugin-dependent webhook events when their features are disabled, such as solved, assign, topic voting, chat, and calendar events.

So they could add:

unless defined?(SiteSetting.discourse_ai_enabled) && SiteSetting.discourse_ai_enabled
  ids_to_exclude.concat(
    [
      TYPES[:ai_artifact_key_value_created],
      TYPES[:ai_artifact_key_value_updated],
      TYPES[:ai_artifact_key_value_deleted],
    ],
  )
end

They may also want to check an artifact-specific setting if one exists or is added.

3. Emit internal events from the model, not just the controller

Although the current write path is controller-based, the robust place is probably the model, using commit callbacks:

class AiArtifactKeyValue < ActiveRecord::Base
  after_create_commit :trigger_created_event
  after_update_commit :trigger_updated_event
  after_destroy_commit :trigger_deleted_event

  private

  def trigger_created_event
    DiscourseEvent.trigger(:ai_artifact_key_value_created, self)
  end

  def trigger_updated_event
    return if previous_changes.slice("value", "public").blank?

    DiscourseEvent.trigger(:ai_artifact_key_value_updated, self)
  end

  def trigger_deleted_event
    DiscourseEvent.trigger(:ai_artifact_key_value_deleted, self)
  end
end

Model-level callbacks would catch future write paths too, not just ArtifactKeyValuesController#set. The after_commit part matters because webhook delivery should only be queued after the database change is safely committed.

One subtlety: they probably should not trigger an “updated” event when the artifact calls set() with the same value and nothing actually changes. Checking previous_changes avoids noisy webhooks.

4. Add a webhook payload serializer

Discourse webhooks generate payloads through serializers. The generic WebHook.enqueue_object_hooks method can take a serializer, and WebHook.generate_payload serializes the object with a system-user guardian.

They could add something like:

class WebHookAiArtifactKeyValueSerializer < ApplicationSerializer
  attributes :id,
             :ai_artifact_id,
             :post_id,
             :topic_id,
             :user_id,
             :key,
             :public,
             :value_included,
             :created_at,
             :updated_at

  def post_id
    object.ai_artifact.post_id
  end

  def topic_id
    object.ai_artifact.post.topic_id
  end

  def value_included
    false
  end
end

I’d strongly recommend not including value by default. Artifact KV data can be private per user, and the model has a public flag. If Discourse wants to support values, they could make it explicit:

include_value: false
include_public_values: true
include_private_values: false

But the safe v1 should probably omit values entirely.

5. Wire internal events to webhook delivery

They could add handlers to config/initializers/012-web_hook_events.rb, mirroring existing patterns.

Something like:

%i[
  ai_artifact_key_value_created
  ai_artifact_key_value_updated
  ai_artifact_key_value_deleted
].each do |event|
  DiscourseEvent.on(event) do |key_value|
    artifact = key_value.ai_artifact
    post = artifact.post
    topic = post.topic

    payload =
      WebHook.generate_payload(
        :ai_artifact_key_value,
        key_value,
        WebHookAiArtifactKeyValueSerializer
      )

    WebHook.enqueue_hooks(
      :ai_artifact_key_value,
      event,
      id: key_value.id,
      category_id: topic&.category_id,
      tag_ids: topic&.tags&.pluck(:id),
      payload: payload
    )
  end
end

That would reuse the existing webhook job pipeline. WebHook.enqueue_hooks finds active webhooks for the event and enqueues Jobs::EmitWebHookEvent. The job already handles delivery, retries, logging, headers, signatures, and admin visibility.

Including category_id and tag_ids is a nice touch because existing webhook jobs can filter by category and tag. The webhook job already checks category and tag constraints before sending. Since an AI artifact belongs to a post, and the artifact belongs to a post in the model, deriving category/topic context should be possible.

What the delivered webhook might look like

Because Discourse’s webhook body uses event_type as the top-level root, this would probably look like:

{
  "ai_artifact_key_value": {
    "id": 456,
    "ai_artifact_id": 123,
    "post_id": 789,
    "topic_id": 321,
    "user_id": 42,
    "key": "score",
    "public": true,
    "value_included": false,
    "created_at": "2026-05-26T12:00:00Z",
    "updated_at": "2026-05-26T12:05:00Z"
  }
}

The event name would be in the headers, consistent with existing webhook delivery:

X-Discourse-Event-Type: ai_artifact_key_value
X-Discourse-Event: ai_artifact_key_value_updated
X-Discourse-Event-Signature: sha256=...

Those headers line up with how EmitWebHookEvent builds webhook headers today.

The devs would probably need to decide these:

Should values be included?
My vote: no by default. Maybe allow only public values, or make value inclusion an admin setting. Private per-user artifact data should not silently leave the site.

Should there be one event or three?
Three is cleaner for webhook subscribers. One event with an action field is simpler internally. Existing Discourse style leans toward multiple event names.

Should category/tag filtering apply?
I think yes. The artifact is attached to a post/topic, so category and tag filters are meaningful.

Should this be implemented in Discourse core or the Discourse AI plugin?
The data model lives in the Discourse AI plugin, but the webhook event registry lives in core. Since bundled plugins like solved/chat/calendar already have webhook event types in the shared WebHookEventType list, Discourse may be comfortable adding AI artifact events there too. The active method can hide them when AI is disabled.

Should this also emit an internal DiscourseEvent even if no webhook exists?
Yes. That gives plugin authors a clean hook point even apart from webhooks.

Complexity

I’d frame it as moderate but very contained.

The likely work is:

  1. Add 3 event type constants.

  2. Add 1 webhook group.

  3. Add 3 seed records.

  4. Add 1 serializer.

  5. Add 3 model callbacks or service-level event triggers.

  6. Add webhook initializer handlers.

  7. Add specs for create/update/delete.

  8. Add privacy decision around whether value is included.

The delivery system, retries, signatures, event log, ping/redelivery UI, and admin webhook configuration are already there. The feature is mostly about making artifact KV mutations visible to that existing system.