mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-24 01:40:02 +08:00
feat: add time limit for StartToGoal
This commit is contained in:
@@ -23,7 +23,7 @@ func TestGetImageFromBuffer(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
cvResult, err := service.ReadFromBuffer(buf)
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(fmt.Sprintf("cvResult: %v", cvResult))
|
||||
fmt.Printf("cvResult: %v\n", cvResult)
|
||||
}
|
||||
|
||||
func TestGetImageFromPath(t *testing.T) {
|
||||
@@ -32,5 +32,5 @@ func TestGetImageFromPath(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
cvResult, err := service.ReadFromPath(imagePath)
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(fmt.Sprintf("cvResult: %v", cvResult))
|
||||
fmt.Printf("cvResult: %v\n", cvResult)
|
||||
}
|
||||
|
||||
@@ -21,18 +21,24 @@ func (dExt *XTDriver) StartToGoal(ctx context.Context, prompt string, opts ...op
|
||||
if options.MaxRetryTimes > 0 {
|
||||
logger = logger.Int("max_retry_times", options.MaxRetryTimes)
|
||||
}
|
||||
if options.Timeout > 0 {
|
||||
logger = logger.Int("timeout_seconds", options.Timeout)
|
||||
}
|
||||
logger.Msg("StartToGoal")
|
||||
|
||||
// Create timeout context for entire StartToGoal process if Timeout is specified
|
||||
if options.Timeout > 0 {
|
||||
// Handle TimeLimit and Timeout with unified context mechanism
|
||||
var isTimeLimitMode bool
|
||||
if options.TimeLimit > 0 {
|
||||
// TimeLimit takes precedence over Timeout
|
||||
logger = logger.Int("time_limit_seconds", options.TimeLimit)
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, time.Duration(options.TimeLimit)*time.Second)
|
||||
defer cancel()
|
||||
isTimeLimitMode = true
|
||||
} else if options.Timeout > 0 {
|
||||
// Use Timeout only if TimeLimit is not set
|
||||
logger = logger.Int("timeout_seconds", options.Timeout)
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, time.Duration(options.Timeout)*time.Second)
|
||||
defer cancel()
|
||||
log.Info().Int("timeout_seconds", options.Timeout).Msg("StartToGoal timeout configured for entire process")
|
||||
}
|
||||
logger.Msg("StartToGoal")
|
||||
|
||||
var allPlannings []*PlanningExecutionResult
|
||||
var attempt int
|
||||
@@ -40,16 +46,26 @@ func (dExt *XTDriver) StartToGoal(ctx context.Context, prompt string, opts ...op
|
||||
attempt++
|
||||
log.Info().Int("attempt", attempt).Msg("planning attempt")
|
||||
|
||||
// Check for context cancellation (interrupt signal or timeout)
|
||||
// Check for context cancellation (timeout, time limit, or interrupt)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
cause := context.Cause(ctx)
|
||||
// Handle TimeLimit timeout - return success
|
||||
if isTimeLimitMode && errors.Is(cause, context.DeadlineExceeded) {
|
||||
log.Info().
|
||||
Int("attempt", attempt).
|
||||
Int("completed_plannings", len(allPlannings)).
|
||||
Int("time_limit_seconds", options.TimeLimit).
|
||||
Msg("StartToGoal time limit reached, stopping gracefully")
|
||||
return allPlannings, nil
|
||||
}
|
||||
|
||||
// Handle other cancellations (Timeout, interrupt, external cancellation) - return error
|
||||
log.Warn().
|
||||
Int("attempt", attempt).
|
||||
Int("completed_plannings", len(allPlannings)).
|
||||
Err(cause).
|
||||
Msg("StartToGoal cancelled")
|
||||
// Return the specific error type based on the cancellation cause
|
||||
return allPlannings, errors.Wrap(cause, "StartToGoal cancelled")
|
||||
default:
|
||||
}
|
||||
@@ -99,10 +115,25 @@ func (dExt *XTDriver) StartToGoal(ctx context.Context, prompt string, opts ...op
|
||||
|
||||
// Invoke tool calls
|
||||
for _, toolCall := range planningResult.ToolCalls {
|
||||
// Check for context cancellation before each action
|
||||
// Check for context cancellation (timeout, time limit, or interrupt) before each action
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
cause := context.Cause(ctx)
|
||||
// Handle TimeLimit timeout - return success
|
||||
if isTimeLimitMode && errors.Is(cause, context.DeadlineExceeded) {
|
||||
log.Info().
|
||||
Int("attempt", attempt).
|
||||
Int("completed_plannings", len(allPlannings)).
|
||||
Int("completed_tool_calls", len(planningResult.SubActions)).
|
||||
Int("total_tool_calls", len(planningResult.ToolCalls)).
|
||||
Int("time_limit_seconds", options.TimeLimit).
|
||||
Msg("StartToGoal time limit reached during tool call execution, stopping gracefully")
|
||||
planningResult.Elapsed = time.Since(planningStartTime).Milliseconds()
|
||||
allPlannings = append(allPlannings, planningResult)
|
||||
return allPlannings, nil
|
||||
}
|
||||
|
||||
// Handle other cancellations (Timeout, external cancellation) - return error
|
||||
log.Warn().
|
||||
Int("attempt", attempt).
|
||||
Int("completed_plannings", len(allPlannings)).
|
||||
@@ -112,7 +143,6 @@ func (dExt *XTDriver) StartToGoal(ctx context.Context, prompt string, opts ...op
|
||||
Msg("invokeToolCalls cancelled")
|
||||
planningResult.Elapsed = time.Since(planningStartTime).Milliseconds()
|
||||
allPlannings = append(allPlannings, planningResult)
|
||||
// Return the specific error type based on the cancellation cause
|
||||
return allPlannings, errors.Wrap(cause, "invokeToolCalls cancelled")
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -201,6 +201,7 @@ type ActionOptions struct {
|
||||
Steps int `json:"steps,omitempty" yaml:"steps,omitempty" desc:"Number of steps for action"`
|
||||
Direction interface{} `json:"direction,omitempty" yaml:"direction,omitempty" desc:"Direction for swipe operations or custom coordinates"`
|
||||
Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty" desc:"Timeout in seconds for action execution"`
|
||||
TimeLimit int `json:"time_limit,omitempty" yaml:"time_limit,omitempty" desc:"Time limit in seconds for action execution, stops gracefully when reached"`
|
||||
Frequency int `json:"frequency,omitempty" yaml:"frequency,omitempty" desc:"Action frequency"`
|
||||
|
||||
ScreenOptions
|
||||
@@ -281,6 +282,9 @@ func (o *ActionOptions) Options() []ActionOption {
|
||||
if o.Timeout != 0 {
|
||||
options = append(options, WithTimeout(o.Timeout))
|
||||
}
|
||||
if o.TimeLimit != 0 {
|
||||
options = append(options, WithTimeLimit(o.TimeLimit))
|
||||
}
|
||||
if o.Frequency != 0 {
|
||||
options = append(options, WithFrequency(o.Frequency))
|
||||
}
|
||||
@@ -562,6 +566,12 @@ func WithTimeout(seconds int) ActionOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithTimeLimit(seconds int) ActionOption {
|
||||
return func(o *ActionOptions) {
|
||||
o.TimeLimit = seconds
|
||||
}
|
||||
}
|
||||
|
||||
func WithIgnoreNotFoundError(ignoreError bool) ActionOption {
|
||||
return func(o *ActionOptions) {
|
||||
o.IgnoreNotFoundError = ignoreError
|
||||
|
||||
Reference in New Issue
Block a user