yao/agent/llm/providers/base/base.go
Max 9eef569e4b feat(agent): enhance system configuration and connector management
- Updated the system configuration to include new role-level defaults for Light, Vision, and Audio connectors.
- Refactored the resolveSystemConnector function to prioritize per-agent overrides, improving connector resolution logic.
- Enhanced LLMConnector integration across various components to streamline settings retrieval and capabilities management.
- Improved error handling and logging for connector-related operations, ensuring better diagnostics and user feedback.
2026-05-01 10:31:58 +08:00

169 lines
4.5 KiB
Go

package base
import (
"fmt"
"github.com/yaoapp/gou/connector"
"github.com/yaoapp/gou/llm"
"github.com/yaoapp/yao/agent/context"
)
// Provider base provider implementation
// Provides common functionality for all LLM providers
type Provider struct {
Connector connector.Connector
Capabilities *llm.Capabilities
}
// NewProvider create a new base provider
func NewProvider(conn connector.Connector, capabilities *llm.Capabilities) *Provider {
return &Provider{
Connector: conn,
Capabilities: capabilities,
}
}
// PreprocessMessages preprocess messages before sending to LLM
// Handles vision messages, audio messages, tool messages, etc.
// Filters out unsupported content types based on model capabilities
func (p *Provider) PreprocessMessages(messages []context.Message) ([]context.Message, error) {
processed := make([]context.Message, 0, len(messages))
for _, msg := range messages {
processedMsg := msg
// Handle multimodal content (array of ContentPart)
if contentParts, ok := msg.Content.([]context.ContentPart); ok {
filteredParts := make([]context.ContentPart, 0, len(contentParts))
for _, part := range contentParts {
// Filter vision content if not supported
if part.Type == context.ContentImageURL {
if !p.SupportsVision() {
// Skip image content if vision not supported
continue
}
}
// Filter audio content if not supported
if part.Type == context.ContentInputAudio {
if !p.SupportsAudio() {
// Skip audio content if audio not supported
continue
}
}
filteredParts = append(filteredParts, part)
}
// If all parts were filtered out, convert to text message
if len(filteredParts) == 0 {
processedMsg.Content = "[Content not supported by this model]"
} else {
processedMsg.Content = filteredParts
}
}
processed = append(processed, processedMsg)
}
return processed, nil
}
// SupportsVision check if this provider supports vision
func (p *Provider) SupportsVision() bool {
if p.Capabilities == nil {
return false
}
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
}
// SupportsTools check if this provider supports tool calls
func (p *Provider) SupportsTools() bool {
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
}
// SupportsJSON check if this provider supports JSON mode
func (p *Provider) SupportsJSON() bool {
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
}
// GetConnectorSetting gets a setting value from the connector
func (p *Provider) GetConnectorSetting(key string) (interface{}, error) {
if p.Connector == nil {
return nil, fmt.Errorf("connector is nil")
}
settings := p.Connector.Setting()
if settings == nil {
return nil, fmt.Errorf("connector settings are nil")
}
value, exists := settings[key]
if !exists {
return nil, fmt.Errorf("setting '%s' not found", key)
}
return value, nil
}
// GetConnectorStringSetting gets a string setting value from the connector
func (p *Provider) GetConnectorStringSetting(key string) (string, error) {
value, err := p.GetConnectorSetting(key)
if err != nil {
return "", err
}
strValue, ok := value.(string)
if !ok {
return "", fmt.Errorf("setting '%s' is not a string", key)
}
return strValue, nil
}
// GetModel gets the model name from connector settings
func (p *Provider) GetModel() (string, error) {
if lc, ok := p.Connector.(llm.LLMConnector); ok {
if m := lc.GetModel(); m != "" {
return m, nil
}
}
return p.GetConnectorStringSetting("model")
}
// GetAPIKey gets the API key from connector settings
func (p *Provider) GetAPIKey() (string, error) {
if lc, ok := p.Connector.(llm.LLMConnector); ok {
if k := lc.GetKey(); k != "" {
return k, nil
}
}
return p.GetConnectorStringSetting("key")
}
// GetHost gets the host URL from connector settings
func (p *Provider) GetHost() (string, error) {
if lc, ok := p.Connector.(llm.LLMConnector); ok {
if u := lc.GetURL(); u != "" {
return u, nil
}
}
return p.GetConnectorStringSetting("host")
}