Add endpoint to append messages to running completions

- Introduced a new POST endpoint `/completions/:context_id/append` to allow appending messages to ongoing chat completions.
- Implemented `GinAppendMessages` function to handle request validation, including context ID and message requirements.
- Added `AppendMessagesRequest` struct to define the request body format, ensuring proper handling of interrupt types and messages.
- Enhanced error responses for invalid requests to improve user feedback and debugging capabilities.
This commit is contained in:
Max 2025-11-22 18:15:40 +08:00
parent ea167b35a7
commit 7285972aa9
3 changed files with 84 additions and 0 deletions

View file

@ -30,6 +30,9 @@ func Attach(group *gin.RouterGroup, oauth types.OAuth) {
// Delete Chat Completion
group.DELETE("/completions/:completion_id", placeholder)
// Append messages to running completion
group.POST("/completions/:context_id/append", GinAppendMessages)
}
func placeholder(c *gin.Context) {

View file

@ -2,6 +2,7 @@ package chat
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
"github.com/yaoapp/kun/log"
@ -189,3 +190,74 @@ func GinCreateCompletions(c *gin.Context) {
// GinUpdateCompletions handles PUT /chat/:assistant_id/completions - Update a chat completion metadata
func GinUpdateCompletions(c *gin.Context) {}
// GinAppendMessages handles POST /chat/:assistant_id/completions/:context_id/append
// Appends messages to a running completion (for user pre-input while AI is still generating)
func GinAppendMessages(c *gin.Context) {
// Get context_id from URL parameter
contextID := c.Param("context_id")
if contextID == "" {
response.RespondWithError(c, response.StatusBadRequest, &response.ErrorResponse{
Code: response.ErrInvalidRequest.Code,
ErrorDescription: "context_id is required",
})
return
}
// Parse request body
var req AppendMessagesRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.RespondWithError(c, response.StatusBadRequest, &response.ErrorResponse{
Code: response.ErrInvalidRequest.Code,
ErrorDescription: "Invalid request body: " + err.Error(),
})
return
}
// Validate interrupt type
if req.Type != context.InterruptGraceful && req.Type != context.InterruptForce {
response.RespondWithError(c, response.StatusBadRequest, &response.ErrorResponse{
Code: response.ErrInvalidRequest.Code,
ErrorDescription: "Invalid interrupt type. Must be 'graceful' or 'force'",
})
return
}
// Validate messages
if len(req.Messages) == 0 {
response.RespondWithError(c, response.StatusBadRequest, &response.ErrorResponse{
Code: response.ErrInvalidRequest.Code,
ErrorDescription: "At least one message is required",
})
return
}
// Create interrupt signal
signal := &context.InterruptSignal{
Type: req.Type,
Messages: req.Messages,
Timestamp: time.Now().UnixMilli(),
Metadata: req.Metadata,
}
// Send interrupt signal to the context
if err := context.SendInterrupt(contextID, signal); err != nil {
log.Trace("[INTERRUPT] Failed to send interrupt signal: context_id=%s, error=%v", contextID, err)
response.RespondWithError(c, response.StatusInternalServerError, &response.ErrorResponse{
Code: response.ErrServerError.Code,
ErrorDescription: "Failed to send interrupt: " + err.Error(),
})
return
}
log.Trace("[INTERRUPT] Interrupt signal sent successfully: context_id=%s, type=%s, messages=%d",
contextID, req.Type, len(req.Messages))
// Return success response
response.RespondWithSuccess(c, response.StatusOK, gin.H{
"message": "Messages appended successfully",
"context_id": contextID,
"type": req.Type,
"timestamp": signal.Timestamp,
})
}

View file

@ -1 +1,10 @@
package chat
import "github.com/yaoapp/yao/agent/context"
// AppendMessagesRequest represents the request body for appending messages to running completion
type AppendMessagesRequest struct {
Type context.InterruptType `json:"type" binding:"required"` // Interrupt type: "graceful" or "force"
Messages []context.Message `json:"messages" binding:"required"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}