Orca ORCA v0.174.2

maestro

Maestro Gopher

Maestro is a Go library that provides a shared implementation for tracking usage metrics and sending them to Metronome for billing purposes. It includes HTTP middleware for automatic JWT extraction, context management, and balance checking.

Installation

go get github.com/corticph/gocomo/maestro

Quick Start

1. Initialize the Service

import "github.com/corticph/gocomo/maestro"

service, err := maestro.NewService(
    ctx,
    "https://api.metronome.com",  // Metronome base URL
    "your-metronome-token",       // Bearer token
    "production",                 // Environment type
    nil,                          // Logger (nil uses slog.Default())
    // Optional: override default cache TTLs
    maestro.WithBalanceCacheTTL(30 * time.Second),  // default 5m
    maestro.WithRateCacheTTL(10 * time.Minute),     // default 5min
    // Optional: on a balance/commit cache miss, allow the request and fetch in the background
    // instead of waiting for Metronome. Default true (the first request per customer is not enforced);
    // pass false to wait for Metronome on a cache miss.
    maestro.WithSkipBalanceCheckOnCacheMiss(false),
)
if err != nil {
    // Service is still valid but operates in degraded mode.
    // All metering methods no-op and balance checks fail open.
    log.Printf("Maestro running in degraded mode: %v", err)
}

On success the service pre-loads the Metronome rate card so that MeterTokens and MeterMinutes can calculate credit costs. If rate loading fails the service is returned in degraded mode — metering calls become no-ops and CheckBalance always allows the request. You can check this state with service.Degraded().

2. Add Billing Context Middleware

mux := http.NewServeMux()
mux.HandleFunc("/api/transcribe", transcribeHandler)

// Wrap your handler with billing context middleware
handler := service.BillingContextMiddleware(mux)
http.ListenAndServe(":8080", handler)

The middleware will:

  • Extract JWT from Authorization: Bearer <token> header
  • Parse the token and extract claims (without signature verification)
  • Extract request metadata (Origin header, Route, RequestMethod)
  • Store billing context in the request context
  • Continue processing even if token is missing or invalid

3. Add Balance Check Middleware (Optional)

IMPORTANT: The BalanceCheckMiddleware must come AFTER BillingContextMiddleware in your middleware chain, as it depends on billing context being in the request context.

// Chain middlewares in correct order
handler := service.BillingContextMiddleware(
    service.BalanceCheckMiddleware()(mux),
)
http.ListenAndServe(":8080", handler)

The balance check middleware will:

  • Check the customer's Metronome balance before allowing the request
  • Return HTTP 402 Payment Required if balance <= 0
  • Fail open (allow request) if:
    • No billing context or customer ID found
    • Metronome API call fails
  • Cache balance results for the specified duration to reduce Metronome API calls

4. Use Metering Methods

All metering methods use CommonEventAttributes which contains common fields from the JWT context and request-specific fields.

MeterSuccessRequest

Track successful API requests (2xx responses). Should be called in middleware after request completion.

err := service.MeterSuccessRequest(ctx, maestro.CommonEventAttributes{
    RequestID:           "req-123",
    MetronomeCustomerID: "customer-456",  // Required (auto-populated from JWT if using middleware)
})

MeterTokens

Track LLM token usage. Call after receiving LLM response. Returns UsageInfo with the calculated credit cost.

usageInfo, err := service.MeterTokens(ctx, maestro.TokenEventAttributes{
    InputTokens:  150,
    OutputTokens: 300,
    ModelName:    "gpt-4o",       // Defaults to "default" if empty
    TemplateKey:  "chat-template",  // Optional
    LanguageUsed: "en",             // Optional
    AgentUsed:    "support-agent",  // Optional
    CommonEventAttributes: maestro.CommonEventAttributes{
        RequestID:           "req-123",
        MetronomeCustomerID: "customer-456",  // Required (auto-populated from JWT if using middleware)
    },
})
// usageInfo.CreditsConsumed contains the calculated cost

MeterMinutes

Track transcription usage in minutes. Call after transcription completion. Returns UsageInfo with the calculated credit cost.

usageInfo, err := service.MeterMinutes(ctx, maestro.TranscribeEventAttributes{
    Minutes:      2.5,
    ModelName:    "whisper-1",      // Defaults to "default" if empty
    TemplateKey:  "meeting-notes",  // Optional
    LanguageUsed: "en-US",          // Optional
    LanguageTier: "premium",        // Optional: defaults to "default" if empty
    CommonEventAttributes: maestro.CommonEventAttributes{
        RequestID:           "req-123",
        MetronomeCustomerID: "customer-456",  // Required (auto-populated from JWT if using middleware)
    },
})
// usageInfo.CreditsConsumed contains the calculated cost

Common Attributes

All metering methods accept CommonEventAttributes which can be automatically populated from the billing context (JWT claims + request metadata) if using the BillingContextMiddleware.

CommonEventAttributes Structure

type CommonEventAttributes struct {
    // Billing/User Context (auto-populated from JWT if using middleware)
    MetronomeCustomerID string  // Required: Metronome customer identifier
    ApiClientID         string  // OAuth client ID (from JWT: client_id or azp)
    UserEmail           string  // User's email address (from JWT: email)
    ProjectID           string  // Project identifier (from JWT: project_info.project_id)
    Tenant              string  // Tenant/realm name (extracted from JWT: iss)

    // Request Context (auto-populated from request if using middleware)
    RequestID           string  // Required: unique identifier for the request (must be set explicitly)
    Route               string  // API route (e.g., "/v1/chat/completions")
    RequestMethod       string  // HTTP method (e.g., "POST")
    Origin              string  // HTTP Origin header value

    // Request Context (auto-populated from x-corti-analytics header/query param)
    Analytics           Analytics // Optional: dynamic client analytics metadata
}

Note: CommonEventAttributes is used directly for MeterSuccessRequest, while TokenEventAttributes and TranscribeEventAttributes embed it.

Analytics Metadata

Maestro accepts dynamic client analytics metadata received as a single x-corti-analytics header on HTTP requests, or an equivalent x-corti-analytics query parameter during WebSocket handshakes (since browser WebSocket clients may not set custom headers).

The header/query value is a JSON object. For WebSocket clients the JSON value must be URL-encoded when constructing the URL:

x-corti-analytics: {"sdk_client":"corti-sdk-javascript","sdk_version":"1.2.3","web_component":"dictation","web_component_version":"2.4.0","is_console_ai_studio":true}

Attribution metadata is preserved as a dynamic set of keys and included in the corresponding Metronome event properties:

  • Fields (sdk_client, sdk_version, web_component, web_component_version, is_console_ai_studio) are supported but not hard-coded — any key present is passed through.
  • Partial data is supported; clients do not need to provide every field.
  • Unknown fields are ignored rather than failing the request.
  • Empty or unavailable fields do not need to be sent.

When using BillingContextMiddleware, the parsed analytics metadata is stored in the billing context and automatically included in metering event properties.

Attribute Population Behavior

The metering methods merge attributes from two sources:

  1. JWT Context (auto-populated by middleware) - Base attributes from the authenticated user
  2. Explicit Attributes (provided in method call) - Override context attributes

Priority: Explicit attributes > Context attributes

If not using the middleware, you must provide all common attributes explicitly.

JWT Claims Mapping

The middleware extracts these fields from JWT tokens:

CommonEventAttribute JWT Claim Notes
MetronomeCustomerID metronome_customer_id Required for all metering calls
ApiClientID client_id or azp Falls back to azp if client_id is empty
UserEmail email User's email address
ProjectID project_info.project_id Nested field in JWT
Tenant iss Extracted from issuer URL
Origin (not from JWT) HTTP Origin request header
Route (not from JWT) HTTP request path
RequestMethod (not from JWT) HTTP method
RequestID (not from JWT) Must be provided explicitly

Expected JWT Format

{
  "iss": "https://auth.example.com/realms/myrealm",
  "client_id": "client-123",
  "azp": "authorized-party-456",
  "email": "user@example.com",
  "metronome_customer_id": "customer-789",
  "project_info": {
    "project_id": "project-001"
  }
}

Pricing & Credit Calculation

The service loads a Metronome rate card (named "Usage rate card") at startup and caches pricing by (route, model_name). If a rate is not found in the cache at metering time, it is fetched on demand.

MeterTokens and MeterMinutes return a UsageInfo struct containing CreditsConsumed, calculated from the rate card's conversion factors and prices.

Error Handling

All metering methods return errors that should be logged but typically not block the main request flow:

if err := service.MeterSuccessRequest(ctx, attrs); err != nil {
    // Log the error but continue processing
    log.Printf("Metering failed: %v", err)
}

The service includes built-in structured logging that will also log failures. The metronome package handles retries for transient errors.