mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-05 15:37:35 +08:00
fix: extracts JSON content from various formats in the response
This commit is contained in:
@@ -1 +1 @@
|
|||||||
v5.0.0-beta-2506101319
|
v5.0.0-beta-2506101402
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/cloudwego/eino/schema"
|
||||||
"github.com/httprunner/httprunner/v5/internal/json"
|
"github.com/httprunner/httprunner/v5/internal/json"
|
||||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||||
"github.com/httprunner/httprunner/v5/uixt/types"
|
"github.com/httprunner/httprunner/v5/uixt/types"
|
||||||
@@ -45,13 +46,72 @@ func (p *JSONContentParser) SystemPrompt() string {
|
|||||||
return p.systemPrompt
|
return p.systemPrompt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractJSONContent extracts JSON content from various formats in the response
|
||||||
|
func (p *JSONContentParser) extractJSONContent(content string) string {
|
||||||
|
content = strings.TrimSpace(content)
|
||||||
|
|
||||||
|
// Case 1: Content wrapped in ```json ... ```
|
||||||
|
if strings.Contains(content, "```json") {
|
||||||
|
start := strings.Index(content, "```json")
|
||||||
|
if start != -1 {
|
||||||
|
start += 7 // length of "```json"
|
||||||
|
end := strings.Index(content[start:], "```")
|
||||||
|
if end != -1 {
|
||||||
|
jsonContent := strings.TrimSpace(content[start : start+end])
|
||||||
|
return jsonContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 2: Content wrapped in ``` ... ``` (without json specifier)
|
||||||
|
if strings.HasPrefix(content, "```") && strings.HasSuffix(content, "```") {
|
||||||
|
lines := strings.Split(content, "\n")
|
||||||
|
if len(lines) >= 3 {
|
||||||
|
// Remove first and last lines (the ``` markers)
|
||||||
|
jsonLines := lines[1 : len(lines)-1]
|
||||||
|
jsonContent := strings.Join(jsonLines, "\n")
|
||||||
|
jsonContent = strings.TrimSpace(jsonContent)
|
||||||
|
// Check if it looks like JSON
|
||||||
|
if strings.HasPrefix(jsonContent, "{") && strings.HasSuffix(jsonContent, "}") {
|
||||||
|
return jsonContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 3: Look for JSON object in the content
|
||||||
|
start := strings.Index(content, "{")
|
||||||
|
if start != -1 {
|
||||||
|
// Find the matching closing brace
|
||||||
|
braceCount := 0
|
||||||
|
for i := start; i < len(content); i++ {
|
||||||
|
if content[i] == '{' {
|
||||||
|
braceCount++
|
||||||
|
} else if content[i] == '}' {
|
||||||
|
braceCount--
|
||||||
|
if braceCount == 0 {
|
||||||
|
jsonContent := strings.TrimSpace(content[start : i+1])
|
||||||
|
return jsonContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 4: If content itself looks like JSON
|
||||||
|
if strings.HasPrefix(content, "{") && strings.HasSuffix(content, "}") {
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (p *JSONContentParser) Parse(content string, size types.Size) (*PlanningResult, error) {
|
func (p *JSONContentParser) Parse(content string, size types.Size) (*PlanningResult, error) {
|
||||||
content = strings.TrimSpace(content)
|
content = strings.TrimSpace(content)
|
||||||
if strings.HasPrefix(content, "```json") && strings.HasSuffix(content, "```") {
|
|
||||||
content = strings.TrimPrefix(content, "```json")
|
// Extract JSON content from markdown code blocks
|
||||||
content = strings.TrimSuffix(content, "```")
|
jsonContent := p.extractJSONContent(content)
|
||||||
|
if jsonContent == "" {
|
||||||
|
return nil, fmt.Errorf("no valid JSON content found in response")
|
||||||
}
|
}
|
||||||
content = strings.TrimSpace(content)
|
|
||||||
|
|
||||||
// Define a temporary struct to parse the expected JSON format
|
// Define a temporary struct to parse the expected JSON format
|
||||||
var jsonResponse struct {
|
var jsonResponse struct {
|
||||||
@@ -60,7 +120,7 @@ func (p *JSONContentParser) Parse(content string, size types.Size) (*PlanningRes
|
|||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal([]byte(content), &jsonResponse); err != nil {
|
if err := json.Unmarshal([]byte(jsonContent), &jsonResponse); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse VLM response: %v", err)
|
return nil, fmt.Errorf("failed to parse VLM response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +128,18 @@ func (p *JSONContentParser) Parse(content string, size types.Size) (*PlanningRes
|
|||||||
return nil, errors.New(jsonResponse.Error)
|
return nil, errors.New(jsonResponse.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle cases where no actions are returned
|
||||||
if len(jsonResponse.Actions) == 0 {
|
if len(jsonResponse.Actions) == 0 {
|
||||||
|
// If there's a valid thought but no actions, this might be an informational response
|
||||||
|
// rather than an actionable UI task. Return the result with empty tool calls.
|
||||||
|
if jsonResponse.Thought != "" {
|
||||||
|
return &PlanningResult{
|
||||||
|
ToolCalls: []schema.ToolCall{}, // Empty tool calls for informational responses
|
||||||
|
Thought: jsonResponse.Thought,
|
||||||
|
Content: content, // Include the full response content
|
||||||
|
ModelName: string(p.modelType),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
return nil, errors.New("no actions returned from VLM")
|
return nil, errors.New("no actions returned from VLM")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user