MCPcopy Index your code
hub / github.com/coder/coder / SubmitToolResults

Method SubmitToolResults

coderd/x/chatd/chatd.go:2659–2852  ·  view source on GitHub ↗

SubmitToolResults validates and persists client-provided tool results, transitions the chat to pending, and wakes the run loop. The caller is responsible for the fast-path status check; this method performs an authoritative re-check under a row lock.

(
	ctx context.Context,
	opts SubmitToolResultsOptions,
)

Source from the content-addressed store, hash-verified

2657// loop. The caller is responsible for the fast-path status check;
2658// this method performs an authoritative re-check under a row lock.
2659func (p *Server) SubmitToolResults(
2660 ctx context.Context,
2661 opts SubmitToolResultsOptions,
2662) error {
2663 dynamicToolNames, err := parseDynamicToolNames(pqtype.NullRawMessage{
2664 RawMessage: opts.DynamicTools,
2665 Valid: len(opts.DynamicTools) > 0,
2666 })
2667 if err != nil {
2668 return xerrors.Errorf("parse chat dynamic tools: %w", err)
2669 }
2670
2671 // The GetLastChatMessageByRole lookup and all subsequent
2672 // validation and persistence run inside a single transaction
2673 // so the assistant message cannot change between reads.
2674 var statusConflict *ToolResultStatusConflictError
2675 txErr := p.db.InTx(func(tx database.Store) error {
2676 // Authoritative status check under row lock.
2677 locked, lockErr := tx.GetChatByIDForUpdate(ctx, opts.ChatID)
2678 if lockErr != nil {
2679 return xerrors.Errorf("lock chat for update: %w", lockErr)
2680 }
2681 if locked.Archived {
2682 return ErrChatArchived
2683 }
2684 if locked.Status != database.ChatStatusRequiresAction {
2685 statusConflict = &ToolResultStatusConflictError{
2686 ActualStatus: locked.Status,
2687 }
2688 return statusConflict
2689 }
2690
2691 // Get the last assistant message inside the transaction
2692 // for consistency with the row lock above.
2693 lastAssistant, err := tx.GetLastChatMessageByRole(ctx, database.GetLastChatMessageByRoleParams{
2694 ChatID: opts.ChatID,
2695 Role: database.ChatMessageRoleAssistant,
2696 })
2697 if err != nil {
2698 return xerrors.Errorf("get last assistant message: %w", err)
2699 }
2700
2701 // Collect tool-call IDs that already have results.
2702 // When a dynamic tool name collides with a built-in,
2703 // the chatloop executes it as a built-in and persists
2704 // the result. Those calls must not count as pending.
2705 afterMsgs, afterErr := tx.GetChatMessagesByChatID(ctx, database.GetChatMessagesByChatIDParams{
2706 ChatID: opts.ChatID,
2707 AfterID: lastAssistant.ID,
2708 })
2709 if afterErr != nil {
2710 return xerrors.Errorf("get messages after assistant: %w", afterErr)
2711 }
2712 handledCallIDs := make(map[string]bool)
2713 for _, msg := range afterMsgs {
2714 if msg.Role != database.ChatMessageRoleTool {
2715 continue
2716 }

Calls 12

signalWakeMethod · 0.95
ParseContentFunction · 0.92
MarshalPartsFunction · 0.92
parseDynamicToolNamesFunction · 0.85
InTxMethod · 0.65
GetChatByIDForUpdateMethod · 0.65
ValidMethod · 0.65
InsertChatMessagesMethod · 0.65
UpdateChatStatusMethod · 0.65
ErrorfMethod · 0.45