Refactor LLM capabilities handling and remove deprecated model loading

- Remove the `LoadModelCapabilities` test and associated model capabilities initialization from the agent, streamlining the loading process.
- Update the LLM provider implementations to utilize a unified `Capabilities` structure, replacing references to `openai.Capabilities` with `llm.Capabilities`.
- Enhance capability retrieval methods to simplify the extraction of connector capabilities, ensuring compatibility across different LLM providers.
- Clean up unused functions and variables related to model capabilities, improving code maintainability.
This commit is contained in:
Max 2026-02-24 09:32:19 +08:00
parent 1e4e1224e2
commit a38d115465
18 changed files with 49 additions and 235 deletions

View file

@ -6,7 +6,7 @@ import (
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou/connector"
"github.com/yaoapp/gou/connector/openai"
goullm "github.com/yaoapp/gou/llm"
"github.com/yaoapp/yao/agent/assistant/handlers"
"github.com/yaoapp/yao/agent/context"
"github.com/yaoapp/yao/agent/i18n"
@ -570,7 +570,7 @@ func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Messa
// GetConnector get the connector object, capabilities, and error with priority: opts.Connector > ast.Connector
// Note: opts.Connector may be set by Create hook's applyOptionsAdjustments
// Returns: (connector, capabilities, error)
func (ast *Assistant) GetConnector(ctx *context.Context, opts ...*context.Options) (connector.Connector, *openai.Capabilities, error) {
func (ast *Assistant) GetConnector(ctx *context.Context, opts ...*context.Options) (connector.Connector, *goullm.Capabilities, error) {
// Determine connector ID with priority: opts.Connector > ast.Connector
connectorID := ast.Connector
if len(opts) > 0 && opts[0] != nil && opts[0].Connector != "" {
@ -588,9 +588,7 @@ func (ast *Assistant) GetConnector(ctx *context.Context, opts ...*context.Option
return nil, nil, err
}
// Get connector capabilities from settings
// Uses unified capability getter: 1. User-defined models.yml, 2. connector's Setting()["capabilities"], 3. default
capabilities := llm.GetCapabilitiesFromConn(conn, modelCapabilities)
capabilities := llm.GetCapabilitiesFromConn(conn)
return conn, capabilities, nil
}

View file

@ -9,7 +9,6 @@ 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"
"github.com/yaoapp/yao/agent/context"
"github.com/yaoapp/yao/agent/i18n"
@ -21,8 +20,7 @@ import (
// loaded the loaded assistant
var loaded = NewCache(200) // 200 is the default capacity
var storage store.Store = nil
var storeSetting *store.Setting = nil // store setting from agent.yml
var modelCapabilities map[string]gouOpenAI.Capabilities = map[string]gouOpenAI.Capabilities{}
var storeSetting *store.Setting = nil // store setting from agent.yml
var defaultConnector string = "" // default connector
var globalUses *context.Uses = nil // global uses configuration from agent.yml
var globalPrompts []store.Prompt = nil // global prompts from agent/prompts.yml
@ -140,11 +138,6 @@ func GetStorage() store.Store {
return storage
}
// SetModelCapabilities set the model capabilities configuration
func SetModelCapabilities(capabilities map[string]gouOpenAI.Capabilities) {
modelCapabilities = capabilities
}
// SetConnector set the connector
func SetConnector(c string) {
defaultConnector = c

View file

@ -256,20 +256,11 @@ func resolveSystemConnector(agentID string) string {
// findCapableConnector finds the first connector that supports tool calling
func findCapableConnector() string {
// Get all registered connectors
for id, conn := range connector.Connectors {
if !conn.Is(connector.OPENAI) {
continue
}
// Check from modelCapabilities (user-defined in models.yml)
if caps, exists := modelCapabilities[id]; exists {
if caps.ToolCalls {
return id
}
}
// Check capabilities from connector's Options
if connOpenAI, ok := conn.(*gouOpenAI.Connector); ok {
if connOpenAI.Options.Capabilities != nil && connOpenAI.Options.Capabilities.ToolCalls {
return id
@ -277,7 +268,6 @@ func findCapableConnector() string {
}
}
// No capable connector found, return empty
return ""
}

View file

@ -3,7 +3,7 @@ package context
import (
"time"
"github.com/yaoapp/gou/connector/openai"
"github.com/yaoapp/gou/llm"
"github.com/yaoapp/yao/agent/output"
"github.com/yaoapp/yao/agent/output/message"
)
@ -571,9 +571,8 @@ func (ctx *Context) getOutput() (*output.Output, error) {
Accept: string(ctx.Accept),
}
// Set ModelCapabilities (now using openai.Capabilities directly)
if ctx.Capabilities != nil {
caps := openai.Capabilities(*ctx.Capabilities)
caps := llm.Capabilities(*ctx.Capabilities)
options.Capabilities = &caps
}

View file

@ -5,7 +5,7 @@ import (
"sync"
"time"
"github.com/yaoapp/gou/connector/openai"
"github.com/yaoapp/gou/llm"
"github.com/yaoapp/gou/store"
"github.com/yaoapp/yao/agent/memory"
"github.com/yaoapp/yao/agent/output"
@ -253,7 +253,7 @@ type Context struct {
sandboxExecutor SandboxExecutor `json:"-"` // Sandbox executor for hooks (set by assistant when sandbox is configured)
// Model capabilities (set by assistant, used by output adapters)
Capabilities *openai.Capabilities `json:"-"` // Model capabilities for the current connector
Capabilities *llm.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

View file

@ -1,7 +1,7 @@
package context
import (
"github.com/yaoapp/gou/connector/openai"
"github.com/yaoapp/gou/llm"
"github.com/yaoapp/yao/agent/output/message"
)
@ -38,7 +38,7 @@ const (
)
// GetVisionSupport returns whether vision is supported and the format
func GetVisionSupport(cap *openai.Capabilities) (bool, VisionFormat) {
func GetVisionSupport(cap *llm.Capabilities) (bool, VisionFormat) {
if cap == nil || cap.Vision == nil {
return false, VisionFormatNone
}
@ -70,7 +70,7 @@ func GetVisionSupport(cap *openai.Capabilities) (bool, VisionFormat) {
type CompletionOptions struct {
// Model capabilities (used by LLM to select appropriate provider)
// nil means capabilities are not specified/checked
Capabilities *openai.Capabilities `json:"capabilities,omitempty"`
Capabilities *llm.Capabilities `json:"capabilities,omitempty"`
// User-specified tools for vision, audio, search, and fetch processing
Uses *Uses `json:"uses,omitempty"`

View file

@ -2,92 +2,48 @@ package llm
import (
"github.com/yaoapp/gou/connector"
"github.com/yaoapp/gou/connector/anthropic"
"github.com/yaoapp/gou/connector/openai"
goullm "github.com/yaoapp/gou/llm"
)
// GetCapabilities get the capabilities of a connector by connector ID
// This is a unified function to get connector capabilities with proper priority:
// 1. User-defined model capabilities from agent/models.yml (passed via modelCapabilities map)
// 2. Connector's Setting()["capabilities"] (default capabilities from connector)
// 3. Fallback to minimal default capabilities
//
// Usage in Agent with user-defined models:
//
// capabilities := llm.GetCapabilities(connectorID, modelCapabilities)
//
// Usage in API (without user-defined models):
//
// capabilities := llm.GetCapabilities(connectorID, nil)
func GetCapabilities(connectorID string, modelCapabilities map[string]openai.Capabilities) *openai.Capabilities {
// Reads capabilities from connector's Setting()["capabilities"], with fallback to defaults.
func GetCapabilities(connectorID string) *goullm.Capabilities {
if connectorID == "" {
return getDefaultCapabilities()
}
// Priority 1: Check user-defined model capabilities from agent/models.yml
if modelCapabilities != nil {
if modelCaps, exists := modelCapabilities[connectorID]; exists {
return &modelCaps
}
}
// Priority 2: Get connector and extract capabilities from Setting()
conn, err := connector.Select(connectorID)
if err != nil {
// If connector not found, return default
return getDefaultCapabilities()
}
return GetCapabilitiesFromConn(conn, modelCapabilities)
return GetCapabilitiesFromConn(conn)
}
// GetCapabilitiesFromConn get the capabilities from a connector instance
// This is useful when you already have the connector object
func GetCapabilitiesFromConn(conn connector.Connector, modelCapabilities map[string]openai.Capabilities) *openai.Capabilities {
func GetCapabilitiesFromConn(conn connector.Connector) *goullm.Capabilities {
if conn == nil {
return getDefaultCapabilities()
}
connectorID := conn.ID()
// Priority 1: Check user-defined model capabilities from agent/models.yml
if modelCapabilities != nil {
if modelCaps, exists := modelCapabilities[connectorID]; exists {
return &modelCaps
}
}
// Priority 2: Get capabilities from connector's Setting() method
settings := conn.Setting()
if settings != nil {
if caps, ok := settings["capabilities"]; ok {
// Try to convert to *openai.Capabilities
if capabilities, ok := caps.(*openai.Capabilities); ok {
if capabilities, ok := caps.(*goullm.Capabilities); ok {
return capabilities
}
// Try to convert to openai.Capabilities (value type)
if capabilities, ok := caps.(openai.Capabilities); ok {
if capabilities, ok := caps.(goullm.Capabilities); ok {
return &capabilities
}
// Try to convert from *anthropic.Capabilities
if capabilities, ok := caps.(*anthropic.Capabilities); ok {
return convertAnthropicCaps(capabilities)
}
// Try to convert from anthropic.Capabilities (value type)
if capabilities, ok := caps.(anthropic.Capabilities); ok {
return convertAnthropicCaps(&capabilities)
}
}
}
// Priority 3: Fallback to minimal default capabilities
return getDefaultCapabilities()
}
// getDefaultCapabilities returns minimal default capabilities
// This should rarely be used as modern connectors provide capabilities via Setting()
func getDefaultCapabilities() *openai.Capabilities {
return &openai.Capabilities{
func getDefaultCapabilities() *goullm.Capabilities {
return &goullm.Capabilities{
Vision: false,
ToolCalls: false,
Audio: false,
@ -95,14 +51,13 @@ func getDefaultCapabilities() *openai.Capabilities {
Streaming: false,
JSON: false,
Multimodal: false,
TemperatureAdjustable: true, // Default to true for non-reasoning models
TemperatureAdjustable: true,
}
}
// GetCapabilitiesMap get capabilities as map[string]interface{} for API responses
// This is useful for OpenAPI responses that need JSON-serializable format
func GetCapabilitiesMap(connectorID string, modelCapabilities map[string]openai.Capabilities) map[string]interface{} {
caps := GetCapabilities(connectorID, modelCapabilities)
func GetCapabilitiesMap(connectorID string) map[string]interface{} {
caps := GetCapabilities(connectorID)
if caps == nil {
return nil
}
@ -110,16 +65,14 @@ func GetCapabilitiesMap(connectorID string, modelCapabilities map[string]openai.
return ToMap(caps)
}
// ToMap converts openai.Capabilities to map[string]interface{}
// This is useful for JSON serialization in API responses
func ToMap(caps *openai.Capabilities) map[string]interface{} {
// ToMap converts Capabilities to map[string]interface{}
func ToMap(caps *goullm.Capabilities) map[string]interface{} {
if caps == nil {
return nil
}
result := make(map[string]interface{})
// Handle Vision field specially as it can be bool or string
if caps.Vision != nil {
result["vision"] = caps.Vision
}
@ -135,22 +88,3 @@ func ToMap(caps *openai.Capabilities) map[string]interface{} {
return result
}
// convertAnthropicCaps converts anthropic.Capabilities to openai.Capabilities
// This provides a unified capabilities interface across connector types
func convertAnthropicCaps(caps *anthropic.Capabilities) *openai.Capabilities {
if caps == nil {
return getDefaultCapabilities()
}
return &openai.Capabilities{
Vision: caps.Vision,
Audio: caps.Audio,
STT: caps.STT,
ToolCalls: caps.ToolCalls,
Reasoning: caps.Reasoning,
Streaming: caps.Streaming,
JSON: caps.JSON,
Multimodal: caps.Multimodal,
TemperatureAdjustable: caps.TemperatureAdjustable,
}
}

View file

@ -203,7 +203,7 @@ func parseToolCalls(toolCalls []interface{}) []agentContext.ToolCall {
// buildCompletionOptions creates CompletionOptions from JS opts map
func buildCompletionOptions(conn connector.Connector, opts map[string]interface{}) *agentContext.CompletionOptions {
// Get capabilities from connector
capabilities := GetCapabilitiesFromConn(conn, nil)
capabilities := GetCapabilitiesFromConn(conn)
completionOptions := &agentContext.CompletionOptions{
Capabilities: capabilities,
@ -737,12 +737,6 @@ func (api *JSAPI) executeRace(requests []*Request) []interface{} {
return []interface{}{result}
}
// executeSingleRequest executes a single LLM request using the original context
// This is used for single calls (not batch)
func (api *JSAPI) executeSingleRequest(request *Request) interface{} {
return api.StreamWithHandler(request.Connector, request.Messages, request.Options, request.Handler)
}
// executeSingleRequestWithForkedContext executes a single LLM request with a forked context
// This is used by batch operations (All/Any/Race) to avoid race conditions
// when multiple goroutines access shared context state

View file

@ -9,9 +9,8 @@ import (
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou/connector"
gouAnthropicConn "github.com/yaoapp/gou/connector/anthropic"
gouOpenAI "github.com/yaoapp/gou/connector/openai"
"github.com/yaoapp/gou/http"
goullm "github.com/yaoapp/gou/llm"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/agent/context"
"github.com/yaoapp/yao/agent/i18n"
@ -27,34 +26,15 @@ type Provider struct {
}
// New create a new Anthropic provider
func New(conn connector.Connector, capabilities *gouOpenAI.Capabilities) *Provider {
func New(conn connector.Connector, capabilities *goullm.Capabilities) *Provider {
return &Provider{
Provider: base.NewProvider(conn, capabilities),
adapters: buildAdapters(capabilities),
}
}
// NewFromAnthropicCaps create a new Anthropic provider from Anthropic capabilities
func NewFromAnthropicCaps(conn connector.Connector, caps *gouAnthropicConn.Capabilities) *Provider {
// Convert anthropic capabilities to openai capabilities for base provider compatibility
openaiCaps := &gouOpenAI.Capabilities{
Vision: caps.Vision,
Audio: caps.Audio,
ToolCalls: caps.ToolCalls,
Reasoning: caps.Reasoning,
Streaming: caps.Streaming,
JSON: caps.JSON,
Multimodal: caps.Multimodal,
TemperatureAdjustable: caps.TemperatureAdjustable,
}
return &Provider{
Provider: base.NewProvider(conn, openaiCaps),
adapters: buildAdapters(openaiCaps),
}
}
// buildAdapters builds capability adapters based on model capabilities
func buildAdapters(cap *gouOpenAI.Capabilities) []adapters.CapabilityAdapter {
func buildAdapters(cap *goullm.Capabilities) []adapters.CapabilityAdapter {
if cap == nil {
return []adapters.CapabilityAdapter{}
}

View file

@ -7,7 +7,7 @@ import (
"testing"
"github.com/yaoapp/gou/connector"
"github.com/yaoapp/gou/connector/openai"
goullm "github.com/yaoapp/gou/llm"
"github.com/yaoapp/yao/agent/context"
"github.com/yaoapp/yao/agent/llm"
"github.com/yaoapp/yao/agent/output/message"
@ -34,9 +34,8 @@ func TestAnthropicStreamBasic(t *testing.T) {
t.Fatal("Connector is not ANTHROPIC type")
}
// Use openai.Capabilities — SelectProvider auto-detects Anthropic format from connector type
options := &context.CompletionOptions{
Capabilities: &openai.Capabilities{
Capabilities: &goullm.Capabilities{
Streaming: true,
ToolCalls: true,
},
@ -111,7 +110,7 @@ func TestAnthropicStreamWithToolCalls(t *testing.T) {
}
options := &context.CompletionOptions{
Capabilities: &openai.Capabilities{
Capabilities: &goullm.Capabilities{
Streaming: true,
ToolCalls: true,
},
@ -226,7 +225,7 @@ func TestAnthropicStreamRetry(t *testing.T) {
}
options := &context.CompletionOptions{
Capabilities: &openai.Capabilities{
Capabilities: &goullm.Capabilities{
Streaming: true,
ToolCalls: true,
},

View file

@ -4,7 +4,7 @@ import (
"fmt"
"github.com/yaoapp/gou/connector"
"github.com/yaoapp/gou/connector/openai"
"github.com/yaoapp/gou/llm"
"github.com/yaoapp/yao/agent/context"
)
@ -12,11 +12,11 @@ import (
// Provides common functionality for all LLM providers
type Provider struct {
Connector connector.Connector
Capabilities *openai.Capabilities
Capabilities *llm.Capabilities
}
// NewProvider create a new base provider
func NewProvider(conn connector.Connector, capabilities *openai.Capabilities) *Provider {
func NewProvider(conn connector.Connector, capabilities *llm.Capabilities) *Provider {
return &Provider{
Connector: conn,
Capabilities: capabilities,

View file

@ -4,7 +4,6 @@ import (
"fmt"
"github.com/yaoapp/gou/connector"
gouAnthropicConn "github.com/yaoapp/gou/connector/anthropic"
"github.com/yaoapp/yao/agent/context"
"github.com/yaoapp/yao/agent/llm/providers/anthropic"
"github.com/yaoapp/yao/agent/llm/providers/openai"
@ -43,13 +42,6 @@ func SelectProvider(conn connector.Connector, options *context.CompletionOptions
return openai.New(conn, options.Capabilities), nil
case "anthropic":
// Anthropic Messages API (Claude, Kimi Code, etc.)
// Check if connector has native Anthropic capabilities
settings := conn.Setting()
if caps, ok := settings["capabilities"].(*gouAnthropicConn.Capabilities); ok {
return anthropic.NewFromAnthropicCaps(conn, caps), nil
}
// Fallback: use OpenAI capabilities (converted from connector settings)
return anthropic.New(conn, options.Capabilities), nil
default:

View file

@ -8,8 +8,8 @@ import (
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou/connector"
gouOpenAI "github.com/yaoapp/gou/connector/openai"
"github.com/yaoapp/gou/http"
goullm "github.com/yaoapp/gou/llm"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/agent/context"
"github.com/yaoapp/yao/agent/i18n"
@ -137,7 +137,7 @@ func buildAPIURL(host, endpoint string) string {
}
// New create a new OpenAI provider with capability adapters
func New(conn connector.Connector, capabilities *gouOpenAI.Capabilities) *Provider {
func New(conn connector.Connector, capabilities *goullm.Capabilities) *Provider {
return &Provider{
Provider: base.NewProvider(conn, capabilities),
adapters: buildAdapters(capabilities),
@ -145,7 +145,7 @@ func New(conn connector.Connector, capabilities *gouOpenAI.Capabilities) *Provid
}
// buildAdapters builds capability adapters based on model capabilities
func buildAdapters(cap *gouOpenAI.Capabilities) []adapters.CapabilityAdapter {
func buildAdapters(cap *goullm.Capabilities) []adapters.CapabilityAdapter {
if cap == nil {
return []adapters.CapabilityAdapter{}
}
@ -182,7 +182,7 @@ func buildAdapters(cap *gouOpenAI.Capabilities) []adapters.CapabilityAdapter {
}
// detectReasoningFormat detects the reasoning format based on capabilities
func detectReasoningFormat(cap *gouOpenAI.Capabilities) adapters.ReasoningFormat {
func detectReasoningFormat(cap *goullm.Capabilities) adapters.ReasoningFormat {
// TODO: Implement better detection logic
// For now, default to OpenAI o1 format if reasoning is supported
if cap.Reasoning {

View file

@ -6,7 +6,6 @@ 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"
@ -75,12 +74,6 @@ func Load(cfg config.Config) error {
return err
}
// Initialize model capabilities
err = initModelCapabilities()
if err != nil {
return err
}
// Initialize Global I18n
err = initGlobalI18n()
if err != nil {
@ -148,29 +141,6 @@ func GetGlobalPrompts(ctx map[string]string) []store.Prompt {
return store.Prompts(agentDSL.GlobalPrompts).Parse(ctx)
}
// initModelCapabilities initialize the model capabilities configuration
func initModelCapabilities() error {
path := filepath.Join("agent", "models.yml")
if exists, _ := application.App.Exists(path); !exists {
return nil
}
// Read the model capabilities configuration
bytes, err := application.App.Read(path)
if err != nil {
return err
}
var models map[string]gouOpenAI.Capabilities = map[string]gouOpenAI.Capabilities{}
err = application.Parse("models.yml", bytes, &models)
if err != nil {
return err
}
agentDSL.Models = models
return nil
}
// initStore initialize the store
func initStore() error {
@ -231,10 +201,6 @@ func initAssistant() error {
assistant.SetGlobalPrompts(agentDSL.GlobalPrompts)
}
if agentDSL.Models != nil {
assistant.SetModelCapabilities(agentDSL.Models)
}
if agentDSL.KB != nil {
assistant.SetGlobalKBSetting(agentDSL.KB)
}

View file

@ -54,12 +54,6 @@ func TestLoad(t *testing.T) {
assert.Contains(t, agent.GlobalPrompts[0].Content, "$SYS.")
})
t.Run("LoadModelCapabilities", func(t *testing.T) {
// Model capabilities should be loaded from agent/models.yml
assert.NotNil(t, agent.Models)
assert.Greater(t, len(agent.Models), 0)
})
t.Run("LoadKBConfig", func(t *testing.T) {
// KB configuration should be loaded from agent/kb.yml
assert.NotNil(t, agent.KB)

View file

@ -3,7 +3,7 @@ package message
import (
"net/http"
"github.com/yaoapp/gou/connector/openai"
"github.com/yaoapp/gou/llm"
traceTypes "github.com/yaoapp/yao/trace/types"
)
@ -13,7 +13,7 @@ type Options struct {
Accept string
Writer http.ResponseWriter
Trace traceTypes.Manager
Capabilities *openai.Capabilities
Capabilities *llm.Capabilities
Locale string
}

View file

@ -1,7 +1,6 @@
package types
import (
"github.com/yaoapp/gou/connector/openai"
"github.com/yaoapp/yao/agent/assistant"
searchTypes "github.com/yaoapp/yao/agent/search/types"
store "github.com/yaoapp/yao/agent/store/types"
@ -23,11 +22,10 @@ type DSL struct {
// If not set, fallback to the first connector that supports the required capabilities
System *System `json:"system,omitempty" yaml:"system,omitempty"`
// Global External Settings - model capabilities, tools, etc.
// Global External Settings
// ===============================
Models map[string]openai.Capabilities `json:"models,omitempty" yaml:"models,omitempty"` // The model capabilities configuration
KB *store.KBSetting `json:"kb,omitempty" yaml:"kb,omitempty"` // The knowledge base configuration loaded from agent/kb.yml
Search *searchTypes.Config `json:"search,omitempty" yaml:"search,omitempty"` // The search configuration loaded from agent/search.yao
KB *store.KBSetting `json:"kb,omitempty" yaml:"kb,omitempty"` // The knowledge base configuration loaded from agent/kb.yml
Search *searchTypes.Config `json:"search,omitempty" yaml:"search,omitempty"` // The search configuration loaded from agent/search.yao
// Internal
// ===============================

View file

@ -5,8 +5,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/yaoapp/gou/connector"
"github.com/yaoapp/gou/connector/openai"
"github.com/yaoapp/yao/agent"
agentllm "github.com/yaoapp/yao/agent/llm"
oauthTypes "github.com/yaoapp/yao/openapi/oauth/types"
"github.com/yaoapp/yao/openapi/response"
@ -21,16 +19,6 @@ type Provider struct {
Capabilities map[string]interface{} `json:"capabilities"` // Model capabilities from connector settings
}
// getModelCapabilities returns user-defined model capabilities from agent DSL
// Returns nil if agent not initialized or no models configured
func getModelCapabilities() map[string]openai.Capabilities {
agentDSL := agent.GetAgent()
if agentDSL != nil && agentDSL.Models != nil {
return agentDSL.Models
}
return nil
}
// Attach attaches the LLM management handlers to the router with OAuth protection
func Attach(group *gin.RouterGroup, oauth oauthTypes.OAuth) {
@ -56,23 +44,15 @@ func listProviders(c *gin.Context) {
}
}
// Get user-defined model capabilities once at the start of request
modelCapabilities := getModelCapabilities()
// Get all LLM connectors from AIConnectors
// Note: All AI type connectors (openai, anthropic, fastembed) are automatically added to AIConnectors during loading
// See gou/connector/connector.go LoadSource() for details
for _, opt := range connector.AIConnectors {
connType := getConnectorType(opt.Value)
// Include OpenAI-compatible and Anthropic LLM connectors
if connType == "openai" || connType == "anthropic" {
conn, ok := connector.Connectors[opt.Value]
if !ok {
continue
}
// Get capabilities from connector settings
capabilities := getCapabilitiesWithModels(conn, modelCapabilities)
capabilities := getCapabilitiesFromConn(conn)
// Apply capability filters
if len(filters) > 0 && !matchesFilters(capabilities, filters) {
@ -110,16 +90,13 @@ func getConnectorType(id string) string {
return "unknown"
}
// getCapabilitiesWithModels extracts capabilities from connector settings
// Uses the unified capability getter from agent/llm package
// Takes modelCapabilities as parameter to avoid repeated calls to getModelCapabilities()
func getCapabilitiesWithModels(conn connector.Connector, modelCapabilities map[string]openai.Capabilities) map[string]interface{} {
// getCapabilitiesFromConn extracts capabilities from connector settings
func getCapabilitiesFromConn(conn connector.Connector) map[string]interface{} {
if conn == nil {
return nil
}
// Use unified capability getter with user-defined model capabilities
caps := agentllm.GetCapabilitiesFromConn(conn, modelCapabilities)
caps := agentllm.GetCapabilitiesFromConn(conn)
return agentllm.ToMap(caps)
}