diff --git a/internal/ai/runtime/app/service.go b/internal/ai/runtime/app/service.go deleted file mode 100644 index 22a8099..0000000 --- a/internal/ai/runtime/app/service.go +++ /dev/null @@ -1,31 +0,0 @@ -package app - -import ( - "context" - - applicationruntime "cs-agent/internal/ai/application/runtime" -) - -type Service struct { - app *applicationruntime.Service -} - -func NewService() *Service { - return &Service{ - app: applicationruntime.NewService(), - } -} - -func (s *Service) Run(ctx context.Context, req Request) (*Summary, error) { - if s == nil || s.app == nil { - return nil, nil - } - return s.app.Run(ctx, req) -} - -func (s *Service) Resume(ctx context.Context, req ResumeRequest) (*Summary, error) { - if s == nil || s.app == nil { - return nil, nil - } - return s.app.Resume(ctx, req) -} diff --git a/internal/ai/runtime/app/types.go b/internal/ai/runtime/app/types.go deleted file mode 100644 index 1184930..0000000 --- a/internal/ai/runtime/app/types.go +++ /dev/null @@ -1,8 +0,0 @@ -package app - -import applicationruntime "cs-agent/internal/ai/application/runtime" - -type Request = applicationruntime.Request -type ResumeRequest = applicationruntime.ResumeRequest -type InterruptContextSummary = applicationruntime.InterruptContextSummary -type Summary = applicationruntime.Summary diff --git a/internal/ai/runtime/graphs/create_ticket_graph.go b/internal/ai/runtime/graphs/create_ticket_graph.go index 36c69f6..fc902a2 100644 --- a/internal/ai/runtime/graphs/create_ticket_graph.go +++ b/internal/ai/runtime/graphs/create_ticket_graph.go @@ -24,6 +24,13 @@ type CreateTicketGraphInterruptInfo struct { Message string `json:"message"` } +type createTicketGraphArgs struct { + Title string `json:"title"` + Description string `json:"description"` + Priority int64 `json:"priority"` + Severity int `json:"severity"` +} + func init() { schema.RegisterName[CreateTicketGraphState]("cs_agent_create_ticket_graph_state") schema.RegisterName[CreateTicketGraphInterruptInfo]("cs_agent_create_ticket_graph_interrupt_info") @@ -99,16 +106,16 @@ func (g *CreateTicketGraph) buildCreateRequest(argumentsInJSON string) (request. ConversationID: g.conversation.ID, SyncToConversation: true, } - raw := make(map[string]any) + var args createTicketGraphArgs if strings.TrimSpace(argumentsInJSON) != "" { - if err := json.Unmarshal([]byte(argumentsInJSON), &raw); err != nil { + if err := json.Unmarshal([]byte(argumentsInJSON), &args); err != nil { return req, fmt.Errorf("invalid create ticket arguments: %w", err) } } - req.Title = strings.TrimSpace(getStringValue(raw, "title")) - req.Description = strings.TrimSpace(getStringValue(raw, "description")) - req.Priority = getInt64Value(raw, "priority") - req.Severity = int(getInt64Value(raw, "severity")) + req.Title = strings.TrimSpace(args.Title) + req.Description = strings.TrimSpace(args.Description) + req.Priority = args.Priority + req.Severity = args.Severity if req.Title == "" { req.Title = strings.TrimSpace(g.conversation.Subject) } @@ -137,35 +144,3 @@ func (g *CreateTicketGraph) buildAIPrincipal() *dto.AuthPrincipal { Nickname: username, } } - -func getStringValue(data map[string]any, key string) string { - if len(data) == 0 { - return "" - } - value, ok := data[key] - if !ok { - return "" - } - text, _ := value.(string) - return text -} - -func getInt64Value(data map[string]any, key string) int64 { - if len(data) == 0 { - return 0 - } - value, ok := data[key] - if !ok { - return 0 - } - switch v := value.(type) { - case float64: - return int64(v) - case int64: - return v - case int: - return int64(v) - default: - return 0 - } -} diff --git a/internal/ai/runtime/graphs/create_ticket_graph_test.go b/internal/ai/runtime/graphs/create_ticket_graph_test.go new file mode 100644 index 0000000..ccf47c8 --- /dev/null +++ b/internal/ai/runtime/graphs/create_ticket_graph_test.go @@ -0,0 +1,45 @@ +package graphs + +import ( + "testing" + + "cs-agent/internal/models" +) + +func TestCreateTicketGraphBuildCreateRequest(t *testing.T) { + graph := NewCreateTicketGraph(&models.Conversation{ + ID: 12, + Subject: "fallback-title", + LastMessageSummary: "fallback-description", + }, &models.AIAgent{Name: "AI"}) + + req, err := graph.buildCreateRequest(`{"title":" test title ","description":" desc ","priority":2,"severity":3}`) + if err != nil { + t.Fatalf("buildCreateRequest returned error: %v", err) + } + if req.Title != "test title" || req.Description != "desc" { + t.Fatalf("unexpected request text fields: %#v", req) + } + if req.Priority != 2 || req.Severity != 3 { + t.Fatalf("unexpected request numeric fields: %#v", req) + } +} + +func TestCreateTicketGraphBuildCreateRequestFallbacks(t *testing.T) { + graph := NewCreateTicketGraph(&models.Conversation{ + ID: 12, + Subject: "fallback-title", + LastMessageSummary: "fallback-description", + }, &models.AIAgent{Name: "AI"}) + + req, err := graph.buildCreateRequest(`{}`) + if err != nil { + t.Fatalf("buildCreateRequest returned error: %v", err) + } + if req.Title != "fallback-title" { + t.Fatalf("unexpected fallback title: %#v", req) + } + if req.Description != "fallback-description" { + t.Fatalf("unexpected fallback description: %#v", req) + } +} diff --git a/internal/ai/runtime/internal/engine/service.go b/internal/ai/runtime/internal/engine/service.go deleted file mode 100644 index a77f936..0000000 --- a/internal/ai/runtime/internal/engine/service.go +++ /dev/null @@ -1,33 +0,0 @@ -package engine - -import ( - "context" - - runtimeeino "cs-agent/internal/ai/infra/eino" -) - -type Service struct { - executor *runtimeeino.RuntimeExecutor -} - -func NewService() *Service { - return &Service{ - executor: runtimeeino.NewRuntimeExecutor(), - } -} - -func (s *Service) Run(ctx context.Context, req Request) (*Summary, error) { - return s.ExecuteRun(ctx, req) -} - -func (s *Service) ExecuteRun(ctx context.Context, req RunInput) (*RunResult, error) { - return s.executor.ExecuteRun(ctx, runtimeeino.RunInput(req)) -} - -func (s *Service) Resume(ctx context.Context, req ResumeRequest) (*Summary, error) { - return s.ExecuteResume(ctx, req) -} - -func (s *Service) ExecuteResume(ctx context.Context, req ResumeInput) (*RunResult, error) { - return s.executor.ExecuteResume(ctx, runtimeeino.ResumeInput(req)) -} diff --git a/internal/ai/runtime/internal/engine/types.go b/internal/ai/runtime/internal/engine/types.go deleted file mode 100644 index 16783f9..0000000 --- a/internal/ai/runtime/internal/engine/types.go +++ /dev/null @@ -1,13 +0,0 @@ -package engine - -import runtimeeino "cs-agent/internal/ai/infra/eino" - -// TODO 这个地方为什么要定义类型别名,不能直接用吗? -type RunInput = runtimeeino.RunInput -type ResumeInput = runtimeeino.ResumeInput -type InterruptContextSummary = runtimeeino.InterruptContextSummary -type RunResult = runtimeeino.RunResult - -type Request = RunInput -type ResumeRequest = ResumeInput -type Summary = RunResult diff --git a/internal/ai/runtime/internal/executor/service.go b/internal/ai/runtime/internal/executor/service.go deleted file mode 100644 index 55bb34a..0000000 --- a/internal/ai/runtime/internal/executor/service.go +++ /dev/null @@ -1,31 +0,0 @@ -package executor - -import ( - "context" - - publicexecutor "cs-agent/internal/ai/runtime/executor" -) - -type Service struct { - inner *publicexecutor.Service -} - -func NewService() *Service { - return &Service{ - inner: publicexecutor.NewService(), - } -} - -func (s *Service) ExecuteRun(ctx context.Context, req RunInput) (*RunResult, error) { - if s == nil || s.inner == nil { - return nil, nil - } - return s.inner.ExecuteRun(ctx, publicexecutor.RunInput(req)) -} - -func (s *Service) ExecuteResume(ctx context.Context, req ResumeInput) (*RunResult, error) { - if s == nil || s.inner == nil { - return nil, nil - } - return s.inner.ExecuteResume(ctx, publicexecutor.ResumeInput(req)) -} diff --git a/internal/ai/runtime/internal/executor/types.go b/internal/ai/runtime/internal/executor/types.go deleted file mode 100644 index 3a4065e..0000000 --- a/internal/ai/runtime/internal/executor/types.go +++ /dev/null @@ -1,8 +0,0 @@ -package executor - -import publicexecutor "cs-agent/internal/ai/runtime/executor" - -type RunInput = publicexecutor.RunInput -type ResumeInput = publicexecutor.ResumeInput -type InterruptContextSummary = publicexecutor.InterruptContextSummary -type RunResult = publicexecutor.RunResult diff --git a/internal/ai/runtime/tools/tool_search_tool.go b/internal/ai/runtime/tools/tool_search_tool.go index cbb9b5e..dc78ff1 100644 --- a/internal/ai/runtime/tools/tool_search_tool.go +++ b/internal/ai/runtime/tools/tool_search_tool.go @@ -104,9 +104,9 @@ func (t *ToolSearchTool) InvokableRun(ctx context.Context, argumentsInJSON strin } type toolSearchRequest struct { - Query string - ToolCode string - Arguments map[string]any + Query string `json:"query"` + ToolCode string `json:"toolCode"` + Arguments map[string]any `json:"arguments"` } type toolSearchCandidate struct { @@ -122,22 +122,16 @@ func parseToolSearchRequest(argumentsInJSON string) (*toolSearchRequest, error) if argumentsInJSON == "" { return &toolSearchRequest{}, nil } - raw := make(map[string]any) - if err := json.Unmarshal([]byte(argumentsInJSON), &raw); err != nil { + var req toolSearchRequest + if err := json.Unmarshal([]byte(argumentsInJSON), &req); err != nil { return nil, fmt.Errorf("invalid tool_search arguments: %w", err) } - req := &toolSearchRequest{ - Query: strings.TrimSpace(getStringValue(raw, "query")), - ToolCode: strings.TrimSpace(getStringValue(raw, "toolCode")), + req.Query = strings.TrimSpace(req.Query) + req.ToolCode = strings.TrimSpace(req.ToolCode) + if req.Arguments == nil { + req.Arguments = map[string]any{} } - if value, ok := raw["arguments"]; ok { - args, ok := value.(map[string]any) - if !ok { - return nil, fmt.Errorf("tool_search arguments must be an object") - } - req.Arguments = args - } - return req, nil + return &req, nil } func (t *ToolSearchTool) searchCandidates(ctx context.Context, query string) (string, error) { diff --git a/internal/ai/runtime/tools/tool_search_tool_test.go b/internal/ai/runtime/tools/tool_search_tool_test.go new file mode 100644 index 0000000..e7f3ee9 --- /dev/null +++ b/internal/ai/runtime/tools/tool_search_tool_test.go @@ -0,0 +1,32 @@ +package tools + +import "testing" + +func TestParseToolSearchRequest(t *testing.T) { + req, err := parseToolSearchRequest(`{"query":" search docs ","toolCode":" mcp_server/search ","arguments":{"q":"hello"}}`) + if err != nil { + t.Fatalf("parseToolSearchRequest returned error: %v", err) + } + if req.Query != "search docs" { + t.Fatalf("unexpected query: %q", req.Query) + } + if req.ToolCode != "mcp_server/search" { + t.Fatalf("unexpected toolCode: %q", req.ToolCode) + } + if req.Arguments["q"] != "hello" { + t.Fatalf("unexpected arguments: %#v", req.Arguments) + } +} + +func TestParseToolSearchRequestDefaultsArguments(t *testing.T) { + req, err := parseToolSearchRequest(`{"query":"list"}`) + if err != nil { + t.Fatalf("parseToolSearchRequest returned error: %v", err) + } + if req.Arguments == nil { + t.Fatalf("expected non-nil arguments map") + } + if len(req.Arguments) != 0 { + t.Fatalf("expected empty arguments map, got %#v", req.Arguments) + } +}