# MCA SDK Go - Examples

This file provides comprehensive usage examples for all features in v0.8.8.

## Table of Contents

1. [Basic Usage](#basic-usage)
2. [GenAI LLM Tracking](#genai-llm-tracking)
3. [Input/Output Data Capture](#inputoutput-data-capture)
4. [Registry Integration](#registry-integration)
5. [Recovery Tracking](#recovery-tracking)
6. [Singleton Pattern](#singleton-pattern)
7. [Serialization Utilities](#serialization-utilities)
8. [Agentic AI Tracking](#agentic-ai-tracking)
9. [Error Handling](#error-handling)
10. [Complete Application Example](#complete-application-example)

## Basic Usage

```go
package main

import (
    "context"
    "log"
    "time"

    "gitlab.com/bhsf/ai_ml/model-monitoring/sdk/mca-prototype/mca-sdk-go"
    "gitlab.com/bhsf/ai_ml/model-monitoring/sdk/mca-prototype/mca-sdk-go/config"
)

func main() {
    // Create configuration
    cfg := &config.Config{
        ServiceName: "readmission-model",
        ModelID:     "mdl-001",
        TeamName:    "clinical-ai",
    }

    // Initialize client with timeout
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    client, err := mca.NewClient(ctx, cfg)
    if err != nil {
        log.Fatalf("Failed to create client: %v", err)
    }
    defer client.Shutdown()

    // Record a prediction
    latency := 0.15
    pred := &mca.Prediction{
        Latency:      &latency,
        PredictionID: "pred-123",
        Attributes: map[string]string{
            "patient_age": "65",
            "diagnosis":   "pneumonia",
        },
    }

    if err := client.RecordPrediction(ctx, pred); err != nil {
        log.Printf("Failed to record prediction: %v", err)
    }
}
```

## GenAI LLM Tracking

### Track LLM Completion

```go
package main

import (
    "context"
    "log"
    "time"

    "gitlab.com/bhsf/ai_ml/model-monitoring/sdk/mca-prototype/mca-sdk-go"
)

func trackLLMCall(client *mca.Client, ctx context.Context) {
    // Prepare request info
    request := &mca.LLMRequest{
        Model:       "gpt-4",
        Provider:    "openai",
        Prompt:      "Summarize patient history...",  // Ensure PHI is sanitized!
        MaxTokens:   1000,
        Temperature: 0.7,
        Metadata: map[string]interface{}{
            "use_case": "clinical_summary",
        },
    }

    // Make LLM API call (your code)
    start := time.Now()
    // ... call OpenAI API ...
    latency := time.Since(start)

    // Prepare response info
    cost := 0.03  // From API response
    response := &mca.LLMResponse{
        Model:            "gpt-4",
        PromptTokens:     150,
        CompletionTokens: 850,
        TotalTokens:      1000,
        Cost:             &cost,
        Latency:          latency,
        Status:           "success",
        Completion:       "Summary text...",  // Ensure PHI is sanitized!
    }

    // Track the LLM call
    if err := client.TrackLLMCompletion(ctx, request, response); err != nil {
        log.Printf("Failed to track LLM completion: %v", err)
    }
}
```

### Track Embedding Generation

```go
func trackEmbedding(client *mca.Client, ctx context.Context) {
    request := &mca.EmbeddingRequest{
        Model:    "text-embedding-ada-002",
        Provider: "openai",
        Input:    "Patient symptoms include...",  // Ensure PHI is sanitized!
    }

    start := time.Now()
    // ... call embedding API ...
    latency := time.Since(start)

    cost := 0.0001
    response := &mca.EmbeddingResponse{
        Model:       "text-embedding-ada-002",
        InputTokens: 50,
        Dimensions:  1536,
        Cost:        &cost,
        Latency:     latency,
        Status:      "success",
    }

    if err := client.TrackLLMEmbedding(ctx, request, response); err != nil {
        log.Printf("Failed to track embedding: %v", err)
    }
}
```

## Input/Output Data Capture

### Enable Data Capture

```go
cfg := &config.Config{
    ServiceName:       "fraud-detection",
    ModelID:           "mdl-002",
    TeamName:          "security",
    CaptureInputData:  true,   // Enable input capture
    CaptureOutputData: true,   // Enable output capture
}
```

### Capture Prediction Data

```go
// IMPORTANT: Sanitize all PHI/PII before capturing!
func recordPredictionWithData(client *mca.Client, ctx context.Context) {
    latency := 0.25

    pred := &mca.Prediction{
        Latency:      &latency,
        PredictionID: "pred-456",
        
        // Input features (sanitized)
        InputData: map[string]interface{}{
            "transaction_amount":  150.00,
            "merchant_category":   "retail",
            "time_of_day":         "evening",
            "location_distance_km": 5.2,
        },

        // Model output
        OutputData: map[string]interface{}{
            "fraud_score":  0.15,
            "risk_level":   "low",
            "confidence":   0.92,
        },
    }

    if err := client.RecordPrediction(ctx, pred); err != nil {
        log.Printf("Failed to record prediction: %v", err)
    }
}
```

## Registry Integration

### Configure Registry

```go
cfg := &config.Config{
    ServiceName:  "my-model",
    ModelID:      "mdl-003",
    TeamName:     "ml-platform",
    
    // Registry configuration
    RegistryURL:         "https://registry.example.com",
    RegistryToken:       os.Getenv("REGISTRY_TOKEN"),
    RefreshIntervalSecs: 600,  // Refresh every 10 minutes
    PreferRegistry:      true,  // Prefer registry config
}

client, err := mca.NewClient(ctx, cfg)
// Registry configuration automatically fetched and merged
```

### Access Registry Thresholds

```go
func checkThresholds(client *mca.Client, latency float64) {
    thresholds := client.Thresholds()

    // Check latency threshold
    if maxLatency, ok := thresholds["latency_ms"].(float64); ok {
        if latency*1000 > maxLatency {
            log.Printf("WARNING: Latency %.2fms exceeds threshold %.2fms",
                latency*1000, maxLatency)
        }
    }

    // Check accuracy threshold
    if minAccuracy, ok := thresholds["accuracy_min"].(float64); ok {
        // Use threshold in evaluation
        log.Printf("Minimum accuracy threshold: %.2f", minAccuracy)
    }
}
```

## Recovery Tracking

### Track Retry Success

```go
func handlePredictionWithRetry(client *mca.Client, ctx context.Context, data Data) error {
    var lastErr error
    var totalDuration time.Duration
    maxAttempts := 3

    start := time.Now()

    for attempt := 1; attempt <= maxAttempts; attempt++ {
        err := makePrediction(data)
        if err == nil {
            // Success!
            if attempt > 1 {
                // Track recovery if it took multiple attempts
                totalDuration = time.Since(start)
                client.RecordRecovery(ctx, "prediction_error", attempt, totalDuration)
            }
            return nil
        }
        lastErr = err
        time.Sleep(time.Second * time.Duration(attempt))  // Exponential backoff
    }

    return lastErr
}
```

## Singleton Pattern

### Main Package Setup

```go
package main

import (
    "context"
    "log"

    "gitlab.com/bhsf/ai_ml/model-monitoring/sdk/mca-prototype/mca-sdk-go"
    "gitlab.com/bhsf/ai_ml/model-monitoring/sdk/mca-prototype/mca-sdk-go/config"
)

func main() {
    cfg := &config.Config{
        ServiceName: "my-model",
        ModelID:     "mdl-001",
        TeamName:    "ml-team",
    }

    // Initialize singleton instance
    ctx := context.Background()
    client, err := mca.NewClient(ctx, cfg)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Shutdown()

    // Use client in other packages via GetInstance()
    processRequests()
}
```

### Other Package Usage

```go
package processor

import (
    "context"
    "log"

    "gitlab.com/bhsf/ai_ml/model-monitoring/sdk/mca-prototype/mca-sdk-go"
)

func processRequest(ctx context.Context, req Request) error {
    // Access singleton instance
    client, err := mca.GetInstance()
    if err != nil {
        return err
    }

    // Use client
    pred := &mca.Prediction{
        PredictionID: req.ID,
    }

    return client.RecordPrediction(ctx, pred)
}
```

## Serialization Utilities

### Convert Struct to Attributes

```go
type PatientData struct {
    Age    int
    Gender string
    Vitals struct {
        HeartRate      int
        BloodPressure  string
        Temperature    float64
    }
}

func serializePatientData() {
    patient := PatientData{
        Age:    65,
        Gender: "M",
    }
    patient.Vitals.HeartRate = 72
    patient.Vitals.BloodPressure = "120/80"
    patient.Vitals.Temperature = 98.6

    // Convert to flat attribute map
    attrs, err := mca.StructToAttributes(patient)
    if err != nil {
        log.Fatal(err)
    }

    // attrs = {
    //     "Age": 65,
    //     "Gender": "M",
    //     "Vitals.HeartRate": 72,
    //     "Vitals.BloodPressure": "120/80",
    //     "Vitals.Temperature": 98.6,
    // }
}
```

### Serialize Large Data

```go
func serializeLargePayload(data interface{}) {
    // Serialize with 1MB limit (OpenTelemetry max)
    serialized, err := mca.SerializeForTelemetry(data, mca.MaxSerializedLength)
    if err != nil {
        log.Printf("Serialization error: %v", err)
        return
    }

    // serialized is truncated if > 1MB
    log.Printf("Serialized length: %d bytes", len(serialized))
}
```

## Agentic AI Tracking

### Track Agent Goal

```go
func executeAgentGoal(client *mca.Client, ctx context.Context) {
    // Start goal tracking
    goalID, err := client.RecordGoalStarted(
        ctx,
        "Research treatment options for pneumonia",
        "research",
        map[string]interface{}{
            "priority": "high",
            "patient_id": "12345",  // Ensure sanitized
        },
    )
    if err != nil {
        log.Fatal(err)
    }

    // Execute goal steps...
    // ... agent performs research ...

    // Complete goal
    outcome := "Found 3 treatment protocols"
    err = client.RecordGoalCompleted(
        ctx,
        goalID,
        outcome,
        map[string]interface{}{
            "protocols_found": 3,
        },
    )
}
```

### Track Tool Calls

```go
func trackToolExecution(client *mca.Client, ctx context.Context) {
    err := client.RecordToolCall(
        ctx,
        "search_database",
        0.5,  // latency in seconds
        "success",
        map[string]interface{}{
            "query": "pneumonia treatments",
            "results_count": 10,
        },
    )
    if err != nil {
        log.Printf("Failed to track tool call: %v", err)
    }
}
```

## Error Handling

### Record Errors

```go
func handleModelError(client *mca.Client, ctx context.Context, err error) {
    latency := 0.25
    predictionID := "pred-789"

    // Record the error with context
    client.RecordError(
        ctx,
        err,
        &latency,
        predictionID,
        map[string]interface{}{
            "error_type": "model_failure",
            "model_version": "2.0",
        },
    )
}
```

## Complete Application Example

```go
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "gitlab.com/bhsf/ai_ml/model-monitoring/sdk/mca-prototype/mca-sdk-go"
    "gitlab.com/bhsf/ai_ml/model-monitoring/sdk/mca-prototype/mca-sdk-go/config"
)

func main() {
    // Configure SDK
    cfg := &config.Config{
        ServiceName:       "fraud-detection-v2",
        ModelID:           "mdl-fraud-001",
        TeamName:          "security-ml",
        ModelVersion:      "2.1.0",
        ModelType:         "classification",
        ModelCategory:     "internal",
        CollectorEndpoint: "http://otel-collector:4318",
        CaptureInputData:  true,
        CaptureOutputData: true,
        RegistryURL:       "https://registry.example.com",
        RegistryToken:     getEnvOrPanic("REGISTRY_TOKEN"),
    }

    // Initialize client
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    client, err := mca.NewClient(ctx, cfg)
    if err != nil {
        log.Fatalf("Failed to initialize MCA client: %v", err)
    }
    defer client.Shutdown()

    // Process transactions
    transactions := loadTransactions()
    for _, tx := range transactions {
        if err := procesTransaction(client, tx); err != nil {
            log.Printf("Error processing transaction %s: %v", tx.ID, err)
        }
    }

    // Flush telemetry before exit
    if err := client.Flush(ctx); err != nil {
        log.Printf("Flush error: %v", err)
    }
}

func processTransaction(client *mca.Client, tx Transaction) error {
    ctx := context.Background()
    start := time.Now()

    // Make prediction with retry
    var prediction float64
    var err error
    maxAttempts := 3

    for attempt := 1; attempt <= maxAttempts; attempt++ {
        prediction, err = predictFraud(tx)
        if err == nil {
            if attempt > 1 {
                // Track recovery
                client.RecordRecovery(ctx, "api_timeout", attempt, time.Since(start))
            }
            break
        }
        time.Sleep(time.Second * time.Duration(attempt))
    }

    if err != nil {
        // Record error
        latency := time.Since(start).Seconds()
        client.RecordError(ctx, err, &latency, tx.ID, map[string]interface{}{
            "transaction_id": tx.ID,
        })
        return err
    }

    // Record prediction with input/output
    latency := time.Since(start).Seconds()
    pred := &mca.Prediction{
        Latency:      &latency,
        PredictionID: tx.ID,
        InputData: map[string]interface{}{
            "amount":     tx.Amount,
            "merchant":   tx.Merchant,
            "location":   tx.Location,
        },
        OutputData: map[string]interface{}{
            "fraud_score": prediction,
            "risk_level":  getRiskLevel(prediction),
        },
    }

    return client.RecordPrediction(ctx, pred)
}

func getRiskLevel(score float64) string {
    if score > 0.8 {
        return "high"
    } else if score > 0.5 {
        return "medium"
    }
    return "low"
}

func getEnvOrPanic(key string) string {
    value := os.Getenv(key)
    if value == "" {
        panic(fmt.Sprintf("Environment variable %s is required", key))
    }
    return value
}

// Placeholder types
type Transaction struct {
    ID       string
    Amount   float64
    Merchant string
    Location string
}

func loadTransactions() []Transaction { return nil }
func predictFraud(tx Transaction) (float64, error) { return 0.15, nil }
```

## Best Practices

1. **Always sanitize PHI/PII** before passing data to SDK methods
2. **Use defer client.Shutdown()** immediately after creating the client
3. **Enable input/output capture** only when monitoring drift
4. **Set reasonable timeouts** on context objects
5. **Handle errors** from all SDK methods
6. **Use singleton pattern** to avoid passing client everywhere
7. **Check thresholds** from registry for dynamic alerting
8. **Track GenAI costs** using the Cost field in responses
9. **Record recoveries** to monitor retry patterns
10. **Test credential sanitization** with real patterns

For more information, see [README.md](./README.md) and [MIGRATION_1.24_TO_1.25.md](./MIGRATION_1.24_TO_1.25.md).
