Merge pull request #1343 from trheyi/main
Refactor assistant settings to use 'Uses' instead of 'Use'
This commit is contained in:
commit
4f8c06fdee
8 changed files with 311 additions and 44 deletions
|
|
@ -397,7 +397,7 @@ func (agent *API) handleChatLatest(c *gin.Context) {
|
|||
// Create a new chat
|
||||
if len(chats.Groups) == 0 || len(chats.Groups[0].Chats) == 0 {
|
||||
|
||||
assistantID := agent.Use.Default
|
||||
assistantID := agent.Uses.Default
|
||||
queryAssistantID := c.Query("assistant_id")
|
||||
if queryAssistantID != "" {
|
||||
assistantID = queryAssistantID
|
||||
|
|
@ -416,7 +416,7 @@ func (agent *API) handleChatLatest(c *gin.Context) {
|
|||
"assistant_id": ast.ID,
|
||||
"assistant_name": ast.GetName(locale),
|
||||
"assistant_avatar": ast.Avatar,
|
||||
"assistant_deleteable": agent.Use.Default != ast.ID,
|
||||
"assistant_deleteable": agent.Uses.Default != ast.ID,
|
||||
}})
|
||||
c.Done()
|
||||
return
|
||||
|
|
@ -439,10 +439,10 @@ func (agent *API) handleChatLatest(c *gin.Context) {
|
|||
|
||||
// assistant_id is nil return the default assistant
|
||||
if chat.Chat["assistant_id"] == nil {
|
||||
chat.Chat["assistant_id"] = agent.Use.Default
|
||||
chat.Chat["assistant_id"] = agent.Uses.Default
|
||||
|
||||
// Get the assistant info
|
||||
ast, err := assistant.Get(agent.Use.Default)
|
||||
ast, err := assistant.Get(agent.Uses.Default)
|
||||
if err != nil {
|
||||
c.JSON(500, gin.H{"message": err.Error(), "code": 500})
|
||||
c.Done()
|
||||
|
|
@ -452,7 +452,7 @@ func (agent *API) handleChatLatest(c *gin.Context) {
|
|||
chat.Chat["assistant_avatar"] = ast.Avatar
|
||||
}
|
||||
|
||||
chat.Chat["assistant_deleteable"] = agent.Use.Default != chat.Chat["assistant_id"]
|
||||
chat.Chat["assistant_deleteable"] = agent.Uses.Default != chat.Chat["assistant_id"]
|
||||
c.JSON(200, map[string]interface{}{"data": chat})
|
||||
c.Done()
|
||||
}
|
||||
|
|
@ -488,10 +488,10 @@ func (agent *API) handleChatDetail(c *gin.Context) {
|
|||
|
||||
// assistant_id is nil return the default assistant
|
||||
if chat.Chat["assistant_id"] == nil {
|
||||
chat.Chat["assistant_id"] = agent.Use.Default
|
||||
chat.Chat["assistant_id"] = agent.Uses.Default
|
||||
|
||||
// Get the assistant info
|
||||
ast, err := assistant.Get(agent.Use.Default)
|
||||
ast, err := assistant.Get(agent.Uses.Default)
|
||||
if err != nil {
|
||||
c.JSON(500, gin.H{"message": err.Error(), "code": 500})
|
||||
c.Done()
|
||||
|
|
@ -501,7 +501,7 @@ func (agent *API) handleChatDetail(c *gin.Context) {
|
|||
chat.Chat["assistant_avatar"] = ast.Avatar
|
||||
}
|
||||
|
||||
chat.Chat["assistant_deleteable"] = agent.Use.Default != chat.Chat["assistant_id"]
|
||||
chat.Chat["assistant_deleteable"] = agent.Uses.Default != chat.Chat["assistant_id"]
|
||||
c.JSON(200, map[string]interface{}{"data": chat})
|
||||
c.Done()
|
||||
}
|
||||
|
|
@ -674,7 +674,7 @@ func (agent *API) handleGenerateTitle(c *gin.Context) {
|
|||
|
||||
// // Set the assistant ID
|
||||
// ctx = chatctx.WithHistoryVisible(ctx, false)
|
||||
// ctx = chatctx.WithAssistantID(ctx, agent.Use.Title)
|
||||
// ctx = chatctx.WithAssistantID(ctx, agent.Uses.Title)
|
||||
|
||||
err := agent.Answer(ctx, content, c)
|
||||
|
||||
|
|
@ -714,7 +714,7 @@ func (agent *API) handleGeneratePrompts(c *gin.Context) {
|
|||
|
||||
// // Set the assistant ID
|
||||
// ctx = chatctx.WithHistoryVisible(ctx, false)
|
||||
// ctx = chatctx.WithAssistantID(ctx, agent.Use.Prompt)
|
||||
// ctx = chatctx.WithAssistantID(ctx, agent.Uses.Prompt)
|
||||
err := agent.Answer(ctx, content, c)
|
||||
|
||||
// Error handling
|
||||
|
|
|
|||
|
|
@ -376,17 +376,40 @@ func (ast *Assistant) Info(locale ...string) *context.AssistantInfo {
|
|||
}
|
||||
|
||||
// buildMessages builds the final message list with proper priority
|
||||
// Priority: createResponse.Messages > input messages
|
||||
// Priority: Prompts > createResponse.Messages > input messages
|
||||
// If createResponse is nil or has no messages, use input messages
|
||||
func (ast *Assistant) buildMessages(ctx *context.Context, messages []context.Message, createResponse *context.HookCreateResponse) ([]context.Message, error) {
|
||||
// If createResponse is nil or has no messages, return input messages as-is
|
||||
var finalMessages []context.Message
|
||||
|
||||
// If createResponse is nil or has no messages, use input messages
|
||||
if createResponse == nil || len(createResponse.Messages) == 0 {
|
||||
return messages, nil
|
||||
finalMessages = messages
|
||||
} else {
|
||||
// createResponse.Messages takes priority over input messages
|
||||
finalMessages = createResponse.Messages
|
||||
}
|
||||
|
||||
// createResponse.Messages takes highest priority
|
||||
// Return them directly as they override everything
|
||||
return createResponse.Messages, nil
|
||||
// ⚠️ Just for testing, will remove later
|
||||
// If we have prompts, prepend them to the beginning
|
||||
if len(ast.Prompts) > 0 {
|
||||
promptMessages := make([]context.Message, 0, len(ast.Prompts))
|
||||
for _, prompt := range ast.Prompts {
|
||||
msg := context.Message{
|
||||
Role: context.MessageRole(prompt.Role),
|
||||
Content: prompt.Content,
|
||||
}
|
||||
// Add name if provided
|
||||
if prompt.Name != "" {
|
||||
name := prompt.Name
|
||||
msg.Name = &name
|
||||
}
|
||||
promptMessages = append(promptMessages, msg)
|
||||
}
|
||||
// Prepend prompt messages to the beginning
|
||||
finalMessages = append(promptMessages, finalMessages...)
|
||||
}
|
||||
|
||||
return finalMessages, nil
|
||||
}
|
||||
|
||||
// buildCompletionOptions builds completion options from multiple sources
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ func GetCompletionRequest(c *gin.Context, cache store.Store) (*CompletionRequest
|
|||
},
|
||||
Route: GetRoute(c, completionReq),
|
||||
Metadata: GetMetadata(c, completionReq),
|
||||
Skip: GetSkip(c, completionReq),
|
||||
}
|
||||
|
||||
// Initialize interrupt controller
|
||||
|
|
@ -360,6 +361,30 @@ func GetRoute(c *gin.Context, req *CompletionRequest) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// GetSkip extracts skip configuration from request with priority:
|
||||
// 1. CompletionRequest.Skip (from payload body) - Priority
|
||||
// 2. Individual query parameters: "skip_history", "skip_trace"
|
||||
func GetSkip(c *gin.Context, req *CompletionRequest) *Skip {
|
||||
// Priority 1: From CompletionRequest body (most direct)
|
||||
if req != nil && req.Skip != nil {
|
||||
return req.Skip
|
||||
}
|
||||
|
||||
// Priority 2: Individual query parameters (recommended for query usage)
|
||||
skipHistory := c.Query("skip_history") == "true" || c.Query("skip_history") == "1"
|
||||
skipTrace := c.Query("skip_trace") == "true" || c.Query("skip_trace") == "1"
|
||||
|
||||
// Check if any skip parameter is set
|
||||
if c.Query("skip_history") != "" || c.Query("skip_trace") != "" {
|
||||
return &Skip{
|
||||
History: skipHistory,
|
||||
Trace: skipTrace,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMetadata extracts metadata from request with priority:
|
||||
// 1. Query parameter "metadata" (JSON string)
|
||||
// 2. Header "X-Yao-Metadata" (Base64 encoded JSON string)
|
||||
|
|
|
|||
|
|
@ -930,3 +930,208 @@ func TestGetCompletionRequest_ChatIDFallback(t *testing.T) {
|
|||
t.Errorf("Expected ChatID to be at least 8 characters, got %d", len(ctx.ChatID))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSkip_FromBody(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
req := httptest.NewRequest("POST", "/chat/completions", nil)
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
|
||||
completionReq := &CompletionRequest{
|
||||
Skip: &Skip{
|
||||
History: true,
|
||||
Trace: false,
|
||||
},
|
||||
}
|
||||
|
||||
skip := GetSkip(c, completionReq)
|
||||
if skip == nil {
|
||||
t.Fatal("Expected skip to be returned")
|
||||
}
|
||||
|
||||
if !skip.History {
|
||||
t.Error("Expected skip.History to be true")
|
||||
}
|
||||
|
||||
if skip.Trace {
|
||||
t.Error("Expected skip.Trace to be false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSkip_FromQueryParams(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
req := httptest.NewRequest("GET", "/chat/completions?skip_history=true&skip_trace=false", nil)
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
|
||||
skip := GetSkip(c, nil)
|
||||
if skip == nil {
|
||||
t.Fatal("Expected skip to be returned")
|
||||
}
|
||||
|
||||
if !skip.History {
|
||||
t.Error("Expected skip.History to be true from query param")
|
||||
}
|
||||
|
||||
if skip.Trace {
|
||||
t.Error("Expected skip.Trace to be false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSkip_FromQueryParams_ShortForm(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
req := httptest.NewRequest("GET", "/chat/completions?skip_history=1&skip_trace=1", nil)
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
|
||||
skip := GetSkip(c, nil)
|
||||
if skip == nil {
|
||||
t.Fatal("Expected skip to be returned")
|
||||
}
|
||||
|
||||
if !skip.History {
|
||||
t.Error("Expected skip.History to be true from query param (1)")
|
||||
}
|
||||
|
||||
if !skip.Trace {
|
||||
t.Error("Expected skip.Trace to be true from query param (1)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSkip_Priority(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Body should take priority over query
|
||||
req := httptest.NewRequest("POST", "/chat/completions?skip_history=false&skip_trace=false", nil)
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
|
||||
completionReq := &CompletionRequest{
|
||||
Skip: &Skip{
|
||||
History: true,
|
||||
Trace: true,
|
||||
},
|
||||
}
|
||||
|
||||
skip := GetSkip(c, completionReq)
|
||||
if skip == nil {
|
||||
t.Fatal("Expected skip to be returned")
|
||||
}
|
||||
|
||||
// Body should take priority
|
||||
if !skip.History {
|
||||
t.Error("Expected body parameter to take priority, skip.History should be true")
|
||||
}
|
||||
|
||||
if !skip.Trace {
|
||||
t.Error("Expected body parameter to take priority, skip.Trace should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSkip_Nil(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
req := httptest.NewRequest("GET", "/chat/completions", nil)
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
|
||||
skip := GetSkip(c, nil)
|
||||
if skip != nil {
|
||||
t.Errorf("Expected skip to be nil, got %v", skip)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSkip_OnlyHistorySet(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
req := httptest.NewRequest("GET", "/chat/completions?skip_history=true", nil)
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
|
||||
skip := GetSkip(c, nil)
|
||||
if skip == nil {
|
||||
t.Fatal("Expected skip to be returned")
|
||||
}
|
||||
|
||||
if !skip.History {
|
||||
t.Error("Expected skip.History to be true")
|
||||
}
|
||||
|
||||
if skip.Trace {
|
||||
t.Error("Expected skip.Trace to be false (default)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSkip_FromBodyViaParseRequest(t *testing.T) {
|
||||
test.Prepare(t, config.Conf)
|
||||
defer test.Clean()
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Test parsing Skip from full request body
|
||||
messages := []Message{
|
||||
{
|
||||
Role: RoleUser,
|
||||
Content: "Generate a title for this chat",
|
||||
},
|
||||
}
|
||||
|
||||
requestBody := map[string]interface{}{
|
||||
"model": "workers.system.title-yao_test",
|
||||
"messages": messages,
|
||||
"skip": map[string]interface{}{
|
||||
"history": true,
|
||||
"trace": false,
|
||||
},
|
||||
}
|
||||
|
||||
bodyBytes, _ := json.Marshal(requestBody)
|
||||
|
||||
req := httptest.NewRequest("POST", "/chat/completions", bytes.NewBuffer(bodyBytes))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
|
||||
// Parse the request
|
||||
completionReq, err := parseCompletionRequestData(c)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse completion request: %v", err)
|
||||
}
|
||||
|
||||
// Verify Skip was parsed correctly
|
||||
if completionReq.Skip == nil {
|
||||
t.Fatal("Expected Skip to be parsed from body, got nil")
|
||||
}
|
||||
|
||||
if !completionReq.Skip.History {
|
||||
t.Error("Expected Skip.History to be true from body")
|
||||
}
|
||||
|
||||
if completionReq.Skip.Trace {
|
||||
t.Error("Expected Skip.Trace to be false from body")
|
||||
}
|
||||
|
||||
// Now test GetSkip function with the parsed request
|
||||
skip := GetSkip(c, completionReq)
|
||||
if skip == nil {
|
||||
t.Fatal("Expected GetSkip to return skip configuration")
|
||||
}
|
||||
|
||||
if !skip.History {
|
||||
t.Error("Expected GetSkip to return History=true")
|
||||
}
|
||||
|
||||
if skip.Trace {
|
||||
t.Error("Expected GetSkip to return Trace=false")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,6 +186,12 @@ type AssistantInfo struct {
|
|||
Description string `json:"description,omitempty"` // Assistant Description
|
||||
}
|
||||
|
||||
// Skip configuration for what to skip in this request
|
||||
type Skip struct {
|
||||
History bool `json:"history"` // Skip saving chat history (for internal calls like title/prompt generation)
|
||||
Trace bool `json:"trace"` // Skip trace logging
|
||||
}
|
||||
|
||||
// Context the context
|
||||
type Context struct {
|
||||
|
||||
|
|
@ -197,6 +203,7 @@ type Context struct {
|
|||
Stack *Stack `json:"-"` // Stack, current active stack of the request
|
||||
Stacks map[string]*Stack `json:"-"` // Stacks, all stacks in this request (for trace logging)
|
||||
Writer Writer `json:"-"` // Writer, it will be used to write response data to the client
|
||||
Skip *Skip `json:"skip,omitempty"` // Skip configuration (history, trace, etc.), nil means don't skip anything
|
||||
trace traceTypes.Manager `json:"-"` // Trace manager, lazy initialized on first access
|
||||
|
||||
// Model capabilities (set by assistant, used by output adapters)
|
||||
|
|
@ -443,6 +450,7 @@ type CompletionRequest struct {
|
|||
// CUI Context information
|
||||
Route string `json:"route,omitempty"` // Optional: route of the request for CUI context
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"` // Optional: metadata to pass to the page for CUI context
|
||||
Skip *Skip `json:"skip,omitempty"` // Optional: skip configuration (history, trace, etc.)
|
||||
}
|
||||
|
||||
// AudioConfig represents the audio output configuration for models that support audio
|
||||
|
|
|
|||
|
|
@ -45,18 +45,18 @@ func Load(cfg config.Config) error {
|
|||
}
|
||||
|
||||
// Default Assistant, Agent is the developer name, Mohe is the brand name of the assistant
|
||||
if setting.Use == nil {
|
||||
setting.Use = &types.Use{Default: "mohe"} // Agent is the developer name, Mohe is the brand name of the assistant
|
||||
if setting.Uses == nil {
|
||||
setting.Uses = &types.Uses{Default: "mohe"} // Agent is the developer name, Mohe is the brand name of the assistant
|
||||
}
|
||||
|
||||
// Title Assistant
|
||||
if setting.Use.Title == "" {
|
||||
setting.Use.Title = setting.Use.Default
|
||||
if setting.Uses.Title == "" {
|
||||
setting.Uses.Title = setting.Uses.Default
|
||||
}
|
||||
|
||||
// Prompt Assistant
|
||||
if setting.Use.Prompt == "" {
|
||||
setting.Use.Prompt = setting.Use.Default
|
||||
if setting.Uses.Prompt == "" {
|
||||
setting.Uses.Prompt = setting.Uses.Default
|
||||
}
|
||||
|
||||
// Initialize Agent API
|
||||
|
|
@ -173,12 +173,12 @@ func initAssistant() error {
|
|||
}
|
||||
|
||||
// Set global Uses configuration
|
||||
if api.Agent.DSL.Use != nil {
|
||||
if api.Agent.DSL.Uses != nil {
|
||||
globalUses := &context.Uses{
|
||||
Vision: api.Agent.DSL.Use.Vision,
|
||||
Audio: api.Agent.DSL.Use.Audio,
|
||||
Search: api.Agent.DSL.Use.Search,
|
||||
Fetch: api.Agent.DSL.Use.Fetch,
|
||||
Vision: api.Agent.DSL.Uses.Vision,
|
||||
Audio: api.Agent.DSL.Uses.Audio,
|
||||
Search: api.Agent.DSL.Uses.Search,
|
||||
Fetch: api.Agent.DSL.Uses.Fetch,
|
||||
}
|
||||
assistant.SetGlobalUses(globalUses)
|
||||
}
|
||||
|
|
@ -205,8 +205,8 @@ func initAssistant() error {
|
|||
|
||||
// defaultAssistant get the default assistant
|
||||
func defaultAssistant() (*assistant.Assistant, error) {
|
||||
if api.Agent.DSL.Use == nil || api.Agent.DSL.Use.Default == "" {
|
||||
if api.Agent.DSL.Uses == nil || api.Agent.DSL.Uses.Default == "" {
|
||||
return nil, fmt.Errorf("default assistant not found")
|
||||
}
|
||||
return assistant.Get(api.Agent.DSL.Use.Default)
|
||||
return assistant.Get(api.Agent.DSL.Uses.Default)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ type DSL struct {
|
|||
|
||||
// Agent Global Settings
|
||||
// ===============================
|
||||
Use *Use `json:"use,omitempty" yaml:"use,omitempty"` // Which assistant to use default, title, prompt
|
||||
Uses *Uses `json:"uses,omitempty" yaml:"uses,omitempty"` // Which assistant to use default, title, prompt
|
||||
StoreSetting store.Setting `json:"store" yaml:"store"` // The store setting of the assistant
|
||||
Cache string `json:"cache" yaml:"cache"` // The cache store of the assistant, if not set, default is "__yao.agent.cache"
|
||||
|
||||
|
|
@ -38,9 +38,9 @@ type DSL struct {
|
|||
GuardHandlers []gin.HandlerFunc `json:"-" yaml:"-"`
|
||||
}
|
||||
|
||||
// Use the default assistant settings
|
||||
// Uses the default assistant settings
|
||||
// ===============================
|
||||
type Use struct {
|
||||
type Uses struct {
|
||||
Default string `json:"default,omitempty" yaml:"default,omitempty"` // The default assistant to use
|
||||
Title string `json:"title,omitempty" yaml:"title,omitempty"` // The assistant for generating the topic title.
|
||||
Prompt string `json:"prompt,omitempty" yaml:"prompt,omitempty"` // The assistant for generating the prompt.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import (
|
|||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/yaoapp/gou/api"
|
||||
"github.com/yaoapp/gou/application"
|
||||
"github.com/yaoapp/gou/connector"
|
||||
"github.com/yaoapp/gou/process"
|
||||
v8 "github.com/yaoapp/gou/runtime/v8"
|
||||
"github.com/yaoapp/gou/session"
|
||||
|
|
@ -552,6 +551,13 @@ func processXgen(process *process.Process) interface{} {
|
|||
// The default assistant
|
||||
agentConfig := map[string]interface{}{}
|
||||
if agent.Agent != nil {
|
||||
|
||||
// Add Uses Settings
|
||||
if agent.Agent.DSL != nil && agent.Agent.DSL.Uses != nil {
|
||||
agentConfig["uses"] = agent.Agent.DSL.Uses
|
||||
}
|
||||
|
||||
// Add Default Assistant Settings ( Will be removed later )
|
||||
if ast, ok := agent.Agent.Assistant.(*assistant.Assistant); ok {
|
||||
agentConfig["default"] = map[string]interface{}{
|
||||
"assistant_id": ast.ID,
|
||||
|
|
@ -562,8 +568,8 @@ func processXgen(process *process.Process) interface{} {
|
|||
}
|
||||
}
|
||||
|
||||
// Available connectors
|
||||
agentConfig["connectors"] = connector.AIConnectors
|
||||
// Available connectors Removed later, It not be used yet, use the openapi instead.
|
||||
// agentConfig["connectors"] = connector.AIConnectors
|
||||
}
|
||||
|
||||
// OpenAPI Settings
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue