mirror of
https://github.com/httprunner/httprunner.git
synced 2026-07-12 16:01:27 +08:00
refactor: replace hardcoded log messages with constants for better maintainability
This commit is contained in:
@@ -206,6 +206,6 @@ func SendGA4Event(name string, params map[string]interface{}) {
|
|||||||
}
|
}
|
||||||
err := ga4Client.SendEvent(event)
|
err := ga4Client.SendEvent(event)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("send GA4 event failed")
|
log.Warn().Err(err).Msg("send GA4 event failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v5.0.0-beta-2506181659
|
v5.0.0-beta-2506181717
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/httprunner/httprunner/v5/internal/builtin"
|
"github.com/httprunner/httprunner/v5/internal/builtin"
|
||||||
"github.com/httprunner/httprunner/v5/uixt"
|
"github.com/httprunner/httprunner/v5/uixt"
|
||||||
|
"github.com/httprunner/httprunner/v5/uixt/ai"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
@@ -262,11 +263,11 @@ func (g *HTMLReportGenerator) getStepLogs(stepName string, startTime int64, elap
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Track AI conversation patterns
|
// Track AI conversation patterns
|
||||||
if strings.Contains(logEntry.Message, "log request messages") {
|
if strings.Contains(logEntry.Message, ai.LOG_REQUEST_MESSAGES) {
|
||||||
if logTime >= actualStartTime && logTime <= actualEndTime+30000 { // 30s buffer for AI requests
|
if logTime >= actualStartTime && logTime <= actualEndTime+30000 { // 30s buffer for AI requests
|
||||||
inAIConversation = true
|
inAIConversation = true
|
||||||
}
|
}
|
||||||
} else if strings.Contains(logEntry.Message, "log response message") && inAIConversation {
|
} else if strings.Contains(logEntry.Message, ai.LOG_RESPONSE_MESSAGE) && inAIConversation {
|
||||||
// Extend end time to include complete AI conversation, but respect next step boundary
|
// Extend end time to include complete AI conversation, but respect next step boundary
|
||||||
extendedEndTime := logTime
|
extendedEndTime := logTime
|
||||||
if nextStepStartTime > 0 && extendedEndTime >= nextStepStartTime {
|
if nextStepStartTime > 0 && extendedEndTime >= nextStepStartTime {
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ func (h *ConversationHistory) Clear() {
|
|||||||
log.Warn().Msg("conversation history cleared completely")
|
log.Warn().Msg("conversation history cleared completely")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
LOG_REQUEST_MESSAGES = "log request messages"
|
||||||
|
LOG_RESPONSE_MESSAGE = "log response message"
|
||||||
|
)
|
||||||
|
|
||||||
func logRequest(messages ConversationHistory) {
|
func logRequest(messages ConversationHistory) {
|
||||||
msgs := make(ConversationHistory, 0, len(messages))
|
msgs := make(ConversationHistory, 0, len(messages))
|
||||||
for _, message := range messages {
|
for _, message := range messages {
|
||||||
@@ -99,7 +104,7 @@ func logRequest(messages ConversationHistory) {
|
|||||||
}
|
}
|
||||||
msgs = append(msgs, msg)
|
msgs = append(msgs, msg)
|
||||||
}
|
}
|
||||||
log.Debug().Interface("messages", msgs).Msg("log request messages")
|
log.Debug().Interface("messages", msgs).Msg(LOG_REQUEST_MESSAGES)
|
||||||
}
|
}
|
||||||
|
|
||||||
func logResponse(message *schema.Message) {
|
func logResponse(message *schema.Message) {
|
||||||
@@ -126,5 +131,5 @@ func logResponse(message *schema.Message) {
|
|||||||
if message.Extra != nil {
|
if message.Extra != nil {
|
||||||
logger = logger.Interface("extra", message.Extra)
|
logger = logger.Interface("extra", message.Extra)
|
||||||
}
|
}
|
||||||
logger.Msg("log response message")
|
logger.Msg(LOG_RESPONSE_MESSAGE)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,45 @@ type PlanningJSONResponse struct {
|
|||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseStructuredResponse parses model response into structured format with error recovery
|
||||||
|
func parseStructuredResponse(content string, result interface{}) error {
|
||||||
|
// Clean and validate UTF-8 content first
|
||||||
|
cleanContent := sanitizeUTF8Content(content)
|
||||||
|
|
||||||
|
// Extract JSON content from response
|
||||||
|
jsonContent := extractJSONFromContent(cleanContent)
|
||||||
|
if jsonContent == "" {
|
||||||
|
// If JSON extraction failed, try parsing the content directly as a fallback
|
||||||
|
jsonContent = cleanContent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse JSON response with error recovery
|
||||||
|
return parseJSONWithFallback(jsonContent, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanitizeUTF8Content cleans invalid UTF-8 characters from content
|
||||||
|
func sanitizeUTF8Content(content string) string {
|
||||||
|
if utf8.ValidString(content) {
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to bytes and filter out invalid UTF-8 sequences
|
||||||
|
bytes := []byte(content)
|
||||||
|
var validBytes []byte
|
||||||
|
|
||||||
|
for len(bytes) > 0 {
|
||||||
|
r, size := utf8.DecodeRune(bytes)
|
||||||
|
if r != utf8.RuneError {
|
||||||
|
// Valid rune, keep it
|
||||||
|
validBytes = append(validBytes, bytes[:size]...)
|
||||||
|
}
|
||||||
|
// Skip invalid bytes (including RuneError)
|
||||||
|
bytes = bytes[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(validBytes)
|
||||||
|
}
|
||||||
|
|
||||||
// extractJSONFromContent extracts JSON content from various formats in the response
|
// extractJSONFromContent extracts JSON content from various formats in the response
|
||||||
// This function handles multiple formats:
|
// This function handles multiple formats:
|
||||||
// 1. ```json ... ``` markdown code blocks
|
// 1. ```json ... ``` markdown code blocks
|
||||||
@@ -121,29 +160,6 @@ func extractJSONFromContent(content string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// sanitizeUTF8Content cleans invalid UTF-8 characters from content
|
|
||||||
func sanitizeUTF8Content(content string) string {
|
|
||||||
if utf8.ValidString(content) {
|
|
||||||
return content
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to bytes and filter out invalid UTF-8 sequences
|
|
||||||
bytes := []byte(content)
|
|
||||||
var validBytes []byte
|
|
||||||
|
|
||||||
for len(bytes) > 0 {
|
|
||||||
r, size := utf8.DecodeRune(bytes)
|
|
||||||
if r != utf8.RuneError {
|
|
||||||
// Valid rune, keep it
|
|
||||||
validBytes = append(validBytes, bytes[:size]...)
|
|
||||||
}
|
|
||||||
// Skip invalid bytes (including RuneError)
|
|
||||||
bytes = bytes[size:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(validBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseJSONWithFallback attempts to parse JSON with multiple strategies for any struct type
|
// parseJSONWithFallback attempts to parse JSON with multiple strategies for any struct type
|
||||||
func parseJSONWithFallback(jsonContent string, result interface{}) error {
|
func parseJSONWithFallback(jsonContent string, result interface{}) error {
|
||||||
// Strategy 1: Direct JSON unmarshaling
|
// Strategy 1: Direct JSON unmarshaling
|
||||||
@@ -432,22 +448,6 @@ func extractPlanningFieldsManually(content string) (*PlanningJSONResponse, error
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseStructuredResponse parses model response into structured format with error recovery
|
|
||||||
func parseStructuredResponse(content string, result interface{}) error {
|
|
||||||
// Clean and validate UTF-8 content first
|
|
||||||
cleanContent := sanitizeUTF8Content(content)
|
|
||||||
|
|
||||||
// Extract JSON content from response
|
|
||||||
jsonContent := extractJSONFromContent(cleanContent)
|
|
||||||
if jsonContent == "" {
|
|
||||||
// If JSON extraction failed, try parsing the content directly as a fallback
|
|
||||||
jsonContent = cleanContent
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse JSON response with error recovery
|
|
||||||
return parseJSONWithFallback(jsonContent, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// callModelWithLogging is a common function to call model with logging and timing
|
// callModelWithLogging is a common function to call model with logging and timing
|
||||||
// It handles the common pattern of:
|
// It handles the common pattern of:
|
||||||
// 1. Log request
|
// 1. Log request
|
||||||
|
|||||||
Reference in New Issue
Block a user