```go title="Go" package main import ( "encoding/json" "log" "sync" "unsafe" "github.com/kreuzberg-dev/kreuzberg/packages/go/v5" ) /* #cgo CFLAGS: -I${SRCDIR}/../../../crates/kreuzberg-ffi #cgo LDFLAGS: -L${SRCDIR}/../../../target/release -L${SRCDIR}/../../../target/debug -lkreuzberg_ffi #include "../../../crates/kreuzberg-ffi/kreuzberg.h" #include */ import "C" // PluginState manages thread-safe state for the stateful plugin type PluginState struct { mu sync.Mutex callCount int cache map[string]string lastMimeType string } // globalState holds the plugin's persistent state across calls var globalState = &PluginState{ cache: make(map[string]string), } // statefulPlugin demonstrates a thread-safe plugin with persistent state //export statefulPlugin func statefulPlugin(resultJSON *C.char) *C.char { jsonStr := C.GoString(resultJSON) var result map[string]interface{} if err := json.Unmarshal([]byte(jsonStr), &result); err != nil { return C.CString("{\"error\":\"Failed to parse result JSON\"}") } // Acquire lock to safely modify state globalState.mu.Lock() defer globalState.mu.Unlock() // Increment call counter globalState.callCount++ // Extract and store MIME type if mimeType, ok := result["mime_type"].(string); ok { globalState.lastMimeType = mimeType globalState.cache[mimeType] = "processed" } // Ensure metadata exists metadata, ok := result["metadata"].(map[string]interface{}) if !ok { metadata = make(map[string]interface{}) } // Add state information to metadata metadata["plugin_call_count"] = globalState.callCount metadata["last_mime_type"] = globalState.lastMimeType metadata["cached_types_count"] = len(globalState.cache) metadata["plugin_version"] = "1.0.0" result["metadata"] = metadata // Serialize back to JSON outputJSON, err := json.Marshal(result) if err != nil { return C.CString("{\"error\":\"Failed to serialize result\"}") } return C.CString(string(outputJSON)) } // GetPluginStats safely retrieves the current plugin state for logging func GetPluginStats() (int, string, []string) { globalState.mu.Lock() defer globalState.mu.Unlock() callCount := globalState.callCount lastMime := globalState.lastMimeType mimeTypes := make([]string, 0, len(globalState.cache)) for mimeType := range globalState.cache { mimeTypes = append(mimeTypes, mimeType) } return callCount, lastMime, mimeTypes } // ResetPluginState clears the plugin state - useful for testing func ResetPluginState() { globalState.mu.Lock() defer globalState.mu.Unlock() globalState.callCount = 0 globalState.lastMimeType = "" globalState.cache = make(map[string]string) } func main() { // Register the stateful post-processor with priority 60 if err := kreuzberg.RegisterPostProcessor("stateful_plugin", 60, (C.PostProcessorCallback)(C.statefulPlugin)); err != nil { log.Fatalf("failed to register post-processor: %v", err) } defer func() { if err := kreuzberg.UnregisterPostProcessor("stateful_plugin"); err != nil { log.Printf("warning: failed to unregister post-processor: %v", err) } // Print final statistics callCount, lastMime, mimeTypes := GetPluginStats() log.Printf("Plugin Statistics:") log.Printf(" Total calls: %d", callCount) log.Printf(" Last MIME type: %s", lastMime) log.Printf(" Unique MIME types processed: %d", len(mimeTypes)) if len(mimeTypes) > 0 { log.Printf(" Processed types: %v", mimeTypes) } }() // Process multiple documents to demonstrate state accumulation files := []string{ "document1.pdf", "document2.pdf", "image.png", "document3.txt", } for _, file := range files { log.Printf("Processing: %s", file) result, err := kreuzberg.ExtractFileSync(file, nil) if err != nil { log.Printf(" Warning: extraction failed: %v", err) continue } // Parse and display metadata var metadata map[string]interface{} if metaJSON, ok := result.MetadataJSON.(string); ok { if err := json.Unmarshal([]byte(metaJSON), &metadata); err == nil { if callCount, ok := metadata["plugin_call_count"].(float64); ok { log.Printf(" Plugin call count: %.0f", callCount) } if cachedCount, ok := metadata["cached_types_count"].(float64); ok { log.Printf(" Cached MIME types: %.0f", cachedCount) } } } } // Demonstrate thread-safe state access callCount, lastMime, mimeTypes := GetPluginStats() log.Printf("\nFinal Plugin State:") log.Printf(" Total calls: %d", callCount) log.Printf(" Last MIME type: %s", lastMime) log.Printf(" Processed MIME types: %v", mimeTypes) } ```