hotfix(paliadin): ship user_id on /chat/turn (aichat tenant-DB requirement)
m reported "ai chat seems not to be wired anymore" + the frontend
showed "Verbindung verloren. Antwort wird nachgereicht…".
Root cause: aichat on mRiver added a tenant-DB layer that demands
`user_id` on every /chat/turn request:
{"error":{"code":"bad_request",
"message":"user_id is required when a tenant DB is
configured","retryable":false}}
aichat itself is healthy (/chat/health 200, paliadin session ok:true,
last successful turn was ~2.6h ago). The paliad side built and shipped
an aichatTurnRequest without user_id, so every turn since the tenant-DB
flip 400s; paliad's SSE relay receives no upstream data and closes
empty, producing the user-visible "Verbindung verloren".
Fix: add UserID to aichatTurnRequest (json: user_id, mandatory now),
populate from req.UserID.String() at the call site. The userID was
already in scope (used for JWT mint + username lookup); the struct just
wasn't shipping it.
Regression test in TestRunTurn_HappyPath_ViaCallHTTP asserts
captured.UserID == request UUID so a future struct edit that drops the
field fails CI instead of production.
This commit is contained in:
@@ -217,6 +217,7 @@ func (s *AichatPaliadinService) RunTurn(ctx context.Context, req TurnRequest) (*
|
|||||||
body := aichatTurnRequest{
|
body := aichatTurnRequest{
|
||||||
Persona: s.cfg.Persona,
|
Persona: s.cfg.Persona,
|
||||||
Username: username,
|
Username: username,
|
||||||
|
UserID: req.UserID.String(),
|
||||||
SessionID: req.SessionID,
|
SessionID: req.SessionID,
|
||||||
Message: sanitiseForTmux(req.UserMessage),
|
Message: sanitiseForTmux(req.UserMessage),
|
||||||
JWT: jwt,
|
JWT: jwt,
|
||||||
@@ -611,8 +612,13 @@ func (s *AichatPaliadinService) clearPrimed(session string) {
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
type aichatTurnRequest struct {
|
type aichatTurnRequest struct {
|
||||||
Persona string `json:"persona"`
|
Persona string `json:"persona"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
|
// UserID is the paliad user UUID, required by aichat now that a
|
||||||
|
// tenant DB is configured ("user_id is required when a tenant DB
|
||||||
|
// is configured"). Without it /chat/turn 400s and the SSE relay
|
||||||
|
// closes empty → "Verbindung verloren" on the frontend.
|
||||||
|
UserID string `json:"user_id"`
|
||||||
SessionID string `json:"session_id,omitempty"`
|
SessionID string `json:"session_id,omitempty"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
JWT string `json:"jwt,omitempty"`
|
JWT string `json:"jwt,omitempty"`
|
||||||
|
|||||||
@@ -501,6 +501,7 @@ func TestRunTurn_HappyPath_ViaCallHTTP(t *testing.T) {
|
|||||||
body := aichatTurnRequest{
|
body := aichatTurnRequest{
|
||||||
Persona: s.cfg.Persona,
|
Persona: s.cfg.Persona,
|
||||||
Username: s.usernameFor(context.Background(), uid),
|
Username: s.usernameFor(context.Background(), uid),
|
||||||
|
UserID: uid.String(),
|
||||||
Message: "Hello",
|
Message: "Hello",
|
||||||
JWT: jwtTok,
|
JWT: jwtTok,
|
||||||
Meta: buildAichatMeta(TurnRequest{PageOrigin: "/dashboard"}),
|
Meta: buildAichatMeta(TurnRequest{PageOrigin: "/dashboard"}),
|
||||||
@@ -516,6 +517,12 @@ func TestRunTurn_HappyPath_ViaCallHTTP(t *testing.T) {
|
|||||||
if captured.Username != "user-aaaaaaaa" {
|
if captured.Username != "user-aaaaaaaa" {
|
||||||
t.Errorf("username = %q; want user-aaaaaaaa (nil DB fallback)", captured.Username)
|
t.Errorf("username = %q; want user-aaaaaaaa (nil DB fallback)", captured.Username)
|
||||||
}
|
}
|
||||||
|
// Regression for the 2026-05-21 outage: aichat now requires user_id
|
||||||
|
// when a tenant DB is configured; missing → 400 → SSE drop on the
|
||||||
|
// frontend ("Verbindung verloren"). The struct must carry it.
|
||||||
|
if captured.UserID != uid.String() {
|
||||||
|
t.Errorf("user_id = %q; want %q", captured.UserID, uid.String())
|
||||||
|
}
|
||||||
if captured.Message != "Hello" {
|
if captured.Message != "Hello" {
|
||||||
t.Errorf("message = %q; want Hello", captured.Message)
|
t.Errorf("message = %q; want Hello", captured.Message)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user