yao/openapi/agent/robot/completions.go
Max 4f9238ac95 Add chat metadata persistence and enhance executor goals injection tests
- Implement TestEnsureChatMetadata to verify that metadata, including robot_id, is correctly persisted in chat records.
- Update EnsureChat method to store metadata from the context when creating chat records.
- Introduce TestExecutorGoalsInjection to validate that pre-confirmed goals are injected into executions from TriggerInput.Data.
- Enhance executor logic to handle goal injection and persistence, ensuring accurate execution titles.
- Modify chat filtering to support chat_id_prefix for improved chat retrieval based on robot identifiers.
2026-02-28 13:57:45 +08:00

124 lines
3.8 KiB
Go

package robot
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
robotstore "github.com/yaoapp/yao/agent/robot/store"
robottypes "github.com/yaoapp/yao/agent/robot/types"
"github.com/yaoapp/yao/openapi/chat"
"github.com/yaoapp/yao/openapi/response"
)
// resolveHostAssistantID resolves the host assistant ID from a robot member ID.
// It fetches the RobotRecord, parses its config, and returns the PhaseHost agent ID.
func resolveHostAssistantID(ctx context.Context, memberID string) (string, *robotstore.RobotRecord, error) {
store := robotstore.NewRobotStore()
record, err := store.Get(ctx, memberID)
if err != nil {
return "", nil, fmt.Errorf("failed to get robot: %w", err)
}
if record == nil {
return "", nil, fmt.Errorf("robot not found: %s", memberID)
}
config, err := robottypes.ParseConfig(record.RobotConfig)
if err != nil {
return "", nil, fmt.Errorf("failed to parse robot config: %w", err)
}
var hostID string
if config != nil && config.Resources != nil {
hostID = config.Resources.GetPhaseAgent(robottypes.PhaseHost)
} else {
hostID = "__yao." + string(robottypes.PhaseHost)
}
return hostID, record, nil
}
// injectAssistantID sets the assistant_id query parameter on the gin request,
// so that downstream GetCompletionRequest can pick it up.
func injectAssistantID(c *gin.Context, assistantID string) {
q := c.Request.URL.Query()
q.Set("assistant_id", assistantID)
c.Request.URL.RawQuery = q.Encode()
}
// RobotCompletions handles POST /v1/agent/robots/:id/completions
// Mirror API that resolves the robot's host assistant and delegates to standard chat completions.
func RobotCompletions(c *gin.Context) {
robotID := c.Param("id")
if robotID == "" {
response.RespondWithError(c, response.StatusBadRequest, &response.ErrorResponse{
Code: response.ErrInvalidRequest.Code,
ErrorDescription: "robot id is required",
})
return
}
hostID, _, err := resolveHostAssistantID(c.Request.Context(), robotID)
if err != nil {
response.RespondWithError(c, response.StatusInternalServerError, &response.ErrorResponse{
Code: response.ErrServerError.Code,
ErrorDescription: err.Error(),
})
return
}
injectAssistantID(c, hostID)
chat.GinCreateCompletions(c)
}
// RobotAppendMessages handles POST /v1/agent/robots/:id/completions/:context_id/append
// Mirror API that resolves the robot's host assistant and delegates to standard append.
func RobotAppendMessages(c *gin.Context) {
robotID := c.Param("id")
if robotID == "" {
response.RespondWithError(c, response.StatusBadRequest, &response.ErrorResponse{
Code: response.ErrInvalidRequest.Code,
ErrorDescription: "robot id is required",
})
return
}
hostID, _, err := resolveHostAssistantID(c.Request.Context(), robotID)
if err != nil {
response.RespondWithError(c, response.StatusInternalServerError, &response.ErrorResponse{
Code: response.ErrServerError.Code,
ErrorDescription: err.Error(),
})
return
}
injectAssistantID(c, hostID)
chat.GinAppendMessages(c)
}
// RobotHostID handles GET /v1/agent/robots/:id/host
// Returns the host assistant ID for a robot (used by frontend to know which assistant to chat with).
func RobotHostID(c *gin.Context) {
robotID := c.Param("id")
if robotID == "" {
response.RespondWithError(c, response.StatusBadRequest, &response.ErrorResponse{
Code: response.ErrInvalidRequest.Code,
ErrorDescription: "robot id is required",
})
return
}
hostID, _, err := resolveHostAssistantID(c.Request.Context(), robotID)
if err != nil {
response.RespondWithError(c, response.StatusInternalServerError, &response.ErrorResponse{
Code: response.ErrServerError.Code,
ErrorDescription: err.Error(),
})
return
}
response.RespondWithSuccess(c, response.StatusOK, gin.H{
"assistant_id": hostID,
"robot_id": robotID,
})
}