diff --git a/internal/ai/runtime/app/prepare_service.go b/internal/ai/runtime/app/prepare_service.go deleted file mode 100644 index a216120..0000000 --- a/internal/ai/runtime/app/prepare_service.go +++ /dev/null @@ -1,89 +0,0 @@ -package app - -import ( - "context" - "encoding/json" - "strings" - - "cs-agent/internal/ai/skills" - "cs-agent/internal/models" -) - -func newPrepareService(catalog *toolCatalog) *prepareService { - return &prepareService{catalog: catalog} -} - -type prepareService struct { - catalog *toolCatalog -} - -func (s *prepareService) selectSkill(ctx context.Context, req Request) (*models.SkillDefinition, string, string, error) { - if req.AIAgent == nil || req.AIConfig == nil || req.UserMessage == nil || req.Conversation == nil { - return nil, "", "", nil - } - result, err := skills.Select(ctx, skills.RuntimeContext{ - AIAgentID: req.AIAgent.ID, - UserMessage: strings.TrimSpace(req.UserMessage.Content), - ConversationID: req.Conversation.ID, - ManualSkillCode: strings.TrimSpace(req.ManualSkillCode), - }) - if err != nil { - return nil, "", "", err - } - if result == nil || result.Plan == nil || result.Plan.Skill == nil { - traceData := marshalSkillRouteTrace(result) - reason := "" - if result != nil && result.Plan != nil { - reason = strings.TrimSpace(result.Plan.MatchReason) - } - return nil, reason, traceData, nil - } - return result.Plan.Skill, strings.TrimSpace(result.Plan.MatchReason), marshalSkillRouteTrace(result), nil -} - -func (s *prepareService) prepareToolsForRun(req *Request) error { - if req == nil || req.ToolSet != nil || s.catalog == nil { - return nil - } - toolSet, err := s.catalog.resolveForRun(req) - if err != nil { - return err - } - if toolSet != nil { - req.ToolSet = toolSet - } - return nil -} - -func (s *prepareService) prepareToolsForResume(req *ResumeRequest) error { - if req == nil || req.ToolSet != nil || s.catalog == nil { - return nil - } - toolSet, err := s.catalog.resolveForResume(req) - if err != nil { - return err - } - if toolSet != nil { - req.ToolSet = toolSet - } - return nil -} - -func marshalSkillRouteTrace(result *skills.ExecutionResult) string { - if result == nil || result.Plan == nil || result.Plan.RouteTrace == nil { - return "" - } - buf, err := json.Marshal(result.Plan.RouteTrace) - if err != nil { - return "" - } - return string(buf) -} - -func cloneSkillDefinition(item *models.SkillDefinition) *models.SkillDefinition { - if item == nil { - return nil - } - clone := *item - return &clone -} diff --git a/internal/ai/runtime/app/summary_builder.go b/internal/ai/runtime/app/summary_builder.go deleted file mode 100644 index ae2658a..0000000 --- a/internal/ai/runtime/app/summary_builder.go +++ /dev/null @@ -1,46 +0,0 @@ -package app - -import ( - "strings" - - "cs-agent/internal/ai/runtime/internal/executor" -) - -func toSummary(summary *executor.RunResult) *Summary { - if summary == nil { - return nil - } - ret := &Summary{ - RunID: summary.RunID, - Status: summary.Status, - ReplyText: summary.ReplyText, - PlannedSkillCode: strings.TrimSpace(summary.SelectedSkillCode), - PlannedSkillName: strings.TrimSpace(summary.SelectedSkillName), - PlanReason: strings.TrimSpace(summary.SkillRouteReason), - SkillRouteTrace: strings.TrimSpace(summary.SkillRouteTrace), - SkillAllowedToolCodes: append([]string(nil), summary.SkillAllowedToolCodes...), - ModelName: summary.ModelName, - PromptTokens: summary.PromptTokens, - CompletionTokens: summary.CompletionTokens, - HistoryMessageCount: summary.HistoryMessageCount, - RetrieverCount: summary.RetrieverCount, - ToolCallCount: summary.ToolCallCount, - ToolCodes: append([]string(nil), summary.ToolCodes...), - InvokedToolCodes: append([]string(nil), summary.InvokedToolCodes...), - CheckPointID: summary.CheckPointID, - Interrupted: summary.Interrupted, - TraceData: summary.TraceData, - ErrorMessage: summary.ErrorMessage, - } - if len(summary.Interrupts) > 0 { - ret.Interrupts = make([]InterruptContextSummary, 0, len(summary.Interrupts)) - for _, item := range summary.Interrupts { - ret.Interrupts = append(ret.Interrupts, InterruptContextSummary{ - Type: item.Type, - ID: item.ID, - InfoPreview: item.InfoPreview, - }) - } - } - return ret -} diff --git a/internal/ai/runtime/app/tool_catalog.go b/internal/ai/runtime/app/tool_catalog.go deleted file mode 100644 index 12ae172..0000000 --- a/internal/ai/runtime/app/tool_catalog.go +++ /dev/null @@ -1,125 +0,0 @@ -package app - -import ( - "encoding/json" - "strings" - - "cs-agent/internal/ai/runtime/registry" - "cs-agent/internal/ai/runtime/tools" - "cs-agent/internal/models" - "cs-agent/internal/pkg/toolx" -) - -type toolCatalog struct { - registry *registry.Registry -} - -func newToolCatalog() *toolCatalog { - return &toolCatalog{ - registry: registry.NewRegistry( - tools.NewTriageServiceRequestTool(), - tools.NewAnalyzeConversationTool(), - tools.NewPrepareTicketDraftTool(), - tools.NewCreateTicketGraphTool(), - tools.NewHandoffGraphTool(), - ), - } -} - -func (c *toolCatalog) resolveForRun(req *Request) (*registry.ToolSet, error) { - if req == nil || req.ToolSet != nil || c == nil || c.registry == nil { - return nil, nil - } - return c.registry.Resolve(registry.Context{ - Conversation: req.Conversation, - AIAgent: req.AIAgent, - AIConfig: req.AIConfig, - UserMessage: req.UserMessage, - AllowedToolCodes: c.resolveAllowedToolCodes(req.AIAgent, req.SelectedSkill), - }) -} - -func (c *toolCatalog) resolveForResume(req *ResumeRequest) (*registry.ToolSet, error) { - if req == nil || req.ToolSet != nil || c == nil || c.registry == nil { - return nil, nil - } - return c.registry.Resolve(registry.Context{ - Conversation: req.Conversation, - AIAgent: req.AIAgent, - AIConfig: req.AIConfig, - AllowedToolCodes: c.parseAgentAllowedToolCodes(req.AIAgent), - }) -} - -func (c *toolCatalog) parseSkillAllowedToolCodes(skill *models.SkillDefinition) []string { - if skill == nil { - return nil - } - raw := strings.TrimSpace(skill.ToolWhitelist) - if raw == "" { - return nil - } - var items []string - if err := json.Unmarshal([]byte(raw), &items); err != nil { - return nil - } - return normalizeAllowedToolCodes(items) -} - -func (c *toolCatalog) parseAgentAllowedToolCodes(aiAgent *models.AIAgent) []string { - if aiAgent == nil || strings.TrimSpace(aiAgent.AllowedMCPTools) == "" { - return nil - } - items, err := toolx.ParseAgentMCPToolsJSON(aiAgent.AllowedMCPTools) - if err != nil { - return nil - } - ret := make([]string, 0, len(items)) - for _, item := range items { - ret = append(ret, item.ToolCode) - } - return normalizeAllowedToolCodes(ret) -} - -func (c *toolCatalog) resolveAllowedToolCodes(aiAgent *models.AIAgent, skill *models.SkillDefinition) []string { - agentAllowed := c.parseAgentAllowedToolCodes(aiAgent) - skillAllowed := c.parseSkillAllowedToolCodes(skill) - switch { - case len(agentAllowed) == 0: - return skillAllowed - case len(skillAllowed) == 0: - return agentAllowed - default: - skillSet := make(map[string]struct{}, len(skillAllowed)) - for _, item := range skillAllowed { - skillSet[item] = struct{}{} - } - ret := make([]string, 0, len(agentAllowed)) - for _, item := range agentAllowed { - if _, ok := skillSet[item]; ok { - ret = append(ret, item) - } - } - return ret - } -} - -func normalizeAllowedToolCodes(items []string) []string { - if len(items) == 0 { - return nil - } - ret := make([]string, 0, len(items)) - seen := make(map[string]struct{}, len(items)) - for _, item := range items { - item = toolx.NormalizeToolCodeAlias(strings.TrimSpace(item)) - if item == "" { - continue - } - if _, ok := seen[item]; ok { - continue - } - seen[item] = struct{}{} - ret = append(ret, item) - } - return ret -} diff --git a/internal/ai/runtime/app/tool_catalog_test.go b/internal/ai/runtime/app/tool_catalog_test.go deleted file mode 100644 index 106cf3e..0000000 --- a/internal/ai/runtime/app/tool_catalog_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package app - -import ( - "testing" - - "cs-agent/internal/models" -) - -func TestNormalizeAllowedToolCodes(t *testing.T) { - ret := normalizeAllowedToolCodes([]string{ - " ", - "graph/create_ticket_with_confirmation", - "builtin/create_ticket_with_confirmation", - "graph/handoff_to_human", - "graph/handoff_to_human", - }) - if len(ret) != 2 { - t.Fatalf("expected 2 tool codes, got %d: %#v", len(ret), ret) - } - if ret[0] != "graph/create_ticket_with_confirmation" { - t.Fatalf("unexpected first tool code: %s", ret[0]) - } - if ret[1] != "graph/handoff_to_human" { - t.Fatalf("unexpected second tool code: %s", ret[1]) - } -} - -func TestToolCatalogResolveAllowedToolCodes(t *testing.T) { - catalog := newToolCatalog() - agent := &models.AIAgent{ - AllowedMCPTools: `[{"toolCode":"graph/create_ticket_with_confirmation"},{"toolCode":"graph/handoff_to_human"}]`, - } - skill := &models.SkillDefinition{ - ToolWhitelist: `["builtin/create_ticket_with_confirmation","graph/prepare_ticket_draft"]`, - } - ret := catalog.resolveAllowedToolCodes(agent, skill) - if len(ret) != 1 { - t.Fatalf("expected 1 tool code, got %d: %#v", len(ret), ret) - } - if ret[0] != "graph/create_ticket_with_confirmation" { - t.Fatalf("unexpected tool code: %s", ret[0]) - } -} - -func TestToolCatalogResolveAllowedToolCodesFallsBackWhenSkillEmpty(t *testing.T) { - catalog := newToolCatalog() - agent := &models.AIAgent{ - AllowedMCPTools: `[{"toolCode":"graph/create_ticket_with_confirmation"},{"toolCode":"graph/handoff_to_human"}]`, - } - ret := catalog.resolveAllowedToolCodes(agent, nil) - if len(ret) != 2 { - t.Fatalf("expected 2 tool codes, got %d: %#v", len(ret), ret) - } -}