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 }