From f332f4e304dd7a831c5852ac2bd9ff15cc32c655 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Mon, 30 Jun 2025 13:36:44 +0800 Subject: [PATCH] fix: ToolSleepMS tool call --- uixt/mcp_tools_utility.go | 58 +++++++++++++++++++++++++++----------- uixt/option/action.go | 7 ++--- uixt/option/action_test.go | 22 ++++++--------- 3 files changed, 51 insertions(+), 36 deletions(-) diff --git a/uixt/mcp_tools_utility.go b/uixt/mcp_tools_utility.go index 4aa2a267..9246709d 100644 --- a/uixt/mcp_tools_utility.go +++ b/uixt/mcp_tools_utility.go @@ -4,13 +4,15 @@ import ( "context" "encoding/json" "fmt" + "strconv" "time" - "github.com/httprunner/httprunner/v5/internal/builtin" - "github.com/httprunner/httprunner/v5/uixt/option" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" "github.com/rs/zerolog/log" + + "github.com/httprunner/httprunner/v5/internal/builtin" + "github.com/httprunner/httprunner/v5/uixt/option" ) // ToolSleep implements the sleep tool call. @@ -98,7 +100,8 @@ func (t *ToolSleep) ConvertActionToCallToolRequest(action option.MobileAction) ( // ToolSleepMS implements the sleep_ms tool call. type ToolSleepMS struct { // Return data fields - these define the structure of data returned by this tool - Milliseconds int64 `json:"milliseconds" desc:"Duration in milliseconds that was slept"` + Milliseconds int64 `json:"milliseconds" desc:"Duration in milliseconds that was slept"` + Duration string `json:"duration" desc:"Human-readable duration string"` } func (t *ToolSleepMS) Name() option.ActionName { @@ -110,26 +113,44 @@ func (t *ToolSleepMS) Description() string { } func (t *ToolSleepMS) Options() []mcp.ToolOption { - unifiedReq := &option.ActionOptions{} - return unifiedReq.GetMCPOptions(option.ACTION_SleepMS) + return []mcp.ToolOption{ + mcp.WithNumber("milliseconds", mcp.Description("Number of milliseconds to sleep")), + } } func (t *ToolSleepMS) Implement() server.ToolHandlerFunc { return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - unifiedReq, err := parseActionOptions(request.Params.Arguments) - if err != nil { - return nil, err - } - - // Validate required parameters - if unifiedReq.Milliseconds == 0 { - return nil, fmt.Errorf("milliseconds is required") + milliseconds, ok := request.Params.Arguments["milliseconds"] + if !ok { + log.Warn().Msg("milliseconds parameter is required, using default value 1000 milliseconds") + milliseconds = 1000 } // Sleep MS action logic - log.Info().Int64("milliseconds", unifiedReq.Milliseconds).Msg("sleeping in milliseconds") + log.Info().Interface("milliseconds", milliseconds).Msg("sleeping in milliseconds") - duration := time.Duration(unifiedReq.Milliseconds) * time.Millisecond + var duration time.Duration + var actualMilliseconds int64 + switch v := milliseconds.(type) { + case float64: + actualMilliseconds = int64(v) + duration = time.Duration(v) * time.Millisecond + case int: + actualMilliseconds = int64(v) + duration = time.Duration(v) * time.Millisecond + case int64: + actualMilliseconds = v + duration = time.Duration(v) * time.Millisecond + case string: + ms, err := strconv.ParseInt(v, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid sleep duration: %v", v) + } + actualMilliseconds = ms + duration = time.Duration(ms) * time.Millisecond + default: + return nil, fmt.Errorf("unsupported sleep duration type: %T", v) + } // Use context-aware sleep instead of blocking time.Sleep select { @@ -141,8 +162,11 @@ func (t *ToolSleepMS) Implement() server.ToolHandlerFunc { return nil, fmt.Errorf("sleep interrupted: %w", ctx.Err()) } - message := fmt.Sprintf("Successfully slept for %d milliseconds", unifiedReq.Milliseconds) - returnData := ToolSleepMS{Milliseconds: unifiedReq.Milliseconds} + message := fmt.Sprintf("Successfully slept for %d milliseconds", actualMilliseconds) + returnData := ToolSleepMS{ + Milliseconds: actualMilliseconds, + Duration: duration.String(), + } return NewMCPSuccessResponse(message, &returnData), nil } diff --git a/uixt/option/action.go b/uixt/option/action.go index f811562e..f23ecad8 100644 --- a/uixt/option/action.go +++ b/uixt/option/action.go @@ -191,10 +191,6 @@ type ActionOptions struct { ResetHistory bool `json:"reset_history,omitempty" yaml:"reset_history,omitempty" desc:"Whether to reset conversation history before AI planning"` OutputSchema interface{} `json:"output_schema,omitempty" yaml:"output_schema,omitempty" desc:"Custom output schema for structured AI query response"` - // Time related - Seconds float64 `json:"seconds,omitempty" yaml:"seconds,omitempty" desc:"Sleep duration in seconds"` - Milliseconds int64 `json:"milliseconds,omitempty" yaml:"milliseconds,omitempty" desc:"Sleep duration in milliseconds"` - // Control options Context context.Context `json:"-" yaml:"-"` Identifier string `json:"identifier,omitempty" yaml:"identifier,omitempty" desc:"Action identifier for logging"` @@ -368,7 +364,8 @@ func (o *ActionOptions) ApplyTapOffset(absX, absY float64) (float64, float64) { } func (o *ActionOptions) ApplySwipeOffset(absFromX, absFromY, absToX, absToY float64) ( - float64, float64, float64, float64) { + float64, float64, float64, float64, +) { if len(o.SwipeOffset) == 4 { absFromX += float64(o.SwipeOffset[0]) absFromY += float64(o.SwipeOffset[1]) diff --git a/uixt/option/action_test.go b/uixt/option/action_test.go index 9d7bb2e6..c15fdc15 100644 --- a/uixt/option/action_test.go +++ b/uixt/option/action_test.go @@ -140,16 +140,14 @@ func TestUnifiedActionRequest_CustomOptions(t *testing.T) { func TestUnifiedActionRequest_BasicTypeFields(t *testing.T) { // Test basic type fields (no longer pointers) unifiedReq := &ActionOptions{ - Platform: "android", - Serial: "device123", - Count: 5, - Keycode: 123, - Delta: 10, - Width: 800, - Height: 600, - Seconds: 2.5, - Milliseconds: 1500, - TabIndex: 3, + Platform: "android", + Serial: "device123", + Count: 5, + Keycode: 123, + Delta: 10, + Width: 800, + Height: 600, + TabIndex: 3, } // Test direct field access (no need for Getter methods) @@ -158,8 +156,6 @@ func TestUnifiedActionRequest_BasicTypeFields(t *testing.T) { assert.Equal(t, 10, unifiedReq.Delta) assert.Equal(t, 800, unifiedReq.Width) assert.Equal(t, 600, unifiedReq.Height) - assert.Equal(t, 2.5, unifiedReq.Seconds) - assert.Equal(t, int64(1500), unifiedReq.Milliseconds) assert.Equal(t, 3, unifiedReq.TabIndex) // Test zero value detection @@ -169,7 +165,5 @@ func TestUnifiedActionRequest_BasicTypeFields(t *testing.T) { assert.Equal(t, 0, emptyReq.Delta) assert.Equal(t, 0, emptyReq.Width) assert.Equal(t, 0, emptyReq.Height) - assert.Equal(t, 0.0, emptyReq.Seconds) - assert.Equal(t, int64(0), emptyReq.Milliseconds) assert.Equal(t, 0, emptyReq.TabIndex) }