Files
coder/coderd/pubsub/chatconfigevent.go
T
Ethan 2cbc464c72 fix(coderd): invalidate chatd provider cache on AI provider changes (#26987)
chatd subscribes to `ChatConfigEventChannel` and invalidates its
provider cache on a `providers` event kind, but nothing ever published
that kind. AI provider CRUD only publishes on
`AIProvidersChangedChannel` (consumed by aibridged and aibridgeproxyd),
so chatd's provider cache only converged via its 10 second TTL.

Subscribe chatd to the same `AIProvidersChangedChannel` publish instead
of adding a second publish, per the review feedback on #26207: one
publish, multiple subscribers. The now-unused `ChatConfigEventProviders`
kind is removed so `ChatConfigEvent` stays scoped to model configs, user
prompts, and advisor config, and can't regrow a dead subscriber.

Follow-up to CRF-5 from the review of #25673. Supersedes #26207.

Closes CODAGT-499
2026-07-07 00:20:54 +10:00

54 lines
1.8 KiB
Go

package pubsub
import (
"context"
"encoding/json"
"github.com/google/uuid"
"golang.org/x/xerrors"
)
// ChatConfigEventChannel is the pubsub channel for chat config
// changes (model configs, user prompts, advisor config).
// All replicas subscribe to this channel to invalidate their local
// caches.
const ChatConfigEventChannel = "chat:config_change"
// HandleChatConfigEvent wraps a typed callback for ChatConfigEvent
// messages, following the same pattern as HandleChatWatchEvent.
func HandleChatConfigEvent(cb func(ctx context.Context, payload ChatConfigEvent, err error)) func(ctx context.Context, message []byte, err error) {
return func(ctx context.Context, message []byte, err error) {
if err != nil {
cb(ctx, ChatConfigEvent{}, xerrors.Errorf("chat config event pubsub: %w", err))
return
}
var payload ChatConfigEvent
if err := json.Unmarshal(message, &payload); err != nil {
cb(ctx, ChatConfigEvent{}, xerrors.Errorf("unmarshal chat config event: %w", err))
return
}
cb(ctx, payload, err)
}
}
// ChatConfigEvent is published when chat configuration changes
// (model config CRUD, user prompt updates, or advisor config
// updates). Subscribers use this to invalidate their local caches.
type ChatConfigEvent struct {
Kind ChatConfigEventKind `json:"kind"`
// EntityID carries context for the invalidation:
// - For model configs: the specific config ID.
// - For user prompts: the user ID.
// - For advisor config: uuid.Nil (singleton site-config row).
EntityID uuid.UUID `json:"entity_id"`
}
type ChatConfigEventKind string
const (
ChatConfigEventModelConfig ChatConfigEventKind = "model_config"
ChatConfigEventUserPrompt ChatConfigEventKind = "user_prompt"
ChatConfigEventAdvisorConfig ChatConfigEventKind = "advisor_config"
)