yao/openapi/agent/robot/completions.go
Max c4696380f4 feat(robot): implement global phase agent resolution for improved agent configuration
- Introduced a new global phase agent resolver to streamline agent ID retrieval for various robot pipeline phases, enhancing flexibility in agent configuration.
- Updated existing phase agent retrieval logic to prioritize per-robot configurations, falling back to global settings when necessary.
- Enhanced error handling to provide clearer messages when no agent is configured for specific phases.
- Added tests to validate the new resolution logic and ensure proper functionality across different configurations.
2026-03-24 12:11:55 +08:00

122 lines
3.9 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)
}
hostID := robottypes.ResolvePhaseAgent(config, robottypes.PhaseHost)
if hostID == "" {
return "", nil, fmt.Errorf("no Host Agent configured for robot %s (set uses.host in agent.yml or resources.phases in robot config)", memberID)
}
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,
})
}