Refactor model capabilities to use OpenAI struct
- Updated the model capabilities throughout the agent to utilize the new gouOpenAI.Capabilities struct instead of the previous ModelCapabilities. - Adjusted related methods and types to ensure compatibility with the new capabilities structure, enhancing clarity and maintainability. - Improved context handling and message processing by directly integrating OpenAI capabilities, streamlining the overall architecture.
This commit is contained in:
parent
7e8b2d8d9f
commit
3dd63e5530
21 changed files with 244 additions and 363 deletions
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/yaoapp/gou/connector"
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/kun/log"
|
||||
"github.com/yaoapp/yao/agent/assistant/handlers"
|
||||
"github.com/yaoapp/yao/agent/context"
|
||||
|
|
@ -303,7 +304,7 @@ func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Messa
|
|||
// 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) {
|
||||
func (ast *Assistant) GetConnector(ctx *context.Context) (connector.Connector, *openai.Capabilities, error) {
|
||||
// Determine connector ID with priority
|
||||
connectorID := ast.Connector
|
||||
if ctx.Connector != "" {
|
||||
|
|
@ -328,62 +329,27 @@ func (ast *Assistant) GetConnector(ctx *context.Context) (connector.Connector, *
|
|||
}
|
||||
|
||||
// getConnectorCapabilities get the capabilities of a connector from settings
|
||||
func (ast *Assistant) getConnectorCapabilities(connectorID string) *context.ModelCapabilities {
|
||||
// Initialize with default capabilities (all disabled)
|
||||
falseVal := false
|
||||
capabilities := &context.ModelCapabilities{
|
||||
Vision: falseVal,
|
||||
ToolCalls: &falseVal,
|
||||
Audio: &falseVal,
|
||||
Reasoning: &falseVal,
|
||||
Streaming: &falseVal,
|
||||
}
|
||||
|
||||
func (ast *Assistant) getConnectorCapabilities(connectorID string) *openai.Capabilities {
|
||||
// Get model capabilities from global configuration
|
||||
modelCaps, exists := modelCapabilities[connectorID]
|
||||
if !exists {
|
||||
// Return default capabilities if model not found in configuration
|
||||
return capabilities
|
||||
falseVal := false
|
||||
return &openai.Capabilities{
|
||||
Vision: falseVal,
|
||||
ToolCalls: false,
|
||||
Audio: false,
|
||||
Reasoning: false,
|
||||
Streaming: false,
|
||||
JSON: false,
|
||||
Multimodal: false,
|
||||
TemperatureAdjustable: true, // Default to true for non-reasoning models
|
||||
}
|
||||
}
|
||||
|
||||
// Update capabilities based on model configuration
|
||||
// Vision can be bool or string (VisionFormat)
|
||||
if modelCaps.Vision != nil {
|
||||
capabilities.Vision = modelCaps.Vision
|
||||
}
|
||||
|
||||
// Handle both Tools (deprecated) and ToolCalls
|
||||
if modelCaps.ToolCalls || modelCaps.Tools {
|
||||
v := true
|
||||
capabilities.ToolCalls = &v
|
||||
}
|
||||
|
||||
if modelCaps.Audio {
|
||||
v := true
|
||||
capabilities.Audio = &v
|
||||
}
|
||||
|
||||
if modelCaps.Reasoning {
|
||||
v := true
|
||||
capabilities.Reasoning = &v
|
||||
}
|
||||
|
||||
if modelCaps.Streaming {
|
||||
v := true
|
||||
capabilities.Streaming = &v
|
||||
}
|
||||
|
||||
if modelCaps.JSON {
|
||||
v := true
|
||||
capabilities.JSON = &v
|
||||
}
|
||||
|
||||
if modelCaps.Multimodal {
|
||||
v := true
|
||||
capabilities.Multimodal = &v
|
||||
}
|
||||
|
||||
return capabilities
|
||||
// Return capabilities directly
|
||||
// Note: TemperatureAdjustable is automatically set in connector.Setting() based on Reasoning flag
|
||||
return &modelCaps
|
||||
}
|
||||
|
||||
// Info get the assistant information
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/yaoapp/gou/application"
|
||||
gouOpenAI "github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/gou/fs"
|
||||
v8 "github.com/yaoapp/gou/runtime/v8"
|
||||
"github.com/yaoapp/yao/agent/assistant/hook"
|
||||
|
|
@ -26,7 +27,7 @@ import (
|
|||
var loaded = NewCache(200) // 200 is the default capacity
|
||||
var storage store.Store = nil
|
||||
var search interface{} = nil
|
||||
var modelCapabilities map[string]ModelCapabilities = map[string]ModelCapabilities{}
|
||||
var modelCapabilities map[string]gouOpenAI.Capabilities = map[string]gouOpenAI.Capabilities{}
|
||||
var defaultConnector string = "" // default connector
|
||||
var globalUses *context.Uses = nil // global uses configuration from agent.yml
|
||||
|
||||
|
|
@ -131,7 +132,7 @@ func SetStorage(s store.Store) {
|
|||
}
|
||||
|
||||
// SetModelCapabilities set the model capabilities configuration
|
||||
func SetModelCapabilities(capabilities map[string]ModelCapabilities) {
|
||||
func SetModelCapabilities(capabilities map[string]gouOpenAI.Capabilities) {
|
||||
modelCapabilities = capabilities
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package assistant
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/kun/log"
|
||||
"github.com/yaoapp/yao/agent/context"
|
||||
"github.com/yaoapp/yao/agent/i18n"
|
||||
|
|
@ -48,7 +49,7 @@ func (ast *Assistant) traceCreateHook(agentNode types.Node, createResponse *cont
|
|||
}
|
||||
|
||||
// traceConnectorCapabilities logs the connector capabilities to the agent trace node
|
||||
func (ast *Assistant) traceConnectorCapabilities(agentNode types.Node, capabilities *context.ModelCapabilities) {
|
||||
func (ast *Assistant) traceConnectorCapabilities(agentNode types.Node, capabilities *openai.Capabilities) {
|
||||
if agentNode == nil {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,19 +40,6 @@ type Assistant struct {
|
|||
// toolCalls bool // Whether this assistant supports tool_calls
|
||||
}
|
||||
|
||||
// ModelCapabilities defines the capabilities of a language model
|
||||
// This configuration is loaded from agent/models.yml
|
||||
type ModelCapabilities struct {
|
||||
Vision interface{} `json:"vision,omitempty" yaml:"vision,omitempty"` // Supports vision/image input: bool or VisionFormat string ("openai", "claude"/"base64", "default")
|
||||
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
|
||||
var VisionCapableModels = map[string]bool{
|
||||
// OpenAI Models
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package context
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/yao/agent/output"
|
||||
"github.com/yaoapp/yao/agent/output/message"
|
||||
)
|
||||
|
|
@ -290,18 +291,10 @@ func (ctx *Context) getOutput() (*output.Output, error) {
|
|||
Accept: string(ctx.Accept),
|
||||
}
|
||||
|
||||
// Convert ModelCapabilities to message.ModelCapabilities
|
||||
// Set ModelCapabilities (now using openai.Capabilities directly)
|
||||
if ctx.Capabilities != nil {
|
||||
options.Capabilities = &message.ModelCapabilities{
|
||||
Vision: ctx.Capabilities.Vision,
|
||||
ToolCalls: ctx.Capabilities.ToolCalls,
|
||||
Audio: ctx.Capabilities.Audio,
|
||||
Reasoning: ctx.Capabilities.Reasoning,
|
||||
Streaming: ctx.Capabilities.Streaming,
|
||||
JSON: ctx.Capabilities.JSON,
|
||||
Multimodal: ctx.Capabilities.Multimodal,
|
||||
TemperatureAdjustable: ctx.Capabilities.TemperatureAdjustable,
|
||||
}
|
||||
caps := openai.Capabilities(*ctx.Capabilities)
|
||||
options.Capabilities = &caps
|
||||
}
|
||||
|
||||
var err error
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/gou/plan"
|
||||
"github.com/yaoapp/gou/store"
|
||||
"github.com/yaoapp/yao/agent/output"
|
||||
|
|
@ -238,7 +239,7 @@ type Context struct {
|
|||
Skip *Skip `json:"skip,omitempty"` // Skip configuration (history, trace, etc.), nil means don't skip anything
|
||||
|
||||
// Model capabilities (set by assistant, used by output adapters)
|
||||
Capabilities *ModelCapabilities `json:"-"` // Model capabilities for the current connector
|
||||
Capabilities *openai.Capabilities `json:"-"` // Model capabilities for the current connector
|
||||
|
||||
// Interrupt control (all interrupt-related logic is encapsulated in InterruptController)
|
||||
Interrupt *InterruptController `json:"-"` // Interrupt controller for handling user interrupts during streaming
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package context
|
||||
|
||||
import "github.com/yaoapp/yao/agent/output/message"
|
||||
import (
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/yao/agent/output/message"
|
||||
)
|
||||
|
||||
// Uses represents the wrapper configurations for assistant
|
||||
// Used to specify which assistant or MCP server to use for vision, audio, search, and fetch operations
|
||||
|
|
@ -28,17 +31,13 @@ const (
|
|||
VisionFormatDefault VisionFormat = "default"
|
||||
)
|
||||
|
||||
// ModelCapabilities defines the capabilities of a language model
|
||||
// Used by LLM to select appropriate provider and validate requests
|
||||
type ModelCapabilities message.ModelCapabilities
|
||||
|
||||
// GetVisionSupport returns whether vision is supported and the format
|
||||
func (m *ModelCapabilities) GetVisionSupport() (bool, VisionFormat) {
|
||||
if m == nil || m.Vision == nil {
|
||||
func GetVisionSupport(cap *openai.Capabilities) (bool, VisionFormat) {
|
||||
if cap == nil || cap.Vision == nil {
|
||||
return false, VisionFormatNone
|
||||
}
|
||||
|
||||
switch v := m.Vision.(type) {
|
||||
switch v := cap.Vision.(type) {
|
||||
case bool:
|
||||
// Legacy bool format
|
||||
return v, VisionFormatDefault
|
||||
|
|
@ -65,7 +64,7 @@ func (m *ModelCapabilities) GetVisionSupport() (bool, VisionFormat) {
|
|||
type CompletionOptions struct {
|
||||
// Model capabilities (used by LLM to select appropriate provider)
|
||||
// nil means capabilities are not specified/checked
|
||||
Capabilities *ModelCapabilities `json:"capabilities,omitempty"`
|
||||
Capabilities *openai.Capabilities `json:"capabilities,omitempty"`
|
||||
|
||||
// User-specified tools for vision, audio, search, and fetch processing
|
||||
Uses *Uses `json:"uses,omitempty"`
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package adapters
|
||||
|
||||
import (
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/yao/agent/context"
|
||||
)
|
||||
|
||||
|
|
@ -28,7 +29,7 @@ type ReasoningAdapter struct {
|
|||
|
||||
// NewReasoningAdapter creates a new reasoning adapter
|
||||
// If cap.TemperatureAdjustable is provided, it overrides the default behavior
|
||||
func NewReasoningAdapter(format ReasoningFormat, cap *context.ModelCapabilities) *ReasoningAdapter {
|
||||
func NewReasoningAdapter(format ReasoningFormat, cap *openai.Capabilities) *ReasoningAdapter {
|
||||
supportsEffort := false
|
||||
supportsTemperature := true
|
||||
|
||||
|
|
@ -49,8 +50,8 @@ func NewReasoningAdapter(format ReasoningFormat, cap *context.ModelCapabilities)
|
|||
}
|
||||
|
||||
// Override with explicit capability if provided
|
||||
if cap != nil && cap.TemperatureAdjustable != nil {
|
||||
supportsTemperature = *cap.TemperatureAdjustable
|
||||
if cap != nil {
|
||||
supportsTemperature = cap.TemperatureAdjustable
|
||||
}
|
||||
|
||||
return &ReasoningAdapter{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/yaoapp/gou/connector"
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/yao/agent/context"
|
||||
)
|
||||
|
||||
|
|
@ -11,11 +12,11 @@ import (
|
|||
// Provides common functionality for all LLM providers
|
||||
type Provider struct {
|
||||
Connector connector.Connector
|
||||
Capabilities *context.ModelCapabilities
|
||||
Capabilities *openai.Capabilities
|
||||
}
|
||||
|
||||
// NewProvider create a new base provider
|
||||
func NewProvider(conn connector.Connector, capabilities *context.ModelCapabilities) *Provider {
|
||||
func NewProvider(conn connector.Connector, capabilities *openai.Capabilities) *Provider {
|
||||
return &Provider{
|
||||
Connector: conn,
|
||||
Capabilities: capabilities,
|
||||
|
|
@ -74,33 +75,33 @@ func (p *Provider) SupportsVision() bool {
|
|||
if p.Capabilities == nil {
|
||||
return false
|
||||
}
|
||||
supported, _ := p.Capabilities.GetVisionSupport()
|
||||
supported, _ := context.GetVisionSupport(p.Capabilities)
|
||||
return supported
|
||||
}
|
||||
|
||||
// SupportsAudio check if this provider supports audio
|
||||
func (p *Provider) SupportsAudio() bool {
|
||||
return p.Capabilities != nil && p.Capabilities.Audio != nil && *p.Capabilities.Audio
|
||||
return p.Capabilities != 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
|
||||
return p.Capabilities != nil && p.Capabilities.ToolCalls
|
||||
}
|
||||
|
||||
// SupportsStreaming check if this provider supports streaming
|
||||
func (p *Provider) SupportsStreaming() bool {
|
||||
return p.Capabilities != nil && p.Capabilities.Streaming != nil && *p.Capabilities.Streaming
|
||||
return p.Capabilities != nil && p.Capabilities.Streaming
|
||||
}
|
||||
|
||||
// SupportsJSON check if this provider supports JSON mode
|
||||
func (p *Provider) SupportsJSON() bool {
|
||||
return p.Capabilities != nil && p.Capabilities.JSON != nil && *p.Capabilities.JSON
|
||||
return p.Capabilities != nil && p.Capabilities.JSON
|
||||
}
|
||||
|
||||
// SupportsReasoning check if this provider supports reasoning mode
|
||||
func (p *Provider) SupportsReasoning() bool {
|
||||
return p.Capabilities != nil && p.Capabilities.Reasoning != nil && *p.Capabilities.Reasoning
|
||||
return p.Capabilities != nil && p.Capabilities.Reasoning
|
||||
}
|
||||
|
||||
// GetConnectorSetting gets a setting value from the connector
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/yaoapp/gou/connector"
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/gou/plan"
|
||||
"github.com/yaoapp/yao/agent/context"
|
||||
"github.com/yaoapp/yao/agent/llm"
|
||||
|
|
@ -60,15 +61,13 @@ func TestClaudeSonnet4StreamBasic(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
Reasoning: &falseVal, // Claude Sonnet 4 (non-thinking) doesn't expose reasoning
|
||||
ToolCalls: &trueVal,
|
||||
Vision: &trueVal,
|
||||
Multimodal: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
Reasoning: false, // Claude Sonnet 4 (non-thinking) doesn't expose reasoning
|
||||
ToolCalls: true,
|
||||
Vision: "claude", // Claude requires base64 format
|
||||
Multimodal: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -140,15 +139,13 @@ func TestClaudeSonnet4PostBasic(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &falseVal,
|
||||
Reasoning: &falseVal,
|
||||
ToolCalls: &trueVal,
|
||||
Vision: &trueVal,
|
||||
Multimodal: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: false,
|
||||
Reasoning: false,
|
||||
ToolCalls: true,
|
||||
Vision: "claude", // Claude requires base64 format
|
||||
Multimodal: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -206,15 +203,13 @@ func TestClaudeSonnet4WithToolCalls(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &falseVal,
|
||||
Reasoning: &falseVal,
|
||||
ToolCalls: &trueVal,
|
||||
Vision: &trueVal,
|
||||
Multimodal: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: false,
|
||||
Reasoning: false,
|
||||
ToolCalls: true,
|
||||
Vision: "claude", // Claude requires base64 format
|
||||
Multimodal: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -244,8 +239,8 @@ func TestClaudeSonnet4WithToolCalls(t *testing.T) {
|
|||
options.Tools = []map[string]interface{}{simpleTool}
|
||||
options.ToolChoice = "auto"
|
||||
|
||||
// Set lower max_tokens for faster response
|
||||
maxTokens := 50
|
||||
// Set enough tokens for tool call response
|
||||
maxTokens := 150
|
||||
options.MaxTokens = &maxTokens
|
||||
|
||||
llmInstance, err := llm.New(conn, options)
|
||||
|
|
@ -256,7 +251,7 @@ func TestClaudeSonnet4WithToolCalls(t *testing.T) {
|
|||
messages := []context.Message{
|
||||
{
|
||||
Role: context.RoleUser,
|
||||
Content: "Call get_info with query='A' and count=1",
|
||||
Content: "Please use the get_info function to retrieve information. Pass 'A' as the query parameter and 1 as the count parameter.",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -299,15 +294,13 @@ func TestClaudeSonnet4Vision(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &falseVal,
|
||||
Reasoning: &falseVal,
|
||||
ToolCalls: &trueVal,
|
||||
Vision: &trueVal,
|
||||
Multimodal: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: false,
|
||||
Reasoning: false,
|
||||
ToolCalls: true,
|
||||
Vision: "claude", // Claude requires base64 format
|
||||
Multimodal: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -376,15 +369,13 @@ func TestClaudeSonnet4ThinkingStream(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
Reasoning: &trueVal, // Claude Thinking mode exposes reasoning
|
||||
ToolCalls: &falseVal,
|
||||
Vision: &trueVal,
|
||||
Multimodal: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
Reasoning: true, // Claude Thinking mode exposes reasoning
|
||||
ToolCalls: false,
|
||||
Vision: "claude", // Claude requires base64 format
|
||||
Multimodal: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -456,15 +447,13 @@ func TestClaudeSonnet4ThinkingPost(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &falseVal,
|
||||
Reasoning: &trueVal,
|
||||
ToolCalls: &falseVal,
|
||||
Vision: &trueVal,
|
||||
Multimodal: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: false,
|
||||
Reasoning: true,
|
||||
ToolCalls: false,
|
||||
Vision: "claude", // Claude requires base64 format
|
||||
Multimodal: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -550,14 +539,12 @@ func TestClaudeTemperatureHandling(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &falseVal,
|
||||
Reasoning: &tt.reasoning,
|
||||
ToolCalls: &trueVal,
|
||||
Vision: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: false,
|
||||
Reasoning: tt.reasoning,
|
||||
ToolCalls: true,
|
||||
Vision: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/yaoapp/gou/connector"
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/gou/plan"
|
||||
"github.com/yaoapp/yao/agent/context"
|
||||
"github.com/yaoapp/yao/agent/llm"
|
||||
|
|
@ -28,16 +29,14 @@ func TestDeepSeekR1StreamBasic(t *testing.T) {
|
|||
}
|
||||
|
||||
// Create LLM instance with capabilities
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
Reasoning: &trueVal, // DeepSeek R1 supports reasoning
|
||||
ToolCalls: &falseVal, // R1 doesn't support native tool calls
|
||||
Vision: &falseVal,
|
||||
Audio: &falseVal,
|
||||
Multimodal: &falseVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
Reasoning: true, // DeepSeek R1 supports reasoning
|
||||
ToolCalls: false, // R1 doesn't support native tool calls
|
||||
Vision: false,
|
||||
Audio: false,
|
||||
Multimodal: false,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -207,15 +206,13 @@ func TestDeepSeekR1PostBasic(t *testing.T) {
|
|||
}
|
||||
|
||||
// Create LLM instance
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Reasoning: &trueVal,
|
||||
ToolCalls: &falseVal,
|
||||
Vision: &falseVal,
|
||||
Audio: &falseVal,
|
||||
Multimodal: &falseVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Reasoning: true,
|
||||
ToolCalls: false,
|
||||
Vision: false,
|
||||
Audio: false,
|
||||
Multimodal: false,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -295,16 +292,14 @@ func TestDeepSeekR1LogicPuzzle(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
Reasoning: &trueVal,
|
||||
ToolCalls: &falseVal,
|
||||
Vision: &falseVal,
|
||||
Audio: &falseVal,
|
||||
Multimodal: &falseVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
Reasoning: true,
|
||||
ToolCalls: false,
|
||||
Vision: false,
|
||||
Audio: false,
|
||||
Multimodal: false,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/yaoapp/gou/connector"
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/gou/plan"
|
||||
"github.com/yaoapp/yao/agent/context"
|
||||
"github.com/yaoapp/yao/agent/llm"
|
||||
|
|
@ -24,16 +25,14 @@ func TestDeepSeekV3StreamBasic(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
Reasoning: &falseVal, // V3 doesn't support reasoning
|
||||
ToolCalls: &trueVal, // V3 supports tool calls
|
||||
Vision: &falseVal,
|
||||
Audio: &falseVal,
|
||||
Multimodal: &falseVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
Reasoning: false, // V3 doesn't support reasoning
|
||||
ToolCalls: true, // V3 supports tool calls
|
||||
Vision: false,
|
||||
Audio: false,
|
||||
Multimodal: false,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -135,15 +134,13 @@ func TestDeepSeekV3PostBasic(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Reasoning: &falseVal,
|
||||
ToolCalls: &trueVal,
|
||||
Vision: &falseVal,
|
||||
Audio: &falseVal,
|
||||
Multimodal: &falseVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Reasoning: false,
|
||||
ToolCalls: true,
|
||||
Vision: false,
|
||||
Audio: false,
|
||||
Multimodal: false,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -226,12 +223,10 @@ func TestDeepSeekV3WithToolCalls(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Reasoning: &falseVal,
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Reasoning: false,
|
||||
ToolCalls: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -318,13 +313,11 @@ func TestDeepSeekV3NoReasoningEffort(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
effort := "high"
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Reasoning: &falseVal, // V3 doesn't support reasoning
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Reasoning: false, // V3 doesn't support reasoning
|
||||
ToolCalls: true,
|
||||
},
|
||||
ReasoningEffort: &effort, // Should be ignored by adapter
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/yaoapp/gou/connector"
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/gou/plan"
|
||||
"github.com/yaoapp/yao/agent/context"
|
||||
"github.com/yaoapp/yao/agent/llm"
|
||||
|
|
@ -24,14 +25,13 @@ func TestGPT5StreamBasic(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
Reasoning: &trueVal, // GPT-5 supports reasoning
|
||||
ToolCalls: &trueVal,
|
||||
Vision: &trueVal,
|
||||
Multimodal: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
Reasoning: true, // GPT-5 supports reasoning
|
||||
ToolCalls: true,
|
||||
Vision: true,
|
||||
Multimodal: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -105,11 +105,10 @@ func TestGPT5ReasoningEffort(t *testing.T) {
|
|||
|
||||
for _, effort := range effortLevels {
|
||||
t.Run("effort_"+effort, func(t *testing.T) {
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Reasoning: &trueVal,
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Reasoning: true,
|
||||
ToolCalls: true,
|
||||
},
|
||||
ReasoningEffort: &effort,
|
||||
}
|
||||
|
|
@ -172,11 +171,10 @@ func TestGPT5PostWithToolCalls(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Reasoning: &trueVal,
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Reasoning: true,
|
||||
ToolCalls: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -259,12 +257,11 @@ func TestGPT5Vision(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Reasoning: &trueVal,
|
||||
Vision: &trueVal,
|
||||
Multimodal: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Reasoning: true,
|
||||
Vision: true,
|
||||
Multimodal: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -331,13 +328,11 @@ func TestGPT5ReasoningEffortWithGPT4o(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
effort := "high"
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Reasoning: &falseVal, // GPT-4o doesn't support reasoning
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Reasoning: false, // GPT-4o doesn't support reasoning
|
||||
ToolCalls: true,
|
||||
},
|
||||
ReasoningEffort: &effort, // Should be ignored by adapter
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/yaoapp/gou/connector"
|
||||
gouOpenAI "github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/gou/http"
|
||||
"github.com/yaoapp/kun/log"
|
||||
"github.com/yaoapp/yao/agent/context"
|
||||
|
|
@ -142,7 +143,7 @@ func buildAPIURL(host, endpoint string) string {
|
|||
}
|
||||
|
||||
// New create a new OpenAI provider with capability adapters
|
||||
func New(conn connector.Connector, capabilities *context.ModelCapabilities) *Provider {
|
||||
func New(conn connector.Connector, capabilities *gouOpenAI.Capabilities) *Provider {
|
||||
return &Provider{
|
||||
Provider: base.NewProvider(conn, capabilities),
|
||||
adapters: buildAdapters(capabilities),
|
||||
|
|
@ -150,7 +151,7 @@ func New(conn connector.Connector, capabilities *context.ModelCapabilities) *Pro
|
|||
}
|
||||
|
||||
// buildAdapters builds capability adapters based on model capabilities
|
||||
func buildAdapters(cap *context.ModelCapabilities) []adapters.CapabilityAdapter {
|
||||
func buildAdapters(cap *gouOpenAI.Capabilities) []adapters.CapabilityAdapter {
|
||||
if cap == nil {
|
||||
return []adapters.CapabilityAdapter{}
|
||||
}
|
||||
|
|
@ -158,12 +159,10 @@ func buildAdapters(cap *context.ModelCapabilities) []adapters.CapabilityAdapter
|
|||
result := make([]adapters.CapabilityAdapter, 0)
|
||||
|
||||
// Tool call adapter
|
||||
if cap.ToolCalls != nil {
|
||||
result = append(result, adapters.NewToolCallAdapter(*cap.ToolCalls))
|
||||
}
|
||||
result = append(result, adapters.NewToolCallAdapter(cap.ToolCalls))
|
||||
|
||||
// Vision adapter
|
||||
visionSupport, visionFormat := cap.GetVisionSupport()
|
||||
visionSupport, visionFormat := context.GetVisionSupport(cap)
|
||||
if visionSupport {
|
||||
result = append(result, adapters.NewVisionAdapter(true, visionFormat))
|
||||
} else if cap.Vision != nil {
|
||||
|
|
@ -172,31 +171,27 @@ func buildAdapters(cap *context.ModelCapabilities) []adapters.CapabilityAdapter
|
|||
}
|
||||
|
||||
// Audio adapter
|
||||
if cap.Audio != nil {
|
||||
result = append(result, adapters.NewAudioAdapter(*cap.Audio))
|
||||
}
|
||||
result = append(result, adapters.NewAudioAdapter(cap.Audio))
|
||||
|
||||
// Reasoning adapter (always add to handle reasoning_effort and temperature parameters)
|
||||
// Even if the model doesn't support reasoning, we need the adapter to strip reasoning_effort
|
||||
if cap.Reasoning != nil {
|
||||
if *cap.Reasoning {
|
||||
// Detect reasoning format based on capabilities
|
||||
format := detectReasoningFormat(cap)
|
||||
result = append(result, adapters.NewReasoningAdapter(format, cap))
|
||||
} else {
|
||||
// Model doesn't support reasoning, use None format to strip reasoning parameters
|
||||
result = append(result, adapters.NewReasoningAdapter(adapters.ReasoningFormatNone, cap))
|
||||
}
|
||||
if cap.Reasoning {
|
||||
// Detect reasoning format based on capabilities
|
||||
format := detectReasoningFormat(cap)
|
||||
result = append(result, adapters.NewReasoningAdapter(format, cap))
|
||||
} else {
|
||||
// Model doesn't support reasoning, use None format to strip reasoning parameters
|
||||
result = append(result, adapters.NewReasoningAdapter(adapters.ReasoningFormatNone, cap))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// detectReasoningFormat detects the reasoning format based on capabilities
|
||||
func detectReasoningFormat(cap *context.ModelCapabilities) adapters.ReasoningFormat {
|
||||
func detectReasoningFormat(cap *gouOpenAI.Capabilities) adapters.ReasoningFormat {
|
||||
// TODO: Implement better detection logic
|
||||
// For now, default to OpenAI o1 format if reasoning is supported
|
||||
if cap.Reasoning != nil && *cap.Reasoning {
|
||||
if cap.Reasoning {
|
||||
return adapters.ReasoningFormatOpenAI
|
||||
}
|
||||
return adapters.ReasoningFormatNone
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/yaoapp/gou/connector"
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/gou/plan"
|
||||
"github.com/yaoapp/yao/agent/context"
|
||||
"github.com/yaoapp/yao/agent/llm"
|
||||
|
|
@ -29,11 +30,10 @@ func TestOpenAIStreamBasic(t *testing.T) {
|
|||
}
|
||||
|
||||
// Create LLM instance with capabilities
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
ToolCalls: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -117,10 +117,9 @@ func TestOpenAIPostBasic(t *testing.T) {
|
|||
}
|
||||
|
||||
// Create LLM instance
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
ToolCalls: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -192,11 +191,10 @@ func TestOpenAIStreamWithToolCalls(t *testing.T) {
|
|||
}
|
||||
|
||||
// Create LLM instance with tool call capabilities
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
ToolCalls: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -307,10 +305,9 @@ func TestOpenAIPostWithToolCalls(t *testing.T) {
|
|||
}
|
||||
|
||||
// Create LLM instance with tool call capabilities
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
ToolCalls: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -424,11 +421,10 @@ func TestOpenAIStreamWithInvalidToolCall(t *testing.T) {
|
|||
}
|
||||
|
||||
// Create LLM instance
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
ToolCalls: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -523,11 +519,10 @@ func TestOpenAIStreamRetry(t *testing.T) {
|
|||
}
|
||||
|
||||
// Create LLM instance
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
ToolCalls: &trueVal, // Need this to select OpenAI provider
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
ToolCalls: true, // Need this to select OpenAI provider
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -582,11 +577,10 @@ func TestOpenAIStreamChunkTypes(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
ToolCalls: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -649,11 +643,10 @@ func TestOpenAIStreamErrorCallback(t *testing.T) {
|
|||
t.Fatalf("Failed to create test connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
ToolCalls: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -711,11 +704,10 @@ func TestOpenAIToolCallValidationRetry(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
ToolCalls: true,
|
||||
},
|
||||
Tools: []map[string]interface{}{
|
||||
{
|
||||
|
|
@ -815,11 +807,10 @@ func TestOpenAIJSONMode(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
ToolCalls: true,
|
||||
},
|
||||
ResponseFormat: &context.ResponseFormat{
|
||||
Type: context.ResponseFormatJSON,
|
||||
|
|
@ -902,10 +893,9 @@ func TestOpenAIJSONModePost(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
ToolCalls: true,
|
||||
},
|
||||
ResponseFormat: &context.ResponseFormat{
|
||||
Type: context.ResponseFormatJSON,
|
||||
|
|
@ -973,8 +963,6 @@ func TestOpenAIJSONSchema(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
|
||||
// Define a strict JSON schema
|
||||
// Note: For OpenAI strict mode, 'required' must include ALL properties
|
||||
schema := map[string]interface{}{
|
||||
|
|
@ -1009,9 +997,9 @@ func TestOpenAIJSONSchema(t *testing.T) {
|
|||
}
|
||||
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
ToolCalls: true,
|
||||
},
|
||||
ResponseFormat: &context.ResponseFormat{
|
||||
Type: context.ResponseFormatJSONSchema,
|
||||
|
|
@ -1019,7 +1007,7 @@ func TestOpenAIJSONSchema(t *testing.T) {
|
|||
Name: "user_info",
|
||||
Description: "User information schema",
|
||||
Schema: schema,
|
||||
Strict: &trueVal,
|
||||
Strict: func() *bool { v := true; return &v }(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -1121,8 +1109,6 @@ func TestOpenAIJSONSchemaPost(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
|
||||
// Simple schema for testing
|
||||
// Note: For OpenAI strict mode, 'required' must include ALL properties
|
||||
schema := map[string]interface{}{
|
||||
|
|
@ -1144,8 +1130,8 @@ func TestOpenAIJSONSchemaPost(t *testing.T) {
|
|||
}
|
||||
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
ToolCalls: true,
|
||||
},
|
||||
ResponseFormat: &context.ResponseFormat{
|
||||
Type: context.ResponseFormatJSONSchema,
|
||||
|
|
@ -1153,7 +1139,7 @@ func TestOpenAIJSONSchemaPost(t *testing.T) {
|
|||
Name: "api_response",
|
||||
Description: "API response format",
|
||||
Schema: schema,
|
||||
Strict: &trueVal,
|
||||
Strict: func() *bool { v := true; return &v }(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -1263,11 +1249,10 @@ func TestOpenAIStreamLifecycleEvents(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
ToolCalls: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -1369,11 +1354,10 @@ func TestOpenAIStreamContextCancellation(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
ToolCalls: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -1443,13 +1427,12 @@ func TestOpenAIStreamWithTemperature(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
temperature := 0.7 // Moderate temperature
|
||||
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Streaming: &trueVal,
|
||||
ToolCalls: &trueVal, // Need this to select OpenAI provider
|
||||
Capabilities: &openai.Capabilities{
|
||||
Streaming: true,
|
||||
ToolCalls: true, // Need this to select OpenAI provider
|
||||
},
|
||||
Temperature: &temperature,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/yaoapp/gou/connector"
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/gou/plan"
|
||||
"github.com/yaoapp/yao/agent/context"
|
||||
"github.com/yaoapp/yao/agent/llm"
|
||||
|
|
@ -23,11 +24,10 @@ func TestTemperatureGPT5AutoReset(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
invalidTemp := 0.7 // GPT-5 doesn't support this
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Reasoning: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Reasoning: true,
|
||||
},
|
||||
Temperature: &invalidTemp, // Should be reset to 1.0
|
||||
}
|
||||
|
|
@ -73,11 +73,10 @@ func TestTemperatureDeepSeekR1AutoReset(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
invalidTemp := 0.5 // DeepSeek R1 doesn't support this
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Reasoning: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Reasoning: true,
|
||||
},
|
||||
Temperature: &invalidTemp, // Should be reset to 1.0
|
||||
}
|
||||
|
|
@ -126,13 +125,11 @@ func TestTemperatureGPT4oPreserved(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
customTemp := 0.3 // GPT-4o should preserve this
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Reasoning: &falseVal, // Not a reasoning model
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Reasoning: false, // Not a reasoning model
|
||||
ToolCalls: true,
|
||||
},
|
||||
Temperature: &customTemp, // Should be preserved
|
||||
}
|
||||
|
|
@ -178,13 +175,11 @@ func TestTemperatureDeepSeekV3Preserved(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
customTemp := 0.8 // DeepSeek V3 should preserve this
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Reasoning: &falseVal, // Not a reasoning model
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Reasoning: false, // Not a reasoning model
|
||||
ToolCalls: true,
|
||||
},
|
||||
Temperature: &customTemp, // Should be preserved
|
||||
}
|
||||
|
|
@ -230,11 +225,10 @@ func TestTemperatureGPT5Default(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
defaultTemp := 1.0 // GPT-5's valid temperature
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Reasoning: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Reasoning: true,
|
||||
},
|
||||
Temperature: &defaultTemp, // Should work fine
|
||||
}
|
||||
|
|
@ -293,16 +287,14 @@ func TestTemperatureNoTemperatureProvided(t *testing.T) {
|
|||
t.Fatalf("Failed to select connector: %v", err)
|
||||
}
|
||||
|
||||
trueVal := true
|
||||
falseVal := false
|
||||
options := &context.CompletionOptions{
|
||||
Capabilities: &context.ModelCapabilities{
|
||||
Reasoning: &falseVal,
|
||||
ToolCalls: &trueVal,
|
||||
Capabilities: &openai.Capabilities{
|
||||
Reasoning: false,
|
||||
ToolCalls: true,
|
||||
},
|
||||
}
|
||||
if tc.reasoning {
|
||||
options.Capabilities.Reasoning = &trueVal
|
||||
options.Capabilities.Reasoning = true
|
||||
}
|
||||
// Temperature not set - should use API default
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/yaoapp/gou/application"
|
||||
"github.com/yaoapp/gou/connector"
|
||||
gouOpenAI "github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/yao/agent/assistant"
|
||||
"github.com/yaoapp/yao/agent/context"
|
||||
"github.com/yaoapp/yao/agent/i18n"
|
||||
|
|
@ -116,7 +117,7 @@ func initModelCapabilities() error {
|
|||
return err
|
||||
}
|
||||
|
||||
var models map[string]assistant.ModelCapabilities = map[string]assistant.ModelCapabilities{}
|
||||
var models map[string]gouOpenAI.Capabilities = map[string]gouOpenAI.Capabilities{}
|
||||
err = application.Parse("models.yml", bytes, &models)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ type AdapterConfig struct {
|
|||
Locale string
|
||||
}
|
||||
|
||||
// ModelCapabilities is a simplified version of context.ModelCapabilities
|
||||
// ModelCapabilities is a simplified version of openai.Capabilities
|
||||
// We use a local type to avoid circular dependencies
|
||||
type ModelCapabilities struct {
|
||||
Reasoning *bool // Supports reasoning/thinking mode (o1, DeepSeek R1)
|
||||
|
|
|
|||
|
|
@ -22,9 +22,10 @@ type Writer struct {
|
|||
func NewWriter(options message.Options) (*Writer, error) {
|
||||
// Get model capabilities from context (set by assistant)
|
||||
var capabilities *ModelCapabilities
|
||||
if options.Capabilities != nil && options.Capabilities.Reasoning != nil {
|
||||
if options.Capabilities != nil && options.Capabilities.Reasoning {
|
||||
v := true
|
||||
capabilities = &ModelCapabilities{
|
||||
Reasoning: options.Capabilities.Reasoning,
|
||||
Reasoning: &v,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package message
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
traceTypes "github.com/yaoapp/yao/trace/types"
|
||||
)
|
||||
|
||||
|
|
@ -12,23 +13,10 @@ type Options struct {
|
|||
Accept string
|
||||
Writer http.ResponseWriter
|
||||
Trace traceTypes.Manager
|
||||
Capabilities *ModelCapabilities
|
||||
Capabilities *openai.Capabilities
|
||||
Locale string
|
||||
}
|
||||
|
||||
// ModelCapabilities defines the capabilities of a language model
|
||||
// Used by LLM to select appropriate provider and validate requests
|
||||
type ModelCapabilities struct {
|
||||
Vision interface{} `json:"vision,omitempty"` // Supports vision/image input: bool or VisionFormat string ("openai", "claude"/"base64", "default")
|
||||
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)
|
||||
TemperatureAdjustable *bool `json:"temperature_adjustable,omitempty"` // Supports temperature adjustment (reasoning models typically don't)
|
||||
}
|
||||
|
||||
// Message represents a universal message structure (DSL)
|
||||
// All messages are expressed through Type + Props, without predefining specific types
|
||||
type Message struct {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"github.com/yaoapp/gou/connector/openai"
|
||||
"github.com/yaoapp/yao/agent/assistant"
|
||||
store "github.com/yaoapp/yao/agent/store/types"
|
||||
)
|
||||
|
|
@ -16,7 +17,7 @@ type DSL struct {
|
|||
|
||||
// Global External Settings - model capabilities, tools, etc.
|
||||
// ===============================
|
||||
Models map[string]assistant.ModelCapabilities `json:"models,omitempty" yaml:"models,omitempty"` // The model capabilities configuration
|
||||
Models map[string]openai.Capabilities `json:"models,omitempty" yaml:"models,omitempty"` // The model capabilities configuration
|
||||
|
||||
// Internal
|
||||
// ===============================
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue