Documentation
¶
Overview ¶
Package result provides utilities for extracting and parsing JSON responses from AI models.
This package simplifies the process of handling AI model responses that often contain JSON data wrapped in markdown code blocks or other formatting. It provides type-safe extraction and unmarshaling of JSON content from text responses.
Overview ¶
The result package offers the following key features:
- Automatic extraction of JSON from markdown code blocks
- Support for multiple markdown formats (```json, ```, inline)
- Type-safe generic unmarshaling
- Graceful handling of malformed responses
- Thread-safe operations
Basic Usage ¶
When working with AI model responses, you often receive JSON wrapped in markdown:
response := `Here is the data you requested:
```json
{
"name": "example",
"value": 42,
"active": true
}
```
`
// Extract just the JSON content
jsonStr := result.ExtractJSON(response)
fmt.Println(jsonStr) // {"name": "example", "value": 42, "active": true}
// Or extract and unmarshal in one step
type Data struct {
Name string `json:"name"`
Value int `json:"value"`
Active bool `json:"active"`
}
data, err := result.Extract[Data](response)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", data) // {Name:example Value:42 Active:true}
JSON Extraction ¶
The ExtractJSON function handles various response formats:
1. Standard markdown code blocks:
```json
{"key": "value"}
```
2. Generic code blocks:
```
{"key": "value"}
```
3. Inline JSON (no markdown):
{"key": "value"}
4. JSON with surrounding text:
Here's your response:
```json
{"status": "success"}
```
Additional notes here.
Type-Safe Extraction ¶
The generic Extract function combines JSON extraction with unmarshaling:
// Define your response structure
type FixResponse struct {
Files []struct {
Filename string `json:"filename"`
Content string `json:"content"`
} `json:"files"`
Description string `json:"description"`
}
// Extract from AI response
response := `I'll fix those files for you:
```json
{
"files": [
{
"filename": "main.go",
"content": "package main\n\nfunc main() {\n\t// Fixed\n}"
}
],
"description": "Fixed the syntax error"
}
```
`
fix, err := result.Extract[FixResponse](response)
if err != nil {
return err
}
for _, file := range fix.Files {
fmt.Printf("Update %s\n", file.Filename)
}
Error Handling ¶
The package handles various error conditions gracefully:
// Empty JSON block
response := "```json\n```"
data := result.ExtractJSON(response) // Returns ""
// Malformed JSON
response = "```json\n{invalid json}\n```"
var obj map[string]interface{}
_, err := result.Extract[map[string]interface{}](response)
// err will be a json.UnmarshalError
// No JSON found - returns trimmed input
response = "Just plain text"
data = result.ExtractJSON(response) // Returns "Just plain text"
Thread Safety ¶
All functions in this package are thread-safe. They operate on immutable input strings and don't maintain any shared state, making them safe for concurrent use.
Integration with AI Services ¶
This package is designed to work with responses from various AI services:
Claude example:
response, _ := claude.Complete(prompt) analysis, err := result.Extract[AnalysisResult](response.Content)
Google Gemini example:
response, _ := gemini.Generate(prompt) summary, err := result.Extract[Summary](response.Text)
OpenAI example:
response, _ := openai.CreateCompletion(prompt) data, err := result.Extract[ResponseData](response.Choices[0].Text)
Performance Considerations ¶
The package is optimized for typical AI response sizes:
- Efficient string processing without regex
- Single pass extraction for markdown blocks
- Minimal memory allocations
- No external dependencies beyond standard library
Common Patterns ¶
Working with structured AI responses:
// Command AI to return specific JSON structure
prompt := `Analyze this code and return JSON in this format:
{
"issues": [{"line": number, "message": "string", "severity": "string"}],
"summary": "string"
}`
response := callAI(prompt)
type CodeAnalysis struct {
Issues []struct {
Line int `json:"line"`
Message string `json:"message"`
Severity string `json:"severity"`
} `json:"issues"`
Summary string `json:"summary"`
}
analysis, err := result.Extract[CodeAnalysis](response)
if err != nil {
// Handle extraction or parsing error
return fmt.Errorf("failed to parse AI response: %w", err)
}
// Use the structured data
for _, issue := range analysis.Issues {
fmt.Printf("Line %d: %s (%s)\n", issue.Line, issue.Message, issue.Severity)
}
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Extract ¶
Extract extracts JSON content from a text response and unmarshals it into the provided type. It combines ExtractJSON with json.Unmarshal for convenience.
Example ¶
ExampleExtract demonstrates type-safe extraction and unmarshaling.
package main
import (
"fmt"
"log"
"chainguard.dev/driftlessaf/agents/result"
)
func main() {
// AI response with structured data
response := `I've analyzed your request. Here are the results:
` + "```json" + `
{
"analysis": {
"sentiment": "positive",
"confidence": 0.95,
"keywords": ["golang", "example", "documentation"]
},
"metadata": {
"model": "gpt-4",
"timestamp": "2024-01-15T10:30:00Z"
}
}
` + "```" + `
This analysis is based on the provided context.`
// Define the expected structure
type Response struct {
Analysis struct {
Sentiment string `json:"sentiment"`
Confidence float64 `json:"confidence"`
Keywords []string `json:"keywords"`
} `json:"analysis"`
Metadata struct {
Model string `json:"model"`
Timestamp string `json:"timestamp"`
} `json:"metadata"`
}
// Extract and unmarshal
data, err := result.Extract[Response](response)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Sentiment: %s (%.0f%% confidence)\n",
data.Analysis.Sentiment,
data.Analysis.Confidence*100)
fmt.Printf("Keywords: %v\n", data.Analysis.Keywords)
}
Output: Sentiment: positive (95% confidence) Keywords: [golang example documentation]
Example (ArrayResponse) ¶
ExampleExtract_arrayResponse demonstrates extracting array responses.
package main
import (
"fmt"
"log"
"chainguard.dev/driftlessaf/agents/result"
)
func main() {
// AI response with array of items
response := `Here are the search results:
` + "```json" + `
[
{"id": 1, "title": "Introduction to Go", "score": 0.98},
{"id": 2, "title": "Advanced Go Patterns", "score": 0.85},
{"id": 3, "title": "Go Best Practices", "score": 0.79}
]
` + "```" + `
`
type SearchResult struct {
ID int `json:"id"`
Title string `json:"title"`
Score float64 `json:"score"`
}
results, err := result.Extract[[]SearchResult](response)
if err != nil {
log.Fatal(err)
}
for _, r := range results {
fmt.Printf("%d. %s (score: %.2f)\n", r.ID, r.Title, r.Score)
}
}
Output: 1. Introduction to Go (score: 0.98) 2. Advanced Go Patterns (score: 0.85) 3. Go Best Practices (score: 0.79)
Example (ComplexStructure) ¶
ExampleExtract_complexStructure demonstrates extracting deeply nested structures.
package main
import (
"fmt"
"log"
"chainguard.dev/driftlessaf/agents/result"
)
func main() {
// AI response with complex nested structure
response := `Analysis complete:
` + "```json" + `
{
"project": {
"name": "awesome-app",
"language": "go",
"dependencies": {
"direct": [
{"name": "github.com/gorilla/mux", "version": "v1.8.0"},
{"name": "github.com/stretchr/testify", "version": "v1.8.4"}
],
"indirect": 15
},
"metrics": {
"lines_of_code": 5420,
"test_coverage": 87.3,
"complexity": {
"average": 3.2,
"max": 12
}
}
}
}
` + "```" + `
`
type ProjectAnalysis struct {
Project struct {
Name string `json:"name"`
Language string `json:"language"`
Dependencies struct {
Direct []struct {
Name string `json:"name"`
Version string `json:"version"`
} `json:"direct"`
Indirect int `json:"indirect"`
} `json:"dependencies"`
Metrics struct {
LinesOfCode int `json:"lines_of_code"`
TestCoverage float64 `json:"test_coverage"`
Complexity struct {
Average float64 `json:"average"`
Max int `json:"max"`
} `json:"complexity"`
} `json:"metrics"`
} `json:"project"`
}
analysis, err := result.Extract[ProjectAnalysis](response)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Project: %s (%s)\n", analysis.Project.Name, analysis.Project.Language)
fmt.Printf("Test Coverage: %.1f%%\n", analysis.Project.Metrics.TestCoverage)
fmt.Printf("Direct Dependencies: %d\n", len(analysis.Project.Dependencies.Direct))
}
Output: Project: awesome-app (go) Test Coverage: 87.3% Direct Dependencies: 2
Example (ErrorHandling) ¶
ExampleExtract_errorHandling demonstrates error handling during extraction.
package main
import (
"fmt"
"chainguard.dev/driftlessaf/agents/result"
)
func main() {
// Response with invalid JSON
response := `Here's the data:
` + "```json" + `
{
"status": "error",
"message": "Missing closing brace"
` + "```" + `
`
type Status struct {
Status string `json:"status"`
Message string `json:"message"`
}
_, err := result.Extract[Status](response)
if err != nil {
fmt.Printf("Error: JSON parsing failed\n")
}
}
Output: Error: JSON parsing failed
Example (FileOperations) ¶
ExampleExtract_fileOperations demonstrates extracting file modification instructions.
package main
import (
"fmt"
"log"
"chainguard.dev/driftlessaf/agents/result"
)
func main() {
// AI response with file modifications
response := `I'll fix the issue in your code:
` + "```json" + `
{
"files": [
{
"filename": "main.go",
"action": "modify",
"content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Hello, World!\")\n}"
},
{
"filename": "go.mod",
"action": "create",
"content": "module example\n\ngo 1.21"
}
],
"summary": "Added missing import and created go.mod file"
}
` + "```" + `
These changes should resolve the compilation error.`
type FileOperation struct {
Files []struct {
Filename string `json:"filename"`
Action string `json:"action"`
Content string `json:"content"`
} `json:"files"`
Summary string `json:"summary"`
}
ops, err := result.Extract[FileOperation](response)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Summary: %s\n", ops.Summary)
for _, file := range ops.Files {
fmt.Printf("- %s file: %s\n", file.Action, file.Filename)
}
}
Output: Summary: Added missing import and created go.mod file - modify file: main.go - create file: go.mod
func ExtractJSON ¶
ExtractJSON extracts JSON content from a text response that may contain markdown code blocks. It looks for content between ```json and ``` markers, or returns the input trimmed if no markers are found.
Example ¶
ExampleExtractJSON demonstrates extracting JSON from various markdown formats.
package main
import (
"fmt"
"chainguard.dev/driftlessaf/agents/result"
)
func main() {
// Standard markdown code block
response := `Here is the requested data:
` + "```json" + `
{
"status": "success",
"count": 42
}
` + "```" + `
Additional information follows.`
json := result.ExtractJSON(response)
fmt.Println(json)
}
Output: { "status": "success", "count": 42 }
Example (EmptyBlock) ¶
ExampleExtractJSON_emptyBlock demonstrates handling of empty JSON blocks.
package main
import (
"fmt"
"chainguard.dev/driftlessaf/agents/result"
)
func main() {
// Empty JSON block
response := `The operation failed.
` + "```json" + `
` + "```" + `
No data available.`
json := result.ExtractJSON(response)
fmt.Printf("Result: %q\n", json)
}
Output: Result: ""
Example (MultipleBlocks) ¶
ExampleExtractJSON_multipleBlocks demonstrates behavior with multiple JSON blocks.
package main
import (
"fmt"
"chainguard.dev/driftlessaf/agents/result"
)
func main() {
// Response with multiple JSON blocks (only first is extracted)
response := `First dataset:
` + "```json" + `
{"dataset": 1, "value": "first"}
` + "```" + `
Second dataset:
` + "```json" + `
{"dataset": 2, "value": "second"}
` + "```" + `
`
// ExtractJSON returns the first JSON block found
json := result.ExtractJSON(response)
fmt.Println(json)
}
Output: {"dataset": 1, "value": "first"}
Example (PlainJSON) ¶
ExampleExtractJSON_plainJSON demonstrates extraction when no markdown is present.
package main
import (
"fmt"
"chainguard.dev/driftlessaf/agents/result"
)
func main() {
// Plain JSON without markdown
response := `{"message": "Hello, World!", "timestamp": 1234567890}`
json := result.ExtractJSON(response)
fmt.Println(json)
}
Output: {"message": "Hello, World!", "timestamp": 1234567890}
Types ¶
This section is empty.