Skip to content

Architecture

OmniObserve follows a modular architecture with clear separation between interfaces and implementations.

Package Structure

omniobserve/
├── omniobserve.go       # Main package with re-exports
├── omniobserve/         # Unified entry point with HTTP middleware
├── llmops/              # LLM observability interfaces
│   ├── llmops.go        # Core interfaces (Provider, Tracer, Evaluator, etc.)
│   ├── trace.go         # Trace and Span interfaces
│   ├── types.go         # Data types (EvalInput, Dataset, Prompt, etc.)
│   ├── options.go       # Functional options
│   ├── provider.go      # Provider registration system
│   ├── errors.go        # Error definitions
│   ├── metrics/         # Evaluation metrics (hallucination, relevance, etc.)
│   ├── langfuse/        # Langfuse provider adapter
│   └── slog/            # slog provider adapter
├── observops/           # App observability interfaces
│   ├── observops.go     # Core interfaces (Provider, Meter, Tracer)
│   ├── options.go       # Functional options
│   ├── otlp/            # OTLP provider (vendor-agnostic)
│   ├── datadog/         # Datadog provider
│   ├── newrelic/        # New Relic provider
│   └── dynatrace/       # Dynatrace provider
├── agentops/            # Agent operations monitoring
│   ├── ent/             # Ent ORM schemas
│   ├── middleware/      # HTTP middleware
│   └── postgres/        # PostgreSQL storage
├── semconv/             # OpenTelemetry semantic conventions
│   ├── agent/           # Agentic AI conventions (gen_ai.agent.*)
│   └── journey/         # User journey conventions (session.*, page.*, ui.*)
├── specs/               # Observability specifications
│   ├── classes/         # Class-based SLO management
│   ├── red/             # RED metrics (Rate, Errors, Duration)
│   ├── use/             # USE metrics (Utilization, Saturation, Errors)
│   ├── golden/          # 4 Golden Signals mapping
│   ├── openslo/         # OpenSLO SLI/SLO definitions
│   ├── otel/            # OTel semantic conventions
│   ├── recorder/        # Integration with observops.Provider
│   └── schema/          # JSON Schema generation
├── sloghandler/         # slog.Handler implementations
│   ├── dual.go          # Local + remote handler
│   ├── fanout.go        # Multi-handler fanout
│   └── trace.go         # Trace context injection
├── integrations/        # Integrations with external libraries
│   ├── omnillm/         # OmniLLM observability hook
│   ├── sevaluation/     # Structured evaluation integration
│   └── observability/   # HTTP client/server middleware helpers
├── mlops/               # ML operations interfaces (experiments, model registry)
├── examples/            # Usage examples
│   └── evaluation/      # Metrics evaluation example
├── sdk/                 # Provider-specific SDKs
│   └── langfuse/        # Langfuse Go SDK
└── typescript/          # TypeScript SDK
    └── packages/core/   # @omniobserve/core - mirrors Go types and semconv

Provider Adapters

Provider adapters are distributed in two ways:

Embedded Adapters

Adapters included in omniobserve:

  • llmops/langfuse - Langfuse adapter
  • llmops/slog - slog adapter for local development

Standalone SDK Adapters

Adapters in their own repositories:

  • github.com/agentplexus/go-opik/llmops - Opik adapter
  • github.com/agentplexus/go-phoenix/llmops - Phoenix adapter

Core Interfaces

Provider Interface

The main interface that all observability backends implement:

type Provider interface {
    Tracer            // Trace/span operations
    Evaluator         // Evaluation and feedback
    PromptManager     // Prompt template management
    DatasetManager    // Test dataset management
    ProjectManager    // Project/workspace management
    AnnotationManager // Span/trace annotations
    io.Closer

    Name() string
}

Tracer Interface

type Tracer interface {
    StartTrace(ctx context.Context, name string, opts ...TraceOption) (context.Context, Trace, error)
    StartSpan(ctx context.Context, name string, opts ...SpanOption) (context.Context, Span, error)
    TraceFromContext(ctx context.Context) (Trace, bool)
    SpanFromContext(ctx context.Context) (Span, bool)
    Flush(ctx context.Context) error
}

Evaluator Interface

type Evaluator interface {
    AddFeedbackScore(ctx context.Context, traceID, spanID, name string, score float64, opts ...FeedbackOption) error
}

Provider Registration

Providers register themselves using the database/sql style pattern:

// In provider package init()
func init() {
    llmops.Register("opik", &opikDriver{})
}

// In application code
import _ "github.com/agentplexus/go-opik/llmops"

provider, _ := llmops.Open("opik", llmops.WithAPIKey("..."))

Context Propagation

Traces and spans are propagated via context.Context:

// Trace stored in context
ctx, trace, _ := provider.StartTrace(ctx, "workflow")

// Spans automatically link to trace from context
ctx, span, _ := provider.StartSpan(ctx, "step")

// Nested spans link to parent span
ctx, childSpan, _ := provider.StartSpan(ctx, "nested")

Functional Options

Configuration uses the functional options pattern:

// Client options
provider, _ := llmops.Open("opik",
    llmops.WithAPIKey("..."),
    llmops.WithProjectName("my-project"),
    llmops.WithTimeout(30 * time.Second),
)

// Trace options
ctx, trace, _ := provider.StartTrace(ctx, "workflow",
    llmops.WithTraceInput(input),
    llmops.WithTraceMetadata(metadata),
)

// Span options
ctx, span, _ := provider.StartSpan(ctx, "llm-call",
    llmops.WithSpanType(llmops.SpanTypeLLM),
    llmops.WithModel("gpt-4"),
)

Error Handling

The library provides typed errors for common conditions:

var (
    ErrMissingAPIKey  = errors.New("missing API key")
    ErrProviderNotFound = errors.New("provider not found")
    ErrTraceNotFound  = errors.New("trace not found")
)

// Error checking
if errors.Is(err, llmops.ErrMissingAPIKey) {
    // Handle missing API key
}

if llmops.IsNotFound(err) {
    // Handle not found
}

if llmops.IsRateLimited(err) {
    // Handle rate limiting
}

Direct SDK Access

For provider-specific features, use the underlying SDKs directly:

import "github.com/plexusone/omniobserve/sdk/langfuse"  // Langfuse SDK
import "github.com/agentplexus/go-opik"                   // Opik SDK
import "github.com/agentplexus/go-phoenix"                // Phoenix SDK