PromoteQueued promotes a queued message into chat history. On a running chat with a fresh worker heartbeat the promote is deferred to the worker's persist+auto-promote so partial assistant output is not lost; otherwise it inserts the user message synchronously.
( ctx context.Context, opts PromoteQueuedOptions, )
| 2423 | // to the worker's persist+auto-promote so partial assistant output |
| 2424 | // is not lost; otherwise it inserts the user message synchronously. |
| 2425 | func (p *Server) PromoteQueued( |
| 2426 | ctx context.Context, |
| 2427 | opts PromoteQueuedOptions, |
| 2428 | ) (PromoteQueuedResult, error) { |
| 2429 | if opts.ChatID == uuid.Nil { |
| 2430 | return PromoteQueuedResult{}, xerrors.New("chat_id is required") |
| 2431 | } |
| 2432 | |
| 2433 | var ( |
| 2434 | result PromoteQueuedResult |
| 2435 | promoted database.ChatMessage |
| 2436 | updatedChat database.Chat |
| 2437 | remainingQueue []database.ChatQueuedMessage |
| 2438 | deferred bool |
| 2439 | syntheticResults []database.ChatMessage |
| 2440 | ) |
| 2441 | |
| 2442 | txErr := p.db.InTx(func(tx database.Store) error { |
| 2443 | lockedChat, err := tx.GetChatByIDForUpdate(ctx, opts.ChatID) |
| 2444 | if err != nil { |
| 2445 | return xerrors.Errorf("lock chat: %w", err) |
| 2446 | } |
| 2447 | |
| 2448 | if lockedChat.Archived { |
| 2449 | return ErrChatArchived |
| 2450 | } |
| 2451 | |
| 2452 | queuedMessages, err := tx.GetChatQueuedMessages(ctx, opts.ChatID) |
| 2453 | if err != nil { |
| 2454 | return xerrors.Errorf("get queued messages: %w", err) |
| 2455 | } |
| 2456 | |
| 2457 | var ( |
| 2458 | targetContent json.RawMessage |
| 2459 | targetModelConfigID uuid.NullUUID |
| 2460 | targetAPIKeyID sql.NullString |
| 2461 | found bool |
| 2462 | ) |
| 2463 | for _, qm := range queuedMessages { |
| 2464 | if qm.ID == opts.QueuedMessageID { |
| 2465 | targetContent = qm.Content |
| 2466 | targetModelConfigID = qm.ModelConfigID |
| 2467 | targetAPIKeyID = qm.APIKeyID |
| 2468 | found = true |
| 2469 | break |
| 2470 | } |
| 2471 | } |
| 2472 | if !found { |
| 2473 | return xerrors.Errorf("queued message %d not found in chat %s", opts.QueuedMessageID, opts.ChatID) |
| 2474 | } |
| 2475 | |
| 2476 | // Setting pending would trip persistStep's ownership guard |
| 2477 | // and drop the worker's partial output. Set waiting and |
| 2478 | // reorder the queued row so the worker's auto-promote picks |
| 2479 | // it up after the persist. |
| 2480 | heartbeatFresh := lockedChat.HeartbeatAt.Valid && |
| 2481 | p.clock.Now().Sub(lockedChat.HeartbeatAt.Time) < p.inFlightChatStaleAfter |
| 2482 | if lockedChat.Status == database.ChatStatusRunning && heartbeatFresh { |