diff --git a/README.md b/README.md index 3e5bf2e..950b518 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # đŸĒĄ orra -Move beyond simple Crews and Agents. Use orra to build production-ready multi-agent applications that handle complex real-world interactions. +Move beyond simple Crews and Agents. Use orra's production-grade planning to reliably run multi-agent workflows that require complex real-world interactions. ![](images/orra-diagram.png) @@ -9,11 +9,11 @@ orra coordinates tasks across your existing stack, agents and any tools run as s * 🧠 Smart pre-evaluated execution plans * đŸŽ¯ Domain grounded * đŸ—ŋ Durable execution -* 🚀 Go fast with tools as services +* 🚀 Go fast and save cost with tools as services * â†Šī¸ Revert state to handle failures * â›‘ī¸ Automatic service health monitoring * 🔮 Real-time status tracking -* đŸĒ Webhooks for completion and failure notifications +* đŸĒ Webhooks for completion and failure monitoring [Learn why we built orra →](https://tinyurl.com/orra-launch-blog-post) @@ -33,6 +33,7 @@ orra coordinates tasks across your existing stack, agents and any tools run as s - [Docs](#docs) - [Self Hosting & On-premises Deployment](#self-hosting--on-premises-deployment) - [Support](#support) +- [Telemetry](#telemetry) - [License](#license) ## Installation @@ -96,11 +97,11 @@ Download the latest CLI binary for your platform from our [releases page](https: ```shell # macOS -curl -L https://github.com/orra-dev/orra/releases/download/v0.2.5/orra-darwin-arm64 -o /usr/local/bin/orra +curl -L https://github.com/orra-dev/orra/releases/download/v0.2.6/orra-darwin-arm64 -o /usr/local/bin/orra chmod +x /usr/local/bin/orra # Linux -curl -L https://github.com/ezodude/orra/releases/download/v0.2.5/orra-linux-amd64 -o /usr/local/bin/orra +curl -L https://github.com/ezodude/orra/releases/download/v0.2.6/orra-linux-amd64 -o /usr/local/bin/orra chmod +x /usr/local/bin/orra # Verify installation @@ -291,6 +292,10 @@ Need help? We're here to support you: - Report a bug or request a feature by creating an [issue](https://github.com/orra-dev/orra/issues/new?template=bug-report-feature-request.yml) - Start a [discussion](https://github.com/orra-dev/orra/discussions) about your ideas or questions +## Telemetry + +See [telemetry.md](./docs/telemetry.md) for details on what is collected and how to opt out. + ## License Orra is MPL-2.0 licensed. diff --git a/docs/telemetry.md b/docs/telemetry.md new file mode 100644 index 0000000..c89b9f2 --- /dev/null +++ b/docs/telemetry.md @@ -0,0 +1,42 @@ +# Telemetry + +To help us improve orra, we collect minimal, privacy-preserving usage analytics. **Telemetry is anonymous by design, and you can easily opt out.** + +## What We Collect + +- **[Anonymous usage events](../planengine/events.go)** (e.g., server start/stop, project/service changes, execution plan outcomes). +- **Non-identifiable execution plan and project tracking:** + - For events related to projects, we only track a **hashed version of the project ID**. + - For execution plan events, we only track a **hashed version of the execution plan ID**. +- **No personal or sensitive data** is ever collected. +- **No IP addresses** or environment details are sent. + +## How IDs Are Handled + +- We use a **SHA-256 hash** of each project or execution plan ID. + This ensures IDs cannot be reversed or linked to your actual data. + +- Each orra instance also generates a random, anonymous identifier stored locally, used solely to distinguish unique installations. + +## How to Opt Out + +You can fully disable telemetry at any time: + +- **Via environment variable:** + Set the environment variable before starting orra: + ```shell + export ANONYMIZED_TELEMETRY=false + ``` +- **Via `.env` file:** + Add the following line to your project's `.env` file: + ```text + ANONYMIZED_TELEMETRY=false + ``` + +## Transparency and Trust + +- All telemetry code is open source and can be reviewed [here](../planengine/telemetry.go). +- We follow industry best practices for privacy and transparency. +- Telemetry helps us prioritize features, fix bugs, and ensure orra works reliably for everyone. + +**Thank you for helping us make orra better!** diff --git a/planengine/_env b/planengine/_env index 3ea08e7..da2fc14 100644 --- a/planengine/_env +++ b/planengine/_env @@ -30,8 +30,18 @@ EMBEDDINGS_API_BASE_URL=https://api.openai.com/v1 # Default for OpenAI, change # Server port (default: 8005) # PORT=8005 +#======================================== +# PDDL CONFIGURATION +#======================================== + # PDDL validator path (default: /usr/local/bin/Validate) # PDDL_VALIDATOR_PATH=/usr/local/bin/Validate # PDDL validation timeout (default: 30s) # PDDL_VALIDATION_TIMEOUT=30s + +#======================================== +# Telemetry CONFIGURATION +#======================================== + +ANONYMIZED_TELEMETRY=true diff --git a/planengine/app.go b/planengine/app.go index 9acb890..6969bcc 100644 --- a/planengine/app.go +++ b/planengine/app.go @@ -25,13 +25,14 @@ import ( ) type App struct { - Engine *PlanEngine - Router *mux.Router - Db *BadgerDB - Cfg Config - RootCtx context.Context - RootCancel context.CancelFunc - Logger zerolog.Logger + Engine *PlanEngine + Router *mux.Router + Db *BadgerDB + Cfg Config + TelemetrySvc *TelemetryService + RootCtx context.Context + RootCancel context.CancelFunc + Logger zerolog.Logger } func NewApp(cfg Config, args []string) (*App, error) { @@ -125,6 +126,10 @@ func (app *App) Run() { if err := srv.ListenAndServe(); err != nil { app.Logger.Info().Msg(err.Error()) } + + app.TelemetrySvc.TrackEvent(EventServerStart, map[string]any{ + "version": Version, + }) }() c := make(chan os.Signal, 1) @@ -162,6 +167,10 @@ func (app *App) gracefulShutdown(srv *http.Server, ctx context.Context) { app.Logger.Error().Err(err).Msg("Error shutting down plan engine server") } app.Logger.Debug().Msg("http: All connections drained") + + app.TelemetrySvc.TrackEvent(EventServerStop, map[string]any{ + "version": Version, + }) } func (app *App) RegisterProject(w http.ResponseWriter, r *http.Request) { diff --git a/planengine/app_test.go b/planengine/app_test.go index 688275e..730d62e 100644 --- a/planengine/app_test.go +++ b/planengine/app_test.go @@ -47,13 +47,20 @@ func setupTestApp(t *testing.T) (*App, *Project, func()) { assert.NoError(t, err) } + telemetrySvc := &TelemetryService{ + client: nil, + enabled: false, + logger: logger, + } + plane := NewPlanEngine() - plane.Initialise(context.Background(), db, db, db, db, db, nil, nil, nil, &fakePddlValidator{}, nil, logger) + plane.Initialise(context.Background(), db, db, db, db, db, nil, nil, nil, &fakePddlValidator{}, nil, logger, telemetrySvc) app := &App{ - Router: mux.NewRouter(), - Engine: plane, - Logger: logger, + Router: mux.NewRouter(), + Engine: plane, + Logger: logger, + TelemetrySvc: telemetrySvc, } app.configureRoutes() diff --git a/planengine/compworker.go b/planengine/compworker.go index 79a0f92..3b033fc 100644 --- a/planengine/compworker.go +++ b/planengine/compworker.go @@ -47,6 +47,11 @@ func NewCompensationWorker(projectID, orchestrationID string, logManager *LogMan } func (w *CompensationWorker) Start(ctx context.Context, orchestrationID string) { + w.LogManager.planEngine.TelemetrySvc.TrackEvent(EventCompensationStarted, map[string]any{ + "version": Version, + "execution_plan_id": HashUUID(orchestrationID), + }) + for _, candidate := range w.Candidates { if err := w.processCandidate(ctx, candidate); err != nil { w.LogManager.Logger.Error(). @@ -61,6 +66,15 @@ func (w *CompensationWorker) Start(ctx context.Context, orchestrationID string) if strings.Contains(err.Error(), "expired") { status = CompensationExpired logType = CompensationExpiredLogType + w.LogManager.planEngine.TelemetrySvc.TrackEvent(EventCompensationExpired, map[string]any{ + "version": Version, + "execution_plan_id": HashUUID(orchestrationID), + }) + } else { + w.LogManager.planEngine.TelemetrySvc.TrackEvent(EventCompensationFailed, map[string]any{ + "version": Version, + "execution_plan_id": HashUUID(orchestrationID), + }) } // Log the compensation failure @@ -109,6 +123,11 @@ func (w *CompensationWorker) Start(ctx context.Context, orchestrationID string) go w.sendWebhookNotification(webhook, payload) } } + } else { + w.LogManager.planEngine.TelemetrySvc.TrackEvent(EventCompensationCompleted, map[string]any{ + "version": Version, + "execution_plan_id": HashUUID(orchestrationID), + }) } } } diff --git a/planengine/config.go b/planengine/config.go index f0578ce..ce3460e 100644 --- a/planengine/config.go +++ b/planengine/config.go @@ -41,6 +41,7 @@ const ( PauseExecutionCode = "PAUSE_EXECUTION" StartJsonMarker = "```json" EndJsonMarker = "```" + AnonymouseIDFilename = "orra.telemetry.uuid" ) // Supported LLM and Embeddings models @@ -100,6 +101,7 @@ type Config struct { PddlValidatorPath string `envconfig:"default=/usr/local/bin/Validate"` PddlValidationTimeout time.Duration `envconfig:"default=30s"` StoragePath string `envconfig:"optional"` + AnonymizedTelemetry bool `envconfig:"default=true"` } func Load() (Config, error) { diff --git a/planengine/engine.go b/planengine/engine.go index 43e3fb5..780508e 100644 --- a/planengine/engine.go +++ b/planengine/engine.go @@ -51,20 +51,7 @@ func NewPlanEngine() *PlanEngine { return plane } -func (p *PlanEngine) Initialise( - ctx context.Context, - pStorage ProjectStorage, - svcStorage ServiceStorage, - orchestrationStorage OrchestrationStorage, - groundingStorage GroundingStorage, - failedCompStorage FailedCompensationStorage, - logMgr *LogManager, - wsManager *WebSocketManager, - vCache *VectorCache, - pddlValid PddlValidator, - matcher SimilarityMatcher, - Logger zerolog.Logger, -) { +func (p *PlanEngine) Initialise(ctx context.Context, pStorage ProjectStorage, svcStorage ServiceStorage, orchestrationStorage OrchestrationStorage, groundingStorage GroundingStorage, failedCompStorage FailedCompensationStorage, logMgr *LogManager, wsManager *WebSocketManager, vCache *VectorCache, pddlValid PddlValidator, matcher SimilarityMatcher, Logger zerolog.Logger, telemetry *TelemetryService) { p.pStorage = pStorage p.svcStorage = svcStorage p.orchestrationStorage = orchestrationStorage @@ -76,6 +63,7 @@ func (p *PlanEngine) Initialise( p.VectorCache = vCache p.PddlValidator = pddlValid p.SimilarityMatcher = matcher + p.TelemetrySvc = telemetry if projects, err := pStorage.ListProjects(); err == nil { p.Logger.Trace().Interface("Projects", projects).Msg("Loaded projects from DB") @@ -180,6 +168,10 @@ func (p *PlanEngine) RegisterOrUpdateService(service *ServiceInfo) error { Str("ProjectID", service.ProjectID). Str("ServiceName", service.Name). Msgf("Generating new service ID") + p.TelemetrySvc.TrackEvent(EventProjectServiceRegistered, map[string]any{ + "version": Version, + "project_id": HashUUID(service.ProjectID), + }) } else { // Load existing service existingService, err := p.svcStorage.LoadServiceByProjectID(service.ProjectID, service.ID) @@ -194,6 +186,10 @@ func (p *PlanEngine) RegisterOrUpdateService(service *ServiceInfo) error { Str("ServiceName", service.Name). Int64("ServiceVersion", service.Version). Msgf("Updating existing service") + p.TelemetrySvc.TrackEvent(EventProjectServiceUpdated, map[string]any{ + "version": Version, + "project_id": HashUUID(service.ProjectID), + }) } if err := p.svcStorage.StoreService(service); err != nil { @@ -341,6 +337,11 @@ func (p *PlanEngine) ApplyGroundingSpec(ctx context.Context, spec *GroundingSpec Str("domain", spec.Domain). Msgf("Added grounding spec with %d action uses cases", len(spec.UseCases)) + p.TelemetrySvc.TrackEvent(EventProjectGroundingApplied, map[string]any{ + "version": Version, + "project_id": HashUUID(projectID), + }) + return nil } @@ -415,6 +416,11 @@ func (p *PlanEngine) RemoveGroundingSpecByName(projectID string, name string) er Str("name", name). Msg("Removed grounding spec") + p.TelemetrySvc.TrackEvent(EventProjectGroundingRemoved, map[string]any{ + "version": Version, + "project_id": HashUUID(projectID), + }) + return nil } @@ -468,6 +474,7 @@ func (p *PlanEngine) AddProject(project *Project) error { } p.projects[project.ID] = project + p.TelemetrySvc.TrackEvent(EventProjectAdded, map[string]any{"version": Version}) return nil } diff --git a/planengine/events.go b/planengine/events.go new file mode 100644 index 0000000..f4a5439 --- /dev/null +++ b/planengine/events.go @@ -0,0 +1,30 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package main + +// Tracked telemetry events + +const ( + EventServerStart = "server_start" + EventServerStop = "server_stop" + EventProjectAdded = "project_added" + EventProjectServiceRegistered = "service_registered" + EventProjectServiceUpdated = "service_updated" + EventExecutionPlanNotActionable = "execution_plan_not_actionable" + EventExecutionPlanFailedCreation = "execution_plan_failed_creation" + EventExecutionPlanFailedValidation = "execution_plan_failed_validation" + EventExecutionPlanAttempted = "execution_plan_attempted" + EventExecutionPlanCompleted = "execution_plan_completed" + EventExecutionPlanAborted = "execution_plan_aborted" + EventExecutionPlanFailed = "execution_plan_failed" + EventCompensationStarted = "execution_plan_compensation_started" + EventCompensationCompleted = "execution_plan_compensation_completed" + EventCompensationFailed = "execution_plan_compensation_failed" + EventCompensationExpired = "execution_plan_compensation_expired" + EventProjectGroundingApplied = "project_grounding_applied" + EventProjectGroundingRemoved = "project_grounding_removed" +) diff --git a/planengine/go.mod b/planengine/go.mod index f56773f..e12c07b 100644 --- a/planengine/go.mod +++ b/planengine/go.mod @@ -7,7 +7,7 @@ toolchain go1.23.8 require ( github.com/RussellLuo/validating/v3 v3.0.0 github.com/cenkalti/backoff/v4 v4.3.0 - github.com/dgraph-io/badger/v4 v4.6.0 + github.com/dgraph-io/badger/v4 v4.7.0 github.com/gilcrest/diygoapi v0.53.0 github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 @@ -15,22 +15,22 @@ require ( github.com/lithammer/shortuuid/v4 v4.2.0 github.com/olahol/melody v1.2.1 github.com/peterbourgon/ff/v3 v3.4.0 + github.com/posthog/posthog-go v1.4.10 github.com/rs/zerolog v1.34.0 - github.com/sashabaranov/go-openai v1.38.1 + github.com/sashabaranov/go-openai v1.38.3 github.com/stretchr/testify v1.10.0 github.com/vrischmann/envconfig v1.4.1 - golang.org/x/sync v0.12.0 + golang.org/x/sync v0.13.0 gonum.org/v1/gonum v0.16.0 ) require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgraph-io/ristretto/v2 v2.1.0 // indirect + github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/flatbuffers v25.2.10+incompatible // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/klauspost/compress v1.18.0 // indirect @@ -38,14 +38,13 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel v1.34.0 // indirect - go.opentelemetry.io/otel/metric v1.34.0 // indirect - go.opentelemetry.io/otel/trace v1.34.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c // indirect - golang.org/x/net v0.35.0 // indirect - golang.org/x/sys v0.30.0 // indirect - google.golang.org/protobuf v1.36.5 // indirect + golang.org/x/net v0.39.0 // indirect + golang.org/x/sys v0.32.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/planengine/go.sum b/planengine/go.sum index 663d777..a4d0604 100644 --- a/planengine/go.sum +++ b/planengine/go.sum @@ -1,33 +1,22 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/RussellLuo/validating/v3 v3.0.0 h1:CnUjHJgFMQRO3EVb6OGZmkX+5lYc6m5CTdldo+IG0jE= github.com/RussellLuo/validating/v3 v3.0.0/go.mod h1:aXLMAOUVm1Abr2yLXA8g49WVSI6RiiCwn0TXv2iToU0= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgraph-io/badger/v4 v4.5.1 h1:7DCIXrQjo1LKmM96YD+hLVJ2EEsyyoWxJfpdd56HLps= -github.com/dgraph-io/badger/v4 v4.5.1/go.mod h1:qn3Be0j3TfV4kPbVoK0arXCD1/nr1ftth6sbL5jxdoA= -github.com/dgraph-io/badger/v4 v4.6.0 h1:acOwfOOZ4p1dPRnYzvkVm7rUk2Y21TgPVepCy5dJdFQ= -github.com/dgraph-io/badger/v4 v4.6.0/go.mod h1:KSJ5VTuZNC3Sd+YhvVjk2nYua9UZnnTr/SkXvdtiPgI= -github.com/dgraph-io/ristretto/v2 v2.1.0 h1:59LjpOJLNDULHh8MC4UaegN52lC4JnO2dITsie/Pa8I= -github.com/dgraph-io/ristretto/v2 v2.1.0/go.mod h1:uejeqfYXpUomfse0+lO+13ATz4TypQYLJZzBSAemuB4= -github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= -github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgraph-io/badger/v4 v4.7.0 h1:Q+J8HApYAY7UMpL8d9owqiB+odzEc0zn/aqOD9jhc6Y= +github.com/dgraph-io/badger/v4 v4.7.0/go.mod h1:He7TzG3YBy3j4f5baj5B7Zl2XyfNe5bl4Udl0aPemVA= +github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= +github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/gilcrest/diygoapi v0.53.0 h1:ZIMAJSiygrllCwVV6Wpid9TdpbG0b/Dyaqk+NBukIHA= github.com/gilcrest/diygoapi v0.53.0/go.mod h1:hOBJ5+DOvWpzuMBgIZILBm2NPtBpciz0gE3IbEI04gY= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -36,33 +25,10 @@ github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4 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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/flatbuffers v24.12.23+incompatible h1:ubBKR94NR4pXUCY/MUsRVzd9umNW7ht7EG9hHfS9FX8= -github.com/google/flatbuffers v24.12.23+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= @@ -71,10 +37,10 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -88,7 +54,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ 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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/olahol/melody v1.2.1 h1:xdwRkzHxf+B0w4TKbGpUSSkV516ZucQZJIWLztOWICQ= github.com/olahol/melody v1.2.1/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7CvJgJM4= @@ -98,114 +63,53 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/posthog/posthog-go v1.4.10 h1:rpCRxxe2a4UPq9VM7rANRNRFZk0w/5To4mhYvNK9ipU= +github.com/posthog/posthog-go v1.4.10/go.mod h1:uYC2l1Yktc8E+9FAHJ9QZG4vQf/NHJPD800Hsm7DzoM= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= -github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= -github.com/sashabaranov/go-openai v1.36.1 h1:EVfRXwIlW2rUzpx6vR+aeIKCK/xylSrVYAx1TMTSX3g= -github.com/sashabaranov/go-openai v1.36.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= -github.com/sashabaranov/go-openai v1.38.1 h1:TtZabbFQZa1nEni/IhVtDF/WQjVqDgd+cWR5OeddzF8= -github.com/sashabaranov/go-openai v1.38.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= +github.com/sashabaranov/go-openai v1.38.3 h1:cpDHJKH3WOuCx7QOsux4umjMPNZCUJrXW0FkXotjM4Q= +github.com/sashabaranov/go-openai v1.38.3/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= 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/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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vrischmann/envconfig v1.4.1 h1:fucz2HsoAkJCLgIngWdWqLNxNjdWD14zfrLF6EQPdY4= github.com/vrischmann/envconfig v1.4.1/go.mod h1:cX3p+/PEssil6fWwzIS7kf8iFpli3giuxXGHxckucYc= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc= golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= -gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= -google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/planengine/main.go b/planengine/main.go index 76bd794..8235107 100644 --- a/planengine/main.go +++ b/planengine/main.go @@ -13,11 +13,17 @@ import ( "time" _ "github.com/joho/godotenv/autoload" + "github.com/posthog/posthog-go" "github.com/gorilla/mux" ) func main() { + postHogClient, _ := posthog.NewWithConfig("phc_oNzcBG9BiDfVaTE3gJTlCHTIwjBS68HLn4ZdKnkawoC", posthog.Config{Endpoint: "https://eu.i.posthog.com"}) + defer func(postHogClient posthog.Client) { + _ = postHogClient.Close() + }(postHogClient) + cfg, err := Load() if err != nil { log.Fatalf("could not load plan engine config: %s", err.Error()) @@ -45,6 +51,7 @@ func main() { } engine := NewPlanEngine() + telemetrySvc := NewTelemetryService(postHogClient, cfg.AnonymizedTelemetry, app.Logger) wsManager := NewWebSocketManager(app.Logger) matcher := NewMatcher(llmClient, app.Logger) vCache := NewVectorCache(llmClient, matcher, 1000, 24*time.Hour, app.Logger) @@ -54,11 +61,12 @@ func main() { } pddlValidSvc := NewPddlValidationService(cfg.PddlValidatorPath, cfg.PddlValidationTimeout, app.Logger) logManager.Logger = app.Logger - engine.Initialise(rootCtx, db, db, db, db, db, logManager, wsManager, vCache, pddlValidSvc, matcher, app.Logger) + engine.Initialise(rootCtx, db, db, db, db, db, logManager, wsManager, vCache, pddlValidSvc, matcher, app.Logger, telemetrySvc) app.Engine = engine app.Router = mux.NewRouter() app.Db = db + app.TelemetrySvc = telemetrySvc app.RootCtx = rootCtx app.RootCancel = rootCancel app.configureRoutes() diff --git a/planengine/orchestrate.go b/planengine/orchestrate.go index fedc75b..8904006 100644 --- a/planengine/orchestrate.go +++ b/planengine/orchestrate.go @@ -170,10 +170,18 @@ func (p *PlanEngine) PrepareOrchestration(ctx context.Context, projectID string, switch prepErr.Status { case NotActionable: // NotActionable errors are permanent + p.TelemetrySvc.TrackEvent(EventExecutionPlanNotActionable, map[string]any{ + "version": Version, + "execution_plan_id": HashUUID(orchestration.ID), + }) return back.Permanent(prepErr) case FailedNotRetryable: // FailedNotRetryable errors are permanent + p.TelemetrySvc.TrackEvent(EventExecutionPlanFailedCreation, map[string]any{ + "version": Version, + "execution_plan_id": HashUUID(orchestration.ID), + }) return back.Permanent(prepErr) case Failed: @@ -203,6 +211,11 @@ func (p *PlanEngine) PrepareOrchestration(ctx context.Context, projectID string, Err(err). Str("OrchestrationID", orchestration.ID). Msg("Failed to persist orchestration") + + p.TelemetrySvc.TrackEvent(EventExecutionPlanFailedValidation, map[string]any{ + "version": Version, + "execution_plan_id": HashUUID(orchestration.ID), + }) return back.Permanent(fmt.Errorf("exceeded maximum retries (%d): %w", maxPreparationRetries, prepErr.Err)) default: @@ -325,6 +338,11 @@ func (p *PlanEngine) ExecuteOrchestration(ctx context.Context, orchestration *Or orchestration.Status = Processing orchestration.Timestamp = time.Now().UTC() + p.TelemetrySvc.TrackEvent(EventExecutionPlanAttempted, map[string]any{ + "version": Version, + "execution_plan_id": HashUUID(orchestration.ID), + }) + if err := p.orchestrationStorage.StoreOrchestration(orchestration); err != nil { p.Logger.Error(). Err(err). @@ -388,10 +406,27 @@ func (p *PlanEngine) FinalizeOrchestration(orchestrationID string, status Status } p.cleanupLogWorkers(orchestration.ID) - + p.trackOrchestrationTelemetry(orchestration.ID, status) return nil } +func (p *PlanEngine) trackOrchestrationTelemetry(orchestrationID string, status Status) { + var event string + switch status { + case Failed: + event = EventExecutionPlanFailed + case Aborted: + event = EventExecutionPlanAborted + default: + event = EventExecutionPlanCompleted + } + + p.TelemetrySvc.TrackEvent(event, map[string]any{ + "version": Version, + "execution_plan_id": HashUUID(orchestrationID), + }) +} + func (p *PlanEngine) CancelOrchestration(orchestrationID string, reason json.RawMessage) error { p.orchestrationStoreMu.Lock() defer p.orchestrationStoreMu.Unlock() diff --git a/planengine/telemetry.go b/planengine/telemetry.go new file mode 100644 index 0000000..e9d077b --- /dev/null +++ b/planengine/telemetry.go @@ -0,0 +1,92 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package main + +import ( + "crypto/sha256" + "encoding/hex" + "os" + "path/filepath" + + "github.com/google/uuid" + "github.com/posthog/posthog-go" + "github.com/rs/zerolog" +) + +// TelemetryService provides functionality for anonymous usage tracking +// with the ability to opt-out via environment variable. +type TelemetryService struct { + // Client is the PostHog client used to track events + client posthog.Client + + // Logger for telemetry operations + logger zerolog.Logger + + // Whether telemetry is enabled (based on the ANONYMIZED_TELEMETRY env var) + enabled bool +} + +// NewTelemetryService creates a new telemetry service. +// It respects the ANONYMIZED_TELEMETRY environment variable setting. +// If ANONYMIZED_TELEMETRY is set to false, telemetry will be disabled. +func NewTelemetryService(client posthog.Client, enabled bool, logger zerolog.Logger) *TelemetryService { + return &TelemetryService{ + client: client, + enabled: enabled, + logger: logger.With().Str("component", "telemetry").Logger(), + } +} + +// TrackEvent tracks an event in PostHog if telemetry is enabled. +// The event will be ignored if telemetry is disabled. +func (s *TelemetryService) TrackEvent(event string, properties map[string]any) { + if !s.enabled { + return + } + + // Use anonymous ID for privacy + anonymousID := getOrCreateAnonymousID() + + // Remove $ip property if present + delete(properties, "$ip") + + // Add event to PostHog + err := s.client.Enqueue(posthog.Capture{ + DistinctId: anonymousID, + Event: event, + Properties: properties, + }) + + if err != nil { + s.logger.Error().Err(err).Str("event", event).Msg("Failed to track telemetry event") + } else { + s.logger.Debug().Str("event", event).Msg("Telemetry event tracked") + } +} + +// IsEnabled returns whether telemetry is enabled +func (s *TelemetryService) IsEnabled() bool { + return s.enabled +} + +func getOrCreateAnonymousID() string { + tempDir := os.TempDir() + uuidFile := filepath.Join(tempDir, AnonymouseIDFilename) + + if data, err := os.ReadFile(uuidFile); err == nil { + return string(data) + } + + id := uuid.New().String() + _ = os.WriteFile(uuidFile, []byte(id), 0600) + return id +} + +func HashUUID(uuidStr string) string { + hash := sha256.Sum256([]byte(uuidStr)) + return hex.EncodeToString(hash[:]) +} diff --git a/planengine/types.go b/planengine/types.go index fa7ebb1..49d5d79 100644 --- a/planengine/types.go +++ b/planengine/types.go @@ -45,6 +45,7 @@ type PlanEngine struct { orchestrationStorage OrchestrationStorage groundingStorage GroundingStorage failedCompStorage FailedCompensationStorage + TelemetrySvc *TelemetryService failedCompensations map[string]map[string]*FailedCompensation // projectID -> compID -> FailedCompensation failedCompsMu sync.RWMutex Logger zerolog.Logger diff --git a/releases/cli/CHANGELOG.md b/releases/cli/CHANGELOG.md index db6d66d..4be3e22 100644 --- a/releases/cli/CHANGELOG.md +++ b/releases/cli/CHANGELOG.md @@ -1,3 +1,7 @@ +## CLI v0.2.6 + +N/A + ## CLI v0.2.5 ### Features diff --git a/releases/cli/versions/0.2.6.md b/releases/cli/versions/0.2.6.md new file mode 100644 index 0000000..5603199 --- /dev/null +++ b/releases/cli/versions/0.2.6.md @@ -0,0 +1,3 @@ +## CLI v0.2.6 + +N/A diff --git a/releases/planengine/CHANGELOG.md b/releases/planengine/CHANGELOG.md index 39743ff..c80ae23 100644 --- a/releases/planengine/CHANGELOG.md +++ b/releases/planengine/CHANGELOG.md @@ -1,3 +1,11 @@ +## Plan Engine v0.2.6 + +### Features +- Anonymous Telemetry: the Plan Engine now collects minimal, privacy-preserving usage analytics to help improve the project. + - All telemetry is fully anonymous: no personal data, no IP addresses, and no environment details are collected. + - Only hashed project and execution plan IDs are used for tracking related events. + - Telemetry is opt-out at any time by setting ANONYMIZED_TELEMETRY=false in your environment or .env file. + ## Plan Engine v0.2.5 ### Features diff --git a/releases/planengine/versions/0.2.6.md b/releases/planengine/versions/0.2.6.md new file mode 100644 index 0000000..c4583c8 --- /dev/null +++ b/releases/planengine/versions/0.2.6.md @@ -0,0 +1,7 @@ +## Plan Engine v0.2.6 + +### Features +- Anonymous Telemetry: the Plan Engine now collects minimal, privacy-preserving usage analytics to help improve the project. + - All telemetry is fully anonymous: no personal data, no IP addresses, and no environment details are collected. + - Only hashed project and execution plan IDs are used for tracking related events. + - Telemetry is opt-out at any time by setting ANONYMIZED_TELEMETRY=false in your environment or .env file. diff --git a/releases/sdks/CHANGELOG.md b/releases/sdks/CHANGELOG.md index f915b10..6ae328d 100644 --- a/releases/sdks/CHANGELOG.md +++ b/releases/sdks/CHANGELOG.md @@ -1,3 +1,7 @@ +## SDKs v0.2.6 + +N/A + ## SDKs v0.2.5 ### Features diff --git a/releases/sdks/versions/0.2.6.md b/releases/sdks/versions/0.2.6.md new file mode 100644 index 0000000..1df6be0 --- /dev/null +++ b/releases/sdks/versions/0.2.6.md @@ -0,0 +1,3 @@ +## SDKs v0.2.6 + +N/A