From 124bd38f7a817df9ded9717e0bc3cd27cc837a57 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 14 Nov 2025 19:17:21 +0800 Subject: [PATCH] Implement global uses configuration and enhance assistant capabilities - Added global uses configuration to the assistant, allowing for centralized management of vision, audio, search, and fetch settings. - Updated the Assistant struct and related methods to support the new Uses configuration, improving flexibility in assistant operations. - Refactored the Stream method to utilize the new CompletionResponse type, enhancing response handling. - Introduced new methods for building requests and managing capabilities, streamlining the assistant's interaction with various connectors. --- agent/assistant/agent.go | 362 ++++++++++++++++++++- agent/assistant/agent_test.go | 262 +++++++++++++++ agent/assistant/hook/done.go | 2 +- agent/assistant/hook/failback.go | 6 +- agent/assistant/load.go | 6 + agent/assistant/types.go | 11 +- agent/context/types.go | 5 +- agent/context/types_llm.go | 146 +++++++++ agent/context/types_wrapper.go | 49 +++ agent/llm/handlers/handlers.go | 51 +++ agent/llm/handlers/stream.go | 86 +++++ agent/llm/interfaces.go | 4 +- agent/llm/llm.go | 13 +- agent/llm/providers/README.md | 319 ++++++++++++++++++ agent/llm/providers/audio/audio.go | 59 ++++ agent/llm/providers/base/base.go | 65 ++++ agent/llm/providers/factory.go | 56 ++++ agent/llm/providers/legacy/legacy.go | 64 ++++ agent/llm/providers/openai/openai.go | 50 +++ agent/llm/providers/reasoning/reasoning.go | 115 +++++++ agent/llm/providers/vision/vision.go | 47 +++ agent/llm/stream.go | 12 + agent/llm/types.go | 4 - agent/load.go | 11 + agent/store/types/convert.go | 9 + agent/store/types/types.go | 1 + agent/store/xun/assistant.go | 17 +- agent/store/xun/assistant_test.go | 218 +++++++++++++ agent/types/types.go | 3 +- data/bindata.go | 284 ++++++++-------- yao/models/agent/assistant.mod.yao | 7 + 31 files changed, 2166 insertions(+), 178 deletions(-) create mode 100644 agent/assistant/agent_test.go create mode 100644 agent/context/types_llm.go create mode 100644 agent/context/types_wrapper.go create mode 100644 agent/llm/handlers/handlers.go create mode 100644 agent/llm/handlers/stream.go create mode 100644 agent/llm/providers/README.md create mode 100644 agent/llm/providers/audio/audio.go create mode 100644 agent/llm/providers/base/base.go create mode 100644 agent/llm/providers/factory.go create mode 100644 agent/llm/providers/legacy/legacy.go create mode 100644 agent/llm/providers/openai/openai.go create mode 100644 agent/llm/providers/reasoning/reasoning.go create mode 100644 agent/llm/providers/vision/vision.go create mode 100644 agent/llm/stream.go delete mode 100644 agent/llm/types.go diff --git a/agent/assistant/agent.go b/agent/assistant/agent.go index b5f9383d..5675aef0 100644 --- a/agent/assistant/agent.go +++ b/agent/assistant/agent.go @@ -1,12 +1,16 @@ package assistant import ( + "fmt" + + "github.com/yaoapp/gou/connector" "github.com/yaoapp/yao/agent/context" "github.com/yaoapp/yao/agent/llm" ) // Stream stream the agent -func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Message, handler context.StreamFunc) (*context.Response, error) { +// handler is optional, if not provided, a default handler will be used +func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Message, handler ...context.StreamFunc) (*context.Response, error) { var err error @@ -31,27 +35,44 @@ func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Messa return nil, err } } - _ = createResponse // createResponse is available for further processing - var completionOptions *llm.CompletionOptions // default is nil + var completionOptions *context.CompletionOptions // default is nil // LLM Call Stream ( Optional ) var completionMessages []context.Message - var completionResponse *context.ResponseCompletion + var completionResponse *context.CompletionResponse if ast.Prompts != nil || ast.MCP != nil { - llm, err := llm.New(ast.GetConnector(ctx)) + // Build the LLM request first + completionMessages, completionOptions, err = ast.BuildRequest(ctx, inputMessages, createResponse) if err != nil { return nil, err } - // Build the LLM request - completionMessages, completionOptions, err = ast.BuildLLMRequest(ctx, inputMessages, createResponse) + // Get connector object and capabilities + conn, capabilities, err := ast.GetConnector(ctx) if err != nil { return nil, err } + // Set capabilities in options if not already set + if completionOptions.Capabilities == nil && capabilities != nil { + completionOptions.Capabilities = capabilities + } + + // Create LLM instance with connector and options + llmInstance, err := llm.New(conn, completionOptions) + if err != nil { + return nil, err + } + + // Use provided handler or default handler + streamHandler := llm.DefaultStreamHandler(ctx) + if len(handler) > 0 && handler[0] != nil { + streamHandler = handler[0] + } + // Call the LLM Completion Stream - completionResponse, err = llm.Stream(ctx, completionMessages, completionOptions, handler) + completionResponse, err = llmInstance.Stream(ctx, completionMessages, completionOptions, streamHandler) if err != nil { return nil, err } @@ -81,17 +102,328 @@ func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Messa return &context.Response{Create: createResponse, Done: doneResponse, Completion: completionResponse}, nil } -// GetConnector get the connector from the context -func (ast *Assistant) GetConnector(ctx *context.Context) string { +// GetConnector get the connector object, capabilities, and error with priority: createResponse > ctx > ast +// Note: createResponse.Connector is already applied to ctx.Connector by applyContextAdjustments in create.go +// Returns: (connector, capabilities, error) +func (ast *Assistant) GetConnector(ctx *context.Context) (connector.Connector, *context.ModelCapabilities, error) { + // Determine connector ID with priority + connectorID := ast.Connector if ctx.Connector != "" { - return ctx.Connector + connectorID = ctx.Connector } - return ast.Connector + + // If empty, return error + if connectorID == "" { + return nil, nil, fmt.Errorf("connector not specified") + } + + // Load gou connector + conn, err := connector.Select(connectorID) + if err != nil { + return nil, nil, err + } + + // Get connector capabilities from settings + capabilities := ast.getConnectorCapabilities(connectorID) + + return conn, capabilities, nil } -// BuildLLMRequest build the LLM request -func (ast *Assistant) BuildLLMRequest(ctx *context.Context, messages []context.Message, createResponse *context.HookCreateResponse) ([]context.Message, *llm.CompletionOptions, error) { - return messages, nil, nil +// getConnectorCapabilities get the capabilities of a connector from settings +func (ast *Assistant) getConnectorCapabilities(connectorID string) *context.ModelCapabilities { + // Get connector setting from global settings + setting, exists := connectorSettings[connectorID] + if !exists { + return nil + } + + // Convert ConnectorSetting to ModelCapabilities + capabilities := &context.ModelCapabilities{} + + if setting.Vision { + v := true + capabilities.Vision = &v + } + + // Handle both Tools (deprecated) and ToolCalls + if setting.ToolCalls || setting.Tools { + v := true + capabilities.ToolCalls = &v + } + + if setting.Audio { + v := true + capabilities.Audio = &v + } + + if setting.Reasoning { + v := true + capabilities.Reasoning = &v + } + + if setting.Streaming { + v := true + capabilities.Streaming = &v + } + + if setting.JSON { + v := true + capabilities.JSON = &v + } + + if setting.Multimodal { + v := true + capabilities.Multimodal = &v + } + + return capabilities +} + +// BuildRequest build the LLM request +func (ast *Assistant) BuildRequest(ctx *context.Context, messages []context.Message, createResponse *context.HookCreateResponse) ([]context.Message, *context.CompletionOptions, error) { + // Build final messages with proper priority + finalMessages, err := ast.buildMessages(ctx, messages, createResponse) + if err != nil { + return nil, nil, err + } + + // Build completion options from createResponse and ctx + options := ast.buildCompletionOptions(ctx, createResponse) + + return finalMessages, options, nil +} + +// buildMessages builds the final message list with proper priority +// Priority: 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 + if createResponse == nil || len(createResponse.Messages) == 0 { + return messages, nil + } + + // createResponse.Messages takes highest priority + // Return them directly as they override everything + return createResponse.Messages, nil +} + +// buildCompletionOptions builds completion options from multiple sources +// Priority (lowest to highest, later overrides earlier): ast > ctx > createResponse +// The priority means: if createResponse has a value, use it; else use ctx; else use ast +func (ast *Assistant) buildCompletionOptions(ctx *context.Context, createResponse *context.HookCreateResponse) *context.CompletionOptions { + options := &context.CompletionOptions{} + + // Layer 1 (base): Apply ast - Assistant configuration + ast.applyAssistantOptions(options) + + // Layer 2 (middle): Apply ctx - Context configuration (overrides ast) + ast.applyContextOptions(options, ctx) + + // Layer 3 (highest): Apply createResponse - Hook configuration (overrides all) + if createResponse != nil { + ast.applyCreateResponseOptions(options, createResponse) + } + + return options +} + +// applyAssistantOptions applies options from ast.Options to CompletionOptions +// ast.Options can contain any OpenAI API parameters (temperature, top_p, stop, etc.) +func (ast *Assistant) applyAssistantOptions(options *context.CompletionOptions) { + if ast.Options == nil { + return + } + + // Temperature + if v, ok := ast.Options["temperature"].(float64); ok { + options.Temperature = &v + } + + // MaxTokens + if v, ok := ast.Options["max_tokens"].(float64); ok { + intVal := int(v) + options.MaxTokens = &intVal + } else if v, ok := ast.Options["max_tokens"].(int); ok { + options.MaxTokens = &v + } + + // MaxCompletionTokens + if v, ok := ast.Options["max_completion_tokens"].(float64); ok { + intVal := int(v) + options.MaxCompletionTokens = &intVal + } else if v, ok := ast.Options["max_completion_tokens"].(int); ok { + options.MaxCompletionTokens = &v + } + + // TopP + if v, ok := ast.Options["top_p"].(float64); ok { + options.TopP = &v + } + + // N (number of choices) + if v, ok := ast.Options["n"].(float64); ok { + intVal := int(v) + options.N = &intVal + } else if v, ok := ast.Options["n"].(int); ok { + options.N = &v + } + + // Stop sequences (can be string or []string) + if v, ok := ast.Options["stop"]; ok { + options.Stop = v + } + + // PresencePenalty + if v, ok := ast.Options["presence_penalty"].(float64); ok { + options.PresencePenalty = &v + } + + // FrequencyPenalty + if v, ok := ast.Options["frequency_penalty"].(float64); ok { + options.FrequencyPenalty = &v + } + + // LogitBias + if v, ok := ast.Options["logit_bias"].(map[string]interface{}); ok { + logitBias := make(map[string]float64) + for key, val := range v { + if fval, ok := val.(float64); ok { + logitBias[key] = fval + } + } + if len(logitBias) > 0 { + options.LogitBias = logitBias + } + } + + // User + if v, ok := ast.Options["user"].(string); ok { + options.User = v + } + + // ResponseFormat + if v, ok := ast.Options["response_format"].(map[string]interface{}); ok { + options.ResponseFormat = v + } + + // Seed + if v, ok := ast.Options["seed"].(float64); ok { + intVal := int(v) + options.Seed = &intVal + } else if v, ok := ast.Options["seed"].(int); ok { + options.Seed = &v + } + + // Tools + if v, ok := ast.Options["tools"].([]interface{}); ok { + tools := make([]map[string]interface{}, 0, len(v)) + for _, tool := range v { + if toolMap, ok := tool.(map[string]interface{}); ok { + tools = append(tools, toolMap) + } + } + if len(tools) > 0 { + options.Tools = tools + } + } + + // ToolChoice + if v, ok := ast.Options["tool_choice"]; ok { + options.ToolChoice = v + } + + // Stream + if v, ok := ast.Options["stream"].(bool); ok { + options.Stream = &v + } +} + +// applyContextOptions applies options from ctx to CompletionOptions +// ctx provides Route and Metadata for CUI context +func (ast *Assistant) applyContextOptions(options *context.CompletionOptions, ctx *context.Context) { + // Set Route and Metadata from ctx + options.Route = ctx.Route + options.Metadata = ctx.Metadata + + // Set wrapper configurations (assistant.Uses has priority over global settings) + // These can be overridden by createResponse + if visionWrapper := ast.getVisionWrapper(); visionWrapper != "" { + options.VisionWrapper = visionWrapper + } + if audioWrapper := ast.getAudioWrapper(); audioWrapper != "" { + options.AudioWrapper = audioWrapper + } +} + +// applyCreateResponseOptions applies options from createResponse to CompletionOptions +// createResponse takes highest priority and overrides any previous settings +func (ast *Assistant) applyCreateResponseOptions(options *context.CompletionOptions, createResponse *context.HookCreateResponse) { + // Audio configuration + if createResponse.Audio != nil { + options.Audio = createResponse.Audio + } + + // Temperature + if createResponse.Temperature != nil { + options.Temperature = createResponse.Temperature + } + + // MaxTokens + if createResponse.MaxTokens != nil { + options.MaxTokens = createResponse.MaxTokens + } + + // MaxCompletionTokens + if createResponse.MaxCompletionTokens != nil { + options.MaxCompletionTokens = createResponse.MaxCompletionTokens + } + + // Route + if createResponse.Route != "" { + options.Route = createResponse.Route + } + + // Metadata (merge with existing) + if createResponse.Metadata != nil { + if options.Metadata == nil { + options.Metadata = createResponse.Metadata + } else { + // Merge: createResponse.Metadata overrides existing + for key, value := range createResponse.Metadata { + options.Metadata[key] = value + } + } + } +} + +// getVisionWrapper get the vision wrapper with priority: assistant.Uses > global settings +func (ast *Assistant) getVisionWrapper() string { + // Priority 1: Assistant-specific Uses configuration + if ast.Uses != nil && ast.Uses.Vision != "" { + return ast.Uses.Vision + } + + // Priority 2: Global settings from globalUses + if globalUses != nil && globalUses.Vision != "" { + return globalUses.Vision + } + + return "" +} + +// getAudioWrapper get the audio wrapper with priority: assistant.Uses > global settings +func (ast *Assistant) getAudioWrapper() string { + // Priority 1: Assistant-specific Uses configuration + if ast.Uses != nil && ast.Uses.Audio != "" { + return ast.Uses.Audio + } + + // Priority 2: Global settings from globalUses + if globalUses != nil && globalUses.Audio != "" { + return globalUses.Audio + } + + return "" } // WithHistory with the history messages diff --git a/agent/assistant/agent_test.go b/agent/assistant/agent_test.go new file mode 100644 index 00000000..ac47e145 --- /dev/null +++ b/agent/assistant/agent_test.go @@ -0,0 +1,262 @@ +package assistant_test + +import ( + stdContext "context" + "testing" + + "github.com/yaoapp/gou/plan" + "github.com/yaoapp/yao/agent/assistant" + "github.com/yaoapp/yao/agent/context" + "github.com/yaoapp/yao/agent/testutils" + "github.com/yaoapp/yao/openapi/oauth/types" +) + +// newTestContext creates a Context for testing with commonly used fields pre-populated +func newTestContext(chatID, assistantID string) *context.Context { + return &context.Context{ + Context: stdContext.Background(), + Space: plan.NewMemorySharedSpace(), + ChatID: chatID, + AssistantID: assistantID, + Connector: "", + Locale: "en-us", + Theme: "light", + Client: context.Client{ + Type: "web", + UserAgent: "TestAgent/1.0", + IP: "127.0.0.1", + }, + Referer: context.RefererAPI, + Accept: context.AcceptWebCUI, + Route: "/test/route", + Metadata: map[string]interface{}{ + "test": "context_metadata", + }, + Authorized: &types.AuthorizedInfo{ + Subject: "test-user", + ClientID: "test-client-id", + UserID: "test-user-123", + TeamID: "test-team-456", + TenantID: "test-tenant-789", + SessionID: "test-session-id", + }, + } +} + +// TestBuildRequest tests the BuildRequest function +func TestBuildRequest(t *testing.T) { + testutils.Prepare(t) + defer testutils.Clean(t) + + agent, err := assistant.Get("tests.buildrequest") + if err != nil { + t.Fatalf("Failed to get tests.buildrequest assistant: %s", err.Error()) + } + + if agent.Script == nil { + t.Fatalf("The tests.buildrequest assistant has no script") + } + + ctx := newTestContext("chat-test-buildrequest", "tests.buildrequest") + + // Test 1: No override from hook - should use ast.Options and ctx values + t.Run("NoOverride", func(t *testing.T) { + inputMessages := []context.Message{{Role: "user", Content: "no_override"}} + + // Call Create hook + createResponse, err := agent.Script.Create(ctx, inputMessages) + if err != nil { + t.Fatalf("Failed to call Create hook: %s", err.Error()) + } + + // Build LLM request + _, options, err := agent.BuildRequest(ctx, inputMessages, createResponse) + if err != nil { + t.Fatalf("Failed to build LLM request: %s", err.Error()) + } + + // Verify options - should use ast.Options values + if options.Temperature == nil { + t.Error("Expected temperature from ast.Options, got nil") + } else if *options.Temperature != 0.5 { + t.Errorf("Expected temperature 0.5 from ast.Options, got: %f", *options.Temperature) + } + + if options.MaxTokens == nil { + t.Error("Expected max_tokens from ast.Options, got nil") + } else if *options.MaxTokens != 1000 { + t.Errorf("Expected max_tokens 1000 from ast.Options, got: %d", *options.MaxTokens) + } + + if options.TopP == nil { + t.Error("Expected top_p from ast.Options, got nil") + } else if *options.TopP != 0.9 { + t.Errorf("Expected top_p 0.9 from ast.Options, got: %f", *options.TopP) + } + + // Verify ctx values + if options.Route != "/test/route" { + t.Errorf("Expected route '/test/route' from ctx, got: %s", options.Route) + } + + if options.Metadata == nil { + t.Error("Expected metadata from ctx, got nil") + } else if options.Metadata["test"] != "context_metadata" { + t.Errorf("Expected metadata from ctx, got: %v", options.Metadata) + } + + t.Log("✓ No override: ast.Options and ctx values used correctly") + }) + + // Test 2: Override temperature - hook value should take priority + t.Run("OverrideTemperature", func(t *testing.T) { + inputMessages := []context.Message{{Role: "user", Content: "override_temperature"}} + + createResponse, err := agent.Script.Create(ctx, inputMessages) + if err != nil { + t.Fatalf("Failed to call Create hook: %s", err.Error()) + } + + _, options, err := agent.BuildRequest(ctx, inputMessages, createResponse) + if err != nil { + t.Fatalf("Failed to build LLM request: %s", err.Error()) + } + + // Verify temperature override + if options.Temperature == nil { + t.Error("Expected temperature, got nil") + } else if *options.Temperature != 0.9 { + t.Errorf("Expected temperature 0.9 from hook, got: %f", *options.Temperature) + } + + // Other values should still come from ast.Options + if options.MaxTokens == nil { + t.Error("Expected max_tokens from ast.Options, got nil") + } else if *options.MaxTokens != 1000 { + t.Errorf("Expected max_tokens 1000 from ast.Options, got: %d", *options.MaxTokens) + } + + t.Log("✓ Temperature override: hook value takes priority over ast.Options") + }) + + // Test 3: Override all - all hook values should take priority + t.Run("OverrideAll", func(t *testing.T) { + inputMessages := []context.Message{{Role: "user", Content: "override_all"}} + + createResponse, err := agent.Script.Create(ctx, inputMessages) + if err != nil { + t.Fatalf("Failed to call Create hook: %s", err.Error()) + } + + _, options, err := agent.BuildRequest(ctx, inputMessages, createResponse) + if err != nil { + t.Fatalf("Failed to build LLM request: %s", err.Error()) + } + + // Verify all overrides + if options.Temperature == nil || *options.Temperature != 0.8 { + t.Errorf("Expected temperature 0.8 from hook, got: %v", options.Temperature) + } + + if options.MaxTokens == nil || *options.MaxTokens != 2000 { + t.Errorf("Expected max_tokens 2000 from hook, got: %v", options.MaxTokens) + } + + if options.MaxCompletionTokens == nil || *options.MaxCompletionTokens != 1800 { + t.Errorf("Expected max_completion_tokens 1800 from hook, got: %v", options.MaxCompletionTokens) + } + + if options.Audio == nil { + t.Error("Expected audio from hook, got nil") + } else { + if options.Audio.Voice != "alloy" { + t.Errorf("Expected voice 'alloy', got: %s", options.Audio.Voice) + } + if options.Audio.Format != "mp3" { + t.Errorf("Expected format 'mp3', got: %s", options.Audio.Format) + } + } + + if options.Route != "/hook/route" { + t.Errorf("Expected route '/hook/route' from hook, got: %s", options.Route) + } + + if options.Metadata == nil { + t.Error("Expected metadata from hook, got nil") + } else { + if options.Metadata["source"] != "hook" { + t.Errorf("Expected metadata['source'] = 'hook', got: %v", options.Metadata["source"]) + } + } + + t.Log("✓ Override all: all hook values take priority") + }) + + // Test 4: Override route and metadata - tests CUI context priority + t.Run("OverrideRouteMetadata", func(t *testing.T) { + inputMessages := []context.Message{{Role: "user", Content: "override_route_metadata"}} + + createResponse, err := agent.Script.Create(ctx, inputMessages) + if err != nil { + t.Fatalf("Failed to call Create hook: %s", err.Error()) + } + + _, options, err := agent.BuildRequest(ctx, inputMessages, createResponse) + if err != nil { + t.Fatalf("Failed to build LLM request: %s", err.Error()) + } + + // Verify route override + if options.Route != "/custom/route" { + t.Errorf("Expected route '/custom/route' from hook, got: %s", options.Route) + } + + // Verify metadata merge (ctx metadata should be merged with hook metadata) + if options.Metadata == nil { + t.Error("Expected metadata, got nil") + } else { + // Hook metadata should be present + if options.Metadata["custom"] != true { + t.Errorf("Expected metadata['custom'] = true from hook, got: %v", options.Metadata["custom"]) + } + if options.Metadata["hook_data"] != "test" { + t.Errorf("Expected metadata['hook_data'] = 'test' from hook, got: %v", options.Metadata["hook_data"]) + } + // Original ctx metadata should still be there (merged) + if options.Metadata["test"] != "context_metadata" { + t.Errorf("Expected original ctx metadata to be preserved, got: %v", options.Metadata) + } + } + + // Other values should still come from ast.Options + if options.Temperature == nil || *options.Temperature != 0.5 { + t.Errorf("Expected temperature 0.5 from ast.Options, got: %v", options.Temperature) + } + + t.Log("✓ Route and metadata override: hook values take priority, metadata merged") + }) + + // Test 5: Nil createResponse - should use ast.Options and ctx values + t.Run("NilCreateResponse", func(t *testing.T) { + // Create a fresh context for this test + freshCtx := newTestContext("chat-test-nil", "tests.buildrequest") + inputMessages := []context.Message{{Role: "user", Content: "test message"}} + + _, options, err := agent.BuildRequest(freshCtx, inputMessages, nil) + if err != nil { + t.Fatalf("Failed to build LLM request: %s", err.Error()) + } + + // Should use ast.Options values + if options.Temperature == nil || *options.Temperature != 0.5 { + t.Errorf("Expected temperature 0.5 from ast.Options, got: %v", options.Temperature) + } + + // Should use ctx values + if options.Route != "/test/route" { + t.Errorf("Expected route '/test/route' from ctx, got: %s", options.Route) + } + + t.Log("✓ Nil createResponse: ast.Options and ctx values used") + }) +} diff --git a/agent/assistant/hook/done.go b/agent/assistant/hook/done.go index 69e990a9..21545227 100644 --- a/agent/assistant/hook/done.go +++ b/agent/assistant/hook/done.go @@ -5,6 +5,6 @@ import ( ) // Done done hook -func (s *Script) Done(ctx *context.Context, inputMessages []context.Message, completionResponse *context.ResponseCompletion, mcpResponse *context.ResponseHookMCP) (*context.ResponseHookDone, error) { +func (s *Script) Done(ctx *context.Context, inputMessages []context.Message, completionResponse *context.CompletionResponse, mcpResponse *context.ResponseHookMCP) (*context.ResponseHookDone, error) { return &context.ResponseHookDone{}, nil } diff --git a/agent/assistant/hook/failback.go b/agent/assistant/hook/failback.go index 0b345cf4..1e37fabb 100644 --- a/agent/assistant/hook/failback.go +++ b/agent/assistant/hook/failback.go @@ -1,8 +1,10 @@ package hook -import "github.com/yaoapp/yao/agent/context" +import ( + "github.com/yaoapp/yao/agent/context" +) // Failback failback hook -func (s *Script) Failback(ctx *context.Context, inputMessages []context.Message, completionResponse *context.ResponseCompletion) (*context.ResponseHookFailback, error) { +func (s *Script) Failback(ctx *context.Context, inputMessages []context.Message, completionResponse *context.CompletionResponse) (*context.ResponseHookFailback, error) { return &context.ResponseHookFailback{}, nil } diff --git a/agent/assistant/load.go b/agent/assistant/load.go index 236ad3ab..5f721606 100644 --- a/agent/assistant/load.go +++ b/agent/assistant/load.go @@ -29,6 +29,7 @@ var search interface{} = nil var connectorSettings map[string]ConnectorSetting = map[string]ConnectorSetting{} var vision *agentvision.Vision = nil var defaultConnector string = "" // default connector +var globalUses *store.Uses = nil // global uses configuration from agent.yml // LoadBuiltIn load the built-in assistants func LoadBuiltIn() error { @@ -145,6 +146,11 @@ func SetConnector(c string) { defaultConnector = c } +// SetGlobalUses set the global uses configuration +func SetGlobalUses(uses *store.Uses) { + globalUses = uses +} + // SetCache set the cache func SetCache(capacity int) { ClearCache() diff --git a/agent/assistant/types.go b/agent/assistant/types.go index 46f0962d..6c8a20db 100644 --- a/agent/assistant/types.go +++ b/agent/assistant/types.go @@ -109,9 +109,16 @@ type Assistant struct { } // ConnectorSetting the connector setting +// Defines the capabilities of a connector/model type ConnectorSetting struct { - Vision bool `json:"vision,omitempty" yaml:"vision,omitempty"` - Tools bool `json:"tools,omitempty" yaml:"tools,omitempty"` + Vision bool `json:"vision,omitempty" yaml:"vision,omitempty"` // Supports vision/image input + Tools bool `json:"tools,omitempty" yaml:"tools,omitempty"` // Supports tool/function calling (deprecated, use ToolCalls) + ToolCalls bool `json:"tool_calls,omitempty" yaml:"tool_calls,omitempty"` // Supports tool/function calling + Audio bool `json:"audio,omitempty" yaml:"audio,omitempty"` // Supports audio input/output + Reasoning bool `json:"reasoning,omitempty" yaml:"reasoning,omitempty"` // Supports reasoning/thinking mode (o1, DeepSeek R1) + Streaming bool `json:"streaming,omitempty" yaml:"streaming,omitempty"` // Supports streaming responses + JSON bool `json:"json,omitempty" yaml:"json,omitempty"` // Supports JSON mode + Multimodal bool `json:"multimodal,omitempty" yaml:"multimodal,omitempty"` // Supports multimodal input } // VisionCapableModels list of LLM models that support vision capabilities diff --git a/agent/context/types.go b/agent/context/types.go index 712e7e74..2a0f6896 100644 --- a/agent/context/types.go +++ b/agent/context/types.go @@ -188,7 +188,7 @@ type Response struct { MCP *ResponseHookMCP `json:"mcp,omitempty"` Done *ResponseHookDone `json:"done,omitempty"` Failback *ResponseHookFailback `json:"failback,omitempty"` - Completion *ResponseCompletion `json:"completion,omitempty"` + Completion *CompletionResponse `json:"completion,omitempty"` } // HookCreateResponse the response of the create hook @@ -223,9 +223,6 @@ type ResponseHookMCP struct{} // ResponseHookFailback the response of the failback hook type ResponseHookFailback struct{} -// ResponseCompletion the response of the completion -type ResponseCompletion struct{} - // Message Structure ( OpenAI Chat Completion Input Message Structure, https://platform.openai.com/docs/api-reference/chat/create#chat/create-messages ) // =============================== diff --git a/agent/context/types_llm.go b/agent/context/types_llm.go new file mode 100644 index 00000000..57e8cf32 --- /dev/null +++ b/agent/context/types_llm.go @@ -0,0 +1,146 @@ +package context + +// ModelCapabilities defines the capabilities of a language model +// Used by LLM to select appropriate provider and validate requests +type ModelCapabilities struct { + Vision *bool `json:"vision,omitempty"` // Supports vision/image input + ToolCalls *bool `json:"tool_calls,omitempty"` // Supports tool/function calling + Audio *bool `json:"audio,omitempty"` // Supports audio input/output + Reasoning *bool `json:"reasoning,omitempty"` // Supports reasoning/thinking mode (o1, DeepSeek R1) + Streaming *bool `json:"streaming,omitempty"` // Supports streaming responses + JSON *bool `json:"json,omitempty"` // Supports JSON mode + Multimodal *bool `json:"multimodal,omitempty"` // Supports multimodal input (text + images + audio) +} + +// CompletionOptions the completion request options +// These options are extracted from HookCreateResponse and Context, then passed to the LLM connector +// Compatible with OpenAI Chat Completion API: https://platform.openai.com/docs/api-reference/chat/create +type CompletionOptions struct { + // Model capabilities (used by LLM to select appropriate provider) + // nil means capabilities are not specified/checked + Capabilities *ModelCapabilities `json:"capabilities,omitempty"` + + // Wrapper configurations for vision and audio processing + // Format: "agent" (default) or "mcp:mcp_server_id" + VisionWrapper string `json:"vision_wrapper,omitempty"` // Vision processing wrapper (for image/video description) + AudioWrapper string `json:"audio_wrapper,omitempty"` // Audio processing wrapper (for speech-to-text/text-to-speech) + + // Audio configuration (for models that support audio output) + Audio *AudioConfig `json:"audio,omitempty"` + + // Generation parameters + Temperature *float64 `json:"temperature,omitempty"` // Sampling temperature (0-2), defaults to 1 + MaxTokens *int `json:"max_tokens,omitempty"` // Maximum tokens to generate (deprecated, use MaxCompletionTokens) + MaxCompletionTokens *int `json:"max_completion_tokens,omitempty"` // Maximum tokens in completion + TopP *float64 `json:"top_p,omitempty"` // Nucleus sampling parameter (0-1), alternative to temperature + N *int `json:"n,omitempty"` // Number of chat completion choices to generate + + // Control parameters + Stop interface{} `json:"stop,omitempty"` // Up to 4 sequences where the API will stop generating (string or []string) + PresencePenalty *float64 `json:"presence_penalty,omitempty"` // Presence penalty (-2.0 to 2.0) + FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"` // Frequency penalty (-2.0 to 2.0) + LogitBias map[string]float64 `json:"logit_bias,omitempty"` // Modify likelihood of specified tokens appearing + + // User and response format + User string `json:"user,omitempty"` // Unique identifier representing end-user + ResponseFormat map[string]interface{} `json:"response_format,omitempty"` // Format of the response (e.g., {"type": "json_object"}) + Seed *int `json:"seed,omitempty"` // Seed for deterministic sampling + + // Tool calling + Tools []map[string]interface{} `json:"tools,omitempty"` // List of tools the model may call + ToolChoice interface{} `json:"tool_choice,omitempty"` // Controls which tool is called ("none", "auto", "required", or specific tool) + + // Streaming configuration + Stream *bool `json:"stream,omitempty"` // If true, stream partial message deltas + StreamOptions *StreamOptions `json:"stream_options,omitempty"` // Options for streaming response + + // CUI Context information (from Context) + Route string `json:"route,omitempty"` // Route of the request for CUI context + Metadata map[string]interface{} `json:"metadata,omitempty"` // Metadata to pass to the page for CUI context +} + +// CompletionResponse represents the unified completion response +// Compatible with OpenAI chat completion response format +type CompletionResponse struct { + // Response metadata + ID string `json:"id"` // Unique identifier for the completion + Object string `json:"object"` // Object type (e.g., "chat.completion") + Created int64 `json:"created"` // Unix timestamp of creation + Model string `json:"model"` // Model used for completion + + // Completion content (these fields can coexist) + Content string `json:"content"` // Text content (regular response text) + ReasoningContent string `json:"reasoning_content,omitempty"` // Reasoning/thinking content (for o1, DeepSeek R1, etc.) + ToolCalls []ToolCallResult `json:"tool_calls,omitempty"` // Tool calls made by the model + Refusal string `json:"refusal,omitempty"` // Refusal message if model refused to answer + ContentTypes []ContentType `json:"content_types"` // Types of content present (can have multiple simultaneously) + + // Raw response data + Raw interface{} `json:"raw,omitempty"` // Original raw response from the LLM provider (for debugging and special cases) + + // Completion metadata + FinishReason string `json:"finish_reason"` // Reason for completion (stop, length, tool_calls, content_filter, etc.) + + // Usage statistics + Usage *UsageInfo `json:"usage,omitempty"` // Token usage statistics + + // Additional metadata + SystemFingerprint string `json:"system_fingerprint,omitempty"` // System fingerprint for reproducibility + Metadata map[string]interface{} `json:"metadata,omitempty"` // Additional metadata +} + +// ContentType represents the type of content in the response +// A response can contain multiple content types simultaneously +type ContentType string + +// Content type constants - a response can have multiple types simultaneously +// For example: text + reasoning, or text + tool_call, or all three +const ( + ContentTypeText ContentType = "text" // Regular text content + ContentTypeReasoning ContentType = "reasoning" // Reasoning/thinking content (o1, DeepSeek R1, etc.) + ContentTypeToolCall ContentType = "tool_call" // Tool/function call + ContentTypeRefusal ContentType = "refusal" // Model refused to answer + ContentTypeEmpty ContentType = "empty" // Empty response (no content) +) + +// UsageInfo represents token usage statistics +type UsageInfo struct { + PromptTokens int `json:"prompt_tokens"` // Tokens in the prompt + CompletionTokens int `json:"completion_tokens"` // Tokens in the completion + TotalTokens int `json:"total_tokens"` // Total tokens used + + // Detailed token breakdown (for models with reasoning) + PromptTokensDetails *TokenDetails `json:"prompt_tokens_details,omitempty"` // Detailed prompt token breakdown + CompletionTokensDetails *TokenDetails `json:"completion_tokens_details,omitempty"` // Detailed completion token breakdown +} + +// TokenDetails provides detailed token usage breakdown +type TokenDetails struct { + CachedTokens int `json:"cached_tokens,omitempty"` // Tokens from cache + ReasoningTokens int `json:"reasoning_tokens,omitempty"` // Tokens used for reasoning/thinking + AudioTokens int `json:"audio_tokens,omitempty"` // Tokens used for audio + TextTokens int `json:"text_tokens,omitempty"` // Tokens used for text +} + +// ToolCallResult represents a tool call result in the completion +type ToolCallResult struct { + ID string `json:"id"` // Tool call ID + Type string `json:"type"` // Tool call type (usually "function") + Function FunctionCallResult `json:"function"` // Function call details +} + +// FunctionCallResult represents a function call result +type FunctionCallResult struct { + Name string `json:"name"` // Function name + Arguments string `json:"arguments"` // Function arguments as JSON string +} + +// FinishReason constants +const ( + FinishReasonStop = "stop" // Natural stop point + FinishReasonLength = "length" // Max tokens reached + FinishReasonToolCalls = "tool_calls" // Tool calls made + FinishReasonContentFilter = "content_filter" // Content filtered + FinishReasonFunctionCall = "function_call" // Function call (deprecated) + FinishReasonError = "error" // Error occurred +) diff --git a/agent/context/types_wrapper.go b/agent/context/types_wrapper.go new file mode 100644 index 00000000..a9909301 --- /dev/null +++ b/agent/context/types_wrapper.go @@ -0,0 +1,49 @@ +package context + +import "strings" + +// WrapperType represents the type of wrapper for processing +type WrapperType string + +const ( + WrapperTypeAgent WrapperType = "agent" // Use agent for processing + WrapperTypeMCP WrapperType = "mcp" // Use MCP server for processing +) + +// ParseWrapper parses a wrapper string and returns the type and ID +// Format: "agent" or "mcp:mcp_server_id" +func ParseWrapper(wrapper string) (WrapperType, string) { + if wrapper == "" || wrapper == "agent" { + return WrapperTypeAgent, "" + } + + if strings.HasPrefix(wrapper, "mcp:") { + mcpID := strings.TrimPrefix(wrapper, "mcp:") + return WrapperTypeMCP, mcpID + } + + // Default to agent if format is unknown + return WrapperTypeAgent, "" +} + +// IsAgentWrapper checks if the wrapper is an agent wrapper +func IsAgentWrapper(wrapper string) bool { + wrapperType, _ := ParseWrapper(wrapper) + return wrapperType == WrapperTypeAgent +} + +// IsMCPWrapper checks if the wrapper is an MCP wrapper +func IsMCPWrapper(wrapper string) bool { + wrapperType, _ := ParseWrapper(wrapper) + return wrapperType == WrapperTypeMCP +} + +// GetMCPServerID extracts the MCP server ID from wrapper string +// Returns empty string if not an MCP wrapper +func GetMCPServerID(wrapper string) string { + wrapperType, id := ParseWrapper(wrapper) + if wrapperType == WrapperTypeMCP { + return id + } + return "" +} diff --git a/agent/llm/handlers/handlers.go b/agent/llm/handlers/handlers.go new file mode 100644 index 00000000..53fb4a93 --- /dev/null +++ b/agent/llm/handlers/handlers.go @@ -0,0 +1,51 @@ +package handlers + +import ( + "github.com/yaoapp/yao/agent/context" +) + +// Handler interface for stream handlers +type Handler interface { + OnChunk(chunk *StreamChunk) error + OnComplete() error + OnError(err error) error +} + +// NewDefaultHandler creates a default handler that sends chunks via context +func NewDefaultHandler(ctx *context.Context) Handler { + return &DefaultHandler{ + ctx: ctx, + } +} + +// DefaultHandler default stream handler implementation +type DefaultHandler struct { + ctx *context.Context +} + +// OnChunk handles a streaming chunk +func (h *DefaultHandler) OnChunk(chunk *StreamChunk) error { + // TODO: Implement chunk handling + // - Send chunk via ctx + // - Handle different chunk types + // - Aggregate content for final response + return SendStreamChunk(h.ctx, chunk) +} + +// OnComplete handles stream completion +func (h *DefaultHandler) OnComplete() error { + // TODO: Implement completion handling + // - Send final message + // - Close stream + // - Return aggregated response + return nil +} + +// OnError handles stream errors +func (h *DefaultHandler) OnError(err error) error { + // TODO: Implement error handling + // - Send error message to client + // - Log error + // - Clean up resources + return err +} diff --git a/agent/llm/handlers/stream.go b/agent/llm/handlers/stream.go new file mode 100644 index 00000000..22fc3aea --- /dev/null +++ b/agent/llm/handlers/stream.go @@ -0,0 +1,86 @@ +package handlers + +import ( + "github.com/yaoapp/yao/agent/context" +) + +// DefaultStreamHandler creates a default stream handler that sends messages via context +// This handler is used when no custom handler is provided +func DefaultStreamHandler(ctx *context.Context) context.StreamFunc { + return func(data []byte) int { + // TODO: Implement default stream handling + // - Parse streaming chunk data + // - Extract content from chunk + // - Send message via ctx (SSE, WebSocket, etc.) + // - Handle different chunk types (content, tool_calls, reasoning) + // - Return 1 to continue streaming, 0 to stop + return 1 + } +} + +// SendStreamChunk sends a stream chunk via context +// Used internally by DefaultStreamHandler +func SendStreamChunk(ctx *context.Context, chunk *StreamChunk) error { + // TODO: Implement sending stream chunk + // - Format chunk for transport (SSE, WebSocket) + // - Send via ctx's connection + // - Handle errors and retries + return nil +} + +// StreamChunk represents a parsed streaming chunk +type StreamChunk struct { + Type ChunkType `json:"type"` // Type of chunk (content, reasoning, tool_call, etc.) + Content string `json:"content,omitempty"` // Text content + + // For reasoning chunks + ReasoningContent string `json:"reasoning_content,omitempty"` + + // For tool call chunks + ToolCallID string `json:"tool_call_id,omitempty"` + ToolCallFunction string `json:"tool_call_function,omitempty"` + ToolCallArgs string `json:"tool_call_args,omitempty"` + + // Metadata + Done bool `json:"done"` // Whether this is the final chunk + FinishReason string `json:"finish_reason,omitempty"` // Reason for completion (if done) +} + +// ChunkType represents the type of streaming chunk +type ChunkType string + +const ( + ChunkTypeContent ChunkType = "content" // Regular text content + ChunkTypeReasoning ChunkType = "reasoning" // Reasoning/thinking content + ChunkTypeToolCall ChunkType = "tool_call" // Tool call chunk + ChunkTypeDone ChunkType = "done" // Final chunk (completion) + ChunkTypeError ChunkType = "error" // Error chunk +) + +// ParseStreamChunk parses raw streaming data into StreamChunk +func ParseStreamChunk(data []byte) (*StreamChunk, error) { + // TODO: Implement stream chunk parsing + // - Parse SSE format (data: {...}) + // - Handle different provider formats (OpenAI, DeepSeek, etc.) + // - Extract content, reasoning, tool calls + // - Detect completion (done: true) + return nil, nil +} + +// FormatSSE formats a StreamChunk as Server-Sent Events format +func FormatSSE(chunk *StreamChunk) string { + // TODO: Implement SSE formatting + // - Format as "data: {...}\n\n" + // - Handle special cases (done, error) + // - Ensure proper JSON encoding + return "" +} + +// FormatWebSocket formats a StreamChunk as WebSocket message +func FormatWebSocket(chunk *StreamChunk) []byte { + // TODO: Implement WebSocket formatting + // - Format as JSON message + // - Add message type/metadata + // - Handle binary vs text frames + return nil +} diff --git a/agent/llm/interfaces.go b/agent/llm/interfaces.go index 53562b40..0fd26b58 100644 --- a/agent/llm/interfaces.go +++ b/agent/llm/interfaces.go @@ -4,6 +4,6 @@ import "github.com/yaoapp/yao/agent/context" // LLM the LLM interface type LLM interface { - Stream(ctx *context.Context, messages []context.Message, options *CompletionOptions, handler context.StreamFunc) (*context.ResponseCompletion, error) - Post(ctx *context.Context, messages []context.Message, options *CompletionOptions) (*context.ResponseCompletion, error) + Stream(ctx *context.Context, messages []context.Message, options *context.CompletionOptions, handler context.StreamFunc) (*context.CompletionResponse, error) + Post(ctx *context.Context, messages []context.Message, options *context.CompletionOptions) (*context.CompletionResponse, error) } diff --git a/agent/llm/llm.go b/agent/llm/llm.go index 4e4c1be3..e7b8e2e6 100644 --- a/agent/llm/llm.go +++ b/agent/llm/llm.go @@ -1,6 +1,15 @@ package llm +import ( + "github.com/yaoapp/gou/connector" + "github.com/yaoapp/yao/agent/context" + "github.com/yaoapp/yao/agent/llm/providers" +) + // New create a new LLM instance -func New(connector string) (LLM, error) { - return nil, nil +// conn: connector object from connector.Select() +// options: completion options containing capabilities and other settings +func New(conn connector.Connector, options *context.CompletionOptions) (LLM, error) { + // Select appropriate provider based on capabilities + return providers.SelectProvider(conn, options) } diff --git a/agent/llm/providers/README.md b/agent/llm/providers/README.md new file mode 100644 index 00000000..f21b4ca0 --- /dev/null +++ b/agent/llm/providers/README.md @@ -0,0 +1,319 @@ +# LLM Providers Architecture + +## Overview + +This directory contains different LLM provider implementations, each optimized for specific model capabilities. + +## Provider Selection Strategy + +The `factory.SelectProvider()` function automatically selects the appropriate provider based on model capabilities: + +```go +Priority 1: Reasoning models → reasoning.Provider +Priority 2: Native tool support → openai.Provider +Priority 3: Legacy models → legacy.Provider +``` + +## Provider Types + +### 1. Base Provider (`base/`) + +**Purpose**: Common functionality shared across all providers + +**Features**: + +- Message preprocessing +- Request body building +- Response parsing + +**Usage**: Embedded in all other providers + +--- + +### 2. OpenAI Provider (`openai/`) + +**Purpose**: OpenAI-compatible models with full feature support + +**Capabilities**: + +- ✅ Vision (image input) +- ✅ Native tool calls +- ✅ Streaming +- ✅ JSON mode + +**Models**: + +- GPT-4, GPT-4o, GPT-4-turbo +- GPT-3.5-turbo +- Claude (via OpenAI-compatible API) + +--- + +### 3. Reasoning Provider (`reasoning/`) + +**Purpose**: Reasoning models with special response format + +**Capabilities**: + +- ✅ Reasoning content (`reasoning_content` field) +- ✅ Thinking + Answer phases +- ⚠️ Tool calls support varies by model + +**Models**: + +- **OpenAI o1** (supports native tool calls) +- **DeepSeek R1** (no native tool calls, uses prompt engineering) + +**Special Handling**: + +```go +// DeepSeek R1 scenario +if !supportsNativeTools && hasTools { + // Inject tool instructions into prompt + messages = injectToolInstructions(messages, tools) + // Extract tool calls from text response + toolCalls = extractToolCallsFromText(response.Content) +} +``` + +**Response Format**: + +```json +{ + "content": "The answer is 42", + "reasoning_content": "Let me think... first we need to...", + "content_types": ["text", "reasoning"] +} +``` + +--- + +### 4. Legacy Provider (`legacy/`) + +**Purpose**: Older models without native tool calling + +**Capabilities**: + +- ✅ Text generation +- ⚠️ Tool calls via prompt engineering +- ❌ No native vision support +- ❌ No native tool API + +**Models**: + +- GPT-3 (davinci, curie) +- Older open-source models +- Custom models without tool API + +**Tool Call Flow**: + +1. Inject tool schemas into system prompt +2. Model returns tool call in text format (JSON) +3. Extract and parse tool calls from text +4. Execute tools +5. Continue conversation + +--- + +### 5. Vision Utils (`vision/`) + +**Purpose**: Vision-related preprocessing utilities + +**Functions**: + +- `PreprocessVisionMessages()` - Handle image content +- `ConvertImageToText()` - Convert images to descriptions (for non-vision models) +- `ValidateImageURL()` - Validate image URLs +- `ExtractImagesFromMessages()` - Extract all images from messages + +**Usage**: + +```go +// When model doesn't support vision +if !supportsVision { + messages = vision.PreprocessVisionMessages(messages, false) + // Images converted to text descriptions +} +``` + +--- + +### 6. Audio Utils (`audio/`) + +**Purpose**: Audio-related preprocessing utilities + +**Functions**: + +- `PreprocessAudioMessages()` - Handle audio content +- `ConvertAudioToText()` - Convert audio to text transcription (for non-audio models) +- `ValidateAudioFormat()` - Validate audio format and encoding +- `ExtractAudioFromMessages()` - Extract all audio data from messages +- `RemoveAudioConfig()` - Remove audio configuration from options + +**Usage**: + +```go +// When model doesn't support audio +if !supportsAudio { + messages = audio.PreprocessAudioMessages(messages, false) + options = audio.RemoveAudioConfig(options) + // Audio converted to text transcriptions +} +``` + +--- + +## Special Scenarios + +### Scenario 1: DeepSeek R1 (Reasoning + No Tool Support) + +**Provider**: `reasoning.Provider` + +**Handling**: + +```go +// Check if reasoning model supports tools +if !p.supportsNativeTools && len(options.Tools) > 0 { + // Use prompt engineering approach + messages = p.injectToolInstructions(messages, tools) + options = p.removeToolsFromOptions(options) +} + +// After getting response +if !p.supportsNativeTools { + toolCalls = p.extractToolCallsFromText(response.Content) +} +``` + +**Why reasoning provider?** + +- Primary characteristic is reasoning (special response format) +- Tool handling is secondary concern +- Reuses tool injection logic from legacy approach + +--- + +### Scenario 2: Legacy Model + Vision/Audio Request + +**Provider**: `legacy.Provider` + +**Handling**: + +```go +import ( + "github.com/yaoapp/yao/agent/llm/providers/vision" + "github.com/yaoapp/yao/agent/llm/providers/audio" +) + +// Preprocess to remove/convert vision content +if !supportsVision { + messages = vision.PreprocessVisionMessages(messages, false) + // Images converted to text: "[Image: description]" +} + +// Preprocess to remove/convert audio content +if !supportsAudio { + messages = audio.PreprocessAudioMessages(messages, false) + options = audio.RemoveAudioConfig(options) + // Audio converted to text: "[Audio transcription: ...]" +} +``` + +--- + +### Scenario 3: OpenAI o1 (Reasoning + Tool Support) + +**Provider**: `reasoning.Provider` + +**Handling**: + +```go +// o1 supports native tools, no special handling needed +if p.supportsNativeTools { + // Use standard OpenAI tool calling API +} +``` + +--- + +## Configuration Example + +In `connectors.yml`: + +```yaml +# GPT-4o with all features +gpt-4o: + vision: true + tool_calls: true + audio: true + streaming: true + json: true + multimodal: true + +# OpenAI o1 - reasoning with tool support +o1-preview: + reasoning: true + tool_calls: true + streaming: true + +# DeepSeek R1 - reasoning without tool support +deepseek-reasoner: + reasoning: true + tool_calls: false # Will use prompt engineering + streaming: true + +# GPT-3 - legacy model +gpt-3.5-turbo-instruct: + tool_calls: false # Will use prompt engineering + vision: false # Will convert images to text + audio: false # Will convert audio to text + streaming: false + +# GPT-4 Vision only +gpt-4-vision: + vision: true + tool_calls: true + audio: false # No audio support + streaming: true +``` + +--- + +## Adding a New Provider + +1. Create new directory: `providers/newprovider/` +2. Implement `LLM` interface: + + ```go + type Provider struct { + *base.Provider + } + + func (p *Provider) Stream(...) (*CompletionResponse, error) + func (p *Provider) Post(...) (*CompletionResponse, error) + ``` + +3. Update `factory.SelectProvider()` selection logic +4. Add capability flags to `ConnectorSetting` + +--- + +## Testing + +Each provider should have tests for: + +- Standard completion +- Streaming completion +- Tool calling (if supported) +- Vision input (if supported) +- Error handling +- Response parsing + +--- + +## Performance Considerations + +- **Caching**: Consider caching connector instances +- **Pooling**: HTTP connection pooling for high throughput +- **Timeouts**: Configurable timeouts per provider +- **Retries**: Exponential backoff for transient errors diff --git a/agent/llm/providers/audio/audio.go b/agent/llm/providers/audio/audio.go new file mode 100644 index 00000000..9b5ea64b --- /dev/null +++ b/agent/llm/providers/audio/audio.go @@ -0,0 +1,59 @@ +package audio + +import ( + "github.com/yaoapp/yao/agent/context" +) + +// PreprocessAudioMessages preprocess messages to handle audio content +// Removes or converts audio content for models that don't support it +func PreprocessAudioMessages(messages []context.Message, supportsAudio bool) []context.Message { + // TODO: Implement audio message preprocessing + // If supportsAudio is false: + // - Remove input_audio content parts + // - Convert to text-only messages + // - Optionally add audio transcriptions + // If supportsAudio is true: + // - Validate audio format + // - Ensure proper encoding + return messages +} + +// ConvertAudioToText convert audio content to text transcription +// Used when model doesn't support audio input +func ConvertAudioToText(audioData string) (string, error) { + // TODO: Implement audio to text conversion + // - Call speech-to-text API (Whisper, etc.) + // - Generate transcription + // - Return as text content + return "", nil +} + +// ValidateAudioFormat validate audio format and encoding +func ValidateAudioFormat(audioConfig *context.AudioConfig) error { + // TODO: Implement audio format validation + // - Check format (wav, mp3, etc.) + // - Validate encoding + // - Check sample rate + return nil +} + +// ExtractAudioFromMessages extract all audio data from messages +func ExtractAudioFromMessages(messages []context.Message) []string { + // TODO: Implement audio extraction + // - Iterate through messages + // - Find ContentPart with type="input_audio" + // - Collect all audio data + return nil +} + +// RemoveAudioConfig remove audio configuration from options +// Used when model doesn't support audio output +func RemoveAudioConfig(options *context.CompletionOptions) *context.CompletionOptions { + // TODO: Remove audio config from options + if options == nil { + return options + } + newOptions := *options + newOptions.Audio = nil + return &newOptions +} diff --git a/agent/llm/providers/base/base.go b/agent/llm/providers/base/base.go new file mode 100644 index 00000000..ad9250b0 --- /dev/null +++ b/agent/llm/providers/base/base.go @@ -0,0 +1,65 @@ +package base + +import ( + "github.com/yaoapp/gou/connector" + "github.com/yaoapp/yao/agent/context" +) + +// Provider base provider implementation +// Provides common functionality for all LLM providers +type Provider struct { + Connector connector.Connector + Capabilities *context.ModelCapabilities +} + +// NewProvider create a new base provider +func NewProvider(conn connector.Connector, capabilities *context.ModelCapabilities) *Provider { + return &Provider{ + Connector: conn, + Capabilities: capabilities, + } +} + +// PreprocessMessages preprocess messages before sending to LLM +// Handles vision messages, audio messages, tool messages, etc. +func (p *Provider) PreprocessMessages(messages []context.Message) ([]context.Message, error) { + // TODO: Implement message preprocessing + // - Remove vision content if not supported + // - Remove audio content if not supported + // - Convert tool messages if needed + // - Validate message format + return messages, nil +} + +// SupportsVision check if this provider supports vision +func (p *Provider) SupportsVision() bool { + return p.Capabilities != nil && p.Capabilities.Vision != nil && *p.Capabilities.Vision +} + +// SupportsAudio check if this provider supports audio +func (p *Provider) SupportsAudio() bool { + return p.Capabilities != nil && p.Capabilities.Audio != nil && *p.Capabilities.Audio +} + +// SupportsTools check if this provider supports tool calls +func (p *Provider) SupportsTools() bool { + return p.Capabilities != nil && p.Capabilities.ToolCalls != nil && *p.Capabilities.ToolCalls +} + +// BuildRequestBody build the request body for the LLM API +func (p *Provider) BuildRequestBody(messages []context.Message, options *context.CompletionOptions) (map[string]interface{}, error) { + // TODO: Implement request body building + // - Convert messages to API format + // - Apply options (temperature, max_tokens, etc.) + // - Add model-specific parameters + return nil, nil +} + +// ParseResponse parse the response from LLM API +func (p *Provider) ParseResponse(data []byte, isStreaming bool) (*context.CompletionResponse, error) { + // TODO: Implement response parsing + // - Parse JSON response + // - Extract content, tool calls, reasoning, etc. + // - Handle streaming chunks + return nil, nil +} diff --git a/agent/llm/providers/factory.go b/agent/llm/providers/factory.go new file mode 100644 index 00000000..e003b250 --- /dev/null +++ b/agent/llm/providers/factory.go @@ -0,0 +1,56 @@ +package providers + +import ( + "fmt" + + "github.com/yaoapp/gou/connector" + "github.com/yaoapp/yao/agent/context" + "github.com/yaoapp/yao/agent/llm/providers/legacy" + "github.com/yaoapp/yao/agent/llm/providers/openai" + "github.com/yaoapp/yao/agent/llm/providers/reasoning" +) + +// LLM interface (copied to avoid import cycle) +type LLM interface { + Stream(ctx *context.Context, messages []context.Message, options *context.CompletionOptions, handler context.StreamFunc) (*context.CompletionResponse, error) + Post(ctx *context.Context, messages []context.Message, options *context.CompletionOptions) (*context.CompletionResponse, error) +} + +// SelectProvider select the appropriate provider based on connector and capabilities +func SelectProvider(conn connector.Connector, options *context.CompletionOptions) (LLM, error) { + if options == nil || options.Capabilities == nil { + return nil, fmt.Errorf("options and capabilities are required") + } + + capabilities := options.Capabilities + + // Priority 1: Reasoning models (special response format) + if capabilities.Reasoning != nil && *capabilities.Reasoning { + return reasoning.New(conn, capabilities), nil + } + + // Priority 2: Check if model supports native tool calls + if capabilities.ToolCalls != nil && *capabilities.ToolCalls { + // Use OpenAI-compatible provider (supports tools, vision, streaming) + return openai.New(conn, capabilities), nil + } + + // Priority 3: Legacy models (no native tool support) + // Will use prompt engineering for tool calls + return legacy.New(conn, capabilities), nil +} + +// DetectProvider detect provider type from connector +func DetectProvider(conn connector.Connector) string { + // TODO: Implement provider detection + // - Check connector type (Is(connector.OPENAI)) + // - Check connector settings + // - Determine provider type (openai, claude, deepseek, etc.) + + if conn.Is(connector.OPENAI) { + return "openai" + } + + // Default to OpenAI-compatible + return "openai" +} diff --git a/agent/llm/providers/legacy/legacy.go b/agent/llm/providers/legacy/legacy.go new file mode 100644 index 00000000..1bb387d8 --- /dev/null +++ b/agent/llm/providers/legacy/legacy.go @@ -0,0 +1,64 @@ +package legacy + +import ( + "github.com/yaoapp/gou/connector" + "github.com/yaoapp/yao/agent/context" + "github.com/yaoapp/yao/agent/llm/providers/base" +) + +// Provider legacy LLM provider (no native tool calling support) +// Implements tool calling via prompt engineering +type Provider struct { + *base.Provider +} + +// New create a new legacy provider +func New(conn connector.Connector, capabilities *context.ModelCapabilities) *Provider { + return &Provider{ + Provider: base.NewProvider(conn, capabilities), + } +} + +// Stream stream completion from legacy model +func (p *Provider) Stream(ctx *context.Context, messages []context.Message, options *context.CompletionOptions, handler context.StreamFunc) (*context.CompletionResponse, error) { + // TODO: Implement legacy model streaming + // - Preprocess messages (remove tool-specific fields, vision, audio) + // - Remove vision content (convert to text description) + // - Remove audio content (convert to text transcription) + // - Remove tool messages + // - Add tool calling instructions to system prompt if tools provided + // - Build request body without native tool parameters + // - Make streaming HTTP request + // - Parse response and detect tool calls from text + // - Extract tool calls using regex/JSON parsing + // - Call handler for each chunk + return nil, nil +} + +// Post post completion request to legacy model +func (p *Provider) Post(ctx *context.Context, messages []context.Message, options *context.CompletionOptions) (*context.CompletionResponse, error) { + // TODO: Implement legacy model non-streaming completion + // - Preprocess messages + // - Add tool instructions to prompt + // - Make HTTP POST request + // - Parse response and extract tool calls from text + return nil, nil +} + +// InjectToolInstructions inject tool calling instructions into system prompt +func (p *Provider) InjectToolInstructions(messages []context.Message, tools []map[string]interface{}) []context.Message { + // TODO: Implement tool instruction injection + // - Generate tool description prompt + // - Add to system message or create new system message + // - Include tool schemas and usage instructions + return messages +} + +// ExtractToolCallsFromText extract tool calls from model's text response +func (p *Provider) ExtractToolCallsFromText(text string) []context.ToolCallResult { + // TODO: Implement tool call extraction + // - Look for JSON blocks or specific patterns + // - Parse tool name and arguments + // - Return structured tool calls + return nil +} diff --git a/agent/llm/providers/openai/openai.go b/agent/llm/providers/openai/openai.go new file mode 100644 index 00000000..ed4d4a4e --- /dev/null +++ b/agent/llm/providers/openai/openai.go @@ -0,0 +1,50 @@ +package openai + +import ( + "github.com/yaoapp/gou/connector" + "github.com/yaoapp/yao/agent/context" + "github.com/yaoapp/yao/agent/llm/providers/base" +) + +// Provider OpenAI-compatible provider +// Supports: vision, tool calls, streaming, JSON mode +type Provider struct { + *base.Provider +} + +// New create a new OpenAI provider +func New(conn connector.Connector, capabilities *context.ModelCapabilities) *Provider { + return &Provider{ + Provider: base.NewProvider(conn, capabilities), + } +} + +// Stream stream completion from OpenAI API +func (p *Provider) Stream(ctx *context.Context, messages []context.Message, options *context.CompletionOptions, handler context.StreamFunc) (*context.CompletionResponse, error) { + // TODO: Implement OpenAI streaming + // - Preprocess messages (vision, audio, tools) + // - Remove vision content if not supported + // - Remove audio content if not supported + // - Convert to text where needed + // - Build request body + // - Make streaming HTTP request + // - Parse SSE chunks + // - Call handler for each chunk + // - Aggregate final response + return nil, nil +} + +// Post post completion request to OpenAI API +func (p *Provider) Post(ctx *context.Context, messages []context.Message, options *context.CompletionOptions) (*context.CompletionResponse, error) { + // TODO: Implement OpenAI non-streaming completion + // - Preprocess messages + // - Build request body + // - Make HTTP POST request + // - Parse response + return nil, nil +} + +// SupportsAudio check if this provider supports audio +func (p *Provider) SupportsAudio() bool { + return p.Capabilities != nil && p.Capabilities.Audio != nil && *p.Capabilities.Audio +} diff --git a/agent/llm/providers/reasoning/reasoning.go b/agent/llm/providers/reasoning/reasoning.go new file mode 100644 index 00000000..e9a2cb7d --- /dev/null +++ b/agent/llm/providers/reasoning/reasoning.go @@ -0,0 +1,115 @@ +package reasoning + +import ( + "github.com/yaoapp/gou/connector" + "github.com/yaoapp/yao/agent/context" + "github.com/yaoapp/yao/agent/llm/providers/base" +) + +// Provider reasoning model provider (o1, DeepSeek R1, etc.) +// Handles special response format with reasoning_content +// Note: Some reasoning models (e.g. DeepSeek R1) don't support native tool calls +type Provider struct { + *base.Provider + supportsNativeTools bool // Whether this reasoning model supports native tool calling +} + +// New create a new reasoning provider +func New(conn connector.Connector, capabilities *context.ModelCapabilities) *Provider { + // Check if this reasoning model supports native tool calls + supportsTools := false + if capabilities != nil && capabilities.ToolCalls != nil && *capabilities.ToolCalls { + supportsTools = true + } + + return &Provider{ + Provider: base.NewProvider(conn, capabilities), + supportsNativeTools: supportsTools, + } +} + +// Stream stream completion from reasoning model +func (p *Provider) Stream(ctx *context.Context, messages []context.Message, options *context.CompletionOptions, handler context.StreamFunc) (*context.CompletionResponse, error) { + // TODO: Implement reasoning model streaming + // - Preprocess messages (reasoning models have restrictions) + + // Handle tool calls based on model support + if !p.supportsNativeTools && options != nil && len(options.Tools) > 0 { + // Model doesn't support native tool calls (e.g. DeepSeek R1) + // Inject tool instructions into messages + messages = p.injectToolInstructions(messages, options.Tools) + // Remove tools from options to avoid API error + options = p.removeToolsFromOptions(options) + } + + // - Build request body (special parameters for reasoning) + // - Make streaming HTTP request + // - Parse SSE chunks with reasoning_content + // - Handle both thinking and answer phases + // - Call handler for each chunk + + // If tools were injected, extract tool calls from text response + // if !p.supportsNativeTools && hasTools { + // toolCalls = p.extractToolCallsFromText(response.Content) + // } + + // - Aggregate final response with reasoning content + return nil, nil +} + +// Post post completion request to reasoning model +func (p *Provider) Post(ctx *context.Context, messages []context.Message, options *context.CompletionOptions) (*context.CompletionResponse, error) { + // TODO: Implement reasoning model non-streaming completion + // - Preprocess messages + // - Build request body + // - Make HTTP POST request + // - Parse response with reasoning_content field + // - Separate thinking from final answer + return nil, nil +} + +// ParseReasoningResponse parse response with reasoning content +// Handles both OpenAI o1 format and DeepSeek R1 format +func (p *Provider) ParseReasoningResponse(data []byte) (*context.CompletionResponse, error) { + // TODO: Implement reasoning response parsing + // - Detect format (OpenAI vs DeepSeek) + // - Extract reasoning_content + // - Extract final content + // - Set ContentTypes correctly (text + reasoning) + return nil, nil +} + +// injectToolInstructions inject tool calling instructions into messages +// Used for reasoning models that don't support native tool calls (e.g. DeepSeek R1) +func (p *Provider) injectToolInstructions(messages []context.Message, tools []map[string]interface{}) []context.Message { + // TODO: Implement tool instruction injection for reasoning models + // - Generate tool description prompt (optimized for reasoning models) + // - Add to system message or create new system message + // - Include tool schemas and usage instructions + // - Format should encourage reasoning about tool usage + return messages +} + +// extractToolCallsFromText extract tool calls from reasoning model's text response +// Used when model doesn't support native tool calls +func (p *Provider) extractToolCallsFromText(text string) []context.ToolCallResult { + // TODO: Implement tool call extraction from text + // - Look for JSON blocks or specific patterns + // - Parse tool name and arguments + // - Return structured tool calls + // - Handle reasoning model's specific output format + return nil +} + +// removeToolsFromOptions remove tool-related parameters from options +// Used when sending request to models that don't support native tool calls +func (p *Provider) removeToolsFromOptions(options *context.CompletionOptions) *context.CompletionOptions { + // TODO: Create a copy of options without tool parameters + // - Remove Tools field + // - Remove ToolChoice field + // - Keep other options intact + newOptions := *options + newOptions.Tools = nil + newOptions.ToolChoice = nil + return &newOptions +} diff --git a/agent/llm/providers/vision/vision.go b/agent/llm/providers/vision/vision.go new file mode 100644 index 00000000..fd5e7b0e --- /dev/null +++ b/agent/llm/providers/vision/vision.go @@ -0,0 +1,47 @@ +package vision + +import ( + "github.com/yaoapp/yao/agent/context" +) + +// PreprocessVisionMessages preprocess messages to handle vision content +// Removes or converts vision content for models that don't support it +func PreprocessVisionMessages(messages []context.Message, supportsVision bool) []context.Message { + // TODO: Implement vision message preprocessing + // If supportsVision is false: + // - Remove image_url content parts + // - Convert to text-only messages + // - Optionally add image descriptions from vision API + // If supportsVision is true: + // - Validate image URLs + // - Ensure proper format + return messages +} + +// ConvertImageToText convert image content to text description +// Used when model doesn't support vision +func ConvertImageToText(imageURL string) (string, error) { + // TODO: Implement image to text conversion + // - Call vision API (if configured) + // - Generate description + // - Return as text content + return "", nil +} + +// ValidateImageURL validate image URL format +func ValidateImageURL(imageURL string) error { + // TODO: Implement image URL validation + // - Check URL format + // - Validate image type + // - Check accessibility + return nil +} + +// ExtractImagesFromMessages extract all image URLs from messages +func ExtractImagesFromMessages(messages []context.Message) []string { + // TODO: Implement image extraction + // - Iterate through messages + // - Find ContentPart with type="image_url" + // - Collect all image URLs + return nil +} diff --git a/agent/llm/stream.go b/agent/llm/stream.go new file mode 100644 index 00000000..3ac5a185 --- /dev/null +++ b/agent/llm/stream.go @@ -0,0 +1,12 @@ +package llm + +import ( + "github.com/yaoapp/yao/agent/context" + "github.com/yaoapp/yao/agent/llm/handlers" +) + +// DefaultStreamHandler creates a default stream handler +// This is a convenience function that wraps handlers.DefaultStreamHandler +func DefaultStreamHandler(ctx *context.Context) context.StreamFunc { + return handlers.DefaultStreamHandler(ctx) +} diff --git a/agent/llm/types.go b/agent/llm/types.go deleted file mode 100644 index 764efb7a..00000000 --- a/agent/llm/types.go +++ /dev/null @@ -1,4 +0,0 @@ -package llm - -// CompletionOptions the completion request -type CompletionOptions struct{} diff --git a/agent/load.go b/agent/load.go index c3a4313a..7a6eed93 100644 --- a/agent/load.go +++ b/agent/load.go @@ -171,6 +171,17 @@ func initAssistant() error { assistant.SetVision(api.Agent.DSL.Vision) } + // Set global Uses configuration + if api.Agent.DSL.Use != nil { + globalUses := &store.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, + } + assistant.SetGlobalUses(globalUses) + } + if api.Agent.DSL.Connectors != nil { assistant.SetConnectorSettings(api.Agent.DSL.Connectors) } diff --git a/agent/store/types/convert.go b/agent/store/types/convert.go index 43b50260..eb06af03 100644 --- a/agent/store/types/convert.go +++ b/agent/store/types/convert.go @@ -173,6 +173,15 @@ func ToMySQLTime(v interface{}) string { } } +// Uses the wrapper configurations for assistant +// Used to specify which assistant or MCP server to use for vision, audio, etc. +type Uses struct { + Vision string `json:"vision,omitempty"` // Vision processing wrapper. Format: "agent" or "mcp:mcp_server_id" + Audio string `json:"audio,omitempty"` // Audio processing wrapper. Format: "agent" or "mcp:mcp_server_id" + Search string `json:"search,omitempty"` // Search wrapper. Format: "agent" or "mcp:mcp_server_id" + Fetch string `json:"fetch,omitempty"` // Fetch wrapper. Format: "agent" or "mcp:mcp_server_id" +} + // ToAssistantModel converts various types to AssistantModel func ToAssistantModel(v interface{}) (*AssistantModel, error) { if v == nil { diff --git a/agent/store/types/types.go b/agent/store/types/types.go index 0f02f661..7bf2e45a 100644 --- a/agent/store/types/types.go +++ b/agent/store/types/types.go @@ -155,6 +155,7 @@ type AssistantModel struct { Workflow *Workflow `json:"workflow,omitempty"` // Workflow configuration Placeholder *Placeholder `json:"placeholder,omitempty"` // Assistant Placeholder Locales i18n.Map `json:"locales,omitempty"` // Assistant Locales + Uses *Uses `json:"uses,omitempty"` // Assistant-specific wrapper configurations for vision, audio, etc. If not set, use global settings CreatedAt int64 `json:"created_at"` // Creation timestamp UpdatedAt int64 `json:"updated_at"` // Last update timestamp diff --git a/agent/store/xun/assistant.go b/agent/store/xun/assistant.go index 181ea4be..aa8382dc 100644 --- a/agent/store/xun/assistant.go +++ b/agent/store/xun/assistant.go @@ -158,6 +158,7 @@ func (conv *Xun) SaveAssistant(assistant *types.AssistantModel) (string, error) "tools": assistant.Tools, "placeholder": assistant.Placeholder, "locales": assistant.Locales, + "uses": assistant.Uses, } for field, value := range jsonFields { @@ -216,7 +217,7 @@ func (conv *Xun) UpdateAssistant(assistantID string, updates map[string]interfac data := make(map[string]interface{}) // List of fields that need JSON marshaling - jsonFields := []string{"options", "tags", "prompts", "kb", "mcp", "workflow", "tools", "placeholder", "locales"} + jsonFields := []string{"options", "tags", "prompts", "kb", "mcp", "workflow", "tools", "placeholder", "locales", "uses"} jsonFieldSet := make(map[string]bool) for _, field := range jsonFields { jsonFieldSet[field] = true @@ -416,7 +417,7 @@ func (conv *Xun) GetAssistants(filter types.AssistantFilter, locale ...string) ( // Convert rows to types.AssistantModel slice assistants := make([]*types.AssistantModel, 0, len(rows)) - jsonFields := []string{"tags", "options", "prompts", "workflow", "kb", "mcp", "tools", "placeholder", "locales"} + jsonFields := []string{"tags", "options", "prompts", "workflow", "kb", "mcp", "tools", "placeholder", "locales", "uses"} for _, row := range rows { data := row.ToMap() @@ -473,7 +474,7 @@ func (conv *Xun) GetAssistant(assistantID string, locale ...string) (*types.Assi } // Parse JSON fields - jsonFields := []string{"tags", "options", "prompts", "workflow", "kb", "mcp", "tools", "placeholder", "locales"} + jsonFields := []string{"tags", "options", "prompts", "workflow", "kb", "mcp", "tools", "placeholder", "locales", "uses"} conv.parseJSONFields(data, jsonFields) // Convert map to types.AssistantModel @@ -578,6 +579,16 @@ func (conv *Xun) GetAssistant(assistantID string, locale ...string) (*types.Assi } } + if uses, has := data["uses"]; has && uses != nil { + raw, err := jsoniter.Marshal(uses) + if err == nil { + var u types.Uses + if err := jsoniter.Unmarshal(raw, &u); err == nil { + model.Uses = &u + } + } + } + // Apply i18n translation if locale is provided if len(locale) > 0 && locale[0] != "" { conv.translate(model, assistantID, locale[0]) diff --git a/agent/store/xun/assistant_test.go b/agent/store/xun/assistant_test.go index 75280c2e..4d812203 100644 --- a/agent/store/xun/assistant_test.go +++ b/agent/store/xun/assistant_test.go @@ -197,6 +197,125 @@ func TestSaveAssistant(t *testing.T) { t.Errorf("Expected 3 tags, got %d", len(retrieved.Tags)) } }) + + t.Run("UsesConfiguration", func(t *testing.T) { + // Test assistant with Uses configuration + assistant := &types.AssistantModel{ + Name: "Uses Test Assistant", + Type: "assistant", + Connector: "openai", + Share: "private", + Uses: &types.Uses{ + Vision: "mcp:vision-server", + Audio: "agent", + Search: "mcp:search-server", + Fetch: "agent", + }, + } + + id, err := store.SaveAssistant(assistant) + if err != nil { + t.Fatalf("Failed to save assistant with uses: %v", err) + } + + // Retrieve and verify uses configuration + retrieved, err := store.GetAssistant(id) + if err != nil { + t.Fatalf("Failed to retrieve assistant: %v", err) + } + + if retrieved.Uses == nil { + t.Fatal("Expected uses to be set") + } + + if retrieved.Uses.Vision != "mcp:vision-server" { + t.Errorf("Expected vision 'mcp:vision-server', got '%s'", retrieved.Uses.Vision) + } + + if retrieved.Uses.Audio != "agent" { + t.Errorf("Expected audio 'agent', got '%s'", retrieved.Uses.Audio) + } + + if retrieved.Uses.Search != "mcp:search-server" { + t.Errorf("Expected search 'mcp:search-server', got '%s'", retrieved.Uses.Search) + } + + if retrieved.Uses.Fetch != "agent" { + t.Errorf("Expected fetch 'agent', got '%s'", retrieved.Uses.Fetch) + } + + t.Logf("Successfully saved and retrieved assistant with uses configuration") + }) + + t.Run("NilUses", func(t *testing.T) { + // Test assistant without Uses configuration + assistant := &types.AssistantModel{ + Name: "No Uses Assistant", + Type: "assistant", + Connector: "openai", + Share: "private", + } + + id, err := store.SaveAssistant(assistant) + if err != nil { + t.Fatalf("Failed to save assistant without uses: %v", err) + } + + // Retrieve and verify uses is nil + retrieved, err := store.GetAssistant(id) + if err != nil { + t.Fatalf("Failed to retrieve assistant: %v", err) + } + + if retrieved.Uses != nil { + t.Errorf("Expected uses to be nil, got %+v", retrieved.Uses) + } + }) + + t.Run("PartialUsesConfiguration", func(t *testing.T) { + // Test assistant with partial Uses configuration + assistant := &types.AssistantModel{ + Name: "Partial Uses Assistant", + Type: "assistant", + Connector: "openai", + Share: "private", + Uses: &types.Uses{ + Vision: "mcp:vision-only", + // Audio, Search, Fetch not set + }, + } + + id, err := store.SaveAssistant(assistant) + if err != nil { + t.Fatalf("Failed to save assistant with partial uses: %v", err) + } + + // Retrieve and verify + retrieved, err := store.GetAssistant(id) + if err != nil { + t.Fatalf("Failed to retrieve assistant: %v", err) + } + + if retrieved.Uses == nil { + t.Fatal("Expected uses to be set") + } + + if retrieved.Uses.Vision != "mcp:vision-only" { + t.Errorf("Expected vision 'mcp:vision-only', got '%s'", retrieved.Uses.Vision) + } + + if retrieved.Uses.Audio != "" { + t.Errorf("Expected audio to be empty, got '%s'", retrieved.Uses.Audio) + } + + if retrieved.Uses.Search != "" { + t.Errorf("Expected search to be empty, got '%s'", retrieved.Uses.Search) + } + + if retrieved.Uses.Fetch != "" { + t.Errorf("Expected fetch to be empty, got '%s'", retrieved.Uses.Fetch) + } + }) } // TestDeleteAssistant tests deleting a single assistant @@ -1953,6 +2072,105 @@ func TestUpdateAssistant(t *testing.T) { } }) + t.Run("UpdateUses", func(t *testing.T) { + // Create assistant without uses + assistant := &types.AssistantModel{ + Name: "Uses Update Test", + Type: "assistant", + Connector: "openai", + Share: "private", + } + + id, err := store.SaveAssistant(assistant) + if err != nil { + t.Fatalf("Failed to create assistant: %v", err) + } + + // Update with uses configuration + updates := map[string]interface{}{ + "uses": &types.Uses{ + Vision: "mcp:new-vision", + Audio: "mcp:new-audio", + Search: "agent", + Fetch: "mcp:fetch-server", + }, + } + + err = store.UpdateAssistant(id, updates) + if err != nil { + t.Fatalf("Failed to update uses: %v", err) + } + + // Verify updates + retrieved, err := store.GetAssistant(id) + if err != nil { + t.Fatalf("Failed to retrieve assistant: %v", err) + } + + if retrieved.Uses == nil { + t.Fatal("Expected uses to be set") + } + + if retrieved.Uses.Vision != "mcp:new-vision" { + t.Errorf("Expected vision 'mcp:new-vision', got '%s'", retrieved.Uses.Vision) + } + if retrieved.Uses.Audio != "mcp:new-audio" { + t.Errorf("Expected audio 'mcp:new-audio', got '%s'", retrieved.Uses.Audio) + } + if retrieved.Uses.Search != "agent" { + t.Errorf("Expected search 'agent', got '%s'", retrieved.Uses.Search) + } + if retrieved.Uses.Fetch != "mcp:fetch-server" { + t.Errorf("Expected fetch 'mcp:fetch-server', got '%s'", retrieved.Uses.Fetch) + } + + // Update to change uses + updates2 := map[string]interface{}{ + "uses": &types.Uses{ + Vision: "agent", + Audio: "agent", + }, + } + + err = store.UpdateAssistant(id, updates2) + if err != nil { + t.Fatalf("Failed to update uses again: %v", err) + } + + // Verify second update + retrieved2, err := store.GetAssistant(id) + if err != nil { + t.Fatalf("Failed to retrieve assistant: %v", err) + } + + if retrieved2.Uses.Vision != "agent" { + t.Errorf("Expected vision 'agent', got '%s'", retrieved2.Uses.Vision) + } + if retrieved2.Uses.Audio != "agent" { + t.Errorf("Expected audio 'agent', got '%s'", retrieved2.Uses.Audio) + } + + // Update to remove uses (set to nil) + updates3 := map[string]interface{}{ + "uses": nil, + } + + err = store.UpdateAssistant(id, updates3) + if err != nil { + t.Fatalf("Failed to set uses to nil: %v", err) + } + + // Verify uses is nil + retrieved3, err := store.GetAssistant(id) + if err != nil { + t.Fatalf("Failed to retrieve assistant: %v", err) + } + + if retrieved3.Uses != nil { + t.Errorf("Expected uses to be nil, got %+v", retrieved3.Uses) + } + }) + t.Run("UpdatePermissionFields", func(t *testing.T) { // Create assistant with permission fields assistant := &types.AssistantModel{ diff --git a/agent/types/types.go b/agent/types/types.go index 446d4a59..787cf86c 100644 --- a/agent/types/types.go +++ b/agent/types/types.go @@ -44,7 +44,8 @@ type Use 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. - Vision string `json:"vision,omitempty" yaml:"vision,omitempty"` // The assistant for generating the image/video description, if the assistant enable the vision and model not support vision, use the vision model to describe the image/video, and return the messages with the image/video's description. + Vision string `json:"vision,omitempty" yaml:"vision,omitempty"` // The assistant for generating the image/video description, if the assistant enable the vision and model not support vision, use the vision model to describe the image/video, and return the messages with the image/video's description. Format: "agent" or "mcp:mcp_server_id" + Audio string `json:"audio,omitempty" yaml:"audio,omitempty"` // The assistant for processing audio (speech-to-text, text-to-speech). If the model doesn't support audio, use this to convert audio to text. Format: "agent" or "mcp:mcp_server_id" Search string `json:"search,omitempty" yaml:"search,omitempty"` // The assistant for searching the knowledge, global web search. If not set, and the assistant enable the knowledge, it will search the result from the knowledge automatically. Fetch string `json:"fetch,omitempty" yaml:"fetch,omitempty"` // The assistant for fetching the http/https/ftp/sftp/etc. file, and return the file's content. if not set, use the http process to fetch the file. } diff --git a/data/bindata.go b/data/bindata.go index 254c6e93..4cdba5c9 100644 --- a/data/bindata.go +++ b/data/bindata.go @@ -319,7 +319,7 @@ func cuiSetupIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "cui/setup/index.html", size: 10, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "cui/setup/index.html", size: 10, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -339,7 +339,7 @@ func cuiV09IndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "cui/v0.9/index.html", size: 13, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "cui/v0.9/index.html", size: 13, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -359,7 +359,7 @@ func cuiV10IndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "cui/v1.0/index.html", size: 49, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "cui/v1.0/index.html", size: 49, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -379,7 +379,7 @@ func cuiV10Layouts__indexAsyncJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "cui/v1.0/layouts__index.async.js", size: 71, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "cui/v1.0/layouts__index.async.js", size: 71, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -399,7 +399,7 @@ func cuiV10UmiJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "cui/v1.0/umi.js", size: 71, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "cui/v1.0/umi.js", size: 71, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -419,7 +419,7 @@ func initEnv() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.env", size: 219, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.env", size: 219, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -439,7 +439,7 @@ func initVscodeSettingsJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/settings.json", size: 4666, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/settings.json", size: 4666, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -459,7 +459,7 @@ func initVscodeTypesRuntimeConsoleDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/console.d.ts", size: 221, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/console.d.ts", size: 221, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -479,7 +479,7 @@ func initVscodeTypesRuntimeExceptionDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/exception.d.ts", size: 738, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/exception.d.ts", size: 738, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -499,7 +499,7 @@ func initVscodeTypesRuntimeFsDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/fs.d.ts", size: 8554, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/fs.d.ts", size: 8554, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -519,7 +519,7 @@ func initVscodeTypesRuntimeGlobalDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/global.d.ts", size: 1759, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/global.d.ts", size: 1759, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -539,7 +539,7 @@ func initVscodeTypesRuntimeHttpDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/http.d.ts", size: 6179, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/http.d.ts", size: 6179, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -559,7 +559,7 @@ func initVscodeTypesRuntimeIoDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/io.d.ts", size: 587, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/io.d.ts", size: 587, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -579,7 +579,7 @@ func initVscodeTypesRuntimeLogDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/log.d.ts", size: 1692, mode: os.FileMode(493), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/log.d.ts", size: 1692, mode: os.FileMode(493), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -599,7 +599,7 @@ func initVscodeTypesRuntimeNeoDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/neo.d.ts", size: 3750, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/neo.d.ts", size: 3750, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -619,7 +619,7 @@ func initVscodeTypesRuntimeProcessFsDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/process/fs.d.ts", size: 11133, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/process/fs.d.ts", size: 11133, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -639,7 +639,7 @@ func initVscodeTypesRuntimeProcessHttpDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/process/http.d.ts", size: 5653, mode: os.FileMode(493), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/process/http.d.ts", size: 5653, mode: os.FileMode(493), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -659,7 +659,7 @@ func initVscodeTypesRuntimeProcessModelDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/process/model.d.ts", size: 6656, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/process/model.d.ts", size: 6656, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -679,7 +679,7 @@ func initVscodeTypesRuntimeProcessDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/process.d.ts", size: 23165, mode: os.FileMode(493), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/process.d.ts", size: 23165, mode: os.FileMode(493), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -699,7 +699,7 @@ func initVscodeTypesRuntimeQueryDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/query.d.ts", size: 6124, mode: os.FileMode(493), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/query.d.ts", size: 6124, mode: os.FileMode(493), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -719,7 +719,7 @@ func initVscodeTypesRuntimeStoreDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/store.d.ts", size: 2251, mode: os.FileMode(493), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/store.d.ts", size: 2251, mode: os.FileMode(493), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -739,7 +739,7 @@ func initVscodeTypesRuntimeSuiDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/sui.d.ts", size: 1713, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/sui.d.ts", size: 1713, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -759,7 +759,7 @@ func initVscodeTypesRuntimeTimeDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/time.d.ts", size: 711, mode: os.FileMode(493), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/time.d.ts", size: 711, mode: os.FileMode(493), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -779,7 +779,7 @@ func initVscodeTypesRuntimeDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime.d.ts", size: 424, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime.d.ts", size: 424, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -799,7 +799,7 @@ func initVscodeTypesSuiDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/sui.d.ts", size: 8931, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/.vscode/types/sui.d.ts", size: 8931, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -819,7 +819,7 @@ func initAppYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/app.yao", size: 3115, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/app.yao", size: 3115, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -839,7 +839,7 @@ func initDataReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/README.md", size: 41, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/data/README.md", size: 41, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -859,7 +859,7 @@ func initDataTemplatesDefault__assetsReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/__assets/README.md", size: 33, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/data/templates/default/__assets/README.md", size: 33, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -879,7 +879,7 @@ func initDataTemplatesDefault__assetsImagesIconsAppPng() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/__assets/images/icons/app.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/data/templates/default/__assets/images/icons/app.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -899,7 +899,7 @@ func initDataTemplatesDefault__assetsImagesLogosLogo_colorSvg() (*asset, error) return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/__assets/images/logos/logo_color.svg", size: 2909, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/data/templates/default/__assets/images/logos/logo_color.svg", size: 2909, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -919,7 +919,7 @@ func initDataTemplatesDefault__assetsImagesLogosWordmarkSvg() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/__assets/images/logos/wordmark.svg", size: 3615, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/data/templates/default/__assets/images/logos/wordmark.svg", size: 3615, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -939,7 +939,7 @@ func initDataTemplatesDefault__dataJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/__data.json", size: 30, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/data/templates/default/__data.json", size: 30, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -959,7 +959,7 @@ func initDataTemplatesDefault__documentHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/__document.html", size: 492, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/data/templates/default/__document.html", size: 492, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -979,7 +979,7 @@ func initDataTemplatesDefaultIndexIndexCss() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/index/index.css", size: 2896, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/data/templates/default/index/index.css", size: 2896, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -999,7 +999,7 @@ func initDataTemplatesDefaultIndexIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/index/index.html", size: 2361, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/data/templates/default/index/index.html", size: 2361, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1019,7 +1019,7 @@ func initDataTemplatesDefaultIndexIndexJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/index/index.json", size: 31, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/data/templates/default/index/index.json", size: 31, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1039,7 +1039,7 @@ func initDbReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/db/README.md", size: 84, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/db/README.md", size: 84, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1059,7 +1059,7 @@ func initFlowsMenuFlowYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/flows/menu.flow.yao", size: 813, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/flows/menu.flow.yao", size: 813, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1079,7 +1079,7 @@ func initFormsAccountFormYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/forms/account.form.yao", size: 1194, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/forms/account.form.yao", size: 1194, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1099,7 +1099,7 @@ func initIconsAppIcns() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/icons/app.icns", size: 67465, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/icons/app.icns", size: 67465, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1119,7 +1119,7 @@ func initIconsAppIco() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/icons/app.ico", size: 54993, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/icons/app.ico", size: 54993, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1139,7 +1139,7 @@ func initIconsAppPng() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/icons/app.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/icons/app.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1159,7 +1159,7 @@ func initLoginsAdminLoginYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/logins/admin.login.yao", size: 302, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/logins/admin.login.yao", size: 302, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1179,7 +1179,7 @@ func initLogsReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/logs/README.md", size: 28, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/logs/README.md", size: 28, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1199,7 +1199,7 @@ func initModelsAdminUserModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/models/admin/user.mod.yao", size: 6416, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/models/admin/user.mod.yao", size: 6416, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1219,7 +1219,7 @@ func initModelsTestsPetModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/models/tests/pet.mod.yao", size: 525, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/models/tests/pet.mod.yao", size: 525, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1239,7 +1239,7 @@ func initNeoNeoYml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/neo/neo.yml", size: 724, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/neo/neo.yml", size: 724, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1259,7 +1259,7 @@ func initPublicReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/README.md", size: 108, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/public/README.md", size: 108, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1279,7 +1279,7 @@ func initPublicAssetsReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/assets/README.md", size: 33, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/public/assets/README.md", size: 33, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1299,7 +1299,7 @@ func initPublicAssetsImagesIconsAppPng() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/assets/images/icons/app.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/public/assets/images/icons/app.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1319,7 +1319,7 @@ func initPublicAssetsImagesLogosLogo_colorSvg() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/assets/images/logos/logo_color.svg", size: 2909, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/public/assets/images/logos/logo_color.svg", size: 2909, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1339,7 +1339,7 @@ func initPublicAssetsImagesLogosWordmarkSvg() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/assets/images/logos/wordmark.svg", size: 3615, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/public/assets/images/logos/wordmark.svg", size: 3615, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1359,7 +1359,7 @@ func initPublicAssetsLibsuiMinJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/assets/libsui.min.js", size: 12569, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/public/assets/libsui.min.js", size: 12569, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1379,7 +1379,7 @@ func initPublicAssetsLibsuiMinJsMap() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/assets/libsui.min.js.map", size: 38553, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/public/assets/libsui.min.js.map", size: 38553, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1399,7 +1399,7 @@ func initPublicIndexCfg() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/index.cfg", size: 85, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/public/index.cfg", size: 85, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1419,7 +1419,7 @@ func initPublicIndexSui() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/index.sui", size: 5682, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/public/index.sui", size: 5682, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1439,7 +1439,7 @@ func initScriptsAccountTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/scripts/account.ts", size: 2521, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/scripts/account.ts", size: 2521, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1459,7 +1459,7 @@ func initScriptsAiNeoTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/scripts/ai/neo.ts", size: 375, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/scripts/ai/neo.ts", size: 375, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1479,7 +1479,7 @@ func initScriptsTestsTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/scripts/tests.ts", size: 1044, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/scripts/tests.ts", size: 1044, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1499,7 +1499,7 @@ func initScriptsUtilsTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/scripts/utils.ts", size: 1230, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/scripts/utils.ts", size: 1230, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1519,7 +1519,7 @@ func initSuisWebSuiYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/suis/web.sui.yao", size: 675, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/suis/web.sui.yao", size: 675, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1539,7 +1539,7 @@ func initTablesAccountTabYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/tables/account.tab.yao", size: 5597, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/tables/account.tab.yao", size: 5597, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1559,7 +1559,7 @@ func initTsconfigJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/tsconfig.json", size: 178, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "init/tsconfig.json", size: 178, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1579,7 +1579,7 @@ func libsuiAgentTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "libsui/agent.ts", size: 15267, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "libsui/agent.ts", size: 15267, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1599,7 +1599,7 @@ func libsuiIndexTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "libsui/index.ts", size: 13049, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "libsui/index.ts", size: 13049, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1619,7 +1619,7 @@ func libsuiUtilsTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "libsui/utils.ts", size: 5959, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "libsui/utils.ts", size: 5959, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1639,7 +1639,7 @@ func libsuiYaoTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "libsui/yao.ts", size: 4338, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "libsui/yao.ts", size: 4338, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1659,7 +1659,7 @@ func publicIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "public/index.html", size: 11, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "public/index.html", size: 11, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1679,7 +1679,7 @@ func uiIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "ui/index.html", size: 11, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "ui/index.html", size: 11, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1699,7 +1699,7 @@ func yaoDataIcons404Png() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/icons/404.png", size: 9342, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/icons/404.png", size: 9342, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1719,7 +1719,7 @@ func yaoDataIconsIconIcns() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/icons/icon.icns", size: 67465, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/icons/icon.icns", size: 67465, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1739,7 +1739,7 @@ func yaoDataIconsIconIco() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/icons/icon.ico", size: 54993, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/icons/icon.ico", size: 54993, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1759,7 +1759,7 @@ func yaoDataIconsIconPng() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/icons/icon.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/icons/icon.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1779,7 +1779,7 @@ func yaoDataIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/index.html", size: 282, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/index.html", size: 282, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1799,7 +1799,7 @@ func yaoDataKbProvidersChunkingSemanticEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/chunking/semantic/en.json", size: 5543, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/chunking/semantic/en.json", size: 5543, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1819,7 +1819,7 @@ func yaoDataKbProvidersChunkingSemanticZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/chunking/semantic/zh-cn.json", size: 5446, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/chunking/semantic/zh-cn.json", size: 5446, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1839,7 +1839,7 @@ func yaoDataKbProvidersChunkingStructuredEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/chunking/structured/en.json", size: 2423, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/chunking/structured/en.json", size: 2423, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1859,7 +1859,7 @@ func yaoDataKbProvidersChunkingStructuredZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/chunking/structured/zh-cn.json", size: 2321, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/chunking/structured/zh-cn.json", size: 2321, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1879,7 +1879,7 @@ func yaoDataKbProvidersConverterMcpEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/mcp/en.json", size: 4235, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/mcp/en.json", size: 4235, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1899,7 +1899,7 @@ func yaoDataKbProvidersConverterMcpZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/mcp/zh-cn.json", size: 4060, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/mcp/zh-cn.json", size: 4060, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1919,7 +1919,7 @@ func yaoDataKbProvidersConverterOcrEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/ocr/en.json", size: 6631, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/ocr/en.json", size: 6631, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1939,7 +1939,7 @@ func yaoDataKbProvidersConverterOcrZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/ocr/zh-cn.json", size: 6501, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/ocr/zh-cn.json", size: 6501, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1959,7 +1959,7 @@ func yaoDataKbProvidersConverterOfficeEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/office/en.json", size: 5476, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/office/en.json", size: 5476, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1979,7 +1979,7 @@ func yaoDataKbProvidersConverterOfficeZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/office/zh-cn.json", size: 5356, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/office/zh-cn.json", size: 5356, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1999,7 +1999,7 @@ func yaoDataKbProvidersConverterUtf8EnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/utf8/en.json", size: 292, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/utf8/en.json", size: 292, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2019,7 +2019,7 @@ func yaoDataKbProvidersConverterUtf8ZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/utf8/zh-cn.json", size: 281, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/utf8/zh-cn.json", size: 281, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2039,7 +2039,7 @@ func yaoDataKbProvidersConverterVideoEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/video/en.json", size: 6411, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/video/en.json", size: 6411, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2059,7 +2059,7 @@ func yaoDataKbProvidersConverterVideoZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/video/zh-cn.json", size: 6297, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/video/zh-cn.json", size: 6297, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2079,7 +2079,7 @@ func yaoDataKbProvidersConverterVisionEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/vision/en.json", size: 4085, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/vision/en.json", size: 4085, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2099,7 +2099,7 @@ func yaoDataKbProvidersConverterVisionZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/vision/zh-cn.json", size: 3949, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/vision/zh-cn.json", size: 3949, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2119,7 +2119,7 @@ func yaoDataKbProvidersConverterWhisperEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/whisper/en.json", size: 4449, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/whisper/en.json", size: 4449, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2139,7 +2139,7 @@ func yaoDataKbProvidersConverterWhisperZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/whisper/zh-cn.json", size: 4312, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/whisper/zh-cn.json", size: 4312, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2159,7 +2159,7 @@ func yaoDataKbProvidersEmbeddingFastembedEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/embedding/fastembed/en.json", size: 6865, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/embedding/fastembed/en.json", size: 6865, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2179,7 +2179,7 @@ func yaoDataKbProvidersEmbeddingFastembedZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/embedding/fastembed/zh-cn.json", size: 6685, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/embedding/fastembed/zh-cn.json", size: 6685, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2199,7 +2199,7 @@ func yaoDataKbProvidersEmbeddingOpenaiEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/embedding/openai/en.json", size: 5636, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/embedding/openai/en.json", size: 5636, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2219,7 +2219,7 @@ func yaoDataKbProvidersEmbeddingOpenaiZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/embedding/openai/zh-cn.json", size: 5463, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/embedding/openai/zh-cn.json", size: 5463, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2239,7 +2239,7 @@ func yaoDataKbProvidersExtractionOpenaiEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/extraction/openai/en.json", size: 9110, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/extraction/openai/en.json", size: 9110, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2259,7 +2259,7 @@ func yaoDataKbProvidersExtractionOpenaiZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/extraction/openai/zh-cn.json", size: 8827, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/extraction/openai/zh-cn.json", size: 8827, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2279,7 +2279,7 @@ func yaoDataKbProvidersFetcherHttpEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/http/en.json", size: 5885, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/http/en.json", size: 5885, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2299,7 +2299,7 @@ func yaoDataKbProvidersFetcherHttpZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/http/zh-cn.json", size: 5925, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/http/zh-cn.json", size: 5925, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2319,7 +2319,7 @@ func yaoDataKbProvidersFetcherMcpEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/mcp/en.json", size: 6819, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/mcp/en.json", size: 6819, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2339,7 +2339,7 @@ func yaoDataKbProvidersFetcherMcpZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/mcp/zh-cn.json", size: 6611, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/mcp/zh-cn.json", size: 6611, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2359,7 +2359,7 @@ func yaoFieldsModelTransJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/fields/model.trans.json", size: 14938, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/fields/model.trans.json", size: 14938, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2379,7 +2379,7 @@ func yaoLangsEnUsJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/en-US.json", size: 66, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/langs/en-US.json", size: 66, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2399,7 +2399,7 @@ func yaoLangsZhCnGlobalYml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/zh-cn/global.yml", size: 1762, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/langs/zh-cn/global.yml", size: 1762, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2419,7 +2419,7 @@ func yaoLangsZhCnLoginsAdminLoginYml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/zh-cn/logins/admin.login.yml", size: 94, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/langs/zh-cn/logins/admin.login.yml", size: 94, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2439,7 +2439,7 @@ func yaoLangsZhCnLoginsUserLoginYml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/zh-cn/logins/user.login.yml", size: 90, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/langs/zh-cn/logins/user.login.yml", size: 90, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2459,7 +2459,7 @@ func yaoLangsZhHkGlobalYml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/zh-hk/global.yml", size: 1762, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/langs/zh-hk/global.yml", size: 1762, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2479,7 +2479,7 @@ func yaoLangsZhHkLoginsAdminLoginYml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/zh-hk/logins/admin.login.yml", size: 94, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/langs/zh-hk/logins/admin.login.yml", size: 94, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2499,12 +2499,12 @@ func yaoLangsZhHkLoginsUserLoginYml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/zh-hk/logins/user.login.yml", size: 90, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/langs/zh-hk/logins/user.login.yml", size: 90, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _yaoModelsAgentAssistantModYao = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x98\xcd\x72\xdc\x36\x0c\x80\xef\x7e\x0a\x0c\xcf\x6e\x92\xf6\xd0\xa9\x7d\xaa\x93\x5c\x3c\x8d\x1b\x4f\x7e\x9a\x43\xc6\xb3\x03\x49\x90\xc4\x9a\x22\x55\x12\xb2\xe3\xf1\xf8\xdd\x3b\xa4\xb4\xfa\x59\x71\xbd\xd2\x3a\x17\x7b\x96\x04\xc0\x0f\x24\x04\x10\x7c\x3c\x01\x10\x1a\x2b\x12\xe7\x20\x2e\x9c\x93\x8e\x51\xb3\x38\xf5\xc3\x0a\x13\x52\x91\xf1\x8c\x5c\x6a\x65\xcd\xd2\xe8\xc9\x2c\x30\x26\x8a\x20\x37\x16\x1c\x1b\x2b\x75\x01\x17\x97\x80\xfd\x74\x6a\x74\x2e\x8b\xc6\xa2\xd7\x74\x80\x3a\x83\x8a\x18\x33\x64\x6c\x0d\x33\x16\x4e\x9c\xc3\x77\x81\x05\xf9\xc5\x40\xb8\x07\xc7\x54\x89\x9b\x30\x9d\x34\x52\xb1\xf4\x6b\xb2\x6d\x28\x0c\x59\xc2\xcc\x68\xf5\x30\x1e\x73\xc6\xb2\x38\x87\xb3\xb3\xb3\xb3\xce\x6a\xa2\xbc\x7b\x8f\x83\xa3\xc1\xfe\x06\x07\xb7\x40\xa4\xa6\xaa\xfc\xa2\xde\x21\x3f\x3b\xe2\x6e\x0d\xc0\x53\xb0\x96\x1a\xd5\x54\x3a\x60\x9e\x00\x00\x3c\x86\xbf\xa3\x4d\x94\x59\x70\x26\x8c\xf1\x43\x1d\xc6\x2e\xdf\x0f\x63\xf3\x5d\x85\xf1\xf4\x88\xe3\xab\x96\xff\x35\x34\x02\x91\x19\x69\x96\xb9\x24\x2b\x82\xf8\xd3\x69\x1c\xa1\xd7\xd8\xc4\x60\x1c\xfb\xa3\x39\x06\xe8\x22\x46\x32\xd8\x21\x5d\x70\x29\xce\xe1\xb7\x37\x6f\xfa\x41\xdd\x28\xd5\xed\x7f\x8e\xca\x51\x3f\xd1\x04\xe7\x46\xe7\x16\x46\xa5\xce\xe8\x47\x37\xf8\xac\x8b\xc1\x99\xe5\xae\x7d\x99\x88\x47\x5d\x9a\x5a\x8c\x3a\x93\x51\x8e\x8d\xe2\xc9\x16\x8b\xf5\xec\xe1\xff\x72\xf6\xbf\x27\xe2\x51\xf6\xa9\xc5\x43\x07\x71\x10\x10\xef\x90\xd1\xae\x89\x9c\x1d\x85\x28\x64\x6b\x15\xbe\x7e\xfa\xf0\x13\x51\x53\xa3\x35\xa5\x6c\xd6\xd0\xbe\x9b\xeb\x44\x81\xbb\xe3\x86\x7e\x8d\x53\x90\x39\x68\xc3\xe0\x88\x4f\xa1\x71\x04\x5c\x12\x14\xca\x24\xa8\xe6\xd2\x2b\xbf\x8c\x67\xdd\x1c\xa7\xdc\xe5\x8e\xbe\x8f\x69\xed\x71\x35\x22\xd9\x63\xff\xbe\xff\x70\xd6\x47\x7f\x8d\x5c\xae\xf0\xe1\x7a\x22\x1e\x85\xf7\xc5\x06\x0b\x82\xa9\xe5\x17\x87\x56\xa8\x23\x33\x50\xa9\x99\x8a\x49\xda\xdb\x92\x7e\x9e\xc8\xc7\x49\x8d\x65\x30\x36\x1b\xeb\x0f\x49\x65\x5b\xb1\xd6\xed\x67\xa8\x8a\x1b\x19\x89\x8b\xc4\x18\x45\xa8\x23\xa8\x6f\xbd\x0e\x5c\xc6\xa3\xe2\x5b\x49\x5c\x92\x05\x2e\xa5\x03\xe9\x00\x21\x2c\xf1\x8b\xd4\x10\xc9\x7a\x03\xfe\x34\xbf\x2f\x8f\x07\x85\x29\x95\x46\x4d\x36\x65\xeb\xc2\xbf\xce\xc4\xf8\xaf\x63\x3a\xd1\x1d\x8f\x5a\x5f\x13\x05\x26\x7c\x14\x6e\x31\xda\xc7\x5d\xf9\x28\xd6\xcc\xea\x1a\xa4\xda\x9a\xaa\xe6\xe5\x48\xd7\xbb\xf2\xf1\x9d\xda\x95\x5a\x83\x74\x6f\xec\x6d\xae\xcc\xfd\x62\xa6\x6f\x33\x85\x28\xd4\xdc\xee\x1a\xaa\xdb\x64\x31\xcf\x5f\xda\xdc\x2b\xca\x0a\x82\xb7\xe8\x0e\x95\xdb\xdb\x5e\x38\x41\x47\x90\x1a\xa5\x28\x7d\xc1\x81\x56\x69\xbd\x18\xf4\xea\xdd\x35\x7c\x26\x7b\x47\x36\x7e\xa0\x7e\xde\xb5\xf3\xbe\xe2\x4a\xd5\xdf\xc9\x7d\xad\x1a\x5d\x6a\x8d\x2f\x60\xc7\xf1\xb2\x31\x6a\x79\xf8\x7d\x99\x4a\xc7\x2f\x5f\x53\x99\x55\x30\xbe\x69\x58\xcc\x32\x11\x8e\xa3\x4c\x44\xd6\x90\xf4\xcd\xc8\x8a\x4c\xfc\x69\xa6\x13\x85\xda\x9a\x06\xc7\xc8\x8d\x3b\x2e\xfd\xee\xc9\x26\x4d\xa2\x64\xba\x86\xf9\x3a\x68\xc0\xc5\xbc\x18\xec\xab\x22\xa3\x16\xc6\x81\x2b\xd1\x52\x06\x98\x5a\xe3\x1c\xa0\x52\xc0\x84\x95\x03\xa9\x43\x88\xd6\x0a\x39\x37\xb6\x3a\xec\xe3\xaa\x1b\x54\x58\x75\xee\x25\xe9\xa6\x8a\xd5\xf2\xa9\x74\xbc\x98\x97\x18\x7a\x5c\x97\x9a\x71\xe7\x60\xb6\x8d\xf1\xf7\x6e\x04\x7c\xc6\x96\x77\xc8\x24\x4e\xe1\xf5\x6b\xf8\xe8\xcf\xf1\x4e\x3a\xe9\x3f\x4c\x36\xc1\x69\x73\xaf\xc9\x0e\xf2\x7e\x43\x84\x97\xfd\x67\x10\xdb\x6e\x14\x54\x54\x25\x64\x5d\x27\x7d\x13\xeb\x4e\xfa\xf5\xf6\x6d\xd5\x11\x71\xa2\x4c\x8a\x8a\x96\x7f\x6a\x1f\x76\xe5\xe3\x8d\xe4\xaf\x7f\x68\x98\x99\x5e\xd5\xac\x34\x6c\x2a\x64\x8a\x74\xba\xfb\x43\xf8\x62\xae\x14\xef\x59\xb6\x72\xcf\x7c\x78\xc7\x5d\x83\xfd\x5a\xd2\xe8\xe0\xe5\x0a\xf0\xab\x98\xda\xe1\xcf\x2e\x45\x0d\x58\xd7\x84\xd6\x7f\x66\x7f\x42\xb7\x3a\x28\xe9\xa2\x77\xb9\x03\x3e\x9d\x74\x71\x27\x2c\xa9\xf6\x35\x47\x9c\x77\x2e\x8a\xb4\x44\x1e\x7e\x8e\x9c\x2a\xd1\x5d\xa1\x1e\x65\xba\xca\x64\xad\x53\x9b\xcd\x03\x9a\x57\xe1\x51\xe6\x95\x57\x1f\x44\x6e\xe9\x61\xff\x83\x46\x6e\x2c\xc9\x42\xcf\x04\x7a\xc6\xf6\xc5\x26\xe0\xd3\xb3\x2f\x36\x3f\x36\x3b\x2f\x42\x1b\x0f\xbd\xd9\x3e\x38\x8d\x36\xba\x7f\xfc\xe9\x5e\x20\x46\xf7\xef\x9b\x48\xaf\xe0\x37\x2e\x76\x4c\x97\x7e\x26\x54\x65\x9c\x3c\x40\x84\x47\xb1\xfe\xba\xdd\xc5\xdc\xb3\x71\x14\x83\x77\x3b\x9d\xc8\x00\xdd\xce\x8c\x3f\x9a\x97\x52\x7b\x8b\x3e\x0b\x7a\xf0\xce\xaa\x4f\x7f\x3b\x71\xd2\x27\xc5\x47\x10\x2c\x2b\x72\x8c\x55\xed\xb6\x81\xe6\x7b\xad\x9c\x37\x19\x29\xe2\x70\x50\x6d\x8e\x02\x51\x93\xad\xa4\x73\xad\xaa\x17\x85\xa7\x93\xa7\x93\xff\x03\x00\x00\xff\xff\xff\x04\x5e\x98\xb4\x14\x00\x00") +var _yaoModelsAgentAssistantModYao = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x98\x5f\x73\xdb\x36\x0c\xc0\xdf\xf3\x29\x70\x7a\x76\xd3\x6e\x0f\xbb\x25\x4f\x4b\xdb\x97\xdc\x9a\x35\xd7\x36\xeb\x43\x2f\xe7\x83\x25\xc8\xe2\x42\x91\x1a\x01\x25\xcd\xe5\xf2\xdd\x77\xa4\x65\xfd\xb1\xe8\x58\x72\xf6\x92\x9c\x49\x00\xfc\x81\x00\x41\x81\x4f\x27\x00\x89\xc1\x92\x92\x73\x48\x2e\x98\x15\x0b\x1a\x49\x16\x7e\x58\xe3\x8a\x74\x64\x3c\x23\x4e\x9d\xaa\x44\x59\x33\x98\x05\xc1\x95\x26\xc8\xad\x03\x16\xeb\x94\x59\xc3\xc5\x25\x60\x3b\x9d\x5a\x93\xab\x75\xed\xd0\x6b\x32\xa0\xc9\xa0\x24\xc1\x0c\x05\x37\x86\x05\xd7\x9c\x9c\xc3\x8f\x04\xd7\xe4\x17\x83\x84\x1f\x59\xa8\x4c\x6e\xc3\xf4\xaa\x56\x5a\x94\x5f\x53\x5c\x4d\x61\xc8\x11\x66\xd6\xe8\xc7\xfe\x18\x5b\x27\xc9\x39\x9c\x9d\x9d\x9d\x35\x56\x57\xda\xbb\xf7\xd4\x39\x1a\xec\x2f\xb1\x73\x0b\x92\xd4\x96\xa5\x5f\xd4\x3b\xe4\x67\x7b\xdc\x1b\x03\xf0\x1c\xac\xa5\x56\xd7\xa5\x09\x98\x27\x00\x00\x4f\xe1\x6f\x6f\x13\x55\x16\x9c\x09\x63\xf2\x58\x85\xb1\xcb\x8f\xdd\xd8\x78\x57\xa1\x3f\xdd\xe3\xb8\x31\xea\xdf\x9a\x7a\x20\x2a\x23\x23\x2a\x57\xe4\x92\x20\xfe\xbc\x88\x23\xb4\x1a\xcb\x18\x0c\x8b\x0f\xcd\x31\x40\x17\x31\x92\xce\x0e\x99\xb5\x14\xc9\x39\xfc\xfa\xee\x5d\x3b\x68\x6a\xad\x9b\xfd\xcf\x51\x33\xb5\x13\x75\x70\xae\x17\xb7\x30\xaa\x4c\x46\x3f\x9b\xc1\x17\x5d\x0c\xce\x4c\x77\xed\xdb\x40\x3c\xea\xd2\xd0\x62\xd4\x99\x8c\x72\xac\xb5\x0c\xb6\x38\x99\xcf\x1e\xfe\x4f\x67\xff\x6b\x20\x1e\x65\x1f\x5a\x3c\x14\x88\x83\x80\x78\x8f\x82\x6e\x4e\xe6\xec\x28\x44\x21\x37\x56\xe1\xe6\xcb\xa7\xff\x11\x35\xb5\xc6\x50\x2a\x76\x0e\xed\x87\xb1\x4e\x14\xb8\x09\x37\xb4\x6b\x2c\x40\xe5\x60\xac\x00\x93\x2c\xa0\x66\x02\x29\x08\xd6\xda\xae\x50\x8f\xa5\x67\x9e\x8c\x17\xdd\xec\x97\xdc\xe9\x8e\x7e\x8c\x69\xed\x71\x35\x22\xd9\x62\xff\xb6\x3f\x38\xf3\xb3\xbf\x42\x29\x66\xf8\x70\x3d\x10\x8f\xc2\xfb\xcb\x06\xd7\x04\x43\xcb\xaf\x4e\xad\x70\x8f\x8c\x40\x95\x11\x5a\x0f\xca\xde\x96\xf4\xeb\x40\x3e\x4e\x6a\x9d\x80\x75\x59\x5f\xbf\x2b\x2a\xdb\x1b\x6b\xde\x7e\x86\x5b\x71\xa9\x22\x79\xb1\xb2\x56\x13\x9a\x08\xea\x7b\xaf\x03\x97\xf1\xac\xf8\x5e\x90\x14\xe4\x40\x0a\xc5\xa0\x18\x10\xc2\x12\x6f\x94\x81\x48\xd5\xeb\xf0\x87\xf5\x7d\x7a\x3e\x68\x4c\xa9\xb0\x7a\xb0\x29\x5b\x17\xfe\x61\x1b\xe3\xbf\x8e\xe9\x44\x77\x3c\x6a\x7d\x4e\x16\xd8\x70\x28\x78\x32\xda\xe7\x5d\xf9\x28\xd6\xc8\xea\x1c\xa4\xca\xd9\xb2\x92\xe9\x48\xd7\xbb\xf2\xf1\x9d\xda\x95\x9a\x83\xf4\x60\xdd\x5d\xae\xed\xc3\x64\xa6\xef\x23\x85\x28\xd4\xd8\xee\x1c\xaa\xbb\xd5\x64\x9e\x3f\x8d\x7d\xd0\x94\xad\x09\xde\x23\x1f\xba\x6e\xef\x5a\xe1\x15\x32\x41\x6a\xb5\xa6\xf4\x15\x01\x2d\xd3\x6a\x32\xe8\xd5\x87\x6b\xf8\x4a\xee\x9e\x5c\x3c\xa0\x7e\x9e\x37\xf3\xfe\xc6\x55\xba\xfd\x26\xf7\x77\x55\xef\xa3\xd6\xfa\x0b\xec\x38\x5e\xb1\x56\x4f\x4f\xbf\x6f\x43\xe9\xf8\xc7\xd7\x50\x66\x16\x8c\x6f\x1a\x26\xb3\x0c\x84\xe3\x28\x03\x91\x39\x24\x6d\x33\x32\xa3\x12\x7f\x19\xe9\x44\xa1\xb6\xa6\x81\x05\xa5\xe6\xe3\xca\xef\x9e\x6a\x52\xaf\xb4\x4a\xe7\x30\x5f\x07\x0d\xb8\x18\x5f\x06\xfb\x6e\x91\x5e\x0b\xc3\xc0\x05\x3a\xca\x00\x53\x67\x99\x01\xb5\x06\x21\x2c\x19\x94\x09\x29\x5a\x69\x94\xdc\xba\xf2\xb0\x8f\xb3\xbe\xa0\xc2\xaa\x63\x2f\xc9\xd4\x65\xec\x2e\x1f\x4a\xc7\x2f\xf3\x02\x43\x8f\xcb\xa9\xed\x77\x0e\x76\xdb\x18\xff\x68\x46\xc0\x57\x6c\x75\x8f\x42\xc9\x02\xde\xbe\x85\xcf\x3e\x8e\xf7\x8a\x95\x3f\x98\x62\x83\xd3\xf6\xc1\x90\xeb\xe4\xfd\x86\x24\x5e\xf6\xef\x4e\x6c\xbb\x51\x50\x52\xb9\x22\xc7\x8d\xf4\x6d\xac\x3b\x69\xd7\xdb\xb7\x55\x47\xe4\x89\xb6\x29\x6a\x9a\x7e\xd4\x3e\xed\xca\xc7\x1b\xc9\x5f\x7e\x37\x30\x32\x3d\xe7\xd4\xd5\x3c\x03\xea\x86\x0f\x11\xbd\xe1\x8a\x52\x95\xab\x14\x1e\x1c\x56\x15\xb9\xdd\x67\x0b\x5f\x4a\x7d\xf4\xac\x59\x00\xd6\x99\xb2\x0b\x20\x49\x4f\xe1\x72\xa7\x35\x68\xda\x02\x26\x11\x65\x8e\x2d\x29\x58\x8b\x2d\x51\x28\xd2\xc6\xef\x3f\x9f\x17\x63\xa5\x78\x43\xb6\x95\x7b\xa1\xaa\x1c\xf7\x8d\xef\xd7\x52\xd6\x04\x2f\x67\x80\x5f\xc5\xd4\x0e\xd7\x94\x14\x0d\xf8\x50\xa1\xf3\x35\xe4\x0f\x68\x56\x07\xad\x38\xfa\xa1\x7a\xc0\xa7\x93\xe6\x50\x25\x8e\xf4\x26\xe6\xc9\x79\xe3\x62\x92\x16\x28\xdd\xcf\x9e\x53\x05\xf2\x15\x9a\x5e\x19\x2f\x6d\xb6\x71\x6a\xb9\x7c\x44\x7b\x1a\x5e\x9c\x4e\xbd\x7a\x27\x72\x47\x8f\xfb\x5f\x6b\x72\xeb\x48\xad\xcd\x48\xa0\x65\xdc\x3c\x47\x05\x7c\x7a\xf1\x39\xea\xe7\x72\xe7\xb9\x6b\xe9\xa1\x97\xdb\xd7\xb4\xde\x46\xb7\x2f\x5b\xcd\xf3\x4a\xaf\xb9\xb8\x8d\x34\x42\x7e\xe3\x62\x61\xba\xf4\x33\xe1\x9c\xe0\xe0\x75\x25\xbc\xf8\xb5\xbd\x44\x93\x73\x2f\xe6\x51\x0c\x9e\x77\xda\xac\x0e\x7a\x33\xd3\x3f\x34\xaf\xa5\xf6\x16\x7d\x89\xf7\xe0\x8d\x55\x5f\xdb\x77\xf2\xa4\xad\xf8\x4f\x90\x88\x2a\x89\x05\xcb\x8a\xb7\x89\xe6\x1b\xc9\x5c\x96\x19\x69\x92\x10\xa8\x4d\x01\x86\xa4\x22\x57\x2a\xe6\x8d\xaa\x17\x85\xe7\x93\xe7\x93\xff\x02\x00\x00\xff\xff\x89\xbf\xed\x86\x91\x15\x00\x00") func yaoModelsAgentAssistantModYaoBytes() ([]byte, error) { return bindataRead( @@ -2519,7 +2519,7 @@ func yaoModelsAgentAssistantModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/agent/assistant.mod.yao", size: 5300, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/agent/assistant.mod.yao", size: 5521, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2539,7 +2539,7 @@ func yaoModelsAgentChatModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/agent/chat.mod.yao", size: 1741, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/agent/chat.mod.yao", size: 1741, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2559,7 +2559,7 @@ func yaoModelsAgentHistoryModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/agent/history.mod.yao", size: 2941, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/agent/history.mod.yao", size: 2941, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2579,7 +2579,7 @@ func yaoModelsAttachmentModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/attachment.mod.yao", size: 4286, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/attachment.mod.yao", size: 4286, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2599,7 +2599,7 @@ func yaoModelsAuditModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/audit.mod.yao", size: 5588, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/audit.mod.yao", size: 5588, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2619,7 +2619,7 @@ func yaoModelsConfigModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/config.mod.yao", size: 1649, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/config.mod.yao", size: 1649, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2639,7 +2639,7 @@ func yaoModelsDslModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/dsl.mod.yao", size: 3826, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/dsl.mod.yao", size: 3826, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2659,7 +2659,7 @@ func yaoModelsInvitationModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/invitation.mod.yao", size: 6693, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/invitation.mod.yao", size: 6693, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2679,7 +2679,7 @@ func yaoModelsJobCategoryModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/job/category.mod.yao", size: 2041, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/job/category.mod.yao", size: 2041, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2699,7 +2699,7 @@ func yaoModelsJobExecutionModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/job/execution.mod.yao", size: 7201, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/job/execution.mod.yao", size: 7201, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2719,7 +2719,7 @@ func yaoModelsJobJobModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/job/job.mod.yao", size: 6330, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/job/job.mod.yao", size: 6330, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2739,7 +2739,7 @@ func yaoModelsJobLogModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/job/log.mod.yao", size: 4711, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/job/log.mod.yao", size: 4711, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2759,7 +2759,7 @@ func yaoModelsKbCollectionModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/kb/collection.mod.yao", size: 5390, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/kb/collection.mod.yao", size: 5390, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2779,7 +2779,7 @@ func yaoModelsKbDocumentModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/kb/document.mod.yao", size: 9906, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/kb/document.mod.yao", size: 9906, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2799,7 +2799,7 @@ func yaoModelsMemberModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/member.mod.yao", size: 14798, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/member.mod.yao", size: 14798, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2819,7 +2819,7 @@ func yaoModelsRoleModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/role.mod.yao", size: 6434, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/role.mod.yao", size: 6434, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2839,7 +2839,7 @@ func yaoModelsTeamModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/team.mod.yao", size: 15823, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/team.mod.yao", size: 15823, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2859,7 +2859,7 @@ func yaoModelsUserOauth_accountModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/user/oauth_account.mod.yao", size: 6928, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/user/oauth_account.mod.yao", size: 6928, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2879,7 +2879,7 @@ func yaoModelsUserTypeModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/user/type.mod.yao", size: 7502, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/user/type.mod.yao", size: 7502, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2899,7 +2899,7 @@ func yaoModelsUserModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/user.mod.yao", size: 12335, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/models/user.mod.yao", size: 12335, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2919,7 +2919,7 @@ func yaoReleaseAppYaz() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/release/app.yaz", size: 181682, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/release/app.yaz", size: 181682, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2939,7 +2939,7 @@ func yaoStoresAgentCacheLruYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/agent/cache.lru.yao", size: 301, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/stores/agent/cache.lru.yao", size: 301, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2959,7 +2959,7 @@ func yaoStoresAgentMemoryBadgerYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/agent/memory.badger.yao", size: 352, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/stores/agent/memory.badger.yao", size: 352, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2979,7 +2979,7 @@ func yaoStoresCacheLruYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/cache.lru.yao", size: 285, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/stores/cache.lru.yao", size: 285, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2999,7 +2999,7 @@ func yaoStoresKbCacheLruYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/kb/cache.lru.yao", size: 304, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/stores/kb/cache.lru.yao", size: 304, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -3019,7 +3019,7 @@ func yaoStoresKbStoreBadgerYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/kb/store.badger.yao", size: 349, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/stores/kb/store.badger.yao", size: 349, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -3039,7 +3039,7 @@ func yaoStoresOauthCacheLruYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/oauth/cache.lru.yao", size: 301, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/stores/oauth/cache.lru.yao", size: 301, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -3059,7 +3059,7 @@ func yaoStoresOauthClientBadgerYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/oauth/client.badger.yao", size: 352, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/stores/oauth/client.badger.yao", size: 352, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -3079,7 +3079,7 @@ func yaoStoresOauthStoreBadgerYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/oauth/store.badger.yao", size: 376, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/stores/oauth/store.badger.yao", size: 376, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -3099,7 +3099,7 @@ func yaoStoresStoreBadgerYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/store.badger.yao", size: 341, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/stores/store.badger.yao", size: 341, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -3119,7 +3119,7 @@ func yaoUploadersAttachmentLocalYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/uploaders/attachment.local.yao", size: 1163, mode: os.FileMode(420), modTime: time.Unix(1762483730, 0)} + info := bindataFileInfo{name: "yao/uploaders/attachment.local.yao", size: 1163, mode: os.FileMode(420), modTime: time.Unix(1763118620, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/yao/models/agent/assistant.mod.yao b/yao/models/agent/assistant.mod.yao index cbf51b47..e13b92c9 100644 --- a/yao/models/agent/assistant.mod.yao +++ b/yao/models/agent/assistant.mod.yao @@ -184,6 +184,13 @@ "comment": "Assistant i18n locales", "nullable": true }, + { + "name": "uses", + "type": "json", + "label": "Uses", + "comment": "Assistant-specific wrapper configurations for vision, audio, etc. If not set, use global settings", + "nullable": true + }, { "name": "automated", "type": "boolean",