first commit
This commit is contained in:
0
plugins/otel/changelog.md
Normal file
0
plugins/otel/changelog.md
Normal file
11
plugins/otel/client.go
Normal file
11
plugins/otel/client.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package otel
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// OtelClient is the interface for the OpenTelemetry client
|
||||
type OtelClient interface {
|
||||
Emit(ctx context.Context, rs []*ResourceSpan) error
|
||||
Close() error
|
||||
}
|
||||
306
plugins/otel/converter.go
Normal file
306
plugins/otel/converter.go
Normal file
@@ -0,0 +1,306 @@
|
||||
package otel
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/maximhq/bifrost/core/schemas"
|
||||
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
resourcepb "go.opentelemetry.io/proto/otlp/resource/v1"
|
||||
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
|
||||
)
|
||||
|
||||
// kvStr creates a key-value pair with a string value
|
||||
func kvStr(k, v string) *KeyValue {
|
||||
return &KeyValue{Key: k, Value: &AnyValue{Value: &StringValue{StringValue: v}}}
|
||||
}
|
||||
|
||||
// kvInt creates a key-value pair with an integer value
|
||||
func kvInt(k string, v int64) *KeyValue {
|
||||
return &KeyValue{Key: k, Value: &AnyValue{Value: &IntValue{IntValue: v}}}
|
||||
}
|
||||
|
||||
// kvDbl creates a key-value pair with a double value
|
||||
func kvDbl(k string, v float64) *KeyValue {
|
||||
return &KeyValue{Key: k, Value: &AnyValue{Value: &DoubleValue{DoubleValue: v}}}
|
||||
}
|
||||
|
||||
// kvBool creates a key-value pair with a boolean value
|
||||
func kvBool(k string, v bool) *KeyValue {
|
||||
return &KeyValue{Key: k, Value: &AnyValue{Value: &BoolValue{BoolValue: v}}}
|
||||
}
|
||||
|
||||
// kvAny creates a key-value pair with an any value
|
||||
func kvAny(k string, v *AnyValue) *KeyValue {
|
||||
return &KeyValue{Key: k, Value: v}
|
||||
}
|
||||
|
||||
// arrValue converts a list of any values to an OpenTelemetry array value
|
||||
func arrValue(vals ...*AnyValue) *AnyValue {
|
||||
return &AnyValue{Value: &ArrayValue{ArrayValue: &ArrayValueValue{Values: vals}}}
|
||||
}
|
||||
|
||||
// listValue converts a list of key-value pairs to an OpenTelemetry list value
|
||||
func listValue(kvs ...*KeyValue) *AnyValue {
|
||||
return &AnyValue{Value: &ListValue{KvlistValue: &KeyValueList{Values: kvs}}}
|
||||
}
|
||||
|
||||
// hexToBytes converts a hex string to bytes, padding/truncating as needed
|
||||
func hexToBytes(hexStr string, length int) []byte {
|
||||
// Remove any non-hex characters
|
||||
cleaned := strings.Map(func(r rune) rune {
|
||||
if (r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F') {
|
||||
return r
|
||||
}
|
||||
return -1
|
||||
}, hexStr)
|
||||
// Ensure even length
|
||||
if len(cleaned)%2 != 0 {
|
||||
cleaned = "0" + cleaned
|
||||
}
|
||||
// Truncate or pad to desired length
|
||||
if len(cleaned) > length*2 {
|
||||
cleaned = cleaned[:length*2]
|
||||
} else if len(cleaned) < length*2 {
|
||||
cleaned = strings.Repeat("0", length*2-len(cleaned)) + cleaned
|
||||
}
|
||||
bytes, _ := hex.DecodeString(cleaned)
|
||||
return bytes
|
||||
}
|
||||
|
||||
// convertTraceToResourceSpan converts a Bifrost trace to OTEL ResourceSpan
|
||||
func (p *OtelPlugin) convertTraceToResourceSpan(trace *schemas.Trace) *ResourceSpan {
|
||||
otelSpans := make([]*Span, 0, len(trace.Spans))
|
||||
for _, span := range trace.Spans {
|
||||
otelSpans = append(otelSpans, p.convertSpanToOTELSpan(trace.TraceID, span))
|
||||
}
|
||||
|
||||
return &ResourceSpan{
|
||||
Resource: &resourcepb.Resource{
|
||||
Attributes: p.getResourceAttributes(),
|
||||
},
|
||||
ScopeSpans: []*ScopeSpan{{
|
||||
Scope: p.getInstrumentationScope(),
|
||||
Spans: otelSpans,
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
// convertSpanToOTELSpan converts a single Bifrost span to OTEL format
|
||||
func (p *OtelPlugin) convertSpanToOTELSpan(traceID string, span *schemas.Span) *Span {
|
||||
otelSpan := &Span{
|
||||
TraceId: hexToBytes(traceID, 16),
|
||||
SpanId: hexToBytes(span.SpanID, 8),
|
||||
Name: span.Name,
|
||||
Kind: convertSpanKind(span.Kind),
|
||||
StartTimeUnixNano: uint64(span.StartTime.UnixNano()),
|
||||
EndTimeUnixNano: uint64(span.EndTime.UnixNano()),
|
||||
Attributes: convertAttributesToKeyValues(span.Attributes),
|
||||
Status: convertSpanStatus(span.Status, span.StatusMsg),
|
||||
Events: convertSpanEvents(span.Events),
|
||||
}
|
||||
|
||||
// Set parent span ID if present
|
||||
if span.ParentID != "" {
|
||||
otelSpan.ParentSpanId = hexToBytes(span.ParentID, 8)
|
||||
}
|
||||
|
||||
return otelSpan
|
||||
}
|
||||
|
||||
// getResourceAttributes returns the resource attributes for the OTEL span
|
||||
func (p *OtelPlugin) getResourceAttributes() []*KeyValue {
|
||||
attrs := []*KeyValue{
|
||||
kvStr("service.name", p.serviceName),
|
||||
kvStr("service.version", p.bifrostVersion),
|
||||
kvStr("telemetry.sdk.name", "bifrost"),
|
||||
kvStr("telemetry.sdk.language", "go"),
|
||||
}
|
||||
// Add environment attributes
|
||||
attrs = append(attrs, p.attributesFromEnvironment...)
|
||||
return attrs
|
||||
}
|
||||
|
||||
// getInstrumentationScope returns the instrumentation scope for OTEL
|
||||
func (p *OtelPlugin) getInstrumentationScope() *commonpb.InstrumentationScope {
|
||||
return &commonpb.InstrumentationScope{
|
||||
Name: p.serviceName,
|
||||
Version: p.bifrostVersion,
|
||||
}
|
||||
}
|
||||
|
||||
// convertAttributesToKeyValues converts map[string]any to OTEL KeyValue slice
|
||||
func convertAttributesToKeyValues(attrs map[string]any) []*KeyValue {
|
||||
if attrs == nil {
|
||||
return nil
|
||||
}
|
||||
kvs := make([]*KeyValue, 0, len(attrs))
|
||||
for k, v := range attrs {
|
||||
kv := anyToKeyValue(k, v)
|
||||
if kv != nil {
|
||||
kvs = append(kvs, kv)
|
||||
}
|
||||
}
|
||||
return kvs
|
||||
}
|
||||
|
||||
// anyToKeyValue converts any Go value to OTEL KeyValue
|
||||
func anyToKeyValue(key string, value any) *KeyValue {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
return kvStr(key, v)
|
||||
case int:
|
||||
return kvInt(key, int64(v))
|
||||
case int32:
|
||||
return kvInt(key, int64(v))
|
||||
case int64:
|
||||
return kvInt(key, v)
|
||||
case uint:
|
||||
return kvInt(key, int64(v))
|
||||
case uint32:
|
||||
return kvInt(key, int64(v))
|
||||
case uint64:
|
||||
return kvInt(key, int64(v))
|
||||
case float32:
|
||||
return kvDbl(key, float64(v))
|
||||
case float64:
|
||||
return kvDbl(key, v)
|
||||
case bool:
|
||||
return kvBool(key, v)
|
||||
case []string:
|
||||
if len(v) == 0 {
|
||||
return nil
|
||||
}
|
||||
vals := make([]*AnyValue, len(v))
|
||||
for i, s := range v {
|
||||
vals[i] = &AnyValue{Value: &StringValue{StringValue: s}}
|
||||
}
|
||||
return kvAny(key, arrValue(vals...))
|
||||
case []int:
|
||||
if len(v) == 0 {
|
||||
return nil
|
||||
}
|
||||
vals := make([]*AnyValue, len(v))
|
||||
for i, n := range v {
|
||||
vals[i] = &AnyValue{Value: &IntValue{IntValue: int64(n)}}
|
||||
}
|
||||
return kvAny(key, arrValue(vals...))
|
||||
case []int64:
|
||||
if len(v) == 0 {
|
||||
return nil
|
||||
}
|
||||
vals := make([]*AnyValue, len(v))
|
||||
for i, n := range v {
|
||||
vals[i] = &AnyValue{Value: &IntValue{IntValue: n}}
|
||||
}
|
||||
return kvAny(key, arrValue(vals...))
|
||||
case []float64:
|
||||
if len(v) == 0 {
|
||||
return nil
|
||||
}
|
||||
vals := make([]*AnyValue, len(v))
|
||||
for i, n := range v {
|
||||
vals[i] = &AnyValue{Value: &DoubleValue{DoubleValue: n}}
|
||||
}
|
||||
return kvAny(key, arrValue(vals...))
|
||||
case []any:
|
||||
if len(v) == 0 {
|
||||
return nil
|
||||
}
|
||||
vals := make([]*AnyValue, 0, len(v))
|
||||
for _, item := range v {
|
||||
if kv := anyToKeyValue("_", item); kv != nil {
|
||||
vals = append(vals, kv.Value)
|
||||
}
|
||||
}
|
||||
if len(vals) == 0 {
|
||||
return nil
|
||||
}
|
||||
return kvAny(key, arrValue(vals...))
|
||||
case map[string]any:
|
||||
if len(v) == 0 {
|
||||
return nil
|
||||
}
|
||||
kvList := make([]*KeyValue, 0, len(v))
|
||||
for k, val := range v {
|
||||
kv := anyToKeyValue(k, val)
|
||||
if kv != nil {
|
||||
kvList = append(kvList, kv)
|
||||
}
|
||||
}
|
||||
return kvAny(key, listValue(kvList...))
|
||||
default:
|
||||
data, err := schemas.MarshalSorted(v)
|
||||
if err != nil {
|
||||
return kvStr(key, fmt.Sprintf("%v", v))
|
||||
}
|
||||
var generic any
|
||||
if err := schemas.Unmarshal(data, &generic); err != nil {
|
||||
return kvStr(key, string(data))
|
||||
}
|
||||
return anyToKeyValue(key, generic)
|
||||
}
|
||||
}
|
||||
|
||||
// convertSpanKind maps Bifrost SpanKind to OTEL SpanKind
|
||||
func convertSpanKind(kind schemas.SpanKind) tracepb.Span_SpanKind {
|
||||
switch kind {
|
||||
case schemas.SpanKindLLMCall:
|
||||
return tracepb.Span_SPAN_KIND_CLIENT
|
||||
case schemas.SpanKindHTTPRequest:
|
||||
return tracepb.Span_SPAN_KIND_SERVER
|
||||
case schemas.SpanKindPlugin:
|
||||
return tracepb.Span_SPAN_KIND_INTERNAL
|
||||
case schemas.SpanKindInternal:
|
||||
return tracepb.Span_SPAN_KIND_INTERNAL
|
||||
case schemas.SpanKindRetry:
|
||||
return tracepb.Span_SPAN_KIND_INTERNAL
|
||||
case schemas.SpanKindFallback:
|
||||
return tracepb.Span_SPAN_KIND_INTERNAL
|
||||
case schemas.SpanKindMCPTool:
|
||||
return tracepb.Span_SPAN_KIND_CLIENT
|
||||
case schemas.SpanKindEmbedding:
|
||||
return tracepb.Span_SPAN_KIND_CLIENT
|
||||
case schemas.SpanKindSpeech:
|
||||
return tracepb.Span_SPAN_KIND_CLIENT
|
||||
case schemas.SpanKindTranscription:
|
||||
return tracepb.Span_SPAN_KIND_CLIENT
|
||||
default:
|
||||
return tracepb.Span_SPAN_KIND_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
// convertSpanStatus maps Bifrost SpanStatus to OTEL Status
|
||||
func convertSpanStatus(status schemas.SpanStatus, msg string) *tracepb.Status {
|
||||
switch status {
|
||||
case schemas.SpanStatusOk:
|
||||
return &tracepb.Status{Code: tracepb.Status_STATUS_CODE_OK}
|
||||
case schemas.SpanStatusError:
|
||||
return &tracepb.Status{Code: tracepb.Status_STATUS_CODE_ERROR, Message: msg}
|
||||
default:
|
||||
return &tracepb.Status{Code: tracepb.Status_STATUS_CODE_UNSET}
|
||||
}
|
||||
}
|
||||
|
||||
// convertSpanEvents converts Bifrost span events to OTEL events
|
||||
func convertSpanEvents(events []schemas.SpanEvent) []*Event {
|
||||
if len(events) == 0 {
|
||||
return nil
|
||||
}
|
||||
otelEvents := make([]*Event, len(events))
|
||||
for i, event := range events {
|
||||
otelEvents[i] = &Event{
|
||||
TimeUnixNano: uint64(event.Timestamp.UnixNano()),
|
||||
Name: event.Name,
|
||||
Attributes: convertAttributesToKeyValues(event.Attributes),
|
||||
}
|
||||
}
|
||||
return otelEvents
|
||||
}
|
||||
229
plugins/otel/docker-compose.yml
Normal file
229
plugins/otel/docker-compose.yml
Normal file
@@ -0,0 +1,229 @@
|
||||
services:
|
||||
otel-collector:
|
||||
image: otel/opentelemetry-collector-contrib:latest
|
||||
container_name: otel-collector
|
||||
command: ["--config=/etc/otelcol/config.yaml"]
|
||||
configs:
|
||||
- source: otel-collector-config
|
||||
target: /etc/otelcol/config.yaml
|
||||
ports:
|
||||
- "4317:4317" # OTLP gRPC
|
||||
- "4318:4318" # OTLP HTTP
|
||||
- "8888:8888" # Collector /metrics
|
||||
- "9464:9464" # Prometheus scrape endpoint
|
||||
- "13133:13133" # Health check
|
||||
- "1777:1777" # pprof
|
||||
- "55679:55679" # zpages
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- tempo
|
||||
|
||||
tempo:
|
||||
image: grafana/tempo:latest
|
||||
container_name: tempo
|
||||
command: ["-config.file=/etc/tempo.yaml"]
|
||||
configs:
|
||||
- source: tempo-config
|
||||
target: /etc/tempo.yaml
|
||||
ports:
|
||||
- "3200:3200" # tempo HTTP/gRPC API (multiplexed)
|
||||
expose:
|
||||
- "4317" # OTLP gRPC (internal)
|
||||
volumes:
|
||||
- tempo-data:/var/tempo
|
||||
restart: unless-stopped
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus:latest
|
||||
container_name: prometheus
|
||||
depends_on:
|
||||
- otel-collector
|
||||
command:
|
||||
- "--config.file=/etc/prometheus/prometheus.yml"
|
||||
- "--storage.tsdb.path=/prometheus"
|
||||
- "--web.console.libraries=/usr/share/prometheus/console_libraries"
|
||||
- "--web.console.templates=/usr/share/prometheus/consoles"
|
||||
- "--web.enable-remote-write-receiver"
|
||||
ports:
|
||||
- "9090:9090"
|
||||
volumes:
|
||||
- prometheus-data:/prometheus
|
||||
configs:
|
||||
- source: prometheus-config
|
||||
target: /etc/prometheus/prometheus.yml
|
||||
restart: unless-stopped
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:latest
|
||||
container_name: grafana
|
||||
depends_on:
|
||||
- prometheus
|
||||
- tempo
|
||||
environment:
|
||||
GF_SECURITY_ADMIN_USER: admin
|
||||
GF_SECURITY_ADMIN_PASSWORD: admin
|
||||
GF_AUTH_ANONYMOUS_ENABLED: "true"
|
||||
GF_AUTH_ANONYMOUS_ORG_ROLE: Viewer
|
||||
GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: "grafana-pyroscope-app,grafana-exploretraces-app,grafana-metricsdrilldown-app"
|
||||
GF_PLUGINS_ENABLE_ALPHA: "true"
|
||||
GF_INSTALL_PLUGINS: ""
|
||||
GF_LOG_LEVEL: "warn"
|
||||
GF_FEATURE_TOGGLES_ENABLE: ""
|
||||
ports:
|
||||
- "4000:3000"
|
||||
volumes:
|
||||
- grafana-data:/var/lib/grafana
|
||||
configs:
|
||||
- source: grafana-datasources
|
||||
target: /etc/grafana/provisioning/datasources/datasources.yml
|
||||
restart: unless-stopped
|
||||
|
||||
configs:
|
||||
otel-collector-config:
|
||||
content: |
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
|
||||
processors:
|
||||
batch:
|
||||
|
||||
exporters:
|
||||
prometheus:
|
||||
endpoint: 0.0.0.0:9464
|
||||
namespace: otel
|
||||
const_labels:
|
||||
source: otelcol
|
||||
|
||||
otlp/tempo:
|
||||
endpoint: tempo:4317
|
||||
tls:
|
||||
insecure: true
|
||||
|
||||
debug:
|
||||
verbosity: detailed
|
||||
|
||||
extensions:
|
||||
health_check:
|
||||
endpoint: 0.0.0.0:13133
|
||||
pprof:
|
||||
endpoint: 0.0.0.0:1777
|
||||
zpages:
|
||||
endpoint: 0.0.0.0:55679
|
||||
|
||||
service:
|
||||
extensions: [health_check, pprof, zpages]
|
||||
telemetry:
|
||||
logs:
|
||||
level: debug
|
||||
metrics:
|
||||
level: detailed
|
||||
pipelines:
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [debug, otlp/tempo]
|
||||
metrics:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [debug, prometheus]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [debug]
|
||||
|
||||
tempo-config:
|
||||
content: |
|
||||
server:
|
||||
http_listen_port: 3200
|
||||
grpc_listen_port: 3201
|
||||
log_level: info
|
||||
|
||||
distributor:
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
|
||||
ingester:
|
||||
max_block_duration: 5m
|
||||
trace_idle_period: 10s
|
||||
|
||||
compactor:
|
||||
compaction:
|
||||
block_retention: 1h
|
||||
|
||||
storage:
|
||||
trace:
|
||||
backend: local
|
||||
wal:
|
||||
path: /var/tempo/wal
|
||||
local:
|
||||
path: /var/tempo/blocks
|
||||
|
||||
metrics_generator:
|
||||
registry:
|
||||
external_labels:
|
||||
source: tempo
|
||||
storage:
|
||||
path: /var/tempo/generator/wal
|
||||
remote_write:
|
||||
- url: http://prometheus:9090/api/v1/write
|
||||
|
||||
prometheus-config:
|
||||
content: |
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
scrape_configs:
|
||||
- job_name: "otelcol-internal"
|
||||
static_configs:
|
||||
- targets: ["otel-collector:8888"]
|
||||
- job_name: "otelcol-exporter"
|
||||
static_configs:
|
||||
- targets: ["otel-collector:9464"]
|
||||
- job_name: "tempo"
|
||||
static_configs:
|
||||
- targets: ["tempo:3200"]
|
||||
|
||||
grafana-datasources:
|
||||
content: |
|
||||
apiVersion: 1
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
uid: prometheus
|
||||
type: prometheus
|
||||
access: proxy
|
||||
orgId: 1
|
||||
url: http://prometheus:9090
|
||||
isDefault: true
|
||||
editable: true
|
||||
- name: Tempo
|
||||
uid: tempo
|
||||
type: tempo
|
||||
access: proxy
|
||||
orgId: 1
|
||||
url: http://tempo:3200
|
||||
editable: true
|
||||
jsonData:
|
||||
nodeGraph:
|
||||
enabled: true
|
||||
tracesToLogs:
|
||||
datasourceUid: prometheus
|
||||
tracesToMetrics:
|
||||
datasourceUid: prometheus
|
||||
serviceMap:
|
||||
datasourceUid: prometheus
|
||||
search:
|
||||
hide: false
|
||||
lokiSearch:
|
||||
datasourceUid: prometheus
|
||||
|
||||
volumes:
|
||||
prometheus-data:
|
||||
grafana-data:
|
||||
tempo-data:
|
||||
167
plugins/otel/go.mod
Normal file
167
plugins/otel/go.mod
Normal file
@@ -0,0 +1,167 @@
|
||||
module github.com/maximhq/bifrost/plugins/otel
|
||||
|
||||
go 1.26.2
|
||||
|
||||
require (
|
||||
github.com/maximhq/bifrost/core v1.5.4
|
||||
github.com/maximhq/bifrost/framework v1.3.4
|
||||
go.opentelemetry.io/otel v1.43.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0
|
||||
go.opentelemetry.io/otel/metric v1.43.0
|
||||
go.opentelemetry.io/otel/sdk v1.43.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0
|
||||
google.golang.org/grpc v1.80.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.25.1 // indirect
|
||||
cloud.google.com/go v0.123.0 // indirect
|
||||
cloud.google.com/go/auth v0.18.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
cloud.google.com/go/iam v1.5.3 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.3 // indirect
|
||||
cloud.google.com/go/storage v1.61.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect
|
||||
github.com/aws/smithy-go v1.24.2 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/buger/jsonparser v1.1.2 // indirect
|
||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/analysis v0.24.2 // indirect
|
||||
github.com/go-openapi/errors v0.22.5 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.4 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.4 // indirect
|
||||
github.com/go-openapi/loads v0.23.2 // indirect
|
||||
github.com/go-openapi/runtime v0.29.2 // indirect
|
||||
github.com/go-openapi/spec v0.22.2 // indirect
|
||||
github.com/go-openapi/strfmt v0.25.0 // indirect
|
||||
github.com/go-openapi/swag v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/cmdutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/fileutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/mangling v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/netutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
|
||||
github.com/go-openapi/validate v0.25.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.19.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/invopop/jsonschema v0.13.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.9.1 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.1 // indirect
|
||||
github.com/mark3labs/mcp-go v0.43.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.32 // indirect
|
||||
github.com/oapi-codegen/runtime v1.1.1 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/pinecone-io/go-pinecone/v5 v5.3.0 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/qdrant/go-client v1.16.2 // indirect
|
||||
github.com/redis/go-redis/v9 v9.17.2 // indirect
|
||||
github.com/rs/zerolog v1.34.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
|
||||
github.com/stretchr/testify v1.11.1 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.68.0 // indirect
|
||||
github.com/weaviate/weaviate v1.36.5 // indirect
|
||||
github.com/weaviate/weaviate-go-client/v5 v5.7.1 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
go.mongodb.org/mongo-driver v1.17.6 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.40.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.starlark.net v0.0.0-20260102030733-3fee463870c9 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
google.golang.org/api v0.274.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/postgres v1.6.0 // indirect
|
||||
gorm.io/driver/sqlite v1.6.0 // indirect
|
||||
gorm.io/gorm v1.31.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.15.0
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0
|
||||
golang.org/x/arch v0.23.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
)
|
||||
399
plugins/otel/go.sum
Normal file
399
plugins/otel/go.sum
Normal file
@@ -0,0 +1,399 @@
|
||||
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
|
||||
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
|
||||
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
|
||||
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
|
||||
cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM=
|
||||
cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=
|
||||
cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=
|
||||
cloud.google.com/go/logging v1.13.2 h1:qqlHCBvieJT9Cdq4QqYx1KPadCQ2noD4FK02eNqHAjA=
|
||||
cloud.google.com/go/logging v1.13.2/go.mod h1:zaybliM3yun1J8mU2dVQ1/qDzjbOqEijZCn6hSBtKak=
|
||||
cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8=
|
||||
cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk=
|
||||
cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE=
|
||||
cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI=
|
||||
cloud.google.com/go/storage v1.61.3 h1:VS//ZfBuPGDvakfD9xyPW1RGF1Vy3BWUoVZXgW1KMOg=
|
||||
cloud.google.com/go/storage v1.61.3/go.mod h1:JtqK8BBB7TWv0HVGHubtUdzYYrakOQIsMLffZ2Z/HWk=
|
||||
cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U=
|
||||
cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0 h1:7t/qx5Ost0s0wbA/VDrByOooURhp+ikYwv20i9Y07TQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 h1:0s6TxfCu2KHkkZPnBfsQ2y5qia0jl3MMrmBhu3nCOYk=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc=
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.11 h1:ftxI5sgz8jZkckuUHXfC/wMUc8u3fG1vQS0plr2F2Zs=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.11/go.mod h1:twF11+6ps9aNRKEDimksp923o44w/Thk9+8YIlzWMmo=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.14 h1:n+UcGWAIZHkXzYt87uMFBv/l8THYELoX6gVcUvgl6fI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.14/go.mod h1:cJKuyWB59Mqi0jM3nFYQRmnHVQIcgoxjEMAbLkpr62w=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 h1:NUS3K4BTDArQqNu2ih7yeDLaS3bmHD0YndtA6UP884g=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21/go.mod h1:YWNWJQNjKigKY1RHVJCuupeWDrrHjRqHm0N9rdrWzYI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5 h1:clHU5fm//kWS1C2HgtgWxfQbFbx4b6rx+5jzhgX9HrI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.5/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22/go.mod h1:zd/JsJ4P7oGfUhXn1VyLqaRZwPmZwg44Jf2dS84Dm3Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13/go.mod h1:CEuVn5WqOMilYl+tbccq8+N2ieCy0gVn3OtRb0vBNNM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 h1:ZlvrNcHSFFWURB8avufQq9gFsheUgjVD9536obIknfM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21/go.mod h1:cv3TNhVrssKR0O/xxLJVRfd2oazSnZnkUeTf6ctUwfQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 h1:HwxWTbTrIHm5qY+CAEur0s/figc3qwvLWsNkF4RPToo=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 h1:lFd1+ZSEYJZYvv9d6kXzhkZu07si3f+GQ1AaYwa2LUM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15/go.mod h1:WSvS1NLr7JaPunCXqpJnWk1Bjo7IxzZXrZi1QQCkuqM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 h1:dzztQ1YmfPrxdrOiuZRMF6fuOwWlWpD2StNLTceKpys=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw=
|
||||
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
|
||||
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk=
|
||||
github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
||||
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
|
||||
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
|
||||
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
|
||||
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
|
||||
github.com/fasthttp/websocket v1.5.12 h1:e4RGPpWW2HTbL3zV0Y/t7g0ub294LkiuXXUuTOUInlE=
|
||||
github.com/fasthttp/websocket v1.5.12/go.mod h1:I+liyL7/4moHojiOgUOIKEWm9EIxHqxZChS+aMFltyg=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/analysis v0.24.2 h1:6p7WXEuKy1llDgOH8FooVeO+Uq2za9qoAOq4ZN08B50=
|
||||
github.com/go-openapi/analysis v0.24.2/go.mod h1:x27OOHKANE0lutg2ml4kzYLoHGMKgRm1Cj2ijVOjJuE=
|
||||
github.com/go-openapi/errors v0.22.5 h1:Yfv4O/PRYpNF3BNmVkEizcHb3uLVVsrDt3LNdgAKRY4=
|
||||
github.com/go-openapi/errors v0.22.5/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk=
|
||||
github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
|
||||
github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
|
||||
github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
|
||||
github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
|
||||
github.com/go-openapi/loads v0.23.2 h1:rJXAcP7g1+lWyBHC7iTY+WAF0rprtM+pm8Jxv1uQJp4=
|
||||
github.com/go-openapi/loads v0.23.2/go.mod h1:IEVw1GfRt/P2Pplkelxzj9BYFajiWOtY2nHZNj4UnWY=
|
||||
github.com/go-openapi/runtime v0.29.2 h1:UmwSGWNmWQqKm1c2MGgXVpC2FTGwPDQeUsBMufc5Yj0=
|
||||
github.com/go-openapi/runtime v0.29.2/go.mod h1:biq5kJXRJKBJxTDJXAa00DOTa/anflQPhT0/wmjuy+0=
|
||||
github.com/go-openapi/spec v0.22.2 h1:KEU4Fb+Lp1qg0V4MxrSCPv403ZjBl8Lx1a83gIPU8Qc=
|
||||
github.com/go-openapi/spec v0.22.2/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs=
|
||||
github.com/go-openapi/strfmt v0.25.0 h1:7R0RX7mbKLa9EYCTHRcCuIPcaqlyQiWNPTXwClK0saQ=
|
||||
github.com/go-openapi/strfmt v0.25.0/go.mod h1:nNXct7OzbwrMY9+5tLX4I21pzcmE6ccMGXl3jFdPfn8=
|
||||
github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU=
|
||||
github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ=
|
||||
github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4=
|
||||
github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
|
||||
github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
|
||||
github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
|
||||
github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y=
|
||||
github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
|
||||
github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
|
||||
github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
|
||||
github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48=
|
||||
github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg=
|
||||
github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0=
|
||||
github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
|
||||
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
|
||||
github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
||||
github.com/go-openapi/validate v0.25.1 h1:sSACUI6Jcnbo5IWqbYHgjibrhhmt3vR6lCzKZnmAgBw=
|
||||
github.com/go-openapi/validate v0.25.1/go.mod h1:RMVyVFYte0gbSTaZ0N4KmTn6u/kClvAFp+mAVfS/DQc=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
|
||||
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
|
||||
github.com/googleapis/gax-go/v2 v2.19.0 h1:fYQaUOiGwll0cGj7jmHT/0nPlcrZDFPrZRhTsoCr8hE=
|
||||
github.com/googleapis/gax-go/v2 v2.19.0/go.mod h1:w2ROXVdfGEVFXzmlciUU4EdjHgWvB5h2n6x/8XSTTJA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo=
|
||||
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
|
||||
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.9.1 h1:uwrxJXBnx76nyISkhr33kQLlUqjv7et7b9FjCen/tdc=
|
||||
github.com/jackc/pgx/v5 v5.9.1/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
|
||||
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=
|
||||
github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mark3labs/mcp-go v0.43.2 h1:21PUSlWWiSbUPQwXIJ5WKlETixpFpq+WBpbMGDSVy/I=
|
||||
github.com/mark3labs/mcp-go v0.43.2/go.mod h1:YnJfOL382MIWDx1kMY+2zsRHU/q78dBg9aFb8W6Thdw=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/maximhq/bifrost/core v1.5.4 h1:hf0BhoHVVpY1EQ4FkyRzW4IBYjrolxdZV0ucgWfHhcE=
|
||||
github.com/maximhq/bifrost/core v1.5.4/go.mod h1:z1/vOalbDAD7v7sYbXQsqR+2qIFP0jKOSIStw6Q4P4U=
|
||||
github.com/maximhq/bifrost/framework v1.3.4 h1:nZPv1FYry1njexZ0Hb6CZQXybwRFKGMTRyGWz2HGcio=
|
||||
github.com/maximhq/bifrost/framework v1.3.4/go.mod h1:e0defDjWWFi6c2Zs3AOkMcRbYzjww4sjkyZtARrP4Zk=
|
||||
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
|
||||
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
|
||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/pinecone-io/go-pinecone/v5 v5.3.0 h1:0YQlEtmXGWK/I8ztkOVM6PuBYgFJZhjSdb0ddU+bHPE=
|
||||
github.com/pinecone-io/go-pinecone/v5 v5.3.0/go.mod h1:6Fg85fcyvMUQFf9KW7zniN81kelSYvsjF+KPLdc1MGA=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/qdrant/go-client v1.16.2 h1:UUMJJfvXTByhwhH1DwWdbkhZ2cTdvSqVkXSIfBrVWSg=
|
||||
github.com/qdrant/go-client v1.16.2/go.mod h1:I+EL3h4HRoRTeHtbfOd/4kDXwCukZfkd41j/9wryGkw=
|
||||
github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=
|
||||
github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||
github.com/savsgio/gotils v0.0.0-20250408102913-196191ec6287 h1:qIQ0tWF9vxGtkJa24bR+2i53WBCz1nW/Pc47oVYauC4=
|
||||
github.com/savsgio/gotils v0.0.0-20250408102913-196191ec6287/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
|
||||
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok=
|
||||
github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4=
|
||||
github.com/weaviate/weaviate v1.36.5 h1:lCiuEfQ08+5wK0DkTCUBb6ayNep9QpBH6JJhmZaRfzk=
|
||||
github.com/weaviate/weaviate v1.36.5/go.mod h1:ljzrgEmGKn3CRzDdcxvhmBUUZIcghwIYd1Lmn54f3Z8=
|
||||
github.com/weaviate/weaviate-go-client/v5 v5.7.1 h1:vEMxh486QqRqWaq58UEe/TiTbGbo9T5x7ZPFd5QENvQ=
|
||||
github.com/weaviate/weaviate-go-client/v5 v5.7.1/go.mod h1:T/JDErjN074GrnYIa0AgK1TGUGP/6A/8vqXNPlv4c6E=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
|
||||
go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss=
|
||||
go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.40.0 h1:Awaf8gmW99tZTOWqkLCOl6aw1/rxAWVlHsHIZ3fT2sA=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.40.0/go.mod h1:99OY9ZCqyLkzJLTh5XhECpLRSxcZl+ZDKBEO+jMBFR4=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0/go.mod h1:HBy4BjzgVE8139ieRI75oXm3EcDN+6GhD88JT1Kjvxg=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.40.0 h1:ZrPRak/kS4xI3AVXy8F7pipuDXmDsrO8Lg+yQjBLjw0=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.40.0/go.mod h1:3y6kQCWztq6hyW8Z9YxQDDm0Je9AJoFar2G0yDcmhRk=
|
||||
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.starlark.net v0.0.0-20260102030733-3fee463870c9 h1:nV1OyvU+0CYrp5eKfQ3rD03TpFYYhH08z31NK1HmtTk=
|
||||
go.starlark.net v0.0.0-20260102030733-3fee463870c9/go.mod h1:YKMCv9b1WrfWmeqdV5MAuEHWsu5iC+fe6kYl2sQjdI8=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
|
||||
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/api v0.274.0 h1:aYhycS5QQCwxHLwfEHRRLf9yNsfvp1JadKKWBE54RFA=
|
||||
google.golang.org/api v0.274.0/go.mod h1:JbAt7mF+XVmWu6xNP8/+CTiGH30ofmCmk9nM8d8fHew=
|
||||
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5 h1:JNfk58HZ8lfmXbYK2vx/UvsqIL59TzByCxPIX4TDmsE=
|
||||
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:x5julN69+ED4PcFk/XWayw35O0lf/nGa4aNgODCmNmw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
||||
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
||||
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
||||
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
||||
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||
79
plugins/otel/grpc.go
Normal file
79
plugins/otel/grpc.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package otel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
collectorpb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
// OtelClientGRPC is the implementation of the OpenTelemetry client for gRPC
|
||||
type OtelClientGRPC struct {
|
||||
client collectorpb.TraceServiceClient
|
||||
conn *grpc.ClientConn
|
||||
headers map[string]string
|
||||
}
|
||||
|
||||
// NewOtelClientGRPC creates a new OpenTelemetry client for gRPC
|
||||
func NewOtelClientGRPC(endpoint string, headers map[string]string, tlsCACert string, insecureMode bool) (*OtelClientGRPC, error) {
|
||||
var creds credentials.TransportCredentials
|
||||
// TLS priority: custom CA > system roots > insecure
|
||||
if tlsCACert != "" {
|
||||
// Validate the CA cert path to prevent path traversal attacks
|
||||
if err := validateCACertPath(tlsCACert); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Use custom CA certificate with MinVersion
|
||||
caCert, err := os.ReadFile(tlsCACert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to load provided CA cert: %w", err)
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
if !caCertPool.AppendCertsFromPEM(caCert) {
|
||||
return nil, fmt.Errorf("fail to parse provided CA cert")
|
||||
}
|
||||
tlsConfig := &tls.Config{
|
||||
RootCAs: caCertPool,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
creds = credentials.NewTLS(tlsConfig)
|
||||
} else if insecureMode {
|
||||
// Skip TLS entirely
|
||||
creds = insecure.NewCredentials()
|
||||
} else {
|
||||
// Use system root CAs with MinVersion
|
||||
tlsConfig := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
creds = credentials.NewTLS(tlsConfig)
|
||||
}
|
||||
conn, err := grpc.NewClient(endpoint, grpc.WithTransportCredentials(creds))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &OtelClientGRPC{client: collectorpb.NewTraceServiceClient(conn), conn: conn, headers: headers}, nil
|
||||
}
|
||||
|
||||
// Emit sends a trace to the OpenTelemetry collector
|
||||
func (c *OtelClientGRPC) Emit(ctx context.Context, rs []*ResourceSpan) error {
|
||||
if c.headers != nil {
|
||||
ctx = metadata.NewOutgoingContext(ctx, metadata.New(c.headers))
|
||||
}
|
||||
_, err := c.client.Export(ctx, &collectorpb.ExportTraceServiceRequest{ResourceSpans: rs})
|
||||
return err
|
||||
}
|
||||
|
||||
// Close closes the gRPC connection
|
||||
func (c *OtelClientGRPC) Close() error {
|
||||
if c.conn != nil {
|
||||
return c.conn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
114
plugins/otel/http.go
Normal file
114
plugins/otel/http.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package otel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
collectorpb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// OtelClientHTTP is the implementation of the OpenTelemetry client for HTTP
|
||||
type OtelClientHTTP struct {
|
||||
client *http.Client
|
||||
endpoint string
|
||||
headers map[string]string
|
||||
}
|
||||
|
||||
// NewOtelClientHTTP creates a new OpenTelemetry client for HTTP
|
||||
func NewOtelClientHTTP(endpoint string, headers map[string]string, tlsCACert string, insecureMode bool) (*OtelClientHTTP, error) {
|
||||
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
transport.MaxIdleConns = 100
|
||||
transport.MaxIdleConnsPerHost = 10
|
||||
transport.IdleConnTimeout = 120 * time.Second
|
||||
|
||||
// TLS priority: custom CA > system roots > insecure
|
||||
if tlsCACert != "" {
|
||||
// Validate the CA cert path to prevent path traversal attacks
|
||||
if err := validateCACertPath(tlsCACert); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
caCert, err := os.ReadFile(tlsCACert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to load provided CA cert: %w", err)
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
if !caCertPool.AppendCertsFromPEM(caCert) {
|
||||
return nil, fmt.Errorf("fail to add provided CA cert")
|
||||
}
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
RootCAs: caCertPool,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
} else if insecureMode {
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
InsecureSkipVerify: true, // #nosec G402
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
} else {
|
||||
// Use system root CAs with MinVersion
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
}
|
||||
|
||||
return &OtelClientHTTP{client: &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
Transport: transport,
|
||||
}, endpoint: endpoint, headers: headers}, nil
|
||||
}
|
||||
|
||||
// Emit sends a trace to the OpenTelemetry collector
|
||||
func (c *OtelClientHTTP) Emit(ctx context.Context, rs []*ResourceSpan) error {
|
||||
payload, err := proto.Marshal(&collectorpb.ExportTraceServiceRequest{ResourceSpans: rs})
|
||||
if err != nil {
|
||||
logger.Error("[otel] failed to marshal trace: %v", err)
|
||||
return err
|
||||
}
|
||||
var body bytes.Buffer
|
||||
body.Write(payload)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.endpoint, &body)
|
||||
if err != nil {
|
||||
logger.Error("[otel] failed to create request: %v", err)
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-protobuf")
|
||||
if c.headers != nil {
|
||||
for key, value := range c.headers {
|
||||
if strings.ToLower(key) == "content-type" {
|
||||
continue
|
||||
}
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
}
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
logger.Error("[otel] failed to send request to %s: %v", c.endpoint, err)
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode/100 != 2 {
|
||||
// Discard the body to avoid leaking memory
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
logger.Error("[otel] collector at %s returned status %s", c.endpoint, resp.Status)
|
||||
return fmt.Errorf("collector returned %s", resp.Status)
|
||||
}
|
||||
logger.Debug("[otel] successfully sent trace to %s, status: %s", c.endpoint, resp.Status)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the HTTP client
|
||||
func (c *OtelClientHTTP) Close() error {
|
||||
if c.client != nil {
|
||||
c.client.CloseIdleConnections()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
470
plugins/otel/main.go
Normal file
470
plugins/otel/main.go
Normal file
@@ -0,0 +1,470 @@
|
||||
// Package otel is OpenTelemetry plugin for Bifrost
|
||||
package otel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/maximhq/bifrost/core/schemas"
|
||||
"github.com/maximhq/bifrost/framework/modelcatalog"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
)
|
||||
|
||||
// logger is the logger for the OTEL plugin
|
||||
var logger schemas.Logger
|
||||
|
||||
// OTELResponseAttributesEnvKey is the environment variable key for the OTEL resource attributes
|
||||
// We check if this is present in the environment variables and if so, we will use it to set the attributes for all spans at the resource level
|
||||
const OTELResponseAttributesEnvKey = "OTEL_RESOURCE_ATTRIBUTES"
|
||||
|
||||
const PluginName = "otel"
|
||||
|
||||
// TraceType is the type of trace to use for the OTEL collector
|
||||
type TraceType string
|
||||
|
||||
// TraceTypeGenAIExtension is the type of trace to use for the OTEL collector
|
||||
const TraceTypeGenAIExtension TraceType = "genai_extension"
|
||||
|
||||
// TraceTypeVercel is the type of trace to use for the OTEL collector
|
||||
const TraceTypeVercel TraceType = "vercel"
|
||||
|
||||
// TraceTypeOpenInference is the type of trace to use for the OTEL collector
|
||||
const TraceTypeOpenInference TraceType = "open_inference"
|
||||
|
||||
// Protocol is the protocol to use for the OTEL collector
|
||||
type Protocol string
|
||||
|
||||
// ProtocolHTTP is the default protocol
|
||||
const ProtocolHTTP Protocol = "http"
|
||||
|
||||
// ProtocolGRPC is the second protocol
|
||||
const ProtocolGRPC Protocol = "grpc"
|
||||
|
||||
type Config struct {
|
||||
ServiceName string `json:"service_name"`
|
||||
CollectorURL string `json:"collector_url"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
TraceType TraceType `json:"trace_type"`
|
||||
Protocol Protocol `json:"protocol"`
|
||||
TLSCACert string `json:"tls_ca_cert"`
|
||||
Insecure bool `json:"insecure"` // Skip TLS when true; ignored if TLSCACert is set. Defaults to true when omitted.
|
||||
|
||||
// Metrics push configuration
|
||||
MetricsEnabled bool `json:"metrics_enabled"`
|
||||
MetricsEndpoint string `json:"metrics_endpoint"`
|
||||
MetricsPushInterval int `json:"metrics_push_interval"` // in seconds, default 15
|
||||
}
|
||||
|
||||
// UnmarshalJSON applies field defaults that the zero-value wouldn't capture.
|
||||
// Specifically, Insecure defaults to true when the key is omitted so http://
|
||||
// collectors work out-of-the-box without forcing users to set it explicitly.
|
||||
func (c *Config) UnmarshalJSON(data []byte) error {
|
||||
type alias Config
|
||||
aux := struct {
|
||||
Insecure *bool `json:"insecure"`
|
||||
*alias
|
||||
}{
|
||||
alias: (*alias)(c),
|
||||
}
|
||||
if err := sonic.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
if aux.Insecure == nil {
|
||||
c.Insecure = true
|
||||
} else {
|
||||
c.Insecure = *aux.Insecure
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OtelPlugin is the plugin for OpenTelemetry.
|
||||
// It implements the ObservabilityPlugin interface to receive completed traces
|
||||
// from the tracing middleware and forward them to an OTEL collector.
|
||||
type OtelPlugin struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
serviceName string
|
||||
url string
|
||||
headers map[string]string
|
||||
traceType TraceType
|
||||
protocol Protocol
|
||||
|
||||
bifrostVersion string
|
||||
|
||||
attributesFromEnvironment []*commonpb.KeyValue
|
||||
|
||||
client OtelClient
|
||||
|
||||
pricingManager *modelcatalog.ModelCatalog
|
||||
|
||||
// Metrics push support
|
||||
metricsExporter *MetricsExporter
|
||||
}
|
||||
|
||||
// Init function for the OTEL plugin
|
||||
func Init(ctx context.Context, config *Config, _logger schemas.Logger, pricingManager *modelcatalog.ModelCatalog, bifrostVersion string) (*OtelPlugin, error) {
|
||||
if config == nil {
|
||||
return nil, fmt.Errorf("config is required")
|
||||
}
|
||||
logger = _logger
|
||||
if pricingManager == nil {
|
||||
logger.Warn("otel plugin requires model catalog to calculate cost, all cost calculations will be skipped.")
|
||||
}
|
||||
var err error
|
||||
// If headers are present, and any of them start with env., we will replace the value with the environment variable
|
||||
if config.Headers != nil {
|
||||
for key, value := range config.Headers {
|
||||
if newValue, ok := strings.CutPrefix(value, "env."); ok {
|
||||
config.Headers[key] = os.Getenv(newValue)
|
||||
if config.Headers[key] == "" {
|
||||
logger.Warn("environment variable %s not found", newValue)
|
||||
return nil, fmt.Errorf("environment variable %s not found", newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if config.ServiceName == "" {
|
||||
config.ServiceName = "bifrost"
|
||||
}
|
||||
// Loading attributes from environment
|
||||
attributesFromEnvironment := make([]*commonpb.KeyValue, 0)
|
||||
if attributes, ok := os.LookupEnv(OTELResponseAttributesEnvKey); ok {
|
||||
// We will split the attributes by , and then split each attribute by =
|
||||
for attribute := range strings.SplitSeq(attributes, ",") {
|
||||
attributeParts := strings.Split(strings.TrimSpace(attribute), "=")
|
||||
if len(attributeParts) == 2 {
|
||||
attributesFromEnvironment = append(attributesFromEnvironment, kvStr(strings.TrimSpace(attributeParts[0]), strings.TrimSpace(attributeParts[1])))
|
||||
}
|
||||
}
|
||||
}
|
||||
// Preparing the plugin
|
||||
p := &OtelPlugin{
|
||||
serviceName: config.ServiceName,
|
||||
url: config.CollectorURL,
|
||||
traceType: config.TraceType,
|
||||
headers: config.Headers,
|
||||
protocol: config.Protocol,
|
||||
pricingManager: pricingManager,
|
||||
bifrostVersion: bifrostVersion,
|
||||
attributesFromEnvironment: attributesFromEnvironment,
|
||||
}
|
||||
p.ctx, p.cancel = context.WithCancel(ctx)
|
||||
if config.Protocol == ProtocolGRPC {
|
||||
p.client, err = NewOtelClientGRPC(config.CollectorURL, config.Headers, config.TLSCACert, config.Insecure)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if config.Protocol == ProtocolHTTP {
|
||||
p.client, err = NewOtelClientHTTP(config.CollectorURL, config.Headers, config.TLSCACert, config.Insecure)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if p.client == nil {
|
||||
return nil, fmt.Errorf("otel client is not initialized. invalid protocol type")
|
||||
}
|
||||
|
||||
// Initialize metrics exporter if enabled
|
||||
if config.MetricsEnabled {
|
||||
if config.MetricsEndpoint == "" {
|
||||
return nil, fmt.Errorf("metrics_endpoint is required when metrics_enabled is true")
|
||||
}
|
||||
pushInterval := config.MetricsPushInterval
|
||||
if pushInterval <= 0 {
|
||||
pushInterval = 15 // default 15 seconds
|
||||
} else if pushInterval > 300 {
|
||||
return nil, fmt.Errorf("metrics_push_interval must be between 1 and 300 seconds, got %d", pushInterval)
|
||||
}
|
||||
metricsConfig := &MetricsConfig{
|
||||
ServiceName: config.ServiceName,
|
||||
Endpoint: config.MetricsEndpoint,
|
||||
Headers: config.Headers,
|
||||
Protocol: config.Protocol,
|
||||
TLSCACert: config.TLSCACert,
|
||||
Insecure: config.Insecure,
|
||||
PushInterval: pushInterval,
|
||||
}
|
||||
p.metricsExporter, err = NewMetricsExporter(p.ctx, metricsConfig)
|
||||
if err != nil {
|
||||
// Clean up trace client if metrics exporter fails
|
||||
if p.client != nil {
|
||||
p.client.Close()
|
||||
}
|
||||
return nil, fmt.Errorf("failed to initialize metrics exporter: %w", err)
|
||||
}
|
||||
logger.Info("OTEL metrics push enabled, pushing to %s every %d seconds", config.MetricsEndpoint, pushInterval)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// GetName function for the OTEL plugin
|
||||
func (p *OtelPlugin) GetName() string {
|
||||
return PluginName
|
||||
}
|
||||
|
||||
// HTTPTransportPreHook is not used for this plugin
|
||||
func (p *OtelPlugin) HTTPTransportPreHook(ctx *schemas.BifrostContext, req *schemas.HTTPRequest) (*schemas.HTTPResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// HTTPTransportPostHook is not used for this plugin
|
||||
func (p *OtelPlugin) HTTPTransportPostHook(ctx *schemas.BifrostContext, req *schemas.HTTPRequest, resp *schemas.HTTPResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HTTPTransportStreamChunkHook passes through streaming chunks unchanged
|
||||
func (p *OtelPlugin) HTTPTransportStreamChunkHook(ctx *schemas.BifrostContext, req *schemas.HTTPRequest, chunk *schemas.BifrostStreamChunk) (*schemas.BifrostStreamChunk, error) {
|
||||
return chunk, nil
|
||||
}
|
||||
|
||||
// ValidateConfig function for the OTEL plugin
|
||||
func (p *OtelPlugin) ValidateConfig(config any) (*Config, error) {
|
||||
var otelConfig Config
|
||||
// Checking if its a string, then we will JSON parse and confirm
|
||||
if configStr, ok := config.(string); ok {
|
||||
if err := sonic.Unmarshal([]byte(configStr), &otelConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Checking if its a map[string]any, then we will JSON parse and confirm
|
||||
if configMap, ok := config.(map[string]any); ok {
|
||||
configString, err := sonic.Marshal(configMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := sonic.Unmarshal([]byte(configString), &otelConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Checking if its a Config, then we will confirm
|
||||
if config, ok := config.(*Config); ok {
|
||||
otelConfig = *config
|
||||
}
|
||||
// Validating fields
|
||||
if otelConfig.CollectorURL == "" {
|
||||
return nil, fmt.Errorf("collector url is required")
|
||||
}
|
||||
if otelConfig.TraceType == "" {
|
||||
return nil, fmt.Errorf("trace type is required")
|
||||
}
|
||||
if otelConfig.Protocol == "" {
|
||||
return nil, fmt.Errorf("protocol is required")
|
||||
}
|
||||
return &otelConfig, nil
|
||||
}
|
||||
|
||||
// PreLLMHook is a no-op - tracing is handled via the Inject method.
|
||||
// The OTEL plugin receives completed traces from TracingMiddleware.
|
||||
func (p *OtelPlugin) PreLLMHook(_ *schemas.BifrostContext, req *schemas.BifrostRequest) (*schemas.BifrostRequest, *schemas.LLMPluginShortCircuit, error) {
|
||||
return req, nil, nil
|
||||
}
|
||||
|
||||
// PostLLMHook is a no-op - tracing is handled via the Inject method.
|
||||
// The OTEL plugin receives completed traces from TracingMiddleware.
|
||||
func (p *OtelPlugin) PostLLMHook(_ *schemas.BifrostContext, resp *schemas.BifrostResponse, bifrostErr *schemas.BifrostError) (*schemas.BifrostResponse, *schemas.BifrostError, error) {
|
||||
return resp, bifrostErr, nil
|
||||
}
|
||||
|
||||
// Inject receives a completed trace and sends it to the OTEL collector.
|
||||
// Implements schemas.ObservabilityPlugin interface.
|
||||
// This method is called asynchronously by TracingMiddleware after the response
|
||||
// has been written to the client.
|
||||
func (p *OtelPlugin) Inject(ctx context.Context, trace *schemas.Trace) error {
|
||||
if trace == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Emit trace to collector if client is initialized
|
||||
if p.client != nil {
|
||||
// Convert schemas.Trace to OTEL ResourceSpan
|
||||
resourceSpan := p.convertTraceToResourceSpan(trace)
|
||||
|
||||
// Emit to collector
|
||||
if err := p.client.Emit(ctx, []*ResourceSpan{resourceSpan}); err != nil {
|
||||
logger.Error("failed to emit trace %s: %v", trace.TraceID, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Record metrics if metrics exporter is enabled
|
||||
if p.metricsExporter != nil {
|
||||
p.recordMetricsFromTrace(ctx, trace)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper functions for type-safe attribute extraction from trace spans
|
||||
func getStringAttr(attrs map[string]any, key string) string {
|
||||
if attrs == nil {
|
||||
return ""
|
||||
}
|
||||
if v, ok := attrs[key].(string); ok {
|
||||
return v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getIntAttr(attrs map[string]any, key string) int {
|
||||
if attrs == nil {
|
||||
return 0
|
||||
}
|
||||
switch v := attrs[key].(type) {
|
||||
case int:
|
||||
return v
|
||||
case int64:
|
||||
return int(v)
|
||||
case float64:
|
||||
return int(v)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func getFloat64Attr(attrs map[string]any, key string) float64 {
|
||||
if attrs == nil {
|
||||
return 0
|
||||
}
|
||||
switch v := attrs[key].(type) {
|
||||
case float64:
|
||||
return v
|
||||
case int:
|
||||
return float64(v)
|
||||
case int64:
|
||||
return float64(v)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// buildSpanAttrs extracts metric dimension attrs from a single attempt span.
|
||||
func buildSpanAttrs(span *schemas.Span) []attribute.KeyValue {
|
||||
attrs := span.Attributes
|
||||
method := getStringAttr(attrs, "request.type")
|
||||
if method == "" {
|
||||
method = span.Name
|
||||
}
|
||||
return BuildBifrostAttributes(
|
||||
getStringAttr(attrs, schemas.AttrProviderName),
|
||||
getStringAttr(attrs, schemas.AttrRequestModel),
|
||||
method,
|
||||
getStringAttr(attrs, schemas.AttrVirtualKeyID),
|
||||
getStringAttr(attrs, schemas.AttrVirtualKeyName),
|
||||
getStringAttr(attrs, schemas.AttrSelectedKeyID),
|
||||
getStringAttr(attrs, schemas.AttrSelectedKeyName),
|
||||
getIntAttr(attrs, schemas.AttrNumberOfRetries),
|
||||
getIntAttr(attrs, schemas.AttrFallbackIndex),
|
||||
getStringAttr(attrs, schemas.AttrTeamID),
|
||||
getStringAttr(attrs, schemas.AttrTeamName),
|
||||
getStringAttr(attrs, schemas.AttrCustomerID),
|
||||
getStringAttr(attrs, schemas.AttrCustomerName),
|
||||
)
|
||||
}
|
||||
|
||||
// recordMetricsFromTrace extracts metrics data from a completed trace and records them
|
||||
// via the OTEL metrics exporter. This is called from Inject after trace emission.
|
||||
//
|
||||
// Per-attempt metrics (upstream_requests, errors, success, latency) are recorded once
|
||||
// per llm.call/retry span so fallback attempts and failed retries are counted with
|
||||
// their own provider/model/fallback_index labels. Per-trace metrics (tokens, cost,
|
||||
// TTFT) are recorded once, keyed off the final (latest) attempt span.
|
||||
func (p *OtelPlugin) recordMetricsFromTrace(ctx context.Context, trace *schemas.Trace) {
|
||||
if trace == nil || p.metricsExporter == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var finalSpan *schemas.Span
|
||||
for _, span := range trace.Spans {
|
||||
if span.Kind != schemas.SpanKindLLMCall && span.Kind != schemas.SpanKindRetry {
|
||||
continue
|
||||
}
|
||||
|
||||
spanAttrs := buildSpanAttrs(span)
|
||||
|
||||
p.metricsExporter.RecordUpstreamRequest(ctx, spanAttrs...)
|
||||
|
||||
if !span.StartTime.IsZero() && !span.EndTime.IsZero() {
|
||||
latencySeconds := span.EndTime.Sub(span.StartTime).Seconds()
|
||||
p.metricsExporter.RecordUpstreamLatency(ctx, latencySeconds, spanAttrs...)
|
||||
}
|
||||
|
||||
if span.Status == schemas.SpanStatusError {
|
||||
p.metricsExporter.RecordErrorRequest(ctx, spanAttrs...)
|
||||
} else {
|
||||
p.metricsExporter.RecordSuccessRequest(ctx, spanAttrs...)
|
||||
}
|
||||
|
||||
if finalSpan == nil || span.EndTime.After(finalSpan.EndTime) {
|
||||
finalSpan = span
|
||||
}
|
||||
}
|
||||
|
||||
if finalSpan == nil {
|
||||
finalSpan = trace.RootSpan
|
||||
}
|
||||
if finalSpan == nil {
|
||||
return
|
||||
}
|
||||
|
||||
attrs := finalSpan.Attributes
|
||||
otelAttrs := buildSpanAttrs(finalSpan)
|
||||
|
||||
// Record token usage - try both naming conventions
|
||||
inputTokens := getIntAttr(attrs, schemas.AttrPromptTokens)
|
||||
if inputTokens == 0 {
|
||||
inputTokens = getIntAttr(attrs, schemas.AttrInputTokens)
|
||||
}
|
||||
if inputTokens > 0 {
|
||||
p.metricsExporter.RecordInputTokens(ctx, int64(inputTokens), otelAttrs...)
|
||||
}
|
||||
|
||||
outputTokens := getIntAttr(attrs, schemas.AttrCompletionTokens)
|
||||
if outputTokens == 0 {
|
||||
outputTokens = getIntAttr(attrs, schemas.AttrOutputTokens)
|
||||
}
|
||||
if outputTokens > 0 {
|
||||
p.metricsExporter.RecordOutputTokens(ctx, int64(outputTokens), otelAttrs...)
|
||||
}
|
||||
|
||||
// Record cost if available
|
||||
cost := getFloat64Attr(attrs, schemas.AttrUsageCost)
|
||||
if cost > 0 {
|
||||
p.metricsExporter.RecordCost(ctx, cost, otelAttrs...)
|
||||
}
|
||||
|
||||
// Record streaming latency metrics if available
|
||||
ttft := getFloat64Attr(attrs, schemas.AttrTimeToFirstToken)
|
||||
if ttft > 0 {
|
||||
// Convert from nanoseconds to seconds if needed (check the unit)
|
||||
p.metricsExporter.RecordStreamFirstTokenLatency(ctx, ttft/1e9, otelAttrs...)
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup function for the OTEL plugin
|
||||
func (p *OtelPlugin) Cleanup() error {
|
||||
if p.cancel != nil {
|
||||
p.cancel()
|
||||
}
|
||||
// Shutdown metrics exporter first
|
||||
if p.metricsExporter != nil {
|
||||
if err := p.metricsExporter.Shutdown(context.Background()); err != nil {
|
||||
logger.Error("failed to shutdown metrics exporter: %v", err)
|
||||
}
|
||||
}
|
||||
if p.client != nil {
|
||||
return p.client.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMetricsExporter returns the metrics exporter for external use (e.g., by telemetry plugin)
|
||||
func (p *OtelPlugin) GetMetricsExporter() *MetricsExporter {
|
||||
return p.metricsExporter
|
||||
}
|
||||
|
||||
// Compile-time check that OtelPlugin implements ObservabilityPlugin
|
||||
var _ schemas.ObservabilityPlugin = (*OtelPlugin)(nil)
|
||||
534
plugins/otel/metrics.go
Normal file
534
plugins/otel/metrics.go
Normal file
@@ -0,0 +1,534 @@
|
||||
package otel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
// MetricsConfig holds configuration for the OTEL metrics exporter
|
||||
type MetricsConfig struct {
|
||||
ServiceName string
|
||||
Endpoint string
|
||||
Headers map[string]string
|
||||
Protocol Protocol
|
||||
TLSCACert string
|
||||
Insecure bool // Skip TLS when true; ignored if TLSCACert is set
|
||||
PushInterval int // in seconds
|
||||
}
|
||||
|
||||
// MetricsExporter handles OTEL metrics export
|
||||
type MetricsExporter struct {
|
||||
provider *sdkmetric.MeterProvider
|
||||
meter metric.Meter
|
||||
|
||||
// Bifrost metrics - counters
|
||||
upstreamRequestsTotal *syncInt64Counter
|
||||
successRequestsTotal *syncInt64Counter
|
||||
errorRequestsTotal *syncInt64Counter
|
||||
inputTokensTotal *syncInt64Counter
|
||||
outputTokensTotal *syncInt64Counter
|
||||
cacheHitsTotal *syncInt64Counter
|
||||
|
||||
// Bifrost metrics - float counters (for cost)
|
||||
costTotal *syncFloat64Counter
|
||||
|
||||
// Bifrost metrics - histograms
|
||||
upstreamLatencySeconds *syncFloat64Histogram
|
||||
streamFirstTokenLatencySeconds *syncFloat64Histogram
|
||||
streamInterTokenLatencySeconds *syncFloat64Histogram
|
||||
|
||||
// HTTP metrics
|
||||
httpRequestsTotal *syncInt64Counter
|
||||
httpRequestDuration *syncFloat64Histogram
|
||||
httpRequestSizeBytes *syncFloat64Histogram
|
||||
httpResponseSizeBytes *syncFloat64Histogram
|
||||
}
|
||||
|
||||
// syncInt64Counter wraps metric.Int64Counter with thread-safe lazy initialization
|
||||
type syncInt64Counter struct {
|
||||
counter metric.Int64Counter
|
||||
once sync.Once
|
||||
name string
|
||||
desc string
|
||||
unit string
|
||||
meter metric.Meter
|
||||
}
|
||||
|
||||
func (c *syncInt64Counter) Add(ctx context.Context, value int64, opts ...metric.AddOption) {
|
||||
c.once.Do(func() {
|
||||
var err error
|
||||
c.counter, err = c.meter.Int64Counter(c.name,
|
||||
metric.WithDescription(c.desc),
|
||||
metric.WithUnit(c.unit),
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error("failed to create counter %s: %v", c.name, err)
|
||||
}
|
||||
})
|
||||
if c.counter != nil {
|
||||
c.counter.Add(ctx, value, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// syncFloat64Counter wraps metric.Float64Counter with thread-safe lazy initialization
|
||||
type syncFloat64Counter struct {
|
||||
counter metric.Float64Counter
|
||||
once sync.Once
|
||||
name string
|
||||
desc string
|
||||
unit string
|
||||
meter metric.Meter
|
||||
}
|
||||
|
||||
func (c *syncFloat64Counter) Add(ctx context.Context, value float64, opts ...metric.AddOption) {
|
||||
c.once.Do(func() {
|
||||
var err error
|
||||
c.counter, err = c.meter.Float64Counter(c.name,
|
||||
metric.WithDescription(c.desc),
|
||||
metric.WithUnit(c.unit),
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error("failed to create float counter %s: %v", c.name, err)
|
||||
}
|
||||
})
|
||||
if c.counter != nil {
|
||||
c.counter.Add(ctx, value, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// syncFloat64Histogram wraps metric.Float64Histogram with thread-safe lazy initialization
|
||||
type syncFloat64Histogram struct {
|
||||
histogram metric.Float64Histogram
|
||||
once sync.Once
|
||||
name string
|
||||
desc string
|
||||
unit string
|
||||
meter metric.Meter
|
||||
}
|
||||
|
||||
func (h *syncFloat64Histogram) Record(ctx context.Context, value float64, opts ...metric.RecordOption) {
|
||||
h.once.Do(func() {
|
||||
var err error
|
||||
h.histogram, err = h.meter.Float64Histogram(h.name,
|
||||
metric.WithDescription(h.desc),
|
||||
metric.WithUnit(h.unit),
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error("failed to create histogram %s: %v", h.name, err)
|
||||
}
|
||||
})
|
||||
if h.histogram != nil {
|
||||
h.histogram.Record(ctx, value, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// NewMetricsExporter creates a new OTEL metrics exporter
|
||||
func NewMetricsExporter(ctx context.Context, config *MetricsConfig) (*MetricsExporter, error) {
|
||||
// Generate a unique instance ID for this node
|
||||
instanceID, err := os.Hostname()
|
||||
if err != nil {
|
||||
instanceID = fmt.Sprintf("bifrost-%d", time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// Create resource with service info
|
||||
res, err := resource.Merge(
|
||||
resource.Default(),
|
||||
resource.NewSchemaless(
|
||||
semconv.ServiceName(config.ServiceName),
|
||||
semconv.ServiceInstanceID(instanceID),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create resource: %w", err)
|
||||
}
|
||||
|
||||
// Create exporter based on protocol
|
||||
var exporter sdkmetric.Exporter
|
||||
if config.Protocol == ProtocolGRPC {
|
||||
exporter, err = createGRPCExporter(ctx, config)
|
||||
} else {
|
||||
exporter, err = createHTTPExporter(ctx, config)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create exporter: %w", err)
|
||||
}
|
||||
|
||||
// Create meter provider with periodic reader
|
||||
provider := sdkmetric.NewMeterProvider(
|
||||
sdkmetric.WithResource(res),
|
||||
sdkmetric.WithReader(
|
||||
sdkmetric.NewPeriodicReader(
|
||||
exporter,
|
||||
sdkmetric.WithInterval(time.Duration(config.PushInterval)*time.Second),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
// Set as global provider
|
||||
otel.SetMeterProvider(provider)
|
||||
|
||||
// Create meter
|
||||
meter := provider.Meter("bifrost",
|
||||
metric.WithInstrumentationVersion("1.0.0"),
|
||||
)
|
||||
|
||||
// Create metrics exporter
|
||||
m := &MetricsExporter{
|
||||
provider: provider,
|
||||
meter: meter,
|
||||
}
|
||||
|
||||
// Initialize metrics with lazy loading wrappers
|
||||
m.initMetrics()
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// validateCACertPath validates the CA certificate path to prevent path traversal attacks.
|
||||
// It ensures the path is absolute, cleaned of traversal sequences, and exists as a regular file.
|
||||
func validateCACertPath(certPath string) error {
|
||||
if certPath == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clean the path to resolve any .. or . components
|
||||
cleanPath := filepath.Clean(certPath)
|
||||
|
||||
// Require absolute paths to prevent relative path attacks
|
||||
if !filepath.IsAbs(cleanPath) {
|
||||
return fmt.Errorf("TLS CA cert path must be absolute: %s", certPath)
|
||||
}
|
||||
|
||||
// Check that the cleaned path doesn't differ significantly from input
|
||||
// (indicates attempted traversal)
|
||||
if cleanPath != filepath.Clean(filepath.FromSlash(certPath)) {
|
||||
return fmt.Errorf("invalid TLS CA cert path: %s", certPath)
|
||||
}
|
||||
|
||||
// Verify the file exists and is not a symlink
|
||||
info, err := os.Lstat(cleanPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("TLS CA cert path not accessible: %w", err)
|
||||
}
|
||||
// Reject symlinks to prevent symlink-based path traversal
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
return fmt.Errorf("TLS CA cert path cannot be a symlink: %s", certPath)
|
||||
}
|
||||
if !info.Mode().IsRegular() {
|
||||
return fmt.Errorf("TLS CA cert path is not a regular file: %s", certPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createHTTPExporter(ctx context.Context, config *MetricsConfig) (sdkmetric.Exporter, error) {
|
||||
opts := []otlpmetrichttp.Option{
|
||||
otlpmetrichttp.WithEndpointURL(config.Endpoint),
|
||||
}
|
||||
|
||||
if len(config.Headers) > 0 {
|
||||
opts = append(opts, otlpmetrichttp.WithHeaders(config.Headers))
|
||||
}
|
||||
|
||||
// TLS priority: custom CA > system roots > insecure
|
||||
if config.TLSCACert != "" {
|
||||
// Validate the CA cert path to prevent path traversal attacks
|
||||
if err := validateCACertPath(config.TLSCACert); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Use custom CA certificate
|
||||
caCert, err := os.ReadFile(config.TLSCACert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read CA cert: %w", err)
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
if !caCertPool.AppendCertsFromPEM(caCert) {
|
||||
return nil, fmt.Errorf("failed to parse CA cert")
|
||||
}
|
||||
tlsConfig := &tls.Config{
|
||||
RootCAs: caCertPool,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
opts = append(opts, otlpmetrichttp.WithTLSClientConfig(tlsConfig))
|
||||
} else if config.Insecure {
|
||||
// Skip TLS entirely
|
||||
opts = append(opts, otlpmetrichttp.WithInsecure())
|
||||
} else {
|
||||
// Use system root CAs (empty tls.Config uses system roots)
|
||||
opts = append(opts, otlpmetrichttp.WithTLSClientConfig(&tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}))
|
||||
}
|
||||
|
||||
return otlpmetrichttp.New(ctx, opts...)
|
||||
}
|
||||
|
||||
func createGRPCExporter(ctx context.Context, config *MetricsConfig) (sdkmetric.Exporter, error) {
|
||||
opts := []otlpmetricgrpc.Option{
|
||||
otlpmetricgrpc.WithEndpoint(config.Endpoint),
|
||||
}
|
||||
|
||||
if len(config.Headers) > 0 {
|
||||
opts = append(opts, otlpmetricgrpc.WithHeaders(config.Headers))
|
||||
}
|
||||
|
||||
// TLS priority: custom CA > system roots > insecure
|
||||
if config.TLSCACert != "" {
|
||||
// Validate the CA cert path to prevent path traversal attacks
|
||||
if err := validateCACertPath(config.TLSCACert); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Use custom CA certificate with MinVersion
|
||||
caCert, err := os.ReadFile(config.TLSCACert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read CA cert: %w", err)
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
if !caCertPool.AppendCertsFromPEM(caCert) {
|
||||
return nil, fmt.Errorf("failed to parse CA cert")
|
||||
}
|
||||
tlsConfig := &tls.Config{
|
||||
RootCAs: caCertPool,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
creds := credentials.NewTLS(tlsConfig)
|
||||
opts = append(opts, otlpmetricgrpc.WithTLSCredentials(creds))
|
||||
} else if config.Insecure {
|
||||
// Skip TLS entirely
|
||||
opts = append(opts, otlpmetricgrpc.WithTLSCredentials(insecure.NewCredentials()))
|
||||
} else {
|
||||
// Use system root CAs with MinVersion
|
||||
tlsConfig := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
creds := credentials.NewTLS(tlsConfig)
|
||||
opts = append(opts, otlpmetricgrpc.WithTLSCredentials(creds))
|
||||
}
|
||||
|
||||
return otlpmetricgrpc.New(ctx, opts...)
|
||||
}
|
||||
|
||||
func (m *MetricsExporter) initMetrics() {
|
||||
// Bifrost upstream metrics
|
||||
m.upstreamRequestsTotal = &syncInt64Counter{
|
||||
name: "bifrost_upstream_requests_total",
|
||||
desc: "Total number of requests forwarded to upstream providers by Bifrost",
|
||||
unit: "{request}",
|
||||
meter: m.meter,
|
||||
}
|
||||
|
||||
m.successRequestsTotal = &syncInt64Counter{
|
||||
name: "bifrost_success_requests_total",
|
||||
desc: "Total number of successful requests forwarded to upstream providers by Bifrost",
|
||||
unit: "{request}",
|
||||
meter: m.meter,
|
||||
}
|
||||
|
||||
m.errorRequestsTotal = &syncInt64Counter{
|
||||
name: "bifrost_error_requests_total",
|
||||
desc: "Total number of error requests forwarded to upstream providers by Bifrost",
|
||||
unit: "{request}",
|
||||
meter: m.meter,
|
||||
}
|
||||
|
||||
m.inputTokensTotal = &syncInt64Counter{
|
||||
name: "bifrost_input_tokens_total",
|
||||
desc: "Total number of input tokens forwarded to upstream providers by Bifrost",
|
||||
unit: "{token}",
|
||||
meter: m.meter,
|
||||
}
|
||||
|
||||
m.outputTokensTotal = &syncInt64Counter{
|
||||
name: "bifrost_output_tokens_total",
|
||||
desc: "Total number of output tokens forwarded to upstream providers by Bifrost",
|
||||
unit: "{token}",
|
||||
meter: m.meter,
|
||||
}
|
||||
|
||||
m.cacheHitsTotal = &syncInt64Counter{
|
||||
name: "bifrost_cache_hits_total",
|
||||
desc: "Total number of cache hits forwarded to upstream providers by Bifrost",
|
||||
unit: "{hit}",
|
||||
meter: m.meter,
|
||||
}
|
||||
|
||||
m.costTotal = &syncFloat64Counter{
|
||||
name: "bifrost_cost_total",
|
||||
desc: "Total cost in USD for requests to upstream providers",
|
||||
unit: "USD",
|
||||
meter: m.meter,
|
||||
}
|
||||
|
||||
m.upstreamLatencySeconds = &syncFloat64Histogram{
|
||||
name: "bifrost_upstream_latency_seconds",
|
||||
desc: "Latency of requests forwarded to upstream providers by Bifrost",
|
||||
unit: "s",
|
||||
meter: m.meter,
|
||||
}
|
||||
|
||||
m.streamFirstTokenLatencySeconds = &syncFloat64Histogram{
|
||||
name: "bifrost_stream_first_token_latency_seconds",
|
||||
desc: "Latency of the first token of a stream response",
|
||||
unit: "s",
|
||||
meter: m.meter,
|
||||
}
|
||||
|
||||
m.streamInterTokenLatencySeconds = &syncFloat64Histogram{
|
||||
name: "bifrost_stream_inter_token_latency_seconds",
|
||||
desc: "Latency of the intermediate tokens of a stream response",
|
||||
unit: "s",
|
||||
meter: m.meter,
|
||||
}
|
||||
|
||||
// HTTP metrics
|
||||
m.httpRequestsTotal = &syncInt64Counter{
|
||||
name: "http_requests_total",
|
||||
desc: "Total number of HTTP requests",
|
||||
unit: "{request}",
|
||||
meter: m.meter,
|
||||
}
|
||||
|
||||
m.httpRequestDuration = &syncFloat64Histogram{
|
||||
name: "http_request_duration_seconds",
|
||||
desc: "Duration of HTTP requests",
|
||||
unit: "s",
|
||||
meter: m.meter,
|
||||
}
|
||||
|
||||
m.httpRequestSizeBytes = &syncFloat64Histogram{
|
||||
name: "http_request_size_bytes",
|
||||
desc: "Size of HTTP requests",
|
||||
unit: "By",
|
||||
meter: m.meter,
|
||||
}
|
||||
|
||||
m.httpResponseSizeBytes = &syncFloat64Histogram{
|
||||
name: "http_response_size_bytes",
|
||||
desc: "Size of HTTP responses",
|
||||
unit: "By",
|
||||
meter: m.meter,
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown gracefully shuts down the metrics exporter
|
||||
func (m *MetricsExporter) Shutdown(ctx context.Context) error {
|
||||
if m.provider != nil {
|
||||
return m.provider.Shutdown(ctx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecordUpstreamRequest records an upstream request metric
|
||||
func (m *MetricsExporter) RecordUpstreamRequest(ctx context.Context, attrs ...attribute.KeyValue) {
|
||||
m.upstreamRequestsTotal.Add(ctx, 1, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// RecordSuccessRequest records a successful request metric
|
||||
func (m *MetricsExporter) RecordSuccessRequest(ctx context.Context, attrs ...attribute.KeyValue) {
|
||||
m.successRequestsTotal.Add(ctx, 1, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// RecordErrorRequest records an error request metric
|
||||
func (m *MetricsExporter) RecordErrorRequest(ctx context.Context, attrs ...attribute.KeyValue) {
|
||||
m.errorRequestsTotal.Add(ctx, 1, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// RecordInputTokens records input tokens metric
|
||||
func (m *MetricsExporter) RecordInputTokens(ctx context.Context, count int64, attrs ...attribute.KeyValue) {
|
||||
m.inputTokensTotal.Add(ctx, count, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// RecordOutputTokens records output tokens metric
|
||||
func (m *MetricsExporter) RecordOutputTokens(ctx context.Context, count int64, attrs ...attribute.KeyValue) {
|
||||
m.outputTokensTotal.Add(ctx, count, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// RecordCacheHit records a cache hit metric
|
||||
func (m *MetricsExporter) RecordCacheHit(ctx context.Context, attrs ...attribute.KeyValue) {
|
||||
m.cacheHitsTotal.Add(ctx, 1, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// RecordCost records cost metric
|
||||
func (m *MetricsExporter) RecordCost(ctx context.Context, cost float64, attrs ...attribute.KeyValue) {
|
||||
m.costTotal.Add(ctx, cost, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// RecordUpstreamLatency records upstream latency metric
|
||||
func (m *MetricsExporter) RecordUpstreamLatency(ctx context.Context, latencySeconds float64, attrs ...attribute.KeyValue) {
|
||||
m.upstreamLatencySeconds.Record(ctx, latencySeconds, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// RecordStreamFirstTokenLatency records first token latency metric
|
||||
func (m *MetricsExporter) RecordStreamFirstTokenLatency(ctx context.Context, latencySeconds float64, attrs ...attribute.KeyValue) {
|
||||
m.streamFirstTokenLatencySeconds.Record(ctx, latencySeconds, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// RecordStreamInterTokenLatency records inter-token latency metric
|
||||
func (m *MetricsExporter) RecordStreamInterTokenLatency(ctx context.Context, latencySeconds float64, attrs ...attribute.KeyValue) {
|
||||
m.streamInterTokenLatencySeconds.Record(ctx, latencySeconds, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// RecordHTTPRequest records an HTTP request metric
|
||||
func (m *MetricsExporter) RecordHTTPRequest(ctx context.Context, attrs ...attribute.KeyValue) {
|
||||
m.httpRequestsTotal.Add(ctx, 1, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// RecordHTTPRequestDuration records HTTP request duration metric
|
||||
func (m *MetricsExporter) RecordHTTPRequestDuration(ctx context.Context, durationSeconds float64, attrs ...attribute.KeyValue) {
|
||||
m.httpRequestDuration.Record(ctx, durationSeconds, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// RecordHTTPRequestSize records HTTP request size metric
|
||||
func (m *MetricsExporter) RecordHTTPRequestSize(ctx context.Context, sizeBytes float64, attrs ...attribute.KeyValue) {
|
||||
m.httpRequestSizeBytes.Record(ctx, sizeBytes, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// RecordHTTPResponseSize records HTTP response size metric
|
||||
func (m *MetricsExporter) RecordHTTPResponseSize(ctx context.Context, sizeBytes float64, attrs ...attribute.KeyValue) {
|
||||
m.httpResponseSizeBytes.Record(ctx, sizeBytes, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// BuildBifrostAttributes builds common Bifrost metric attributes
|
||||
func BuildBifrostAttributes(provider, model, method, virtualKeyID, virtualKeyName, selectedKeyID, selectedKeyName string, numberOfRetries, fallbackIndex int, teamID, teamName, customerID, customerName string) []attribute.KeyValue {
|
||||
return []attribute.KeyValue{
|
||||
attribute.String("provider", provider),
|
||||
attribute.String("model", model),
|
||||
attribute.String("method", method),
|
||||
attribute.String("virtual_key_id", virtualKeyID),
|
||||
attribute.String("virtual_key_name", virtualKeyName),
|
||||
attribute.String("selected_key_id", selectedKeyID),
|
||||
attribute.String("selected_key_name", selectedKeyName),
|
||||
attribute.Int("number_of_retries", numberOfRetries),
|
||||
attribute.Int("fallback_index", fallbackIndex),
|
||||
attribute.String("team_id", teamID),
|
||||
attribute.String("team_name", teamName),
|
||||
attribute.String("customer_id", customerID),
|
||||
attribute.String("customer_name", customerName),
|
||||
}
|
||||
}
|
||||
|
||||
// BuildHTTPAttributes builds common HTTP metric attributes
|
||||
func BuildHTTPAttributes(path, method, status string) []attribute.KeyValue {
|
||||
return []attribute.KeyValue{
|
||||
attribute.String("path", path),
|
||||
attribute.String("method", method),
|
||||
attribute.String("status", status),
|
||||
}
|
||||
}
|
||||
48
plugins/otel/types.go
Normal file
48
plugins/otel/types.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package otel
|
||||
|
||||
import (
|
||||
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
|
||||
)
|
||||
|
||||
// ResourceSpan is a trace in the OpenTelemetry format
|
||||
type ResourceSpan = tracepb.ResourceSpans
|
||||
|
||||
// ScopeSpan is a group of spans in the OpenTelemetry format
|
||||
type ScopeSpan = tracepb.ScopeSpans
|
||||
|
||||
// Span is a span in the OpenTelemetry format
|
||||
type Span = tracepb.Span
|
||||
|
||||
// Event is an event in a span
|
||||
type Event = tracepb.Span_Event
|
||||
|
||||
// KeyValue is a key-value pair in the OpenTelemetry format
|
||||
type KeyValue = commonpb.KeyValue
|
||||
|
||||
// AnyValue is a value in the OpenTelemetry format
|
||||
type AnyValue = commonpb.AnyValue
|
||||
|
||||
// StringValue is a string value in the OpenTelemetry format
|
||||
type StringValue = commonpb.AnyValue_StringValue
|
||||
|
||||
// IntValue is an integer value in the OpenTelemetry format
|
||||
type IntValue = commonpb.AnyValue_IntValue
|
||||
|
||||
// DoubleValue is a double value in the OpenTelemetry format
|
||||
type DoubleValue = commonpb.AnyValue_DoubleValue
|
||||
|
||||
// BoolValue is a boolean value in the OpenTelemetry format
|
||||
type BoolValue = commonpb.AnyValue_BoolValue
|
||||
|
||||
// ArrayValue is an array value in the OpenTelemetry format
|
||||
type ArrayValue = commonpb.AnyValue_ArrayValue
|
||||
|
||||
// ArrayValueValue is an array value in the OpenTelemetry format
|
||||
type ArrayValueValue = commonpb.ArrayValue
|
||||
|
||||
// ListValue is a list value in the OpenTelemetry format
|
||||
type ListValue = commonpb.AnyValue_KvlistValue
|
||||
|
||||
// KeyValueList is a list value in the OpenTelemetry format
|
||||
type KeyValueList = commonpb.KeyValueList
|
||||
1
plugins/otel/version
Normal file
1
plugins/otel/version
Normal file
@@ -0,0 +1 @@
|
||||
1.2.4
|
||||
Reference in New Issue
Block a user