first commit
This commit is contained in:
0
plugins/logging/changelog.md
Normal file
0
plugins/logging/changelog.md
Normal file
159
plugins/logging/go.mod
Normal file
159
plugins/logging/go.mod
Normal file
@@ -0,0 +1,159 @@
|
||||
module github.com/maximhq/bifrost/plugins/logging
|
||||
|
||||
go 1.26.2
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.15.0
|
||||
github.com/maximhq/bifrost/core v1.5.4
|
||||
github.com/maximhq/bifrost/framework v1.3.4
|
||||
)
|
||||
|
||||
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/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // 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/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/klauspost/cpuid/v2 v2.3.0 // 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/twitchyliquid64/golang-asm v0.15.1 // 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 v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.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/arch v0.23.0 // 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/sys v0.42.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
|
||||
google.golang.org/grpc v1.80.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // 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
|
||||
)
|
||||
389
plugins/logging/go.sum
Normal file
389
plugins/logging/go.sum
Normal file
@@ -0,0 +1,389 @@
|
||||
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/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/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/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.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=
|
||||
1293
plugins/logging/main.go
Normal file
1293
plugins/logging/main.go
Normal file
File diff suppressed because it is too large
Load Diff
1450
plugins/logging/operations.go
Normal file
1450
plugins/logging/operations.go
Normal file
File diff suppressed because it is too large
Load Diff
627
plugins/logging/operations_test.go
Normal file
627
plugins/logging/operations_test.go
Normal file
@@ -0,0 +1,627 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/maximhq/bifrost/core/schemas"
|
||||
"github.com/maximhq/bifrost/framework/logstore"
|
||||
)
|
||||
|
||||
type testLogger struct{}
|
||||
|
||||
func (testLogger) Debug(string, ...any) {}
|
||||
func (testLogger) Info(string, ...any) {}
|
||||
func (testLogger) Warn(string, ...any) {}
|
||||
func (testLogger) Error(string, ...any) {}
|
||||
func (testLogger) Fatal(string, ...any) {}
|
||||
func (testLogger) SetLevel(schemas.LogLevel) {}
|
||||
func (testLogger) SetOutputType(schemas.LoggerOutputType) {}
|
||||
func (testLogger) LogHTTPRequest(schemas.LogLevel, string) schemas.LogEventBuilder {
|
||||
return schemas.NoopLogEvent
|
||||
}
|
||||
|
||||
func newTestStore(t *testing.T) logstore.LogStore {
|
||||
t.Helper()
|
||||
|
||||
store, err := logstore.NewLogStore(context.Background(), &logstore.Config{
|
||||
Enabled: true,
|
||||
Type: logstore.LogStoreTypeSQLite,
|
||||
Config: &logstore.SQLiteConfig{
|
||||
Path: filepath.Join(t.TempDir(), "logging.db"),
|
||||
},
|
||||
}, testLogger{})
|
||||
if err != nil {
|
||||
t.Fatalf("NewLogStore() error = %v", err)
|
||||
}
|
||||
return store
|
||||
}
|
||||
|
||||
func TestUpdateLogEntryPreservesResponsesInputContentSummary(t *testing.T) {
|
||||
store := newTestStore(t)
|
||||
plugin := &LoggerPlugin{
|
||||
store: store,
|
||||
logger: testLogger{},
|
||||
}
|
||||
|
||||
requestID := "req-1"
|
||||
now := time.Now().UTC()
|
||||
inputText := "request-side text"
|
||||
initial := &InitialLogData{
|
||||
Object: "responses",
|
||||
Provider: "openai",
|
||||
Model: "gpt-4o-mini",
|
||||
ResponsesInputHistory: []schemas.ResponsesMessage{{
|
||||
Content: &schemas.ResponsesMessageContent{
|
||||
ContentStr: &inputText,
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
if err := plugin.insertInitialLogEntry(context.Background(), requestID, "", now, 0, nil, initial); err != nil {
|
||||
t.Fatalf("insertInitialLogEntry() error = %v", err)
|
||||
}
|
||||
|
||||
responsesText := "responses output"
|
||||
update := &UpdateLogData{
|
||||
Status: "success",
|
||||
ResponsesOutput: []schemas.ResponsesMessage{{
|
||||
Content: &schemas.ResponsesMessageContent{
|
||||
ContentStr: &responsesText,
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
if err := plugin.updateLogEntry(context.Background(), requestID, "", "", 10, "", "", "", "", 0, nil, "", update); err != nil {
|
||||
t.Fatalf("updateLogEntry() error = %v", err)
|
||||
}
|
||||
|
||||
logEntry, err := store.FindByID(context.Background(), requestID)
|
||||
if err != nil {
|
||||
t.Fatalf("FindByID() error = %v", err)
|
||||
}
|
||||
if !strings.Contains(logEntry.ContentSummary, inputText) {
|
||||
t.Fatalf("expected content summary to preserve responses input, got %q", logEntry.ContentSummary)
|
||||
}
|
||||
if strings.Contains(logEntry.ContentSummary, responsesText) {
|
||||
t.Fatalf("expected content summary to avoid overwriting with responses output-only data, got %q", logEntry.ContentSummary)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateLogEntryUpdatesContentSummaryForChatOutput(t *testing.T) {
|
||||
store := newTestStore(t)
|
||||
plugin := &LoggerPlugin{
|
||||
store: store,
|
||||
logger: testLogger{},
|
||||
}
|
||||
|
||||
requestID := "req-chat"
|
||||
now := time.Now().UTC()
|
||||
initial := &InitialLogData{
|
||||
Object: "chat_completion",
|
||||
Provider: "openai",
|
||||
Model: "gpt-4o-mini",
|
||||
}
|
||||
|
||||
if err := plugin.insertInitialLogEntry(context.Background(), requestID, "", now, 0, nil, initial); err != nil {
|
||||
t.Fatalf("insertInitialLogEntry() error = %v", err)
|
||||
}
|
||||
|
||||
chatText := "assistant output"
|
||||
update := &UpdateLogData{
|
||||
Status: "success",
|
||||
ChatOutput: &schemas.ChatMessage{
|
||||
Role: schemas.ChatMessageRoleAssistant,
|
||||
Content: &schemas.ChatMessageContent{
|
||||
ContentStr: &chatText,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := plugin.updateLogEntry(context.Background(), requestID, "", "", 10, "", "", "", "", 0, nil, "", update); err != nil {
|
||||
t.Fatalf("updateLogEntry() error = %v", err)
|
||||
}
|
||||
|
||||
logEntry, err := store.FindByID(context.Background(), requestID)
|
||||
if err != nil {
|
||||
t.Fatalf("FindByID() error = %v", err)
|
||||
}
|
||||
if !strings.Contains(logEntry.ContentSummary, chatText) {
|
||||
t.Fatalf("expected content summary to include chat output, got %q", logEntry.ContentSummary)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateLogEntrySuppressesChatOutputWhenContentLoggingDisabled(t *testing.T) {
|
||||
store := newTestStore(t)
|
||||
disableContentLogging := true
|
||||
plugin := &LoggerPlugin{
|
||||
store: store,
|
||||
logger: testLogger{},
|
||||
disableContentLogging: &disableContentLogging,
|
||||
}
|
||||
|
||||
requestID := "req-chat-disabled"
|
||||
now := time.Now().UTC()
|
||||
initial := &InitialLogData{
|
||||
Object: "chat_completion",
|
||||
Provider: "openai",
|
||||
Model: "gpt-4o-mini",
|
||||
}
|
||||
|
||||
if err := plugin.insertInitialLogEntry(context.Background(), requestID, "", now, 0, nil, initial); err != nil {
|
||||
t.Fatalf("insertInitialLogEntry() error = %v", err)
|
||||
}
|
||||
|
||||
chatText := "assistant output should not be logged"
|
||||
update := &UpdateLogData{
|
||||
Status: "success",
|
||||
ChatOutput: &schemas.ChatMessage{
|
||||
Role: schemas.ChatMessageRoleAssistant,
|
||||
Content: &schemas.ChatMessageContent{
|
||||
ContentStr: &chatText,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := plugin.updateLogEntry(context.Background(), requestID, "", "", 10, "", "", "", "", 0, nil, "", update); err != nil {
|
||||
t.Fatalf("updateLogEntry() error = %v", err)
|
||||
}
|
||||
|
||||
logEntry, err := store.FindByID(context.Background(), requestID)
|
||||
if err != nil {
|
||||
t.Fatalf("FindByID() error = %v", err)
|
||||
}
|
||||
if logEntry.OutputMessage != "" {
|
||||
t.Fatalf("expected output_message to be suppressed, got %q", logEntry.OutputMessage)
|
||||
}
|
||||
if strings.Contains(logEntry.ContentSummary, chatText) {
|
||||
t.Fatalf("expected content summary to suppress chat output, got %q", logEntry.ContentSummary)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreOrEnqueueRetryPreservesAllEntries(t *testing.T) {
|
||||
// Simulate fallback/retry scenario where multiple PostLLMHook calls
|
||||
// store entries under the same traceID. All entries must be preserved.
|
||||
plugin := &LoggerPlugin{
|
||||
logger: testLogger{},
|
||||
writeQueue: make(chan *writeQueueEntry, 10),
|
||||
}
|
||||
|
||||
traceID := "trace-retry-test"
|
||||
ctx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
|
||||
ctx.SetValue(schemas.BifrostContextKeyTraceID, traceID)
|
||||
|
||||
// Simulate 3 retry attempts storing entries under the same traceID
|
||||
entry1 := &logstore.Log{ID: "req-attempt-1", Model: "gpt-4o"}
|
||||
entry2 := &logstore.Log{ID: "req-attempt-2", Model: "gpt-4o"}
|
||||
entry3 := &logstore.Log{ID: "req-attempt-3", Model: "claude-3-5-sonnet"}
|
||||
|
||||
plugin.storeOrEnqueueEntry(ctx, entry1, nil)
|
||||
plugin.storeOrEnqueueEntry(ctx, entry2, nil)
|
||||
plugin.storeOrEnqueueEntry(ctx, entry3, nil)
|
||||
|
||||
// Verify all 3 entries are stored
|
||||
val, ok := plugin.pendingLogsToInject.Load(traceID)
|
||||
if !ok {
|
||||
t.Fatal("expected pending entries for traceID, got none")
|
||||
}
|
||||
pending, ok := val.(*pendingInjectEntries)
|
||||
if !ok {
|
||||
t.Fatal("expected *pendingInjectEntries type")
|
||||
}
|
||||
if len(pending.entries) != 3 {
|
||||
t.Fatalf("expected 3 entries, got %d", len(pending.entries))
|
||||
}
|
||||
if pending.entries[0].ID != "req-attempt-1" || pending.entries[1].ID != "req-attempt-2" || pending.entries[2].ID != "req-attempt-3" {
|
||||
t.Fatalf("entries not in expected order: %v, %v, %v", pending.entries[0].ID, pending.entries[1].ID, pending.entries[2].ID)
|
||||
}
|
||||
|
||||
// Now test Inject flushes all entries with plugin logs attached
|
||||
trace := &schemas.Trace{
|
||||
TraceID: traceID,
|
||||
PluginLogs: []schemas.PluginLogEntry{
|
||||
{PluginName: "hello-world", Level: schemas.LogLevelInfo, Message: "test log"},
|
||||
},
|
||||
}
|
||||
|
||||
if err := plugin.Inject(context.Background(), trace); err != nil {
|
||||
t.Fatalf("Inject() error = %v", err)
|
||||
}
|
||||
|
||||
// Verify all 3 entries were enqueued to writeQueue
|
||||
if len(plugin.writeQueue) != 3 {
|
||||
t.Fatalf("expected 3 entries in writeQueue, got %d", len(plugin.writeQueue))
|
||||
}
|
||||
|
||||
// Verify plugin logs were attached to each entry
|
||||
for i := 0; i < 3; i++ {
|
||||
qe := <-plugin.writeQueue
|
||||
if qe.log.PluginLogs == "" {
|
||||
t.Fatalf("entry %d: expected PluginLogs to be set", i)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify pendingLogsToInject was cleaned up
|
||||
if _, ok := plugin.pendingLogsToInject.Load(traceID); ok {
|
||||
t.Fatal("expected pendingLogsToInject to be cleaned up after Inject")
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyRealtimeOutputToEntryBackfillsUserTranscriptFromRawRequest(t *testing.T) {
|
||||
plugin := &LoggerPlugin{}
|
||||
entry := &logstore.Log{}
|
||||
|
||||
assistantText := "Hello!"
|
||||
messageType := schemas.ResponsesMessageTypeMessage
|
||||
assistantRole := schemas.ResponsesInputMessageRoleAssistant
|
||||
result := &schemas.BifrostResponse{
|
||||
ResponsesResponse: &schemas.BifrostResponsesResponse{
|
||||
Output: []schemas.ResponsesMessage{{
|
||||
Type: &messageType,
|
||||
Role: &assistantRole,
|
||||
Content: &schemas.ResponsesMessageContent{
|
||||
ContentStr: &assistantText,
|
||||
},
|
||||
}},
|
||||
ExtraFields: schemas.BifrostResponseExtraFields{
|
||||
RequestType: schemas.RealtimeRequest,
|
||||
RawRequest: `{"type":"conversation.item.input_audio_transcription.completed","transcript":"Hello."}`,
|
||||
RawResponse: `{"type":"response.done"}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
plugin.applyRealtimeOutputToEntry(entry, result, true)
|
||||
if err := entry.SerializeFields(); err != nil {
|
||||
t.Fatalf("SerializeFields() error = %v", err)
|
||||
}
|
||||
|
||||
if len(entry.InputHistoryParsed) != 1 {
|
||||
t.Fatalf("len(InputHistoryParsed) = %d, want 1", len(entry.InputHistoryParsed))
|
||||
}
|
||||
if entry.InputHistoryParsed[0].Role != schemas.ChatMessageRoleUser {
|
||||
t.Fatalf("InputHistoryParsed[0].Role = %q, want user", entry.InputHistoryParsed[0].Role)
|
||||
}
|
||||
if entry.InputHistoryParsed[0].Content == nil || entry.InputHistoryParsed[0].Content.ContentStr == nil || *entry.InputHistoryParsed[0].Content.ContentStr != "Hello." {
|
||||
t.Fatalf("InputHistoryParsed[0] = %+v, want transcript", entry.InputHistoryParsed[0])
|
||||
}
|
||||
if entry.OutputMessageParsed == nil || entry.OutputMessageParsed.Content == nil || entry.OutputMessageParsed.Content.ContentStr == nil || *entry.OutputMessageParsed.Content.ContentStr != assistantText {
|
||||
t.Fatalf("OutputMessageParsed = %+v, want assistant text", entry.OutputMessageParsed)
|
||||
}
|
||||
if !strings.Contains(entry.ContentSummary, "Hello.") {
|
||||
t.Fatalf("ContentSummary = %q, want user transcript", entry.ContentSummary)
|
||||
}
|
||||
if !strings.Contains(entry.ContentSummary, "Hello!") {
|
||||
t.Fatalf("ContentSummary = %q, want assistant text", entry.ContentSummary)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyRealtimeOutputToEntryBackfillsMissingTranscriptPlaceholder(t *testing.T) {
|
||||
plugin := &LoggerPlugin{}
|
||||
entry := &logstore.Log{}
|
||||
|
||||
assistantText := "Hi there!"
|
||||
messageType := schemas.ResponsesMessageTypeMessage
|
||||
assistantRole := schemas.ResponsesInputMessageRoleAssistant
|
||||
result := &schemas.BifrostResponse{
|
||||
ResponsesResponse: &schemas.BifrostResponsesResponse{
|
||||
Output: []schemas.ResponsesMessage{{
|
||||
Type: &messageType,
|
||||
Role: &assistantRole,
|
||||
Content: &schemas.ResponsesMessageContent{
|
||||
ContentStr: &assistantText,
|
||||
},
|
||||
}},
|
||||
ExtraFields: schemas.BifrostResponseExtraFields{
|
||||
RequestType: schemas.RealtimeRequest,
|
||||
RawRequest: `{"type":"conversation.item.input_audio_transcription.completed","transcript":""}`,
|
||||
RawResponse: `{"type":"response.done"}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
plugin.applyRealtimeOutputToEntry(entry, result, true)
|
||||
if err := entry.SerializeFields(); err != nil {
|
||||
t.Fatalf("SerializeFields() error = %v", err)
|
||||
}
|
||||
|
||||
if len(entry.InputHistoryParsed) != 1 {
|
||||
t.Fatalf("len(InputHistoryParsed) = %d, want 1", len(entry.InputHistoryParsed))
|
||||
}
|
||||
if entry.InputHistoryParsed[0].Content == nil || entry.InputHistoryParsed[0].Content.ContentStr == nil || *entry.InputHistoryParsed[0].Content.ContentStr != realtimeMissingTranscriptText {
|
||||
t.Fatalf("InputHistoryParsed[0] = %+v, want missing transcript placeholder", entry.InputHistoryParsed[0])
|
||||
}
|
||||
if !strings.Contains(entry.ContentSummary, realtimeMissingTranscriptText) {
|
||||
t.Fatalf("ContentSummary = %q, want missing transcript placeholder", entry.ContentSummary)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyRealtimeOutputToEntryBackfillsDoneMissingTranscriptPlaceholder(t *testing.T) {
|
||||
plugin := &LoggerPlugin{}
|
||||
entry := &logstore.Log{}
|
||||
|
||||
assistantText := "Hi there!"
|
||||
messageType := schemas.ResponsesMessageTypeMessage
|
||||
assistantRole := schemas.ResponsesInputMessageRoleAssistant
|
||||
result := &schemas.BifrostResponse{
|
||||
ResponsesResponse: &schemas.BifrostResponsesResponse{
|
||||
Output: []schemas.ResponsesMessage{{
|
||||
Type: &messageType,
|
||||
Role: &assistantRole,
|
||||
Content: &schemas.ResponsesMessageContent{
|
||||
ContentStr: &assistantText,
|
||||
},
|
||||
}},
|
||||
ExtraFields: schemas.BifrostResponseExtraFields{
|
||||
RequestType: schemas.RealtimeRequest,
|
||||
RawRequest: `{"type":"conversation.item.done","item":{"id":"item_user","type":"message","role":"user","status":"completed","content":[{"type":"input_audio","transcript":null}]}}`,
|
||||
RawResponse: `{"type":"response.done"}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
plugin.applyRealtimeOutputToEntry(entry, result, true)
|
||||
if err := entry.SerializeFields(); err != nil {
|
||||
t.Fatalf("SerializeFields() error = %v", err)
|
||||
}
|
||||
|
||||
if len(entry.InputHistoryParsed) != 1 {
|
||||
t.Fatalf("len(InputHistoryParsed) = %d, want 1", len(entry.InputHistoryParsed))
|
||||
}
|
||||
if entry.InputHistoryParsed[0].Content == nil || entry.InputHistoryParsed[0].Content.ContentStr == nil || *entry.InputHistoryParsed[0].Content.ContentStr != realtimeMissingTranscriptText {
|
||||
t.Fatalf("InputHistoryParsed[0] = %+v, want missing transcript placeholder", entry.InputHistoryParsed[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyRealtimeOutputToEntryBackfillsRetrievedUserAndToolHistory(t *testing.T) {
|
||||
plugin := &LoggerPlugin{}
|
||||
entry := &logstore.Log{}
|
||||
|
||||
assistantText := "I checked that for you."
|
||||
messageType := schemas.ResponsesMessageTypeMessage
|
||||
assistantRole := schemas.ResponsesInputMessageRoleAssistant
|
||||
result := &schemas.BifrostResponse{
|
||||
ResponsesResponse: &schemas.BifrostResponsesResponse{
|
||||
Output: []schemas.ResponsesMessage{{
|
||||
Type: &messageType,
|
||||
Role: &assistantRole,
|
||||
Content: &schemas.ResponsesMessageContent{
|
||||
ContentStr: &assistantText,
|
||||
},
|
||||
}},
|
||||
ExtraFields: schemas.BifrostResponseExtraFields{
|
||||
RequestType: schemas.RealtimeRequest,
|
||||
RawRequest: strings.Join([]string{
|
||||
`{"type":"conversation.item.retrieved","item":{"id":"item_user","type":"message","role":"user","status":"completed","content":[{"type":"input_text","text":"Where is my order?"}]}}`,
|
||||
`{"type":"conversation.item.retrieved","item":{"id":"item_tool","type":"function_call_output","call_id":"call_123","status":"completed","output":"{\"status\":\"delivered\"}"}}`,
|
||||
}, "\n\n"),
|
||||
RawResponse: `{"type":"response.done"}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
plugin.applyRealtimeOutputToEntry(entry, result, true)
|
||||
if err := entry.SerializeFields(); err != nil {
|
||||
t.Fatalf("SerializeFields() error = %v", err)
|
||||
}
|
||||
|
||||
if len(entry.InputHistoryParsed) != 2 {
|
||||
t.Fatalf("len(InputHistoryParsed) = %d, want 2", len(entry.InputHistoryParsed))
|
||||
}
|
||||
if entry.InputHistoryParsed[0].Role != schemas.ChatMessageRoleUser {
|
||||
t.Fatalf("InputHistoryParsed[0].Role = %q, want user", entry.InputHistoryParsed[0].Role)
|
||||
}
|
||||
if entry.InputHistoryParsed[0].Content == nil || entry.InputHistoryParsed[0].Content.ContentStr == nil || *entry.InputHistoryParsed[0].Content.ContentStr != "Where is my order?" {
|
||||
t.Fatalf("InputHistoryParsed[0] = %+v, want user content", entry.InputHistoryParsed[0])
|
||||
}
|
||||
if entry.InputHistoryParsed[1].Role != schemas.ChatMessageRoleTool {
|
||||
t.Fatalf("InputHistoryParsed[1].Role = %q, want tool", entry.InputHistoryParsed[1].Role)
|
||||
}
|
||||
if entry.InputHistoryParsed[1].Content == nil || entry.InputHistoryParsed[1].Content.ContentStr == nil || *entry.InputHistoryParsed[1].Content.ContentStr != `{"status":"delivered"}` {
|
||||
t.Fatalf("InputHistoryParsed[1] = %+v, want tool content", entry.InputHistoryParsed[1])
|
||||
}
|
||||
if entry.InputHistoryParsed[1].ChatToolMessage == nil || entry.InputHistoryParsed[1].ChatToolMessage.ToolCallID == nil || *entry.InputHistoryParsed[1].ChatToolMessage.ToolCallID != "call_123" {
|
||||
t.Fatalf("InputHistoryParsed[1].ChatToolMessage = %+v, want tool call id", entry.InputHistoryParsed[1].ChatToolMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyRealtimeOutputToEntryBackfillsCreatedUserAndToolHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
plugin := &LoggerPlugin{}
|
||||
entry := &logstore.Log{}
|
||||
result := &schemas.BifrostResponse{
|
||||
ResponsesResponse: &schemas.BifrostResponsesResponse{
|
||||
ExtraFields: schemas.BifrostResponseExtraFields{
|
||||
RawRequest: strings.Join([]string{
|
||||
`{"type":"conversation.item.created","item":{"id":"item_user","type":"message","role":"user","status":"completed","content":[{"type":"input_text","text":"I need help"}]}}`,
|
||||
`{"type":"conversation.item.created","item":{"id":"item_tool","type":"function_call_output","call_id":"call_456","status":"completed","output":"{\"status\":\"ok\"}"}}`,
|
||||
}, "\n\n"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
plugin.applyRealtimeOutputToEntry(entry, result, true)
|
||||
|
||||
if len(entry.InputHistoryParsed) != 2 {
|
||||
t.Fatalf("len(InputHistoryParsed) = %d, want 2", len(entry.InputHistoryParsed))
|
||||
}
|
||||
if entry.InputHistoryParsed[0].Role != schemas.ChatMessageRoleUser {
|
||||
t.Fatalf("InputHistoryParsed[0].Role = %q, want user", entry.InputHistoryParsed[0].Role)
|
||||
}
|
||||
if entry.InputHistoryParsed[0].Content == nil || entry.InputHistoryParsed[0].Content.ContentStr == nil || *entry.InputHistoryParsed[0].Content.ContentStr != "I need help" {
|
||||
t.Fatalf("InputHistoryParsed[0] = %+v, want user content", entry.InputHistoryParsed[0])
|
||||
}
|
||||
if entry.InputHistoryParsed[1].Role != schemas.ChatMessageRoleTool {
|
||||
t.Fatalf("InputHistoryParsed[1].Role = %q, want tool", entry.InputHistoryParsed[1].Role)
|
||||
}
|
||||
if entry.InputHistoryParsed[1].Content == nil || entry.InputHistoryParsed[1].Content.ContentStr == nil || *entry.InputHistoryParsed[1].Content.ContentStr != `{"status":"ok"}` {
|
||||
t.Fatalf("InputHistoryParsed[1] = %+v, want tool content", entry.InputHistoryParsed[1])
|
||||
}
|
||||
if entry.InputHistoryParsed[1].ChatToolMessage == nil || entry.InputHistoryParsed[1].ChatToolMessage.ToolCallID == nil || *entry.InputHistoryParsed[1].ChatToolMessage.ToolCallID != "call_456" {
|
||||
t.Fatalf("InputHistoryParsed[1].ChatToolMessage = %+v, want tool call id", entry.InputHistoryParsed[1].ChatToolMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyRealtimeOutputToEntryBackfillsAddedUserAndToolHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
plugin := &LoggerPlugin{}
|
||||
entry := &logstore.Log{}
|
||||
|
||||
assistantText := "Done."
|
||||
messageType := schemas.ResponsesMessageTypeMessage
|
||||
assistantRole := schemas.ResponsesInputMessageRoleAssistant
|
||||
result := &schemas.BifrostResponse{
|
||||
ResponsesResponse: &schemas.BifrostResponsesResponse{
|
||||
Output: []schemas.ResponsesMessage{{
|
||||
Type: &messageType,
|
||||
Role: &assistantRole,
|
||||
Content: &schemas.ResponsesMessageContent{
|
||||
ContentStr: &assistantText,
|
||||
},
|
||||
}},
|
||||
ExtraFields: schemas.BifrostResponseExtraFields{
|
||||
RequestType: schemas.RealtimeRequest,
|
||||
RawRequest: strings.Join([]string{
|
||||
`{"type":"conversation.item.added","item":{"id":"item_user","type":"message","role":"user","status":"completed","content":[{"type":"input_text","text":"hello from added item"}]}}`,
|
||||
`{"type":"conversation.item.added","item":{"id":"item_tool","type":"function_call_output","call_id":"call_added","status":"completed","output":"{\"status\":\"ok\"}"}}`,
|
||||
}, "\n\n"),
|
||||
RawResponse: `{"type":"response.done"}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
plugin.applyRealtimeOutputToEntry(entry, result, true)
|
||||
if err := entry.SerializeFields(); err != nil {
|
||||
t.Fatalf("SerializeFields() error = %v", err)
|
||||
}
|
||||
|
||||
if len(entry.InputHistoryParsed) != 2 {
|
||||
t.Fatalf("len(InputHistoryParsed) = %d, want 2", len(entry.InputHistoryParsed))
|
||||
}
|
||||
if entry.InputHistoryParsed[0].Content == nil || entry.InputHistoryParsed[0].Content.ContentStr == nil || *entry.InputHistoryParsed[0].Content.ContentStr != "hello from added item" {
|
||||
t.Fatalf("InputHistoryParsed[0] = %+v, want added user content", entry.InputHistoryParsed[0])
|
||||
}
|
||||
if entry.InputHistoryParsed[1].ChatToolMessage == nil || entry.InputHistoryParsed[1].ChatToolMessage.ToolCallID == nil || *entry.InputHistoryParsed[1].ChatToolMessage.ToolCallID != "call_added" {
|
||||
t.Fatalf("InputHistoryParsed[1].ChatToolMessage = %+v, want added tool call id", entry.InputHistoryParsed[1].ChatToolMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyRealtimeOutputToEntryMergesRawTranscriptIntoStructuredRealtimeHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
plugin := &LoggerPlugin{}
|
||||
entry := &logstore.Log{
|
||||
InputHistoryParsed: []schemas.ChatMessage{
|
||||
{
|
||||
Role: schemas.ChatMessageRoleUser,
|
||||
Content: &schemas.ChatMessageContent{
|
||||
ContentStr: schemas.Ptr("Can you help with my ticket?"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Role: schemas.ChatMessageRoleTool,
|
||||
Content: &schemas.ChatMessageContent{
|
||||
ContentStr: schemas.Ptr(`{"status":"open"}`),
|
||||
},
|
||||
ChatToolMessage: &schemas.ChatToolMessage{
|
||||
ToolCallID: schemas.Ptr("call_789"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assistantText := "Let me check."
|
||||
messageType := schemas.ResponsesMessageTypeMessage
|
||||
assistantRole := schemas.ResponsesInputMessageRoleAssistant
|
||||
result := &schemas.BifrostResponse{
|
||||
ResponsesResponse: &schemas.BifrostResponsesResponse{
|
||||
Output: []schemas.ResponsesMessage{{
|
||||
Type: &messageType,
|
||||
Role: &assistantRole,
|
||||
Content: &schemas.ResponsesMessageContent{
|
||||
ContentStr: &assistantText,
|
||||
},
|
||||
}},
|
||||
ExtraFields: schemas.BifrostResponseExtraFields{
|
||||
RequestType: schemas.RealtimeRequest,
|
||||
RawRequest: strings.Join([]string{
|
||||
`{"type":"conversation.item.input_audio_transcription.completed","transcript":"Hello."}`,
|
||||
`{"type":"conversation.item.retrieved","item":{"id":"item_tool","type":"function_call_output","call_id":"call_789","status":"completed","output":"{\"status\":\"open\"}"}}`,
|
||||
}, "\n\n"),
|
||||
RawResponse: `{"type":"response.done"}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
plugin.applyRealtimeOutputToEntry(entry, result, true)
|
||||
if err := entry.SerializeFields(); err != nil {
|
||||
t.Fatalf("SerializeFields() error = %v", err)
|
||||
}
|
||||
|
||||
if len(entry.InputHistoryParsed) != 3 {
|
||||
t.Fatalf("len(InputHistoryParsed) = %d, want 3", len(entry.InputHistoryParsed))
|
||||
}
|
||||
if entry.InputHistoryParsed[0].Content == nil || entry.InputHistoryParsed[0].Content.ContentStr == nil || *entry.InputHistoryParsed[0].Content.ContentStr != "Can you help with my ticket?" {
|
||||
t.Fatalf("InputHistoryParsed[0] = %+v, want structured user content", entry.InputHistoryParsed[0])
|
||||
}
|
||||
if entry.InputHistoryParsed[1].Role != schemas.ChatMessageRoleUser {
|
||||
t.Fatalf("InputHistoryParsed[1].Role = %q, want user", entry.InputHistoryParsed[1].Role)
|
||||
}
|
||||
if entry.InputHistoryParsed[1].Content == nil || entry.InputHistoryParsed[1].Content.ContentStr == nil || *entry.InputHistoryParsed[1].Content.ContentStr != "Hello." {
|
||||
t.Fatalf("InputHistoryParsed[1] = %+v, want raw transcript merge", entry.InputHistoryParsed[1])
|
||||
}
|
||||
if entry.InputHistoryParsed[2].Role != schemas.ChatMessageRoleTool {
|
||||
t.Fatalf("InputHistoryParsed[2].Role = %q, want tool", entry.InputHistoryParsed[2].Role)
|
||||
}
|
||||
if entry.InputHistoryParsed[2].ChatToolMessage == nil || entry.InputHistoryParsed[2].ChatToolMessage.ToolCallID == nil || *entry.InputHistoryParsed[2].ChatToolMessage.ToolCallID != "call_789" {
|
||||
t.Fatalf("InputHistoryParsed[2].ChatToolMessage = %+v, want original tool call id", entry.InputHistoryParsed[2].ChatToolMessage)
|
||||
}
|
||||
if strings.Count(entry.ContentSummary, "Hello.") != 1 {
|
||||
t.Fatalf("ContentSummary = %q, want one merged transcript", entry.ContentSummary)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyRealtimeOutputToEntryDoesNotPersistRawWhenShouldStoreRawFalse(t *testing.T) {
|
||||
plugin := &LoggerPlugin{}
|
||||
entry := &logstore.Log{}
|
||||
|
||||
assistantText := "Hello!"
|
||||
messageType := schemas.ResponsesMessageTypeMessage
|
||||
assistantRole := schemas.ResponsesInputMessageRoleAssistant
|
||||
result := &schemas.BifrostResponse{
|
||||
ResponsesResponse: &schemas.BifrostResponsesResponse{
|
||||
Output: []schemas.ResponsesMessage{{
|
||||
Type: &messageType,
|
||||
Role: &assistantRole,
|
||||
Content: &schemas.ResponsesMessageContent{
|
||||
ContentStr: &assistantText,
|
||||
},
|
||||
}},
|
||||
ExtraFields: schemas.BifrostResponseExtraFields{
|
||||
RequestType: schemas.RealtimeRequest,
|
||||
RawRequest: `{"type":"conversation.item.input_audio_transcription.completed","transcript":"Hello."}`,
|
||||
RawResponse: `{"type":"response.done"}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
plugin.applyRealtimeOutputToEntry(entry, result, false)
|
||||
|
||||
if entry.RawRequest != "" {
|
||||
t.Fatalf("expected RawRequest to remain empty when shouldStoreRaw=false, got %q", entry.RawRequest)
|
||||
}
|
||||
if entry.RawResponse != "" {
|
||||
t.Fatalf("expected RawResponse to remain empty when shouldStoreRaw=false, got %q", entry.RawResponse)
|
||||
}
|
||||
if len(entry.InputHistoryParsed) == 0 {
|
||||
t.Fatal("expected InputHistoryParsed to still be backfilled when shouldStoreRaw=false")
|
||||
}
|
||||
if entry.InputHistoryParsed[0].Role != schemas.ChatMessageRoleUser {
|
||||
t.Fatalf("InputHistoryParsed[0].Role = %q, want user", entry.InputHistoryParsed[0].Role)
|
||||
}
|
||||
}
|
||||
754
plugins/logging/utils.go
Normal file
754
plugins/logging/utils.go
Normal file
@@ -0,0 +1,754 @@
|
||||
// Package logging provides utility functions and interfaces for the GORM-based logging plugin
|
||||
package logging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
bifrost "github.com/maximhq/bifrost/core"
|
||||
"github.com/maximhq/bifrost/core/schemas"
|
||||
"github.com/maximhq/bifrost/framework/logstore"
|
||||
"github.com/maximhq/bifrost/framework/streaming"
|
||||
)
|
||||
|
||||
// KeyPair represents an ID-Name pair for keys
|
||||
type KeyPair struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// LogManager defines the main interface that combines all logging functionality
|
||||
type LogManager interface {
|
||||
// GetLog retrieves a single log entry by ID (includes all fields, including raw_request/raw_response)
|
||||
GetLog(ctx context.Context, id string) (*logstore.Log, error)
|
||||
|
||||
// Search searches for log entries based on filters and pagination
|
||||
Search(ctx context.Context, filters *logstore.SearchFilters, pagination *logstore.PaginationOptions) (*logstore.SearchResult, error)
|
||||
|
||||
// GetSessionLogs returns paginated logs for a single parent_request_id session.
|
||||
GetSessionLogs(ctx context.Context, sessionID string, pagination *logstore.PaginationOptions) (*logstore.SessionDetailResult, error)
|
||||
|
||||
// GetSessionSummary returns aggregate totals for a single parent_request_id session.
|
||||
GetSessionSummary(ctx context.Context, sessionID string) (*logstore.SessionSummaryResult, error)
|
||||
|
||||
// GetStats calculates statistics for logs matching the given filters
|
||||
GetStats(ctx context.Context, filters *logstore.SearchFilters) (*logstore.SearchStats, error)
|
||||
|
||||
// GetHistogram returns time-bucketed request counts for the given filters
|
||||
GetHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.HistogramResult, error)
|
||||
|
||||
// GetTokenHistogram returns time-bucketed token usage for the given filters
|
||||
GetTokenHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.TokenHistogramResult, error)
|
||||
|
||||
// GetCostHistogram returns time-bucketed cost data with model breakdown for the given filters
|
||||
GetCostHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.CostHistogramResult, error)
|
||||
|
||||
// GetModelHistogram returns time-bucketed model usage with success/error breakdown for the given filters
|
||||
GetModelHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.ModelHistogramResult, error)
|
||||
|
||||
// GetLatencyHistogram returns time-bucketed latency percentiles for the given filters
|
||||
GetLatencyHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.LatencyHistogramResult, error)
|
||||
|
||||
// GetProviderCostHistogram returns time-bucketed cost data with provider breakdown for the given filters
|
||||
GetProviderCostHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.ProviderCostHistogramResult, error)
|
||||
|
||||
// GetProviderTokenHistogram returns time-bucketed token usage with provider breakdown for the given filters
|
||||
GetProviderTokenHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.ProviderTokenHistogramResult, error)
|
||||
|
||||
// GetProviderLatencyHistogram returns time-bucketed latency percentiles with provider breakdown for the given filters
|
||||
GetProviderLatencyHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.ProviderLatencyHistogramResult, error)
|
||||
|
||||
// GetModelRankings returns models ranked by usage with trend comparison
|
||||
GetModelRankings(ctx context.Context, filters *logstore.SearchFilters) (*logstore.ModelRankingResult, error)
|
||||
|
||||
// Get the number of dropped requests
|
||||
GetDroppedRequests(ctx context.Context) int64
|
||||
|
||||
// GetAvailableModels returns all unique models from logs
|
||||
GetAvailableModels(ctx context.Context) []string
|
||||
|
||||
// GetAvailableAliases returns all unique alias values from logs
|
||||
GetAvailableAliases(ctx context.Context) []string
|
||||
|
||||
// GetAvailableSelectedKeys returns all unique selected key ID-Name pairs from logs
|
||||
GetAvailableSelectedKeys(ctx context.Context) []KeyPair
|
||||
|
||||
// GetAvailableVirtualKeys returns all unique virtual key ID-Name pairs from logs
|
||||
GetAvailableVirtualKeys(ctx context.Context) []KeyPair
|
||||
|
||||
// GetAvailableRoutingRules returns all unique routing rule ID-Name pairs from logs
|
||||
GetAvailableRoutingRules(ctx context.Context) []KeyPair
|
||||
|
||||
// GetAvailableRoutingEngines returns all unique routing engine types from logs
|
||||
GetAvailableRoutingEngines(ctx context.Context) []string
|
||||
|
||||
// GetAvailableTeams returns all unique team ID-Name pairs from logs
|
||||
GetAvailableTeams(ctx context.Context) []KeyPair
|
||||
|
||||
// GetAvailableCustomers returns all unique customer ID-Name pairs from logs
|
||||
GetAvailableCustomers(ctx context.Context) []KeyPair
|
||||
|
||||
// GetAvailableUsers returns all unique user IDs from logs
|
||||
GetAvailableUsers(ctx context.Context) []KeyPair
|
||||
|
||||
// GetAvailableBusinessUnits returns all unique business unit ID-Name pairs from logs
|
||||
GetAvailableBusinessUnits(ctx context.Context) []KeyPair
|
||||
|
||||
// GetAvailableMetadataKeys returns distinct metadata keys and their values from recent logs
|
||||
GetAvailableMetadataKeys(ctx context.Context) (map[string][]string, error)
|
||||
|
||||
// GetDimensionCostHistogram returns time-bucketed cost data grouped by the specified dimension
|
||||
GetDimensionCostHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64, dimension logstore.HistogramDimension) (*logstore.DimensionCostHistogramResult, error)
|
||||
|
||||
// GetDimensionTokenHistogram returns time-bucketed token usage grouped by the specified dimension
|
||||
GetDimensionTokenHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64, dimension logstore.HistogramDimension) (*logstore.DimensionTokenHistogramResult, error)
|
||||
|
||||
// GetDimensionLatencyHistogram returns time-bucketed latency percentiles grouped by the specified dimension
|
||||
GetDimensionLatencyHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64, dimension logstore.HistogramDimension) (*logstore.DimensionLatencyHistogramResult, error)
|
||||
|
||||
// DeleteLog deletes a log entry by its ID
|
||||
DeleteLog(ctx context.Context, id string) error
|
||||
|
||||
// DeleteLogs deletes multiple log entries by their IDs
|
||||
DeleteLogs(ctx context.Context, ids []string) error
|
||||
|
||||
// RecalculateCosts recomputes missing costs for logs matching the filters
|
||||
RecalculateCosts(ctx context.Context, filters *logstore.SearchFilters, limit int) (*RecalculateCostResult, error)
|
||||
|
||||
// MCP Tool Log methods
|
||||
// SearchMCPToolLogs searches for MCP tool log entries based on filters and pagination
|
||||
SearchMCPToolLogs(ctx context.Context, filters *logstore.MCPToolLogSearchFilters, pagination *logstore.PaginationOptions) (*logstore.MCPToolLogSearchResult, error)
|
||||
|
||||
// GetMCPToolLogStats calculates statistics for MCP tool logs matching the given filters
|
||||
GetMCPToolLogStats(ctx context.Context, filters *logstore.MCPToolLogSearchFilters) (*logstore.MCPToolLogStats, error)
|
||||
|
||||
// GetAvailableToolNames returns all unique tool names from MCP tool logs
|
||||
GetAvailableToolNames(ctx context.Context) ([]string, error)
|
||||
|
||||
// GetAvailableServerLabels returns all unique server labels from MCP tool logs
|
||||
GetAvailableServerLabels(ctx context.Context) ([]string, error)
|
||||
|
||||
// GetAvailableMCPVirtualKeys returns all unique virtual key ID-Name pairs from MCP tool logs
|
||||
GetAvailableMCPVirtualKeys(ctx context.Context) []KeyPair
|
||||
|
||||
// GetMCPHistogram returns time-bucketed MCP tool call volume
|
||||
GetMCPHistogram(ctx context.Context, filters logstore.MCPToolLogSearchFilters, bucketSizeSeconds int64) (*logstore.MCPHistogramResult, error)
|
||||
|
||||
// GetMCPCostHistogram returns time-bucketed MCP cost data
|
||||
GetMCPCostHistogram(ctx context.Context, filters logstore.MCPToolLogSearchFilters, bucketSizeSeconds int64) (*logstore.MCPCostHistogramResult, error)
|
||||
|
||||
// GetMCPTopTools returns the top N MCP tools by call count
|
||||
GetMCPTopTools(ctx context.Context, filters logstore.MCPToolLogSearchFilters, limit int) (*logstore.MCPTopToolsResult, error)
|
||||
|
||||
// DeleteMCPToolLogs deletes multiple MCP tool log entries by their IDs
|
||||
DeleteMCPToolLogs(ctx context.Context, ids []string) error
|
||||
}
|
||||
|
||||
// PluginLogManager implements LogManager interface wrapping the plugin
|
||||
type PluginLogManager struct {
|
||||
plugin *LoggerPlugin
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetLog(ctx context.Context, id string) (*logstore.Log, error) {
|
||||
return p.plugin.GetLog(ctx, id)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) Search(ctx context.Context, filters *logstore.SearchFilters, pagination *logstore.PaginationOptions) (*logstore.SearchResult, error) {
|
||||
if filters == nil || pagination == nil {
|
||||
return nil, fmt.Errorf("filters and pagination cannot be nil")
|
||||
}
|
||||
return p.plugin.SearchLogs(ctx, *filters, *pagination)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetSessionLogs(ctx context.Context, sessionID string, pagination *logstore.PaginationOptions) (*logstore.SessionDetailResult, error) {
|
||||
if pagination == nil {
|
||||
return nil, fmt.Errorf("pagination cannot be nil")
|
||||
}
|
||||
if strings.TrimSpace(sessionID) == "" {
|
||||
return nil, fmt.Errorf("sessionID cannot be empty")
|
||||
}
|
||||
return p.plugin.GetSessionLogs(ctx, sessionID, *pagination)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetSessionSummary(ctx context.Context, sessionID string) (*logstore.SessionSummaryResult, error) {
|
||||
if strings.TrimSpace(sessionID) == "" {
|
||||
return nil, fmt.Errorf("sessionID cannot be empty")
|
||||
}
|
||||
return p.plugin.GetSessionSummary(ctx, sessionID)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetStats(ctx context.Context, filters *logstore.SearchFilters) (*logstore.SearchStats, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.GetStats(ctx, *filters)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.HistogramResult, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.GetHistogram(ctx, *filters, bucketSizeSeconds)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetTokenHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.TokenHistogramResult, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.GetTokenHistogram(ctx, *filters, bucketSizeSeconds)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetCostHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.CostHistogramResult, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.GetCostHistogram(ctx, *filters, bucketSizeSeconds)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetModelHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.ModelHistogramResult, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.GetModelHistogram(ctx, *filters, bucketSizeSeconds)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetLatencyHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.LatencyHistogramResult, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.GetLatencyHistogram(ctx, *filters, bucketSizeSeconds)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetProviderCostHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.ProviderCostHistogramResult, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.GetProviderCostHistogram(ctx, *filters, bucketSizeSeconds)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetProviderTokenHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.ProviderTokenHistogramResult, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.GetProviderTokenHistogram(ctx, *filters, bucketSizeSeconds)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetProviderLatencyHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64) (*logstore.ProviderLatencyHistogramResult, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.GetProviderLatencyHistogram(ctx, *filters, bucketSizeSeconds)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetModelRankings(ctx context.Context, filters *logstore.SearchFilters) (*logstore.ModelRankingResult, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.GetModelRankings(ctx, *filters)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetDroppedRequests(ctx context.Context) int64 {
|
||||
return p.plugin.droppedRequests.Load()
|
||||
}
|
||||
|
||||
// GetAvailableModels returns all unique models from logs
|
||||
func (p *PluginLogManager) GetAvailableModels(ctx context.Context) []string {
|
||||
return p.plugin.GetAvailableModels(ctx)
|
||||
}
|
||||
|
||||
// GetAvailableAliases returns all unique alias values from logs
|
||||
func (p *PluginLogManager) GetAvailableAliases(ctx context.Context) []string {
|
||||
return p.plugin.GetAvailableAliases(ctx)
|
||||
}
|
||||
|
||||
// GetAvailableSelectedKeys returns all unique selected key ID-Name pairs from logs
|
||||
func (p *PluginLogManager) GetAvailableSelectedKeys(ctx context.Context) []KeyPair {
|
||||
return p.plugin.GetAvailableSelectedKeys(ctx)
|
||||
}
|
||||
|
||||
// GetAvailableVirtualKeys returns all unique virtual key ID-Name pairs from logs
|
||||
func (p *PluginLogManager) GetAvailableVirtualKeys(ctx context.Context) []KeyPair {
|
||||
return p.plugin.GetAvailableVirtualKeys(ctx)
|
||||
}
|
||||
|
||||
// GetAvailableRoutingRules returns all unique routing rule ID-Name pairs from logs
|
||||
func (p *PluginLogManager) GetAvailableRoutingRules(ctx context.Context) []KeyPair {
|
||||
return p.plugin.GetAvailableRoutingRules(ctx)
|
||||
}
|
||||
|
||||
// GetAvailableRoutingEngines returns all unique routing engine types from logs
|
||||
func (p *PluginLogManager) GetAvailableRoutingEngines(ctx context.Context) []string {
|
||||
return p.plugin.GetAvailableRoutingEngines(ctx)
|
||||
}
|
||||
|
||||
// GetAvailableTeams returns all unique team ID-Name pairs from logs.
|
||||
func (p *PluginLogManager) GetAvailableTeams(ctx context.Context) []KeyPair {
|
||||
return p.plugin.GetAvailableTeams(ctx)
|
||||
}
|
||||
|
||||
// GetAvailableCustomers returns all unique customer ID-Name pairs from logs.
|
||||
func (p *PluginLogManager) GetAvailableCustomers(ctx context.Context) []KeyPair {
|
||||
return p.plugin.GetAvailableCustomers(ctx)
|
||||
}
|
||||
|
||||
// GetAvailableUsers returns all unique user IDs from logs.
|
||||
func (p *PluginLogManager) GetAvailableUsers(ctx context.Context) []KeyPair {
|
||||
return p.plugin.GetAvailableUsers(ctx)
|
||||
}
|
||||
|
||||
// GetAvailableBusinessUnits returns all unique business unit ID-Name pairs from logs.
|
||||
func (p *PluginLogManager) GetAvailableBusinessUnits(ctx context.Context) []KeyPair {
|
||||
return p.plugin.GetAvailableBusinessUnits(ctx)
|
||||
}
|
||||
|
||||
// GetDimensionCostHistogram returns time-bucketed cost data grouped by the specified dimension.
|
||||
func (p *PluginLogManager) GetDimensionCostHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64, dimension logstore.HistogramDimension) (*logstore.DimensionCostHistogramResult, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.GetDimensionCostHistogram(ctx, *filters, bucketSizeSeconds, dimension)
|
||||
}
|
||||
|
||||
// GetDimensionTokenHistogram returns time-bucketed token usage grouped by the specified dimension.
|
||||
func (p *PluginLogManager) GetDimensionTokenHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64, dimension logstore.HistogramDimension) (*logstore.DimensionTokenHistogramResult, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.GetDimensionTokenHistogram(ctx, *filters, bucketSizeSeconds, dimension)
|
||||
}
|
||||
|
||||
// GetDimensionLatencyHistogram returns time-bucketed latency percentiles grouped by the specified dimension.
|
||||
func (p *PluginLogManager) GetDimensionLatencyHistogram(ctx context.Context, filters *logstore.SearchFilters, bucketSizeSeconds int64, dimension logstore.HistogramDimension) (*logstore.DimensionLatencyHistogramResult, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.GetDimensionLatencyHistogram(ctx, *filters, bucketSizeSeconds, dimension)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) GetAvailableMetadataKeys(ctx context.Context) (map[string][]string, error) {
|
||||
if p.plugin == nil || p.plugin.store == nil {
|
||||
return map[string][]string{}, nil
|
||||
}
|
||||
return p.plugin.store.GetDistinctMetadataKeys(ctx)
|
||||
}
|
||||
|
||||
// DeleteLog deletes a log from the log store
|
||||
func (p *PluginLogManager) DeleteLog(ctx context.Context, id string) error {
|
||||
if p.plugin == nil || p.plugin.store == nil {
|
||||
return fmt.Errorf("log store not initialized")
|
||||
}
|
||||
return p.plugin.store.DeleteLog(ctx, id)
|
||||
}
|
||||
|
||||
// DeleteLogs deletes multiple logs from the log store
|
||||
func (p *PluginLogManager) DeleteLogs(ctx context.Context, ids []string) error {
|
||||
if p.plugin == nil || p.plugin.store == nil {
|
||||
return fmt.Errorf("log store not initialized")
|
||||
}
|
||||
return p.plugin.store.DeleteLogs(ctx, ids)
|
||||
}
|
||||
|
||||
func (p *PluginLogManager) RecalculateCosts(ctx context.Context, filters *logstore.SearchFilters, limit int) (*RecalculateCostResult, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.RecalculateCosts(ctx, *filters, limit)
|
||||
}
|
||||
|
||||
// SearchMCPToolLogs searches for MCP tool log entries based on filters and pagination
|
||||
func (p *PluginLogManager) SearchMCPToolLogs(ctx context.Context, filters *logstore.MCPToolLogSearchFilters, pagination *logstore.PaginationOptions) (*logstore.MCPToolLogSearchResult, error) {
|
||||
if filters == nil || pagination == nil {
|
||||
return nil, fmt.Errorf("filters and pagination cannot be nil")
|
||||
}
|
||||
return p.plugin.store.SearchMCPToolLogs(ctx, *filters, *pagination)
|
||||
}
|
||||
|
||||
// GetMCPToolLogStats calculates statistics for MCP tool logs matching the given filters
|
||||
func (p *PluginLogManager) GetMCPToolLogStats(ctx context.Context, filters *logstore.MCPToolLogSearchFilters) (*logstore.MCPToolLogStats, error) {
|
||||
if filters == nil {
|
||||
return nil, fmt.Errorf("filters cannot be nil")
|
||||
}
|
||||
return p.plugin.store.GetMCPToolLogStats(ctx, *filters)
|
||||
}
|
||||
|
||||
// GetAvailableToolNames returns all unique tool names from MCP tool logs
|
||||
func (p *PluginLogManager) GetAvailableToolNames(ctx context.Context) ([]string, error) {
|
||||
if p == nil || p.plugin == nil || p.plugin.store == nil {
|
||||
return []string{}, nil
|
||||
}
|
||||
return p.plugin.store.GetAvailableToolNames(ctx)
|
||||
}
|
||||
|
||||
// GetAvailableServerLabels returns all unique server labels from MCP tool logs
|
||||
func (p *PluginLogManager) GetAvailableServerLabels(ctx context.Context) ([]string, error) {
|
||||
if p == nil || p.plugin == nil || p.plugin.store == nil {
|
||||
return []string{}, nil
|
||||
}
|
||||
return p.plugin.store.GetAvailableServerLabels(ctx)
|
||||
}
|
||||
|
||||
// GetAvailableMCPVirtualKeys returns all unique virtual key ID-Name pairs from MCP tool logs
|
||||
func (p *PluginLogManager) GetAvailableMCPVirtualKeys(ctx context.Context) []KeyPair {
|
||||
if p == nil || p.plugin == nil {
|
||||
return []KeyPair{}
|
||||
}
|
||||
return p.plugin.GetAvailableMCPVirtualKeys(ctx)
|
||||
}
|
||||
|
||||
// GetMCPHistogram returns time-bucketed MCP tool call volume
|
||||
func (p *PluginLogManager) GetMCPHistogram(ctx context.Context, filters logstore.MCPToolLogSearchFilters, bucketSizeSeconds int64) (*logstore.MCPHistogramResult, error) {
|
||||
if p.plugin == nil || p.plugin.store == nil {
|
||||
return &logstore.MCPHistogramResult{}, nil
|
||||
}
|
||||
return p.plugin.store.GetMCPHistogram(ctx, filters, bucketSizeSeconds)
|
||||
}
|
||||
|
||||
// GetMCPCostHistogram returns time-bucketed MCP cost data
|
||||
func (p *PluginLogManager) GetMCPCostHistogram(ctx context.Context, filters logstore.MCPToolLogSearchFilters, bucketSizeSeconds int64) (*logstore.MCPCostHistogramResult, error) {
|
||||
if p.plugin == nil || p.plugin.store == nil {
|
||||
return &logstore.MCPCostHistogramResult{}, nil
|
||||
}
|
||||
return p.plugin.store.GetMCPCostHistogram(ctx, filters, bucketSizeSeconds)
|
||||
}
|
||||
|
||||
// GetMCPTopTools returns the top N MCP tools by call count
|
||||
func (p *PluginLogManager) GetMCPTopTools(ctx context.Context, filters logstore.MCPToolLogSearchFilters, limit int) (*logstore.MCPTopToolsResult, error) {
|
||||
if p.plugin == nil || p.plugin.store == nil {
|
||||
return &logstore.MCPTopToolsResult{}, nil
|
||||
}
|
||||
return p.plugin.store.GetMCPTopTools(ctx, filters, limit)
|
||||
}
|
||||
|
||||
// DeleteMCPToolLogs deletes multiple MCP tool log entries by their IDs
|
||||
func (p *PluginLogManager) DeleteMCPToolLogs(ctx context.Context, ids []string) error {
|
||||
if p.plugin == nil || p.plugin.store == nil {
|
||||
return fmt.Errorf("log store not initialized")
|
||||
}
|
||||
return p.plugin.store.DeleteMCPToolLogs(ctx, ids)
|
||||
}
|
||||
|
||||
// GetPluginLogManager returns a LogManager interface for this plugin
|
||||
func (p *LoggerPlugin) GetPluginLogManager() *PluginLogManager {
|
||||
return &PluginLogManager{
|
||||
plugin: p,
|
||||
}
|
||||
}
|
||||
|
||||
// retryOnNotFound retries a function up to 3 times with 1-second delays if it returns logstore.ErrNotFound
|
||||
func retryOnNotFound(ctx context.Context, operation func() error) error {
|
||||
const maxRetries = 3
|
||||
const retryDelay = time.Second
|
||||
|
||||
var lastErr error
|
||||
for attempt := range maxRetries {
|
||||
err := operation()
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if the error is logstore.ErrNotFound
|
||||
if !errors.Is(err, logstore.ErrNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
lastErr = err
|
||||
|
||||
// Don't wait after the last attempt
|
||||
if attempt < maxRetries-1 {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-time.After(retryDelay):
|
||||
// Continue to next retry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lastErr
|
||||
}
|
||||
|
||||
// extractInputHistory extracts input history from request input
|
||||
func (p *LoggerPlugin) extractInputHistory(request *schemas.BifrostRequest) ([]schemas.ChatMessage, []schemas.ResponsesMessage) {
|
||||
if request.ChatRequest != nil {
|
||||
return request.ChatRequest.Input, []schemas.ResponsesMessage{}
|
||||
}
|
||||
if request.RequestType == schemas.RealtimeRequest && request.ResponsesRequest != nil {
|
||||
return extractRealtimeInputHistory(request.ResponsesRequest.Input), []schemas.ResponsesMessage{}
|
||||
}
|
||||
if request.ResponsesRequest != nil && len(request.ResponsesRequest.Input) > 0 {
|
||||
return []schemas.ChatMessage{}, request.ResponsesRequest.Input
|
||||
}
|
||||
if request.TextCompletionRequest != nil {
|
||||
if request.TextCompletionRequest.Input == nil {
|
||||
return []schemas.ChatMessage{}, []schemas.ResponsesMessage{}
|
||||
}
|
||||
var text string
|
||||
if request.TextCompletionRequest.Input.PromptStr != nil {
|
||||
text = *request.TextCompletionRequest.Input.PromptStr
|
||||
} else {
|
||||
var stringBuilder strings.Builder
|
||||
for _, prompt := range request.TextCompletionRequest.Input.PromptArray {
|
||||
stringBuilder.WriteString(prompt)
|
||||
}
|
||||
text = stringBuilder.String()
|
||||
}
|
||||
return []schemas.ChatMessage{
|
||||
{
|
||||
Role: schemas.ChatMessageRoleUser,
|
||||
Content: &schemas.ChatMessageContent{
|
||||
ContentStr: &text,
|
||||
},
|
||||
},
|
||||
}, []schemas.ResponsesMessage{}
|
||||
}
|
||||
if request.EmbeddingRequest != nil {
|
||||
// Large payload passthrough can intentionally leave Input nil to avoid
|
||||
// materializing giant request bodies. Logging should degrade gracefully.
|
||||
if request.EmbeddingRequest.Input == nil {
|
||||
return []schemas.ChatMessage{}, []schemas.ResponsesMessage{}
|
||||
}
|
||||
texts := request.EmbeddingRequest.Input.Texts
|
||||
|
||||
if len(texts) == 0 && request.EmbeddingRequest.Input.Text != nil {
|
||||
texts = []string{*request.EmbeddingRequest.Input.Text}
|
||||
}
|
||||
|
||||
contentBlocks := make([]schemas.ChatContentBlock, len(texts))
|
||||
for i, text := range texts {
|
||||
// Create a per-iteration copy to avoid reusing the same memory address
|
||||
t := text
|
||||
contentBlocks[i] = schemas.ChatContentBlock{
|
||||
Type: schemas.ChatContentBlockTypeText,
|
||||
Text: &t,
|
||||
}
|
||||
}
|
||||
return []schemas.ChatMessage{
|
||||
{
|
||||
Role: schemas.ChatMessageRoleUser,
|
||||
Content: &schemas.ChatMessageContent{
|
||||
ContentBlocks: contentBlocks,
|
||||
},
|
||||
},
|
||||
}, []schemas.ResponsesMessage{}
|
||||
}
|
||||
if request.RerankRequest != nil {
|
||||
query := request.RerankRequest.Query
|
||||
return []schemas.ChatMessage{
|
||||
{
|
||||
Role: schemas.ChatMessageRoleUser,
|
||||
Content: &schemas.ChatMessageContent{
|
||||
ContentStr: &query,
|
||||
},
|
||||
},
|
||||
}, []schemas.ResponsesMessage{}
|
||||
}
|
||||
if request.CountTokensRequest != nil && len(request.CountTokensRequest.Input) > 0 {
|
||||
return []schemas.ChatMessage{}, request.CountTokensRequest.Input
|
||||
}
|
||||
return []schemas.ChatMessage{}, []schemas.ResponsesMessage{}
|
||||
}
|
||||
|
||||
func extractRealtimeInputHistory(input []schemas.ResponsesMessage) []schemas.ChatMessage {
|
||||
messages := make([]schemas.ChatMessage, 0, len(input))
|
||||
for _, item := range input {
|
||||
if item.Type == nil {
|
||||
continue
|
||||
}
|
||||
switch *item.Type {
|
||||
case schemas.ResponsesMessageTypeMessage:
|
||||
if item.Role == nil || item.Content == nil {
|
||||
continue
|
||||
}
|
||||
content := extractRealtimeResponsesContent(item.Content)
|
||||
if content == "" {
|
||||
continue
|
||||
}
|
||||
messages = append(messages, schemas.ChatMessage{
|
||||
Role: mapRealtimeResponsesRole(*item.Role),
|
||||
Content: &schemas.ChatMessageContent{
|
||||
ContentStr: schemas.Ptr(content),
|
||||
},
|
||||
})
|
||||
case schemas.ResponsesMessageTypeFunctionCallOutput,
|
||||
schemas.ResponsesMessageTypeCustomToolCallOutput,
|
||||
schemas.ResponsesMessageTypeLocalShellCallOutput,
|
||||
schemas.ResponsesMessageTypeComputerCallOutput:
|
||||
content := extractRealtimeToolOutputContent(item.ResponsesToolMessage)
|
||||
if content == "" {
|
||||
continue
|
||||
}
|
||||
messages = append(messages, schemas.ChatMessage{
|
||||
Role: schemas.ChatMessageRoleTool,
|
||||
Content: &schemas.ChatMessageContent{
|
||||
ContentStr: schemas.Ptr(content),
|
||||
},
|
||||
ChatToolMessage: &schemas.ChatToolMessage{
|
||||
ToolCallID: item.ResponsesToolMessage.CallID,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
return messages
|
||||
}
|
||||
|
||||
func mapRealtimeResponsesRole(role schemas.ResponsesMessageRoleType) schemas.ChatMessageRole {
|
||||
switch role {
|
||||
case schemas.ResponsesInputMessageRoleAssistant:
|
||||
return schemas.ChatMessageRoleAssistant
|
||||
case schemas.ResponsesInputMessageRoleSystem:
|
||||
return schemas.ChatMessageRoleSystem
|
||||
case schemas.ResponsesInputMessageRoleDeveloper:
|
||||
return schemas.ChatMessageRoleDeveloper
|
||||
default:
|
||||
return schemas.ChatMessageRoleUser
|
||||
}
|
||||
}
|
||||
|
||||
func extractRealtimeResponsesContent(content *schemas.ResponsesMessageContent) string {
|
||||
if content == nil {
|
||||
return ""
|
||||
}
|
||||
if content.ContentStr != nil {
|
||||
return strings.TrimSpace(*content.ContentStr)
|
||||
}
|
||||
parts := make([]string, 0, len(content.ContentBlocks))
|
||||
for _, block := range content.ContentBlocks {
|
||||
switch {
|
||||
case block.Text != nil && strings.TrimSpace(*block.Text) != "":
|
||||
parts = append(parts, strings.TrimSpace(*block.Text))
|
||||
case block.ResponsesOutputMessageContentRefusal != nil && strings.TrimSpace(block.Refusal) != "":
|
||||
parts = append(parts, strings.TrimSpace(block.Refusal))
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(strings.Join(parts, "\n"))
|
||||
}
|
||||
|
||||
func extractRealtimeToolOutputContent(toolMessage *schemas.ResponsesToolMessage) string {
|
||||
if toolMessage == nil || toolMessage.Output == nil {
|
||||
return ""
|
||||
}
|
||||
switch {
|
||||
case toolMessage.Output.ResponsesToolCallOutputStr != nil:
|
||||
return strings.TrimSpace(*toolMessage.Output.ResponsesToolCallOutputStr)
|
||||
case len(toolMessage.Output.ResponsesFunctionToolCallOutputBlocks) > 0:
|
||||
content := &schemas.ResponsesMessageContent{ContentBlocks: toolMessage.Output.ResponsesFunctionToolCallOutputBlocks}
|
||||
return extractRealtimeResponsesContent(content)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// convertToProcessedStreamResponse converts a StreamAccumulatorResult to ProcessedStreamResponse
|
||||
// for use with the logging plugin's streaming log update functionality.
|
||||
func convertToProcessedStreamResponse(result *schemas.StreamAccumulatorResult, requestType schemas.RequestType) *streaming.ProcessedStreamResponse {
|
||||
if result == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Determine stream type from request type
|
||||
var streamType streaming.StreamType
|
||||
switch requestType {
|
||||
case schemas.TextCompletionStreamRequest:
|
||||
streamType = streaming.StreamTypeText
|
||||
case schemas.ChatCompletionStreamRequest:
|
||||
streamType = streaming.StreamTypeChat
|
||||
case schemas.ResponsesStreamRequest:
|
||||
streamType = streaming.StreamTypeResponses
|
||||
case schemas.SpeechStreamRequest:
|
||||
streamType = streaming.StreamTypeAudio
|
||||
case schemas.TranscriptionStreamRequest:
|
||||
streamType = streaming.StreamTypeTranscription
|
||||
case schemas.ImageGenerationStreamRequest:
|
||||
streamType = streaming.StreamTypeImage
|
||||
default:
|
||||
streamType = streaming.StreamTypeChat
|
||||
}
|
||||
|
||||
// Build accumulated data
|
||||
data := &streaming.AccumulatedData{
|
||||
RequestID: result.RequestID,
|
||||
Model: result.RequestedModel,
|
||||
Status: result.Status,
|
||||
Stream: true,
|
||||
Latency: result.Latency,
|
||||
TimeToFirstToken: result.TimeToFirstToken,
|
||||
OutputMessage: result.OutputMessage,
|
||||
OutputMessages: result.OutputMessages,
|
||||
ErrorDetails: result.ErrorDetails,
|
||||
TokenUsage: result.TokenUsage,
|
||||
Cost: result.Cost,
|
||||
AudioOutput: result.AudioOutput,
|
||||
TranscriptionOutput: result.TranscriptionOutput,
|
||||
ImageGenerationOutput: result.ImageGenerationOutput,
|
||||
FinishReason: result.FinishReason,
|
||||
RawResponse: result.RawResponse,
|
||||
}
|
||||
|
||||
// Handle tool calls if present
|
||||
if result.OutputMessage != nil && result.OutputMessage.ChatAssistantMessage != nil {
|
||||
data.ToolCalls = result.OutputMessage.ChatAssistantMessage.ToolCalls
|
||||
}
|
||||
|
||||
resp := &streaming.ProcessedStreamResponse{
|
||||
RequestID: result.RequestID,
|
||||
StreamType: streamType,
|
||||
Provider: result.Provider,
|
||||
RequestedModel: result.RequestedModel,
|
||||
ResolvedModel: result.ResolvedModel,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
if result.RawRequest != nil {
|
||||
rawReq := result.RawRequest
|
||||
resp.RawRequest = &rawReq
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func mergeRealtimeMetadata(metadata map[string]interface{}, ctx *schemas.BifrostContext) map[string]interface{} {
|
||||
if ctx == nil {
|
||||
return metadata
|
||||
}
|
||||
set := func(key string, ctxKey schemas.BifrostContextKey) {
|
||||
if value := bifrost.GetStringFromContext(ctx, ctxKey); value != "" {
|
||||
if metadata == nil {
|
||||
metadata = make(map[string]interface{})
|
||||
}
|
||||
metadata[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
set("realtime_session_id", schemas.BifrostContextKeyRealtimeSessionID)
|
||||
set("provider_session_id", schemas.BifrostContextKeyRealtimeProviderSessionID)
|
||||
set("realtime_source", schemas.BifrostContextKeyRealtimeSource)
|
||||
set("realtime_event_type", schemas.BifrostContextKeyRealtimeEventType)
|
||||
if bifrost.GetStringFromContext(ctx, schemas.BifrostContextKeyRealtimeSessionID) != "" {
|
||||
if metadata == nil {
|
||||
metadata = make(map[string]interface{})
|
||||
}
|
||||
metadata["realtime"] = true
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
// formatRoutingEngineLogs formats routing engine logs into a human-readable string.
|
||||
// Format: [timestamp] [engine] - message
|
||||
// Parameters:
|
||||
// - logs: Slice of routing engine log entries
|
||||
//
|
||||
// Returns:
|
||||
// - string: Formatted log string (empty string if no logs)
|
||||
func formatRoutingEngineLogs(logs []schemas.RoutingEngineLogEntry) string {
|
||||
if len(logs) == 0 {
|
||||
return ""
|
||||
}
|
||||
var sb strings.Builder
|
||||
for _, log := range logs {
|
||||
sb.WriteString(fmt.Sprintf("[%d] [%s] - %s\n", log.Timestamp, log.Engine, log.Message))
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
1
plugins/logging/version
Normal file
1
plugins/logging/version
Normal file
@@ -0,0 +1 @@
|
||||
1.5.4
|
||||
424
plugins/logging/writer.go
Normal file
424
plugins/logging/writer.go
Normal file
@@ -0,0 +1,424 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/maximhq/bifrost/core/schemas"
|
||||
"github.com/maximhq/bifrost/framework/logstore"
|
||||
)
|
||||
|
||||
const (
|
||||
// maxBatchSize is the maximum number of entries to collect before flushing
|
||||
maxBatchSize = 1000
|
||||
// batchInterval is the maximum time to wait before flushing a partial batch
|
||||
batchInterval = 2 * time.Second
|
||||
// maxBatchBytes is the approximate byte-size ceiling for a batch (100 MB).
|
||||
// When the cumulative estimated size of queued entries hits this limit the
|
||||
// batch is flushed immediately, even if maxBatchSize hasn't been reached.
|
||||
maxBatchBytes = 100 * 1024 * 1024
|
||||
// writeQueueCapacity is the buffer size for the write queue channel
|
||||
writeQueueCapacity = 10000
|
||||
// maxDeferredUsageConcurrency limits concurrent deferred usage DB updates
|
||||
maxDeferredUsageConcurrency = 5
|
||||
// pendingLogTTL is how long a pending log entry can stay in memory before cleanup
|
||||
pendingLogTTL = 5 * time.Minute
|
||||
)
|
||||
|
||||
// PendingLogData holds PreLLMHook input data until PostLLMHook fires.
|
||||
// Stored in pendingLogs sync.Map keyed by requestID.
|
||||
type PendingLogData struct {
|
||||
RequestID string
|
||||
ParentRequestID string
|
||||
Timestamp time.Time
|
||||
FallbackIndex int
|
||||
Status string
|
||||
RoutingEnginesUsed []string
|
||||
InitialData *InitialLogData
|
||||
CreatedAt time.Time // For cleanup of stale entries
|
||||
}
|
||||
|
||||
// pendingInjectEntries wraps a slice of log entries so it can be used with sync.Map.
|
||||
// The mutex protects concurrent appends to the entries slice within the same traceID.
|
||||
type pendingInjectEntries struct {
|
||||
mu sync.Mutex
|
||||
entries []*logstore.Log
|
||||
createdAt time.Time
|
||||
}
|
||||
|
||||
// writeQueueEntry is an entry pushed to the batch write queue.
|
||||
type writeQueueEntry struct {
|
||||
log *logstore.Log // Complete log entry ready for INSERT
|
||||
callback func(entry *logstore.Log) // Post-commit callback receives the inserted entry (no DB re-read needed)
|
||||
}
|
||||
|
||||
// batchWriter is the single writer goroutine that drains the write queue
|
||||
// and processes entries in batched transactions.
|
||||
func (p *LoggerPlugin) batchWriter() {
|
||||
defer p.wg.Done()
|
||||
|
||||
batch := make([]*writeQueueEntry, 0, maxBatchSize)
|
||||
batchBytes := 0
|
||||
timer := time.NewTimer(batchInterval)
|
||||
timer.Stop()
|
||||
timerRunning := false
|
||||
|
||||
flush := func() {
|
||||
if timerRunning {
|
||||
if !timer.Stop() {
|
||||
select {
|
||||
case <-timer.C:
|
||||
default:
|
||||
}
|
||||
}
|
||||
timerRunning = false
|
||||
}
|
||||
p.safeProcessBatch(batch)
|
||||
clear(batch)
|
||||
batch = batch[:0]
|
||||
batchBytes = 0
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case entry, ok := <-p.writeQueue:
|
||||
if !ok {
|
||||
// Channel closed - flush remaining batch and exit
|
||||
p.safeProcessBatch(batch)
|
||||
return
|
||||
}
|
||||
batch = append(batch, entry)
|
||||
batchBytes += estimateLogEntrySize(entry.log)
|
||||
if len(batch) >= maxBatchSize || batchBytes >= maxBatchBytes {
|
||||
flush()
|
||||
} else if !timerRunning {
|
||||
timer.Reset(batchInterval)
|
||||
timerRunning = true
|
||||
}
|
||||
|
||||
case <-timer.C:
|
||||
timerRunning = false
|
||||
if len(batch) > 0 {
|
||||
flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// safeProcessBatch wraps processBatch with panic recovery so a single
|
||||
// bad entry cannot kill the batchWriter goroutine.
|
||||
func (p *LoggerPlugin) safeProcessBatch(batch []*writeQueueEntry) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
p.logger.Error("panic in batch writer processBatch (recovered, %d entries dropped): %v", len(batch), r)
|
||||
p.droppedRequests.Add(int64(len(batch)))
|
||||
}
|
||||
}()
|
||||
p.processBatch(batch)
|
||||
}
|
||||
|
||||
// processBatch executes a batch of log entries in a single database transaction.
|
||||
func (p *LoggerPlugin) processBatch(batch []*writeQueueEntry) {
|
||||
if len(batch) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Collect all log entries for batch insert
|
||||
logs := make([]*logstore.Log, 0, len(batch))
|
||||
for _, entry := range batch {
|
||||
if entry.log != nil {
|
||||
logs = append(logs, entry.log)
|
||||
}
|
||||
}
|
||||
|
||||
if len(logs) > 0 {
|
||||
if err := p.store.BatchCreateIfNotExists(p.ctx, logs); err != nil {
|
||||
p.logger.Warn("batch insert failed for %d entries, falling back to individual inserts: %v", len(logs), err)
|
||||
// Individual fallback — isolate the bad entry instead of losing the whole batch
|
||||
for _, log := range logs {
|
||||
if err := p.store.BatchCreateIfNotExists(p.ctx, []*logstore.Log{log}); err != nil {
|
||||
p.logger.Warn("individual insert failed for log %s: %v", log.ID, err)
|
||||
p.droppedRequests.Add(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect callbacks that need to fire, then run them in a single goroutine.
|
||||
// This avoids blocking the batch writer (synchronous was causing 1+ second stalls
|
||||
// during WebSocket broadcast) without creating a goroutine per entry (which caused
|
||||
// goroutine explosion to 13K+).
|
||||
type cbPair struct {
|
||||
cb func(*logstore.Log)
|
||||
log *logstore.Log
|
||||
}
|
||||
var callbacks []cbPair
|
||||
for _, entry := range batch {
|
||||
if entry.callback != nil {
|
||||
callbacks = append(callbacks, cbPair{cb: entry.callback, log: entry.log})
|
||||
}
|
||||
}
|
||||
if len(callbacks) > 0 {
|
||||
go func(callbacks []cbPair) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
p.logger.Warn("log callback panicked: %v", r)
|
||||
}
|
||||
}()
|
||||
for _, pair := range callbacks {
|
||||
pair.cb(pair.log)
|
||||
}
|
||||
}(callbacks)
|
||||
}
|
||||
}
|
||||
|
||||
// cleanupStalePendingLogs removes entries from pendingLogs that have been
|
||||
// waiting longer than pendingLogTTL. This handles cases where PostLLMHook
|
||||
// never fires for a request (e.g., request was cancelled before reaching the provider).
|
||||
func (p *LoggerPlugin) cleanupStalePendingLogs() {
|
||||
cutoff := time.Now().Add(-pendingLogTTL)
|
||||
p.pendingLogsEntries.Range(func(key, value any) bool {
|
||||
if pending, ok := value.(*PendingLogData); ok {
|
||||
if pending.CreatedAt.Before(cutoff) {
|
||||
p.pendingLogsEntries.Delete(key)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
p.pendingLogsToInject.Range(func(key, value any) bool {
|
||||
if pending, ok := value.(*pendingInjectEntries); ok {
|
||||
if pending.createdAt.Before(cutoff) {
|
||||
p.pendingLogsToInject.Delete(key)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// enqueueLogEntry pushes a complete log entry to the write queue.
|
||||
// If the queue is full, the entry is dropped to prevent Postgres slowness
|
||||
// from cascading into request handling goroutines.
|
||||
func (p *LoggerPlugin) enqueueLogEntry(entry *logstore.Log, callback func(entry *logstore.Log)) {
|
||||
if p.closed.Load() {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// Channel was closed between the check and send; entry is dropped
|
||||
p.droppedRequests.Add(1)
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case p.writeQueue <- &writeQueueEntry{log: entry, callback: callback}:
|
||||
// enqueued successfully
|
||||
default:
|
||||
p.droppedRequests.Add(1)
|
||||
p.logger.Warn("log write queue full, dropping log entry %s", entry.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// estimateLogEntrySize returns a rough byte-size estimate for a log entry
|
||||
// based on its serialized text fields. This is intentionally cheap — no
|
||||
// marshaling, just string lengths — and is used to cap batch memory.
|
||||
//
|
||||
// NOTE: At enqueue time the string fields may still be empty (data lives in the
|
||||
// Parsed struct fields until GORM's BeforeCreate hook serializes them), so this
|
||||
// can undercount significantly. That is acceptable — the byte limit is a
|
||||
// coarse safety valve, not a precise memory cap. Overshooting by 2× is fine;
|
||||
// maxBatchSize is the primary batching control.
|
||||
func estimateLogEntrySize(log *logstore.Log) int {
|
||||
if log == nil {
|
||||
return 0
|
||||
}
|
||||
// Sum the dominant text/blob fields. Fixed-width columns (IDs, timestamps,
|
||||
// ints, bools) are negligible compared to these and covered by the 512-byte
|
||||
// baseline below.
|
||||
n := len(log.InputHistory) +
|
||||
len(log.ResponsesInputHistory) +
|
||||
len(log.OutputMessage) +
|
||||
len(log.ResponsesOutput) +
|
||||
len(log.EmbeddingOutput) +
|
||||
len(log.RerankOutput) +
|
||||
len(log.OCROutput) +
|
||||
len(log.Params) +
|
||||
len(log.Tools) +
|
||||
len(log.ToolCalls) +
|
||||
len(log.SpeechInput) +
|
||||
len(log.SpeechOutput) +
|
||||
len(log.TranscriptionInput) +
|
||||
len(log.TranscriptionOutput) +
|
||||
len(log.ImageGenerationInput) +
|
||||
len(log.ImageGenerationOutput) +
|
||||
len(log.VideoGenerationInput) +
|
||||
len(log.VideoGenerationOutput) +
|
||||
len(log.VideoRetrieveOutput) +
|
||||
len(log.VideoDownloadOutput) +
|
||||
len(log.VideoListOutput) +
|
||||
len(log.VideoDeleteOutput) +
|
||||
len(log.ListModelsOutput) +
|
||||
len(log.TokenUsage) +
|
||||
len(log.ErrorDetails) +
|
||||
len(log.RawRequest) +
|
||||
len(log.RawResponse) +
|
||||
len(log.PassthroughRequestBody) +
|
||||
len(log.PassthroughResponseBody) +
|
||||
len(log.ContentSummary) +
|
||||
len(log.CacheDebug) +
|
||||
len(log.RoutingEngineLogs)
|
||||
// Baseline for fixed-width columns and struct overhead
|
||||
return n + 512
|
||||
}
|
||||
|
||||
// buildInitialLogEntry constructs a logstore.Log from PendingLogData (input)
|
||||
// without writing to the database. Used for the UI callback in PreLLMHook.
|
||||
func buildInitialLogEntry(pending *PendingLogData) *logstore.Log {
|
||||
entry := &logstore.Log{
|
||||
ID: pending.RequestID,
|
||||
Timestamp: pending.Timestamp,
|
||||
Object: pending.InitialData.Object,
|
||||
Provider: pending.InitialData.Provider,
|
||||
Model: pending.InitialData.Model,
|
||||
FallbackIndex: pending.FallbackIndex,
|
||||
Status: "processing",
|
||||
Stream: false,
|
||||
CreatedAt: pending.Timestamp,
|
||||
InputHistoryParsed: pending.InitialData.InputHistory,
|
||||
ResponsesInputHistoryParsed: pending.InitialData.ResponsesInputHistory,
|
||||
ParamsParsed: pending.InitialData.Params,
|
||||
ToolsParsed: pending.InitialData.Tools,
|
||||
PassthroughRequestBody: pending.InitialData.PassthroughRequestBody,
|
||||
}
|
||||
if pending.ParentRequestID != "" {
|
||||
entry.ParentRequestID = &pending.ParentRequestID
|
||||
}
|
||||
if len(pending.RoutingEnginesUsed) > 0 {
|
||||
entry.RoutingEnginesUsed = pending.RoutingEnginesUsed
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
// buildCompleteLogEntryFromPending constructs a logstore.Log with both input (from PendingLogData)
|
||||
// and output fields fully populated. The caller provides a function to apply output-specific fields.
|
||||
func buildCompleteLogEntryFromPending(pending *PendingLogData) *logstore.Log {
|
||||
entry := &logstore.Log{
|
||||
ID: pending.RequestID,
|
||||
Timestamp: pending.Timestamp,
|
||||
Object: pending.InitialData.Object,
|
||||
Provider: pending.InitialData.Provider,
|
||||
Model: pending.InitialData.Model,
|
||||
FallbackIndex: pending.FallbackIndex,
|
||||
Status: "success",
|
||||
CreatedAt: pending.Timestamp,
|
||||
// Set parsed fields for serialization via GORM hooks
|
||||
InputHistoryParsed: pending.InitialData.InputHistory,
|
||||
ResponsesInputHistoryParsed: pending.InitialData.ResponsesInputHistory,
|
||||
ParamsParsed: pending.InitialData.Params,
|
||||
ToolsParsed: pending.InitialData.Tools,
|
||||
SpeechInputParsed: pending.InitialData.SpeechInput,
|
||||
TranscriptionInputParsed: pending.InitialData.TranscriptionInput,
|
||||
OCRInputParsed: pending.InitialData.OCRInput,
|
||||
ImageGenerationInputParsed: pending.InitialData.ImageGenerationInput,
|
||||
ImageEditInputParsed: pending.InitialData.ImageEditInput,
|
||||
ImageVariationInputParsed: pending.InitialData.ImageVariationInput,
|
||||
VideoGenerationInputParsed: pending.InitialData.VideoGenerationInput,
|
||||
PassthroughRequestBody: pending.InitialData.PassthroughRequestBody,
|
||||
}
|
||||
if pending.ParentRequestID != "" {
|
||||
entry.ParentRequestID = &pending.ParentRequestID
|
||||
}
|
||||
if len(pending.RoutingEnginesUsed) > 0 {
|
||||
entry.RoutingEnginesUsed = pending.RoutingEnginesUsed
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
// applyModelAlias sets entry.Model to resolvedModel (falling back to requestedModel if empty)
|
||||
// and entry.Alias to requestedModel when the two differ (i.e. an alias mapping was applied).
|
||||
func applyModelAlias(entry *logstore.Log, requestedModel, resolvedModel string) {
|
||||
if resolvedModel != "" && resolvedModel != requestedModel {
|
||||
entry.Model = resolvedModel
|
||||
entry.Alias = &requestedModel
|
||||
} else {
|
||||
// No alias mapping; keep whichever value is non-empty as the model.
|
||||
if resolvedModel != "" {
|
||||
entry.Model = resolvedModel
|
||||
} else if requestedModel != "" {
|
||||
entry.Model = requestedModel
|
||||
}
|
||||
entry.Alias = nil
|
||||
}
|
||||
}
|
||||
|
||||
// applyOutputFieldsToEntry sets common output fields on a log entry.
|
||||
func applyOutputFieldsToEntry(
|
||||
entry *logstore.Log,
|
||||
selectedKeyID, selectedKeyName string,
|
||||
virtualKeyID, virtualKeyName string,
|
||||
routingRuleID, routingRuleName string,
|
||||
selectedPromptID, selectedPromptName, selectedPromptVersion string,
|
||||
teamID, teamName string,
|
||||
customerID, customerName string,
|
||||
userID, userName string,
|
||||
businessUnitID, businessUnitName string,
|
||||
numberOfRetries int,
|
||||
latency int64,
|
||||
attemptTrail []schemas.KeyAttemptRecord,
|
||||
) {
|
||||
entry.SelectedKeyID = selectedKeyID
|
||||
entry.SelectedKeyName = selectedKeyName
|
||||
if virtualKeyID != "" {
|
||||
entry.VirtualKeyID = &virtualKeyID
|
||||
}
|
||||
if virtualKeyName != "" {
|
||||
entry.VirtualKeyName = &virtualKeyName
|
||||
}
|
||||
if routingRuleID != "" {
|
||||
entry.RoutingRuleID = &routingRuleID
|
||||
}
|
||||
if routingRuleName != "" {
|
||||
entry.RoutingRuleName = &routingRuleName
|
||||
}
|
||||
if selectedPromptID != "" {
|
||||
entry.SelectedPromptID = &selectedPromptID
|
||||
}
|
||||
if selectedPromptName != "" {
|
||||
entry.SelectedPromptName = &selectedPromptName
|
||||
}
|
||||
if selectedPromptVersion != "" {
|
||||
entry.SelectedPromptVersion = &selectedPromptVersion
|
||||
}
|
||||
if teamID != "" {
|
||||
entry.TeamID = &teamID
|
||||
}
|
||||
if teamName != "" {
|
||||
entry.TeamName = &teamName
|
||||
}
|
||||
if customerID != "" {
|
||||
entry.CustomerID = &customerID
|
||||
}
|
||||
if customerName != "" {
|
||||
entry.CustomerName = &customerName
|
||||
}
|
||||
if userID != "" {
|
||||
entry.UserID = &userID
|
||||
}
|
||||
if userName != "" {
|
||||
entry.UserName = &userName
|
||||
}
|
||||
if businessUnitID != "" {
|
||||
entry.BusinessUnitID = &businessUnitID
|
||||
}
|
||||
if businessUnitName != "" {
|
||||
entry.BusinessUnitName = &businessUnitName
|
||||
}
|
||||
if numberOfRetries != 0 {
|
||||
entry.NumberOfRetries = numberOfRetries
|
||||
}
|
||||
if latency != 0 {
|
||||
latF := float64(latency)
|
||||
entry.Latency = &latF
|
||||
}
|
||||
if len(attemptTrail) > 0 {
|
||||
entry.AttemptTrailParsed = attemptTrail
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user