108 lines
3.1 KiB
Go
108 lines
3.1 KiB
Go
|
|
package services
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"sort"
|
||
|
|
"strings"
|
||
|
|
"sync"
|
||
|
|
|
||
|
|
"code.tczkiot.com/wlw/ai-agent/contract"
|
||
|
|
)
|
||
|
|
|
||
|
|
var BusinessReadToolService = &businessReadToolService{}
|
||
|
|
|
||
|
|
type businessReadToolService struct {
|
||
|
|
mu sync.RWMutex
|
||
|
|
tools map[string]contract.BusinessReadTool
|
||
|
|
}
|
||
|
|
|
||
|
|
func SetBusinessReadTools(tools []contract.BusinessReadTool) error {
|
||
|
|
registered := make(map[string]contract.BusinessReadTool, len(tools))
|
||
|
|
for _, tool := range tools {
|
||
|
|
tool.Code = strings.TrimSpace(tool.Code)
|
||
|
|
tool.Description = strings.TrimSpace(tool.Description)
|
||
|
|
if tool.Code == "" {
|
||
|
|
return fmt.Errorf("ai-agent: business read tool code is required")
|
||
|
|
}
|
||
|
|
if !strings.HasPrefix(tool.Code, "business/") {
|
||
|
|
return fmt.Errorf("ai-agent: business read tool code must start with business/: %s", tool.Code)
|
||
|
|
}
|
||
|
|
if tool.Description == "" {
|
||
|
|
return fmt.Errorf("ai-agent: business read tool description is required: %s", tool.Code)
|
||
|
|
}
|
||
|
|
if tool.Execute == nil {
|
||
|
|
return fmt.Errorf("ai-agent: business read tool executor is required: %s", tool.Code)
|
||
|
|
}
|
||
|
|
if _, exists := registered[tool.Code]; exists {
|
||
|
|
return fmt.Errorf("ai-agent: duplicate business read tool code: %s", tool.Code)
|
||
|
|
}
|
||
|
|
if tool.InputSchema == nil {
|
||
|
|
tool.InputSchema = map[string]any{"type": "object", "properties": map[string]any{}}
|
||
|
|
}
|
||
|
|
registered[tool.Code] = tool
|
||
|
|
}
|
||
|
|
|
||
|
|
BusinessReadToolService.mu.Lock()
|
||
|
|
BusinessReadToolService.tools = registered
|
||
|
|
BusinessReadToolService.mu.Unlock()
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *businessReadToolService) ListForCustomerType(customerType string) []contract.BusinessReadTool {
|
||
|
|
s.mu.RLock()
|
||
|
|
defer s.mu.RUnlock()
|
||
|
|
|
||
|
|
ret := make([]contract.BusinessReadTool, 0, len(s.tools))
|
||
|
|
for _, tool := range s.tools {
|
||
|
|
if businessReadToolSupportsCustomerType(tool, customerType) {
|
||
|
|
ret = append(ret, tool)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
sort.Slice(ret, func(i, j int) bool { return ret[i].Code < ret[j].Code })
|
||
|
|
return ret
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *businessReadToolService) ResolveForCustomerType(code, customerType string) (contract.BusinessReadTool, bool) {
|
||
|
|
s.mu.RLock()
|
||
|
|
defer s.mu.RUnlock()
|
||
|
|
|
||
|
|
tool, ok := s.tools[strings.TrimSpace(code)]
|
||
|
|
if !ok || !businessReadToolSupportsCustomerType(tool, customerType) {
|
||
|
|
return contract.BusinessReadTool{}, false
|
||
|
|
}
|
||
|
|
return tool, true
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *businessReadToolService) Resolve(code string) (contract.BusinessReadTool, bool) {
|
||
|
|
s.mu.RLock()
|
||
|
|
defer s.mu.RUnlock()
|
||
|
|
tool, ok := s.tools[strings.TrimSpace(code)]
|
||
|
|
return tool, ok
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *businessReadToolService) Execute(
|
||
|
|
ctx context.Context,
|
||
|
|
tool contract.BusinessReadTool,
|
||
|
|
businessContext contract.BusinessReadContext,
|
||
|
|
arguments map[string]any,
|
||
|
|
) (any, error) {
|
||
|
|
if tool.Execute == nil {
|
||
|
|
return nil, fmt.Errorf("business read tool executor is unavailable: %s", tool.Code)
|
||
|
|
}
|
||
|
|
return tool.Execute(ctx, businessContext, arguments)
|
||
|
|
}
|
||
|
|
|
||
|
|
func businessReadToolSupportsCustomerType(tool contract.BusinessReadTool, customerType string) bool {
|
||
|
|
if len(tool.CustomerTypes) == 0 {
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
customerType = strings.TrimSpace(customerType)
|
||
|
|
for _, candidate := range tool.CustomerTypes {
|
||
|
|
if strings.EqualFold(strings.TrimSpace(candidate), customerType) {
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false
|
||
|
|
}
|