diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6ec63229..8252dcd7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,4 +4,5 @@ # someone opens a pull request. * @temporalio/sdk +/googleadk/ @temporalio/sdk @temporalio/ai-sdk /workflowstreams/ @temporalio/sdk @temporalio/ai-sdk diff --git a/Makefile b/Makefile index 30b727ea..910f91bb 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ errcheck: workflowcheck: @printf $(COLOR) "Run workflow check..." @go install go.temporal.io/sdk/contrib/tools/workflowcheck@latest - @workflowcheck -show-pos ./... + @workflowcheck -show-pos -config workflowcheck.config.yaml ./... update-sdk: $(foreach MOD_FILES_DIR,$(MOD_FILES_DIR),\ diff --git a/README.md b/README.md index a22962cc..bddf1116 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,13 @@ with an external configuration file, like TOML, decoupling connection settings f reconnecting subscriber, an external publisher, a truncating ticker, and LLM token streaming. +- [**Google ADK agent**](./googleadk): Demonstrates running Google ADK (`adk-go`) agents + durably using the `googleadk` contrib integration: the agent loop runs in a Workflow, the + model call runs as an Activity, and a tool is exposed as a durable Activity via + `ActivityAsTool`. Includes scenario subdirectories for a [multi-agent](./googleadk/multiagent) + system, [human-in-the-loop](./googleadk/humanintheloop) tool approval (durable wait on a + signal), and a [continue-as-new chat](./googleadk/chat). + ### Dynamic Workflow logic examples These samples demonstrate some common control flow patterns using Temporal's Go SDK API. diff --git a/go.mod b/go.mod index fa3e4a7c..89d48e53 100644 --- a/go.mod +++ b/go.mod @@ -17,12 +17,12 @@ require ( github.com/openai/openai-go v1.12.0 github.com/opentracing/opentracing-go v1.2.0 github.com/pborman/uuid v1.2.1 - github.com/prometheus/client_golang v1.23.2 + github.com/prometheus/client_golang v1.23.0 github.com/stretchr/testify v1.11.1 github.com/uber-go/tally/v4 v4.1.7 github.com/uber/jaeger-client-go v2.30.0+incompatible go.opentelemetry.io/otel v1.43.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 go.opentelemetry.io/otel/sdk v1.43.0 go.opentelemetry.io/otel/trace v1.43.0 go.temporal.io/api v1.63.0 @@ -38,8 +38,10 @@ require ( go.temporal.io/sdk/contrib/tally v0.2.0 go.temporal.io/sdk/contrib/workflowstreams v0.1.1 go.uber.org/multierr v1.11.0 - go.uber.org/zap v1.27.1 - google.golang.org/grpc v1.79.3 + go.uber.org/zap v1.27.0 + google.golang.org/adk/v2 v2.0.1-0.20260707195420-2a04f92f1776 + google.golang.org/genai v1.62.0 + google.golang.org/grpc v1.81.0 google.golang.org/protobuf v1.36.11 gopkg.in/DataDog/dd-trace-go.v1 v1.59.0 gopkg.in/square/go-jose.v2 v2.6.0 @@ -47,31 +49,30 @@ require ( ) require ( + cloud.google.com/go v0.123.0 // indirect + cloud.google.com/go/auth v0.20.0 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect github.com/BurntSushi/toml v1.4.0 // indirect github.com/DataDog/appsec-internal-go v1.4.0 // indirect - github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.77.0 // indirect - github.com/DataDog/datadog-agent/pkg/obfuscate v0.77.0 // indirect - github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.77.0 // indirect - github.com/DataDog/datadog-agent/pkg/proto v0.77.0 // indirect - github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.77.0 // indirect - github.com/DataDog/datadog-agent/pkg/template v0.77.0 // indirect - github.com/DataDog/datadog-agent/pkg/trace v0.77.0 // indirect - github.com/DataDog/datadog-agent/pkg/trace/log v0.77.0 // indirect - github.com/DataDog/datadog-agent/pkg/trace/otel v0.77.0 // indirect - github.com/DataDog/datadog-agent/pkg/trace/stats v0.77.0 // indirect - github.com/DataDog/datadog-agent/pkg/trace/traceutil v0.77.0 // indirect - github.com/DataDog/datadog-agent/pkg/util/log v0.77.0 // indirect - github.com/DataDog/datadog-agent/pkg/util/scrubber v0.77.0 // indirect - github.com/DataDog/datadog-agent/pkg/version v0.77.0 // indirect - github.com/DataDog/datadog-go/v5 v5.8.3 // indirect - github.com/DataDog/dd-trace-go/v2 v2.8.1 // indirect + github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.71.0 // indirect + github.com/DataDog/datadog-agent/pkg/obfuscate v0.71.0 // indirect + github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.71.0 // indirect + github.com/DataDog/datadog-agent/pkg/proto v0.71.0 // indirect + github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.73.0-rc.1 // indirect + github.com/DataDog/datadog-agent/pkg/trace v0.71.0 // indirect + github.com/DataDog/datadog-agent/pkg/util/log v0.71.0 // indirect + github.com/DataDog/datadog-agent/pkg/util/scrubber v0.71.0 // indirect + github.com/DataDog/datadog-agent/pkg/version v0.71.0 // indirect + github.com/DataDog/datadog-go/v5 v5.6.0 // indirect + github.com/DataDog/dd-trace-go/v2 v2.4.0 // indirect github.com/DataDog/go-libddwaf/v2 v2.2.3 // indirect - github.com/DataDog/go-libddwaf/v4 v4.9.0 // indirect - github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20260217080614-b0f4edc38a6d // indirect - github.com/DataDog/go-sqllexer v0.1.13 // indirect + github.com/DataDog/go-libddwaf/v4 v4.6.1 // indirect + github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20250721125240-fdf1ef85b633 // indirect + github.com/DataDog/go-sqllexer v0.1.8 // indirect github.com/DataDog/go-tuf v1.1.1-0.5.2 // indirect - github.com/DataDog/sketches-go v1.4.8 // indirect + github.com/DataDog/sketches-go v1.4.7 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect + github.com/Masterminds/semver/v3 v3.3.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/aws/aws-lambda-go v1.47.0 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.10 // indirect @@ -94,75 +95,85 @@ require ( github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/ebitengine/purego v0.10.0 // indirect + github.com/ebitengine/purego v0.8.4 // indirect github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/jsonschema-go v0.4.2 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/google/safehtml v0.1.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.15 // indirect + github.com/googleapis/gax-go/v2 v2.22.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-version v1.8.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.18.4 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/linkdata/deadlock v0.5.5 // indirect - github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect github.com/minio/simdjson-go v0.4.5 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nexus-rpc/nexus-proto-annotations v0.1.0 // indirect github.com/outcaste-io/ristretto v0.2.3 // indirect - github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6 // indirect github.com/philhofer/fwd v1.2.0 // 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/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.67.5 // indirect - github.com/prometheus/procfs v0.19.2 // indirect + github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect github.com/robfig/cron v1.2.0 // indirect github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect - github.com/secure-systems-lab/go-securesystemslib v0.10.0 // indirect - github.com/shirou/gopsutil/v4 v4.26.2 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect + github.com/shirou/gopsutil/v4 v4.25.8-0.20250809033336-ffcdc2b7662f // indirect github.com/stretchr/objx v0.5.2 // indirect + github.com/theckman/httpforwarded v0.4.0 // indirect github.com/tidwall/gjson v1.16.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect - github.com/tinylib/msgp v1.6.3 // indirect - github.com/tklauser/go-sysconf v0.3.16 // indirect - github.com/tklauser/numcpus v0.11.0 // indirect - github.com/trailofbits/go-mutexasserts v0.0.0-20250514102930-c1f3d2e37561 // indirect + github.com/tinylib/msgp v1.3.0 // indirect + github.com/tklauser/go-sysconf v0.3.15 // indirect + github.com/tklauser/numcpus v0.10.0 // indirect github.com/twmb/murmur3 v1.1.8 // indirect github.com/uber/jaeger-lib v2.4.1+incompatible // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/collector/component v1.51.1-0.20260205185216-81bc641f26c0 // indirect - go.opentelemetry.io/collector/featuregate v1.51.1-0.20260205185216-81bc641f26c0 // indirect - go.opentelemetry.io/collector/pdata v1.51.1-0.20260205185216-81bc641f26c0 // indirect - go.opentelemetry.io/collector/pdata/pprofile v0.145.1-0.20260205185216-81bc641f26c0 // indirect + go.opentelemetry.io/collector/component v1.39.0 // indirect + go.opentelemetry.io/collector/featuregate v1.39.0 // indirect + go.opentelemetry.io/collector/internal/telemetry v0.133.0 // indirect + go.opentelemetry.io/collector/pdata v1.39.0 // indirect + go.opentelemetry.io/contrib/bridges/otelzap v0.12.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect go.opentelemetry.io/contrib/propagators/aws v1.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 // indirect + go.opentelemetry.io/otel/log v0.19.0 // indirect go.opentelemetry.io/otel/metric v1.43.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect - go.opentelemetry.io/proto/otlp v1.9.0 // indirect + go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.shabbyrobe.org/gocovmerge v0.0.0-20230507111327-fa4f82cfbf4d // indirect + go.temporal.io/sdk/contrib/googleadk v0.1.0 go.uber.org/atomic v1.11.0 // indirect - go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect golang.org/x/crypto v0.52.0 // indirect - golang.org/x/exp v0.0.0-20260209203927-2842357ff358 // indirect + golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect golang.org/x/mod v0.35.0 // indirect golang.org/x/net v0.55.0 // indirect golang.org/x/sync v0.20.0 // indirect @@ -171,8 +182,11 @@ require ( golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.44.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect - gopkg.in/ini.v1 v1.67.1 // indirect + google.golang.org/api v0.279.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260427160629-7cedc36a6bc4 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect + rsc.io/omap v1.2.0 // indirect + rsc.io/ordered v1.1.1 // indirect ) diff --git a/go.sum b/go.sum index b598314e..4c48a491 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,11 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +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.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA= +cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= @@ -7,54 +13,46 @@ github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/appsec-internal-go v1.4.0 h1:KFI8ElxkJOgpw+cUm9TXK/jh5EZvRaWM07sXlxGg9Ck= github.com/DataDog/appsec-internal-go v1.4.0/go.mod h1:ONW8aV6R7Thgb4g0bB9ZQCm+oRgyz5eWiW7XoQ19wIc= -github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.77.0 h1:Lu/HEo5svx/UwE7XWh8vOrEHCrVRsein9X1N0jGK5bo= -github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.77.0/go.mod h1:+Ty3r23MjcmMSkr8JbFeqA3utgtc1wxsZ0KaQ9CzoWA= -github.com/DataDog/datadog-agent/pkg/obfuscate v0.77.0 h1:mrHaNnDAIOFAVYhCqDpkenUtbadswHN68ZlG5krv40o= -github.com/DataDog/datadog-agent/pkg/obfuscate v0.77.0/go.mod h1:E6RGAcEOr/d8wsV5/khYHvaHkijWex6dfNEvsBIgR7A= -github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.77.0 h1:g1d9d1CfG54WjXgvkysTFL9yjXexWeDbYssQaf1PG6c= -github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.77.0/go.mod h1:N/AB9VGpVwHxCcyX+7GLNYMfnTZvn65vp5cHb5Ed0ow= -github.com/DataDog/datadog-agent/pkg/proto v0.77.0 h1:21nDAKD+LdxZz2pMsLAQTZ+w9Z4JqecjKpt8xY1b7Ig= -github.com/DataDog/datadog-agent/pkg/proto v0.77.0/go.mod h1:g2QYJe1CheZdssiDQpSYWra9hORkh+S3WO8aOqDNLkg= -github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.77.0 h1:SxFGFN/Dd/uREaUTxuVTi0R7fRABzvUtu32YOXcjf6c= -github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.77.0/go.mod h1:TpW5ZwsQTrlRBPjtZH5/OFwpmOqxV/v2i9BiF4Xfcac= -github.com/DataDog/datadog-agent/pkg/template v0.77.0 h1:sUbTCoQyU9kXMc6/aDd4YTP2pe9PlNkgySM11ydMEbE= -github.com/DataDog/datadog-agent/pkg/template v0.77.0/go.mod h1:ZUjICHSlN0of0cmWrYk9Pof0DV0eqHSpTUK1NTnN26Y= -github.com/DataDog/datadog-agent/pkg/trace v0.77.0 h1:B7M6IW0sd60XnLfeP6HEneeR4lRnaNtI9bboU4R1cV0= -github.com/DataDog/datadog-agent/pkg/trace v0.77.0/go.mod h1:+7zMNPjHTDidiphECirrpq5jLK09S9kmLtGRv7di29Q= -github.com/DataDog/datadog-agent/pkg/trace/log v0.77.0 h1:2VY1byEA2XnYVg7+eLQSTgX2f76ZCf/AwpCBJDXCiDc= -github.com/DataDog/datadog-agent/pkg/trace/log v0.77.0/go.mod h1:thnxBOGfMU9uRlFUClXud6J7DdI8qWtElELSds5jqts= -github.com/DataDog/datadog-agent/pkg/trace/otel v0.77.0 h1:9zSto72E+wSETaKs47Yiq5D9du9H71IqWgbDJzGpzSs= -github.com/DataDog/datadog-agent/pkg/trace/otel v0.77.0/go.mod h1:IxBidgqUt8aBrKYq4VKynBHWYZYNoflk+0+m7w+lfbI= -github.com/DataDog/datadog-agent/pkg/trace/stats v0.77.0 h1:InA6JO5R8TFAUcKRxsdmIF1hJpVZqVO5Aqux+Iw/2V0= -github.com/DataDog/datadog-agent/pkg/trace/stats v0.77.0/go.mod h1:iZVotmInV8qaU6Q5h+tsKk4CBYupDcOgTzonzLlMi0k= -github.com/DataDog/datadog-agent/pkg/trace/traceutil v0.77.0 h1:b/2+uA/cG2xEV0LzgwnxloMFWe5sdJa3xtaQhSGN0+s= -github.com/DataDog/datadog-agent/pkg/trace/traceutil v0.77.0/go.mod h1:csT+8o3GOUjhKPs/GqWMb5Zh4iQpuZ/HZQ4Z5ls8Sak= -github.com/DataDog/datadog-agent/pkg/util/log v0.77.0 h1:YFa+8kIg2qQZca9zvowtwCPdHDhGMcTIF+PMIQsLSRs= -github.com/DataDog/datadog-agent/pkg/util/log v0.77.0/go.mod h1:DFK2U5RcB8/BcObgmVEZ4VxqXUi2t7y2svLTtJwQqeo= -github.com/DataDog/datadog-agent/pkg/util/scrubber v0.77.0 h1:dd0W9e39rv0R3DSgnaurVnQ43/jX/juqQPwLpGAJgFs= -github.com/DataDog/datadog-agent/pkg/util/scrubber v0.77.0/go.mod h1:nkhevws2pJvoXSGhjc8wuTbptNQ9ECRBjwVr4hSvoq0= -github.com/DataDog/datadog-agent/pkg/version v0.77.0 h1:fxpMWuoaRHS5vHzCNHftvJ6wdQrGhEmuozjjl8wZG5k= -github.com/DataDog/datadog-agent/pkg/version v0.77.0/go.mod h1:h9eJjfeTHlYYv+kzq6n3rQ07qXGirdCCacn1Ryu4TFQ= -github.com/DataDog/datadog-go/v5 v5.8.3 h1:s58CUJ9s8lezjhTNJO/SxkPBv2qZjS3ktpRSqGF5n0s= -github.com/DataDog/datadog-go/v5 v5.8.3/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= -github.com/DataDog/dd-trace-go/v2 v2.8.1 h1:O/lPXXcJof4hqfcBGsL6p/PiVa5xTfvYzv5iv/4S66U= -github.com/DataDog/dd-trace-go/v2 v2.8.1/go.mod h1:IVkBpsq66Cw/YIRM/Te3pl2F0M9n4zguAB2ReGczWeo= +github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.71.0 h1:xjmjXOsiLfUF1wWXYXc8Gg6M7Jbz6a7FtqbnvGKfTvA= +github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.71.0/go.mod h1:y05SPqKEtrigKul+JBVM69ehv3lOgyKwrUIwLugoaSI= +github.com/DataDog/datadog-agent/pkg/obfuscate v0.71.0 h1:jX8qS7CkNzL1fdcDptrOkbWpsRFTQ58ICjp/mj02u1k= +github.com/DataDog/datadog-agent/pkg/obfuscate v0.71.0/go.mod h1:B3T0If+WdWAwPMpawjm1lieJyqSI0v04dQZHq15WGxY= +github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.71.0 h1:bowQteds9+7I4Dd+CsBRVXdlMOOGuBm5zdUQdB/6j1M= +github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.71.0/go.mod h1:XeZj0IgsiL3vgeEGTucf61JvJRh1LxWMUbZA/XJsPD0= +github.com/DataDog/datadog-agent/pkg/proto v0.71.0 h1:YTwecwy8kF1zsL2HK6KVa7XLRZYZ0Ypb2anlG0zDLeE= +github.com/DataDog/datadog-agent/pkg/proto v0.71.0/go.mod h1:KSn4jt3CykV6CT1C8Rknn/Nj3E+VYHK/UDWolg/+kzw= +github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.73.0-rc.1 h1:fVqr9ApWmUMEExmgn8iFPfwm9ZrlEfFWgTKp1IcNH18= +github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.73.0-rc.1/go.mod h1:lwkSvCXABHXyqy6mG9WBU6MTK9/E0i0R8JVApUtT+XA= +github.com/DataDog/datadog-agent/pkg/trace v0.71.0 h1:9UrKHDacMlAWfP2wpSxrZOQbtkwLY2AOAjYgGkgM96Y= +github.com/DataDog/datadog-agent/pkg/trace v0.71.0/go.mod h1:wfVwOlKORIB4IB1vdncTuCTx/OrVU69TLBIiBpewe1Q= +github.com/DataDog/datadog-agent/pkg/util/log v0.71.0 h1:VJ+nm5E0+UdLPkg2H7FKapx0syNcKzCFXA2vfcHz0Bc= +github.com/DataDog/datadog-agent/pkg/util/log v0.71.0/go.mod h1:oG6f6Qe23zPTLOVh0nXjlIXohrjUGXeFjh7S3Na/WyU= +github.com/DataDog/datadog-agent/pkg/util/scrubber v0.71.0 h1:lA3CL+2yHU9gulyR/C0VssVzmvCs/jCHzt+CBs9uH4Q= +github.com/DataDog/datadog-agent/pkg/util/scrubber v0.71.0/go.mod h1:/JHi9UFqdFYy/SFmFozY26dNOl/ODVLSQaF1LKDPiBI= +github.com/DataDog/datadog-agent/pkg/version v0.71.0 h1:jqkKmhFrhHSLpiC3twQFDCXU7nyFcC1EnwagDQxFWVs= +github.com/DataDog/datadog-agent/pkg/version v0.71.0/go.mod h1:FYj51C1ib86rpr5tlLEep9jitqvljIJ5Uz2rrimGTeY= +github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= +github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/dd-trace-go/v2 v2.4.0 h1:ZbH4h/S5p0tH5Prr4EIo5QwEnXXGUNtxJcOwB7c1d1o= +github.com/DataDog/dd-trace-go/v2 v2.4.0/go.mod h1:EEOkhOJlb37u+k07/9cwKCvtDC/mWjWnHrGkkk/iZCo= github.com/DataDog/go-libddwaf/v2 v2.2.3 h1:LpKE8AYhVrEhlmlw6FGD41udtDf7zW/aMdLNbCXpegQ= github.com/DataDog/go-libddwaf/v2 v2.2.3/go.mod h1:8nX0SYJMB62+fbwYmx5J7zuCGEjiC/RxAo3+AuYJuFE= -github.com/DataDog/go-libddwaf/v4 v4.9.0 h1:a788e37iuH7sR9uIYHkulvTnp2FkXTiZ3yY/kuaHgZE= -github.com/DataDog/go-libddwaf/v4 v4.9.0/go.mod h1:/AZqP6zw3qGJK5mLrA0PkfK3UQDk1zCI2fUNCt4xftE= -github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20260217080614-b0f4edc38a6d h1:cH9Bm0tJ8FEQbA4FRi0iRm7Zr/5Lata/Or31c+Dth0E= -github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20260217080614-b0f4edc38a6d/go.mod h1:yDuvU+Ak1TKwgd4K8DNcpJmUrrK8ONLkBMGNAppmBRk= -github.com/DataDog/go-sqllexer v0.1.13 h1:HhT2G21y7SDZYQx9i1b+3Sy/CHhESHet/YKMSm06XcE= -github.com/DataDog/go-sqllexer v0.1.13/go.mod h1:vOw7Ia7z+z6nl3zGZlLIZe0vQlPtCPR906WIPBJadxc= +github.com/DataDog/go-libddwaf/v4 v4.6.1 h1:wGUioRkQ2a5MYr2wTn5uZfMENbLV4uKXrkr6zCVInCs= +github.com/DataDog/go-libddwaf/v4 v4.6.1/go.mod h1:/AZqP6zw3qGJK5mLrA0PkfK3UQDk1zCI2fUNCt4xftE= +github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20250721125240-fdf1ef85b633 h1:ZRLR9Lbym748e8RznWzmSoK+OfV+8qW6SdNYA4/IqdA= +github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20250721125240-fdf1ef85b633/go.mod h1:YFoTl1xsMzdSRFIu33oCSPS/3+HZAPGpO3oOM96wXCM= +github.com/DataDog/go-sqllexer v0.1.8 h1:ku9DpghFHeyyviR28W/3R4cCJwzpsuC08YIoltnx5ds= +github.com/DataDog/go-sqllexer v0.1.8/go.mod h1:GGpo1h9/BVSN+6NJKaEcJ9Jn44Hqc63Rakeb+24Mjgo= github.com/DataDog/go-tuf v1.1.1-0.5.2 h1:YWvghV4ZvrQsPcUw8IOUMSDpqc3W5ruOIC+KJxPknv0= github.com/DataDog/go-tuf v1.1.1-0.5.2/go.mod h1:zBcq6f654iVqmkk8n2Cx81E1JnNTMOAx1UEO/wZR+P0= github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/IGQh4= github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= -github.com/DataDog/sketches-go v1.4.8 h1:pFk9BNn+Rzv8IMIoPUttoOpOr3bJOqU3P6EP5wK+Lv8= -github.com/DataDog/sketches-go v1.4.8/go.mod h1:a/wjRUqzqtGS8qRHRPDCs4EAQfmvPDZGDlMIF5mxXOE= +github.com/DataDog/sketches-go v1.4.7 h1:eHs5/0i2Sdf20Zkj0udVFWuCrXGRFig2Dcfm5rtcTxc= +github.com/DataDog/sketches-go v1.4.7/go.mod h1:eAmQ/EBmtSO+nQp7IZMZVRPT4BQTmIc5RZQ+deGlTPM= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= @@ -139,8 +137,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn 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/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= -github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= -github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= +github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= 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= @@ -149,6 +147,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -167,6 +167,8 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +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/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -211,13 +213,25 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8= +github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +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/safehtml v0.1.0 h1:EwLKo8qawTKfsi0orxcQAZzu07cICaBeFMegAU9eaT8= +github.com/google/safehtml v0.1.0/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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.15 h1:xolVQTEXusUcAA5UgtyRLjelpFFHWlPQ4XfWGc7MBas= +github.com/googleapis/enterprise-certificate-proxy v0.3.15/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/gax-go/v2 v2.22.0 h1:PjIWBpgGIVKGoCXuiCoP64altEJCj3/Ei+kSU5vlZD4= +github.com/googleapis/gax-go/v2 v2.22.0/go.mod h1:irWBbALSr0Sk3qlqb9SyJ1h68WjgeFuiOzI4Rqw5+aY= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= @@ -229,8 +243,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= -github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/johannesboyne/gofakes3 v0.0.0-20260208201424-4c385a1f6a73 h1:0xkWp+RMC2ImuKacheMHEAtrbOTMOa0kYkxyzM1Z/II= github.com/johannesboyne/gofakes3 v0.0.0-20260208201424-4c385a1f6a73/go.mod h1:S4S9jGBVlLri0OeqrSSbCGG5vsI6he06UJyuz1WT1EE= @@ -245,10 +259,10 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= -github.com/klauspost/compress v1.18.4/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/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/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -261,10 +275,8 @@ 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/linkdata/deadlock v0.5.5 h1:d6O+rzEqasSfamGDA8u7bjtaq7hOX8Ha4Zn36Wxrkvo= -github.com/linkdata/deadlock v0.5.5/go.mod h1:tXb28stzAD3trzEEK0UJWC+rZKuobCoPktPYzebb1u0= -github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 h1:PTw+yKnXcOFCR6+8hHTyWBeQ/P4Nb7dd4/0ohEcWQuM= -github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= +github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/minio/simdjson-go v0.4.5 h1:r4IQwjRGmWCQ2VeMc7fGiilu1z5du0gJ/I/FsKwgo5A= github.com/minio/simdjson-go v0.4.5/go.mod h1:eoNz0DcLQRyEDeaPr4Ru6JpjlZPzbA0IodxVJk8lO8E= @@ -285,10 +297,10 @@ github.com/nexus-rpc/nexus-proto-annotations v0.1.0/go.mod h1:n3UjF1bPCW8llR8tHv github.com/nexus-rpc/sdk-go v0.6.0 h1:QRgnP2zTbxEbiyWG/aXH8uSC5LV/Mg1fqb19jb4DBlo= github.com/nexus-rpc/sdk-go v0.6.0/go.mod h1:FHdPfVQwRuJFZFTF0Y2GOAxCrbIBNrcPna9slkGKPYk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.145.0 h1:7rdLY2Ewa1WVnjMfJTEKwQ5uPDHYeA1tqNPNROi957U= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.145.0/go.mod h1:jYlQAaJO4ZyJAW2jcKAbjN+nt5BRCyu49mlZv4Rui7U= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.145.0 h1:12mxn+8YLeAjMZ1kLGulBcvHrdhRNUmxLVIDnaLkJbQ= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.145.0/go.mod h1:V87HYJpfmvCeQ6Cjy3Q4xylxfCn2wVSS80wvv5ECc0s= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.133.0 h1:iPei+89a2EK4LuN4HeIRzZNE6XxCyrKfBKG3BkK/ViU= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.133.0/go.mod h1:asV77TgnGfc7A+a9jggdsnlLlW5dnJT8RroVuf5slko= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.133.0 h1:4ca2pM3+xDMB9H3UnhjAiNg7EpIydZ7HdohOexU8xb8= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.133.0/go.mod h1:3N2Saf55l9vrxjbf3KCEcBjbLHDZtbN4nPcxREztpPU= github.com/openai/openai-go v1.12.0 h1:NBQCnXzqOTv5wsgNC36PrFEiskGfO5wccfCWDo9S1U0= github.com/openai/openai-go v1.12.0/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -298,9 +310,6 @@ github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOv github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= -github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6 h1:rh2lKw/P/EqHa724vYH2+VVQ1YnW4u6EOXl0PMAovZE= -github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -318,8 +327,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= -github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= +github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -329,14 +338,14 @@ github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvM github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= -github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= -github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3 h1:4+LEVOB87y175cLJC/mbsgKmoDOjrBldtXvioEy96WY= @@ -348,10 +357,10 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= -github.com/secure-systems-lab/go-securesystemslib v0.10.0 h1:l+H5ErcW0PAehBNrBxoGv1jjNpGYdZ9RcheFkB2WI14= -github.com/secure-systems-lab/go-securesystemslib v0.10.0/go.mod h1:MRKONWmRoFzPNQ9USRF9i1mc7MvAVvF1LlW8X5VWDvk= -github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI= -github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc= +github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw= +github.com/shirou/gopsutil/v4 v4.25.8-0.20250809033336-ffcdc2b7662f h1:S+PHRM3lk96X0/cGEGUukqltzkX/ekUx0F9DoCGK1G0= +github.com/shirou/gopsutil/v4 v4.25.8-0.20250809033336-ffcdc2b7662f/go.mod h1:4f4j4w8HLMPWEFs3BO2UBBLigKAaWYwkSkbIt/6Q4Ss= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -376,9 +385,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/theckman/httpforwarded v0.4.0 h1:N55vGJT+6ojTnLY3LQCNliJC4TW0P0Pkeys1G1WpX2w= +github.com/theckman/httpforwarded v0.4.0/go.mod h1:GVkFynv6FJreNbgH/bpOU9ITDZ7a5WuzdNCtIMI1pVI= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -389,14 +399,12 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/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/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= -github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= -github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= -github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= -github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= -github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= -github.com/trailofbits/go-mutexasserts v0.0.0-20250514102930-c1f3d2e37561 h1:qqa3P9AtNn6RMe90l/lxd3eJWnIRxjI4eb5Rx8xqCLA= -github.com/trailofbits/go-mutexasserts v0.0.0-20250514102930-c1f3d2e37561/go.mod h1:GA3+Mq3kt3tYAfM0WZCu7ofy+GW9PuGysHfhr+6JX7s= +github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= +github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= +github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= +github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= +github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= +github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= @@ -421,69 +429,77 @@ go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= 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/collector/component v1.51.1-0.20260205185216-81bc641f26c0 h1:ZSlXxE90IY0Cl53RTqzyEgRgRPLTeTNBdGhaTmvj9eY= -go.opentelemetry.io/collector/component v1.51.1-0.20260205185216-81bc641f26c0/go.mod h1:944C7vEIdk13Pn1fBbyaU8C1qKf2XC0jRBlc69NAsRY= -go.opentelemetry.io/collector/component/componentstatus v0.145.0 h1:EwUZfSaagdpRXnlrb0TqReJXXW2p9HWBU5YiIeXPCAE= -go.opentelemetry.io/collector/component/componentstatus v0.145.0/go.mod h1:OiYb8rT4FtSJPFSGCKYvOaajdueDUTJZncixGrmy5aM= -go.opentelemetry.io/collector/component/componenttest v0.145.1-0.20260205185216-81bc641f26c0 h1:+VCK6wX/WN170dcaWJweRAkxpmAEyVucfrUV13NwUlY= -go.opentelemetry.io/collector/component/componenttest v0.145.1-0.20260205185216-81bc641f26c0/go.mod h1:U2wUjKMGwgqM49/q8ORkzzYzSWY2m6zpG/e606eK1wc= -go.opentelemetry.io/collector/consumer v1.51.1-0.20260205185216-81bc641f26c0 h1:WNkJ1bKnRVAEJtBm1bwEkoLG2x7GyANc3/OnErZJ338= -go.opentelemetry.io/collector/consumer v1.51.1-0.20260205185216-81bc641f26c0/go.mod h1:Erk6qdfVj+24QTrGCpurcrF+qdUlHkb4dgMy5wJxLvY= -go.opentelemetry.io/collector/consumer/consumertest v0.145.1-0.20260205185216-81bc641f26c0 h1:FHyDIlTbqt0Y6tDI9EbI3hr9uWthwkeLY7uGF1jZYqQ= -go.opentelemetry.io/collector/consumer/consumertest v0.145.1-0.20260205185216-81bc641f26c0/go.mod h1:IFc/FeaIHQClb8KK0aVn0tFDNMc+/MmfQ+aBT1cJNeo= -go.opentelemetry.io/collector/consumer/xconsumer v0.145.1-0.20260205185216-81bc641f26c0 h1:zg2Jqfy7n7o/LEmLsXB4sFhxWtOEMFCKRyQUFLFUS9M= -go.opentelemetry.io/collector/consumer/xconsumer v0.145.1-0.20260205185216-81bc641f26c0/go.mod h1:SryDCLP2ZaFeZJtA2CSksJ0XvjH8k3LmlfXvy/kC7Wc= -go.opentelemetry.io/collector/featuregate v1.51.1-0.20260205185216-81bc641f26c0 h1:fOXhfT2xKqNhfalTXaT/Wic9EBRK8+9ZH0y8phReQS4= -go.opentelemetry.io/collector/featuregate v1.51.1-0.20260205185216-81bc641f26c0/go.mod h1:/1bclXgP91pISaEeNulRxzzmzMTm4I5Xih2SnI4HRSo= -go.opentelemetry.io/collector/internal/componentalias v0.145.1-0.20260205185216-81bc641f26c0 h1:s4/vCxeIxgQpuWmX1AK1DRbZmEdmNBq925EKES8ebiI= -go.opentelemetry.io/collector/internal/componentalias v0.145.1-0.20260205185216-81bc641f26c0/go.mod h1:Z0TtMbzaMp2qhj1dw4toya8toyQzqoTF46/WhJXplVw= -go.opentelemetry.io/collector/internal/testutil v0.145.0 h1:H/KL0GH3kGqSMKxZvnQ0B0CulfO9xdTg4DZf28uV7fY= -go.opentelemetry.io/collector/internal/testutil v0.145.0/go.mod h1:YAD9EAkwh/l5asZNbEBEUCqEjoL1OKMjAMoPjPqH76c= -go.opentelemetry.io/collector/pdata v1.51.1-0.20260205185216-81bc641f26c0 h1:8tgf9W3aW3vFabyVxPNHKsaoyUytudfVOQbqZI9xBHQ= -go.opentelemetry.io/collector/pdata v1.51.1-0.20260205185216-81bc641f26c0/go.mod h1:GoX1bjKDR++mgFKdT7Hynv9+mdgQ1DDXbjs7/Ww209Q= -go.opentelemetry.io/collector/pdata/pprofile v0.145.1-0.20260205185216-81bc641f26c0 h1:MJcnK8txYZlqHZyfZ1rVf66kt5/kEveIdR6KX//BY1Y= -go.opentelemetry.io/collector/pdata/pprofile v0.145.1-0.20260205185216-81bc641f26c0/go.mod h1:a60GC7wQPhLAixWzKbbP51QLwwc+J0Cmp4SurOlhGUk= -go.opentelemetry.io/collector/pdata/testdata v0.145.0 h1:iFsxsCMtE3lnAc/5kZbhZHpRv1OMmM+O5ry46xdQHbg= -go.opentelemetry.io/collector/pdata/testdata v0.145.0/go.mod h1:0y2ERArdzqmYdJHdKLKue+AUubSEGlwK49F+23+Mbic= -go.opentelemetry.io/collector/pipeline v1.51.1-0.20260205185216-81bc641f26c0 h1:1KP5gXGF9qN1mEzJupZDUQVIND35qe/0Hy6Cptvdk0s= -go.opentelemetry.io/collector/pipeline v1.51.1-0.20260205185216-81bc641f26c0/go.mod h1:xUrAqiebzYbrgxyoXSkk6/Y3oi5Sy3im2iCA51LwUAI= -go.opentelemetry.io/collector/processor v1.51.0 h1:PKpCzkLQmqaW08TOVh/zM0qx07Ihq+DR5J/OBkPiL9o= -go.opentelemetry.io/collector/processor v1.51.0/go.mod h1:rtIPFS+EFRAkG+CSwtjxs2IsIkuZStObvALeueD02XI= -go.opentelemetry.io/collector/processor/processorhelper v0.145.0 h1:vXdv6lHz20Tm3ZEsg0i6jPZJBQgy9kzk/PuqWhHWiiM= -go.opentelemetry.io/collector/processor/processorhelper v0.145.0/go.mod h1:3Ecpe5jHRHGf24EvJHeJ/ekK/a1DLByyq0CSUxjjURg= -go.opentelemetry.io/collector/processor/processortest v0.145.0 h1:RDGBmyZnHk7XVK/EdLt/8iPWj+QLStbbVi1nFTNR01s= -go.opentelemetry.io/collector/processor/processortest v0.145.0/go.mod h1:WAvxAzSojkdoZB915Z1lsVHCPDJBb2fepjJBjenrzjg= -go.opentelemetry.io/collector/processor/xprocessor v0.145.0 h1:DaIE7MxRlg0OL1o2P0GQZtmZeExAmVso3qWv8S0RLps= -go.opentelemetry.io/collector/processor/xprocessor v0.145.0/go.mod h1:kUwRyKBU/kjCmXodd+0z7CpvcP0A9G9/QL+MaJt4U2o= +go.opentelemetry.io/collector/component v1.39.0 h1:GJw80zXURBG4h0sh97bPLEn2Ra+NAWUpskaooA0wru4= +go.opentelemetry.io/collector/component v1.39.0/go.mod h1:NPaMPTLQuxm5QaaWdqkxYKztC0bRdV+86Q9ir7xS/2k= +go.opentelemetry.io/collector/component/componentstatus v0.133.0 h1:fIcFKg+yPhpvOJKeMph9TtSC4DIGdIuNmxvUB0UGcoc= +go.opentelemetry.io/collector/component/componentstatus v0.133.0/go.mod h1:biQWms9cgXSZu3nb92Z0bA9uHh9lEhgmQ8CF4HLmu8Y= +go.opentelemetry.io/collector/component/componenttest v0.133.0 h1:mg54QqXC+GNqLHa9y6Efh3X5Di4XivjgJr6mzvfVQR8= +go.opentelemetry.io/collector/component/componenttest v0.133.0/go.mod h1:E+oqRK03WjG/b1aX1pd0CfTKh12MPTKbEBaBROp4w0M= +go.opentelemetry.io/collector/consumer v1.39.0 h1:Jc6la3uacHbznX5ORmh16Nddh23ZxBzoiNF2L0wD2Ks= +go.opentelemetry.io/collector/consumer v1.39.0/go.mod h1:tW2BXyntjvlKrRc+mwistt1KuC/b4mTfTkc8zWjeeRY= +go.opentelemetry.io/collector/consumer/consumertest v0.133.0 h1:MteqaGpgmHVHFqnB7A2voGleA2j51qJyVfX5x/wm+8I= +go.opentelemetry.io/collector/consumer/consumertest v0.133.0/go.mod h1:vHGknLn/RRUcMQuuBDt+SgrpDN46DBJyqRnWXm3gLwY= +go.opentelemetry.io/collector/consumer/xconsumer v0.133.0 h1:Xx4Yna/We4qDlbAla1nfxgkvujzWRuR8bqqwsLLvYSg= +go.opentelemetry.io/collector/consumer/xconsumer v0.133.0/go.mod h1:he874Md/0uAS2Fs+TDHAy10OBLRSw8233LdREizVvG4= +go.opentelemetry.io/collector/featuregate v1.39.0 h1:OlXZWW+WUP8cgKh2mnwgWXUJO/29irb0hG6jvwscRKM= +go.opentelemetry.io/collector/featuregate v1.39.0/go.mod h1:A72x92glpH3zxekaUybml1vMSv94BH6jQRn5+/htcjw= +go.opentelemetry.io/collector/internal/telemetry v0.133.0 h1:YxbckZC9HniNOZgnSofTOe0AB/bEsmISNdQeS+3CU3o= +go.opentelemetry.io/collector/internal/telemetry v0.133.0/go.mod h1:akUK7X6ZQ+CbbCjyXLv9y/EHt5jIy+J+nGoLvndZN14= +go.opentelemetry.io/collector/pdata v1.39.0 h1:jr0f033o57Hpbj2Il8M15tPbvrOgY/Aoc+/+sxzhSFU= +go.opentelemetry.io/collector/pdata v1.39.0/go.mod h1:jmolu6zwqNaq8qJ4IgCpNWBEwJNPLE1qqOz9GnpqxME= +go.opentelemetry.io/collector/pdata/pprofile v0.133.0 h1:ewFYqV2FU4D0ixTdkJueaI2JGCoeiIJisX8EdHejDi8= +go.opentelemetry.io/collector/pdata/pprofile v0.133.0/go.mod h1:5l4/B0iCxzoVkA7eOLzIHV0AUEO2IKypTHTLq9JKsHs= +go.opentelemetry.io/collector/pdata/testdata v0.133.0 h1:K0q47qecWVJf0sWbeWfifbJ72TiqR+A2PCsMkCEKvus= +go.opentelemetry.io/collector/pdata/testdata v0.133.0/go.mod h1:/emFpIox/mi7FucvsSn54KsiMh/iy7BUviqgURNVT6U= +go.opentelemetry.io/collector/pipeline v1.39.0 h1:CcEn30qdoHEzehFxgx0Ma0pWYGhrrIkRkcu218NG4V4= +go.opentelemetry.io/collector/pipeline v1.39.0/go.mod h1:NdM+ZqkPe9KahtOXG28RHTRQu4m/FD1i3Ew4qCRdOr8= +go.opentelemetry.io/collector/processor v1.39.0 h1:QwPJxJnFZwojo09Vfnvph7A27TauxxvA1koO6nr87O8= +go.opentelemetry.io/collector/processor v1.39.0/go.mod h1:WQWZqKmrlJcLjirnQOULxYgWV6h5oxK6FQNiFgw53i8= +go.opentelemetry.io/collector/processor/processorhelper v0.133.0 h1:3w/wvSmzyCvyNXjUQihH/VLQ+Tnzn3MlQNbv1AEoXiU= +go.opentelemetry.io/collector/processor/processorhelper v0.133.0/go.mod h1:lTlC8tGOBqkpdwGXCmaDnWXc2jqIrRUKvV7eK26Thc4= +go.opentelemetry.io/collector/processor/processortest v0.133.0 h1:PAuOr8Pwj/LAuey2LW1fix0vvnE+WwGpSF7bghaxjEE= +go.opentelemetry.io/collector/processor/processortest v0.133.0/go.mod h1:fEhWs9DCe431+iFke1WmlxqjcRDN25GLRXdktKAPyw8= +go.opentelemetry.io/collector/processor/xprocessor v0.133.0 h1:V5YMrXUgClh3awWOdigGXHxvq/Ira2wLDj4DJLqB+Eo= +go.opentelemetry.io/collector/processor/xprocessor v0.133.0/go.mod h1:5gDFI+pGIzoFQeBUM4QZ4E0B+SaU0e+2V7Td+ONoU4M= +go.opentelemetry.io/contrib/bridges/otelzap v0.12.0 h1:FGre0nZh5BSw7G73VpT3xs38HchsfPsa2aZtMp0NPOs= +go.opentelemetry.io/contrib/bridges/otelzap v0.12.0/go.mod h1:X2PYPViI2wTPIMIOBjG17KNybTzsrATnvPJ02kkz7LM= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= go.opentelemetry.io/contrib/propagators/aws v1.42.0 h1:Kbr3xDxs6kcxp5ThXTKWK2OtwLhNoXBVtqguNYcsZL0= go.opentelemetry.io/contrib/propagators/aws v1.42.0/go.mod h1:Jzw9hZHtxdpCN7x8S17UH59X/EiFivp6VXLs9bdM1OQ= go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0 h1:MdKucPl/HbzckWWEisiNqMPhRrAOQX8r4jTuGr636gk= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0/go.mod h1:RolT8tWtfHcjajEH5wFIZ4Dgh5jpPdFXYV9pTAk/qjc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0/go.mod h1:J2pvYM5NGHofZ2/Ru6zw/TNWnEQp5crgyDeSrYpXkAw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 h1:zWWrB1U6nqhS/k6zYB74CjRpuiitRtLLi68VcgmOEto= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0/go.mod h1:2qXPNBX1OVRC0IwOnfo1ljoid+RD0QK3443EaqVlsOU= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0 h1:8UPA4IbVZxpsD76ihGOQiFml99GPAEZLohDXvqHdi6U= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0/go.mod h1:MZ1T/+51uIVKlRzGw1Fo46KEWThjlCBZKl2LzY5nv4g= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 h1:+XWJd3jf75RXJq29mxbuXhCXFDG3S3R4vBUeSI2P7tE= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0/go.mod h1:hqgzBPTf4yONMFgdZvL/bK42R/iinTyVQtiWihs3SZc= +go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4= +go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk= +go.opentelemetry.io/otel/log/logtest v0.13.0 h1:xxaIcgoEEtnwdgj6D6Uo9K/Dynz9jqIxSDu2YObJ69Q= +go.opentelemetry.io/otel/log/logtest v0.13.0/go.mod h1:+OrkmsAH38b+ygyag1tLjSFMYiES5UHggzrtY1IIEA8= 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/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko= +go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg= go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= -go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= -go.opentelemetry.io/proto/slim/otlp v1.9.0 h1:fPVMv8tP3TrsqlkH1HWYUpbCY9cAIemx184VGkS6vlE= -go.opentelemetry.io/proto/slim/otlp v1.9.0/go.mod h1:xXdeJJ90Gqyll+orzUkY4bOd2HECo5JofeoLpymVqdI= -go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0 h1:o13nadWDNkH/quoDomDUClnQBpdQQ2Qqv0lQBjIXjE8= -go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0/go.mod h1:Gyb6Xe7FTi/6xBHwMmngGoHqL0w29Y4eW8TGFzpefGA= -go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0 h1:EiUYvtwu6PMrMHVjcPfnsG3v+ajPkbUeH+IL93+QYyk= -go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0/go.mod h1:mUUHKFiN2SST3AhJ8XhJxEoeVW12oqfXog0Bo8W3Ec4= +go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= +go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= +go.opentelemetry.io/proto/slim/otlp v1.7.1 h1:lZ11gEokjIWYM3JWOUrIILr2wcf6RX+rq5SPObV9oyc= +go.opentelemetry.io/proto/slim/otlp v1.7.1/go.mod h1:uZ6LJWa49eNM/EXnnvJGTTu8miokU8RQdnO980LJ57g= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.0.1 h1:Tr/eXq6N7ZFjN+THBF/BtGLUz8dciA7cuzGRsCEkZ88= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.0.1/go.mod h1:riqUmAOJFDFuIAzZu/3V6cOrTyfWzpgNJnG5UwrapCk= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.0.1 h1:z/oMlrCv3Kopwh/dtdRagJy+qsRRPA86/Ux3g7+zFXM= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.0.1/go.mod h1:C7EHYSIiaALi9RnNORCVaPCQDuJgJEn/XxkctaTez1E= go.shabbyrobe.org/gocovmerge v0.0.0-20230507111327-fa4f82cfbf4d h1:Ns9kd1Rwzw7t0BR8XMphenji4SmIoNZPn8zhYmaVKP8= go.shabbyrobe.org/gocovmerge v0.0.0-20230507111327-fa4f82cfbf4d/go.mod h1:92Uoe3l++MlthCm+koNi0tcUCX3anayogF0Pa/sp24k= go.temporal.io/api v1.5.0/go.mod h1:BqKxEJJYdxb5dqf0ODfzfMxh8UEQ5L3zKS51FiIYYkA= @@ -504,6 +520,8 @@ go.temporal.io/sdk/contrib/datadog v0.5.0 h1:GfiDiqWNzkHqxO00H3Inxv66H+KLwS8ifz/ go.temporal.io/sdk/contrib/datadog v0.5.0/go.mod h1:MeHebmCM0ujSoNk2P4b+inyPmcR4IOZqygCVgpekAls= go.temporal.io/sdk/contrib/envconfig v1.0.1 h1:HZCcS6vNPJiUxJrkc5Wdeen+056LWmYe2dI0D6UuB5g= go.temporal.io/sdk/contrib/envconfig v1.0.1/go.mod h1:aCFIuADlPNv6bYK5Zp4YfB/PnGfpiSPVzJfvaSTCYtw= +go.temporal.io/sdk/contrib/googleadk v0.1.0 h1:ICu1nswKKu3TlO2hzVHRjdliZIq7aiUegGBMP5aCWMc= +go.temporal.io/sdk/contrib/googleadk v0.1.0/go.mod h1:blDAd6sIyExkjtPP1Q52mUXHNJCfkRyWaNPtr51977g= go.temporal.io/sdk/contrib/opentelemetry v0.7.0 h1:GSna1HP+1ibNXZ9xlVdQU2zFVqdt5VcdF0dzpeaYccQ= go.temporal.io/sdk/contrib/opentelemetry v0.7.0/go.mod h1:oQJC6UIl3FbSYh4f2MlUAIYSE6FPw02X1Tw8/bOvfxg= go.temporal.io/sdk/contrib/opentracing v0.3.0 h1:IKJgyvZiaOSFl0YHSxJpwXwQa08W3KaZCJkbx05rX7Q= @@ -524,10 +542,8 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= -go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= -go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA= @@ -550,8 +566,8 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20260209203927-2842357ff358 h1:kpfSV7uLwKJbFSEgNhWzGSL47NDSF/5pYYQw1V0ub6c= -golang.org/x/exp v0.0.0-20260209203927-2842357ff358/go.mod h1:R3t0oliuryB5eenPWl3rrQxwnNM3WTwnsRZZiXLAAW8= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -626,6 +642,7 @@ golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -670,10 +687,16 @@ gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +google.golang.org/adk/v2 v2.0.1-0.20260707195420-2a04f92f1776 h1:/Vrjt6Sxj1EbNhvADSHF4WiF6SHihEybMaso9y5lacQ= +google.golang.org/adk/v2 v2.0.1-0.20260707195420-2a04f92f1776/go.mod h1:fPuMPT5s3LsWu97mdeFjTPZu/02tIALWRWeqHL2FWKE= +google.golang.org/api v0.279.0 h1:hsx2M2OaRcaKtVYK6vXEUnQvdjnend7ZYES+lYaot74= +google.golang.org/api v0.279.0/go.mod h1:B9TqLBwJqVjp1mtt7WeoQwWRwvu/400y5lETOql+giQ= 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/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genai v1.62.0 h1:PaBju84orf4Vbcc6OfHe4vxhxhjwulKTgOpEc3iIc00= +google.golang.org/genai v1.62.0/go.mod h1:mDdPDFXo1Ats7f1WXVyZgWb/CkMzFWTWJruIMy7hGIU= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 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= @@ -681,10 +704,12 @@ google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0= -google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I= +google.golang.org/genproto/googleapis/api v0.0.0-20260427160629-7cedc36a6bc4 h1:yOzSCGPx+cp5VO7IxvZ9SBFF7j1tZVcNtlHR2iYKtVo= +google.golang.org/genproto/googleapis/api v0.0.0-20260427160629-7cedc36a6bc4/go.mod h1:Q9HWtNeE7tM9npdIsEvqXj1QJIvVoeAV3rtXtS715Cw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 h1:seT2EwLWM78plQ7wcDfuWBc/4FAEAXDDiaSol4ku4qo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -694,8 +719,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= -google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw= +google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= 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= @@ -719,8 +744,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 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/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k= -gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= @@ -743,6 +768,10 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a h1:1XCVEdxrvL6c0TGOhecLuB7U9zYNdxZEjvOqJreKZiM= inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a/go.mod h1:e83i32mAQOW1LAqEIweALsuK2Uw4mhQadA5r7b0Wobo= -k8s.io/apimachinery v0.35.1 h1:yxO6gV555P1YV0SANtnTjXYfiivaTPvCTKX6w6qdDsU= -k8s.io/apimachinery v0.35.1/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= +k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +rsc.io/omap v1.2.0 h1:c1M8jchnHbzmJALzGLclfH3xDWXrPxSUHXzH5C+8Kdw= +rsc.io/omap v1.2.0/go.mod h1:C8pkI0AWexHopQtZX+qiUeJGzvc8HkdgnsWK4/mAa00= +rsc.io/ordered v1.1.1 h1:1kZM6RkTmceJgsFH/8DLQvkCVEYomVDJfBRLT595Uak= +rsc.io/ordered v1.1.1/go.mod h1:evAi8739bWVBRG9aaufsjVc202+6okf8u2QeVL84BCM= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/googleadk/README.md b/googleadk/README.md new file mode 100644 index 00000000..1e8d8301 --- /dev/null +++ b/googleadk/README.md @@ -0,0 +1,78 @@ +## Google ADK on Temporal + +Run [Google ADK](https://google.github.io/adk-docs/) (`adk-go`) agents **durably on +Temporal** using the [`googleadk`](https://pkg.go.dev/go.temporal.io/sdk/contrib/googleadk) +contrib integration. The agent's orchestration loop runs inside a Workflow; each +model call runs as a Temporal Activity (via `googleadk.NewModel`), and tools can be +ordinary Temporal activities exposed to the agent with `googleadk.ActivityAsTool` +— so model calls and tools are retried, timed-out, and visible in the Temporal UI, +and the whole run is replayable. + +Agents are built the native ADK way (`llmagent.New` + `runner.New`); the only +Temporal-specific pieces are `googleadk.NewModel(...)` as the agent's model, +`googleadk.NewContext(ctx)` passed to `Run`, and the worker-side +`googleadk.NewActivities(...)` registry that holds the real Gemini client (so the +API key stays worker-side, never crossing into the workflow). + +Every sample runs against a scripted `FakeModel` in its `*_test.go`, so +`go test ./googleadk/...` needs no API key or network. + +### Samples + +- **Basic agent** — the root files in this directory + ([`workflow.go`](workflow.go), [`worker/`](worker), [`starter/`](starter)): a + single agent that answers a question, calling a `get_weather` tool implemented as + a Temporal activity via `googleadk.ActivityAsTool`. +- **[multiagent/](multiagent)** — a `coordinator` root agent that delegates to + `weather` and `jokes` specialist SubAgents via ADK's in-workflow + `transfer_to_agent`. +- **[humanintheloop/](humanintheloop)** — an agent with a sensitive + `delete_resource` tool whose workflow **durably waits on a Temporal signal** for a + human's approval before the tool runs. +- **[chat/](chat)** — a long-lived, signal-driven conversation on one ADK session + that **continues-as-new** (exporting/importing the session) to keep history + bounded. + +### Prerequisites (for running against a live model) + +- A running [Temporal server](https://github.com/temporalio/samples-go/tree/main/#how-to-use) + (e.g. `temporal server start-dev`). +- A Gemini API key from , exported worker-side: + ```bash + export GEMINI_API_KEY=... + ``` + +### Running the basic agent + +1) Start a Temporal server (see prerequisites). + +2) In a second terminal, start the worker (blocks until Ctrl+C): +```bash +export GEMINI_API_KEY=... +go run googleadk/worker/main.go +``` + +3) In a third terminal, run the starter: +```bash +go run googleadk/starter/main.go +``` + +The starter asks "What's the weather in San Francisco?"; the agent calls the +`get_weather` tool and answers. You should see a final log line similar to: +```bash +2025/12/22 15:07:25 Agent answer: It's currently sunny and about 72°F in San Francisco. +``` + +The exact wording comes from the model and will vary. In the Temporal UI you will +see the run's `googleadk.InvokeModel` and `get_weather` Activities. + +Each scenario subdirectory has its own README with run and test instructions. + +### Test without a live LLM + +`workflow_test.go` (and each scenario's `*_test.go`) drives the workflow through a +scripted `FakeModel` from the `googleadk` contrib package, so it needs no API key +or network: +```bash +go test ./googleadk/... +``` diff --git a/googleadk/chat/README.md b/googleadk/chat/README.md new file mode 100644 index 00000000..fe82527f --- /dev/null +++ b/googleadk/chat/README.md @@ -0,0 +1,59 @@ +### Google ADK long-lived chat (bounded history via continue-as-new) + +A **long-lived, update-driven** Google ADK chat running durably on Temporal with +the [`googleadk`](https://pkg.go.dev/go.temporal.io/sdk/contrib/googleadk) contrib +integration. A single `ChatWorkflow` serves an ongoing conversation: + +- each user message arrives as a Temporal **Update** (`send-message`); +- the agent answers it on the **same** ADK session, so conversation history + accumulates and later turns have full context; +- the answer is **returned on the Update itself** — the caller sends a message and + gets the reply on one call, with no signal + query polling. Turns are serialized + so concurrent Updates can't interleave on the shared session. + +To keep a conversation from growing unbounded in one workflow run, the workflow +**continues-as-new** once Temporal suggests it +(`workflow.GetInfo(ctx).GetContinueAsNewSuggested()`) — or, for the demo, after a +small `MaxTurns` cap. Before continuing it drains any in-flight turn +(`workflow.AllHandlersFinished`), then calls `googleadk.ExportSession` to capture the +session (identity, session-scoped state, and event history) into a serializable +`googleadk.SessionSnapshot`, passes it in the `ChatInput` of the next run, and the +next run calls `googleadk.ImportSession` to rebuild the session before serving the +next message. The conversation therefore survives the boundary while each run's +history stays bounded. + +### Prerequisites + +- A running [Temporal server](https://github.com/temporalio/samples-go/tree/main/#how-to-use) + (e.g. `temporal server start-dev`). +- A Gemini API key from , exported worker-side. + +### Steps to run this sample + +1) Start a Temporal server (see prerequisites). + +2) In a second terminal, start the worker: +```bash +export GEMINI_API_KEY=... +go run googleadk/chat/worker/main.go +``` + +3) In a third terminal, run the starter: +```bash +go run googleadk/chat/starter/main.go +``` + +The starter starts the chat (with a small `MaxTurns` so continue-as-new fires +quickly) and sends a couple of messages via Updates, printing each answer as it +comes back. The workflow keeps running (continuing-as-new to bound history); +terminate it from the Temporal UI when you're done. + +### Test without a live LLM + +`workflow_test.go` drives two messages via Updates and asserts the second turn's +model request carried prior history (proving the session persisted across turns), +and exercises the continue-as-new path with `MaxTurns=1`. No API key or network +needed: +```bash +go test ./googleadk/chat/... +``` diff --git a/googleadk/chat/starter/main.go b/googleadk/chat/starter/main.go new file mode 100644 index 00000000..c9af1d07 --- /dev/null +++ b/googleadk/chat/starter/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "context" + "log" + + "go.temporal.io/sdk/client" + "go.temporal.io/sdk/contrib/envconfig" + + "github.com/temporalio/samples-go/googleadk/chat" +) + +func main() { + // The client is a heavyweight object that should be created once per process. + c, err := client.Dial(envconfig.MustLoadDefaultClientOptions()) + if err != nil { + log.Fatalln("Unable to create client", err) + } + defer c.Close() + + workflowID := "google-adk-chat_workflowID" + workflowOptions := client.StartWorkflowOptions{ + ID: workflowID, + TaskQueue: chat.TaskQueue, + } + + // A small MaxTurns forces the continue-as-new boundary quickly for the demo. + we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, chat.ChatWorkflow, chat.ChatInput{MaxTurns: 3}) + if err != nil { + log.Fatalln("Unable to execute workflow", err) + } + log.Println("Started chat workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) + + messages := []string{ + "Hi! My name is David.", + "What's a fun fact about durable execution?", + } + for _, m := range messages { + // Send the message as an Update and get the agent's answer back on the same + // call — no signal + query polling. + handle, err := c.UpdateWorkflow(context.Background(), client.UpdateWorkflowOptions{ + WorkflowID: workflowID, + UpdateName: chat.SendMessageUpdateName, + WaitForStage: client.WorkflowUpdateStageCompleted, + Args: []interface{}{m}, + }) + if err != nil { + log.Fatalln("Unable to send message update", err) + } + var answer string + if err := handle.Get(context.Background(), &answer); err != nil { + log.Fatalln("Unable to get update result", err) + } + log.Printf("You: %q", m) + log.Printf("Assistant: %q", answer) + } + + log.Println("Done. The chat workflow keeps running (continuing-as-new to bound history); terminate it from the UI when finished.") +} diff --git a/googleadk/chat/worker/main.go b/googleadk/chat/worker/main.go new file mode 100644 index 00000000..0532212d --- /dev/null +++ b/googleadk/chat/worker/main.go @@ -0,0 +1,48 @@ +package main + +import ( + "context" + "log" + + "go.temporal.io/sdk/client" + "go.temporal.io/sdk/contrib/envconfig" + "go.temporal.io/sdk/worker" + + "google.golang.org/adk/v2/model" + "google.golang.org/adk/v2/model/gemini" + + "go.temporal.io/sdk/contrib/googleadk" + + chat "github.com/temporalio/samples-go/googleadk/chat" +) + +func main() { + // The client and worker are heavyweight objects that should be created once per process. + c, err := client.Dial(envconfig.MustLoadDefaultClientOptions()) + if err != nil { + log.Fatalln("Unable to create client", err) + } + defer c.Close() + + w := worker.New(c, chat.TaskQueue, worker.Options{}) + + w.RegisterWorkflow(chat.ChatWorkflow) + // The chat agent has no tools, so only the model Activity is registered. + + acts, err := googleadk.NewActivities(googleadk.Config{ + Models: map[string]googleadk.ModelFactory{ + chat.ModelName: func(ctx context.Context, name string) (model.LLM, error) { + // nil config reads GEMINI_API_KEY / GOOGLE_API_KEY from the env. + return gemini.NewModel(ctx, name, nil) + }, + }, + }) + if err != nil { + log.Fatalln("Unable to build googleadk activities", err) + } + acts.Register(w) + + if err := w.Run(worker.InterruptCh()); err != nil { + log.Fatalln("Unable to start worker", err) + } +} diff --git a/googleadk/chat/workflow.go b/googleadk/chat/workflow.go new file mode 100644 index 00000000..053f9985 --- /dev/null +++ b/googleadk/chat/workflow.go @@ -0,0 +1,169 @@ +// Package chat demonstrates a long-lived, update-driven Google ADK (adk-go) chat +// running durably on Temporal with the go.temporal.io/sdk/contrib/googleadk +// integration. A single Workflow serves an ongoing conversation: each user message +// arrives as a Temporal Update, the agent answers it on the SAME ADK session (so +// history accumulates), and the answer is returned on the Update itself — no signal +// + query polling. +// +// To keep history bounded, the Workflow continues-as-new once Temporal suggests it +// (or after a demo turn cap): it exports the session with googleadk.ExportSession, +// then re-imports it at the top of the next run with googleadk.ImportSession — so +// the conversation carries across the continue-as-new boundary without the history +// growing unbounded in a single run. +package chat + +import ( + "fmt" + + "go.temporal.io/sdk/workflow" + + "google.golang.org/adk/v2/agent" + "google.golang.org/adk/v2/agent/llmagent" + "google.golang.org/adk/v2/runner" + "google.golang.org/adk/v2/session" + "google.golang.org/genai" + + "go.temporal.io/sdk/contrib/googleadk" +) + +const ( + // TaskQueue is the task queue the worker listens on and the starter targets. + TaskQueue = "google-adk-chat" + + // ModelName is the Gemini model name the agent ships in-workflow. + ModelName = "gemini-2.0-flash" + + // SendMessageUpdateName is the Update that delivers a user message and returns + // the agent's answer. An Update (rather than a signal + query) lets the caller + // send the message and receive the answer on one call, with no polling. + SendMessageUpdateName = "send-message" + + // AppName / UserID / SessionID identify the single conversation session. + AppName = "chat" + UserID = "user-1" + SessionID = "session-1" +) + +// ChatInput is the workflow argument. On first start Snapshot is nil; on a +// continue-as-new it carries the exported session so the conversation resumes. +type ChatInput struct { + // Snapshot, when non-nil, is the session exported by the previous run. + Snapshot *googleadk.SessionSnapshot + // MaxTurns caps the number of messages served before continuing-as-new, so + // the demo can force the boundary without waiting for Temporal's suggestion. + // Zero means "only continue-as-new when Temporal suggests it". + MaxTurns int +} + +// ChatWorkflow serves a long-lived conversation. It imports any prior session, then +// registers a SendMessage Update handler that runs one agent turn per message on the +// shared session and returns the answer. When Temporal suggests continue-as-new (or +// MaxTurns is reached) it drains any in-flight turn, exports the session, and +// continues-as-new carrying the snapshot forward. +// @@@SNIPSTART googleadk-chat-workflow +func ChatWorkflow(ctx workflow.Context, in ChatInput) error { + // A fresh in-memory session service, kept in a local so we can Export it later. + svc := session.InMemoryService() + + adkCtx := googleadk.NewContext(ctx) + + // Resume a prior conversation if this run was continued-as-new. + if in.Snapshot != nil { + if _, err := googleadk.ImportSession(adkCtx, svc, in.Snapshot); err != nil { + return err + } + } + + root, err := llmagent.New(llmagent.Config{ + Name: "assistant", + Description: "a friendly conversational assistant", + Model: googleadk.NewModel(ModelName), + Instruction: "You are a helpful assistant. Answer the user, using the conversation history for context.", + }) + if err != nil { + return err + } + + r, err := runner.New(runner.Config{ + AppName: AppName, + Agent: root, + SessionService: svc, + AutoCreateSession: true, + }) + if err != nil { + return err + } + + turns := 0 + // One agent turn runs at a time: serialize concurrent Updates so they can't + // interleave on the shared ADK session. + busy := false + + err = workflow.SetUpdateHandlerWithOptions( + ctx, + SendMessageUpdateName, + func(ctx workflow.Context, text string) (string, error) { + if err := workflow.Await(ctx, func() bool { return !busy }); err != nil { + return "", err + } + busy = true + defer func() { busy = false }() + + // Build the ADK context from this Update handler's own workflow.Context so + // the model Activity is scheduled on the handler's coroutine. + turnCtx := googleadk.NewContext(ctx) + var answer string + msg := genai.NewContentFromText(text, genai.RoleUser) + for ev, err := range r.Run(turnCtx, UserID, SessionID, msg, agent.RunConfig{}) { + if err != nil { + return "", err + } + if ev == nil || ev.Content == nil { + continue + } + for _, p := range ev.Content.Parts { + if p != nil && p.Text != "" { + answer = p.Text + } + } + } + turns++ + return answer, nil + }, + workflow.UpdateHandlerOptions{ + Validator: func(ctx workflow.Context, text string) error { + if text == "" { + return fmt.Errorf("message must not be empty") + } + return nil + }, + }, + ) + if err != nil { + return err + } + + // Serve messages until Temporal suggests continue-as-new (history getting large) + // or the demo turn cap is reached. + if err := workflow.Await(ctx, func() bool { + return workflow.GetInfo(ctx).GetContinueAsNewSuggested() || (in.MaxTurns > 0 && turns >= in.MaxTurns) + }); err != nil { + return err + } + + // Let any in-flight Update finish so its turn is captured in the snapshot. + if err := workflow.Await(ctx, func() bool { return workflow.AllHandlersFinished(ctx) }); err != nil { + return err + } + + snap, err := googleadk.ExportSession(adkCtx, svc, AppName, UserID, SessionID) + if err != nil { + return err + } + return workflow.NewContinueAsNewError(ctx, ChatWorkflow, ChatInput{ + Snapshot: snap, + MaxTurns: in.MaxTurns, + }) +} + +// @@@SNIPEND diff --git a/googleadk/chat/workflow_test.go b/googleadk/chat/workflow_test.go new file mode 100644 index 00000000..593354f3 --- /dev/null +++ b/googleadk/chat/workflow_test.go @@ -0,0 +1,145 @@ +package chat_test + +import ( + "context" + "iter" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.temporal.io/sdk/activity" + "go.temporal.io/sdk/testsuite" + "go.temporal.io/sdk/workflow" + + "google.golang.org/adk/v2/model" + + "go.temporal.io/sdk/contrib/googleadk" + + chat "github.com/temporalio/samples-go/googleadk/chat" +) + +// recordingModel wraps a FakeModel and records the number of Contents in each +// request it serves, so a test can prove that a later turn's request carried the +// prior conversation history (proving the session persisted across turns). +type recordingModel struct { + inner *googleadk.FakeModel + + mu sync.Mutex + requestContents []int +} + +func (m *recordingModel) Name() string { return "recording-model" } + +func (m *recordingModel) GenerateContent(ctx context.Context, req *model.LLMRequest, stream bool) iter.Seq2[*model.LLMResponse, error] { + if req != nil { + m.mu.Lock() + m.requestContents = append(m.requestContents, len(req.Contents)) + m.mu.Unlock() + } + return m.inner.GenerateContent(ctx, req, stream) +} + +func (m *recordingModel) contentsAt(turn int) int { + m.mu.Lock() + defer m.mu.Unlock() + if turn < 0 || turn >= len(m.requestContents) { + return -1 + } + return m.requestContents[turn] +} + +// failOnReject returns update callbacks that fail the test if the update is +// rejected or completes with an error. +func failOnReject(t *testing.T) *testsuite.TestUpdateCallback { + return &testsuite.TestUpdateCallback{ + OnAccept: func() {}, + OnReject: func(err error) { t.Errorf("update rejected: %v", err) }, + OnComplete: func(_ interface{}, err error) { + if err != nil { + t.Errorf("update completed with error: %v", err) + } + }, + } +} + +// TestChatCarriesHistory drives two user messages through the chat workflow via +// Updates and asserts the second turn's model request carried more Contents than +// the first — proving the two messages ran on the SAME ADK session, so history +// accumulated. MaxTurns is high enough that no continue-as-new fires here. +func TestChatCarriesHistory(t *testing.T) { + var s testsuite.WorkflowTestSuite + env := s.NewTestWorkflowEnvironment() + env.RegisterWorkflow(chat.ChatWorkflow) + + rec := &recordingModel{inner: googleadk.NewFakeModel( + googleadk.TextResponse("Hi David, nice to meet you!"), + googleadk.TextResponse("Durable execution means your program's state survives crashes."), + )} + acts, err := googleadk.NewActivities(googleadk.Config{ + Models: map[string]googleadk.ModelFactory{ + chat.ModelName: func(context.Context, string) (model.LLM, error) { return rec, nil }, + }, + }) + require.NoError(t, err) + env.RegisterActivityWithOptions(acts.InvokeModel, activity.RegisterOptions{Name: googleadk.InvokeModelActivityName}) + + // Send two messages, spaced so each is served before the next arrives. Use a + // high MaxTurns so this test does not hit the continue-as-new path. + env.RegisterDelayedCallback(func() { + env.UpdateWorkflow(chat.SendMessageUpdateName, "msg-1", failOnReject(t), "Hi! My name is David.") + }, time.Second) + env.RegisterDelayedCallback(func() { + env.UpdateWorkflow(chat.SendMessageUpdateName, "msg-2", failOnReject(t), "What is durable execution?") + }, 5*time.Second) + // End the (otherwise long-lived) workflow by cancelling after both are served. + env.RegisterDelayedCallback(func() { + env.CancelWorkflow() + }, 10*time.Second) + + env.ExecuteWorkflow(chat.ChatWorkflow, chat.ChatInput{MaxTurns: 100}) + + require.True(t, env.IsWorkflowCompleted()) + + // Turn 1's request had just the first user message. Turn 2's request also + // carried turn 1's user message + the model's reply — strictly more Contents. + first := rec.contentsAt(0) + second := rec.contentsAt(1) + require.GreaterOrEqual(t, first, 1, "the first turn must have served at least one request") + require.GreaterOrEqual(t, second, 1, "the second turn must have served at least one request") + assert.Greater(t, second, first, "the second turn's request must carry prior history (same session)") +} + +// TestChatContinueAsNew exercises the bounded-history path: with MaxTurns=1 the +// workflow serves one message and then continues-as-new, exporting the session. In +// the test environment a continue-as-new surfaces as a workflow error of type +// *ContinueAsNewError, which is the assertion here. +func TestChatContinueAsNew(t *testing.T) { + var s testsuite.WorkflowTestSuite + env := s.NewTestWorkflowEnvironment() + env.RegisterWorkflow(chat.ChatWorkflow) + + fm := googleadk.NewFakeModel(googleadk.TextResponse("Hello!")) + acts, err := googleadk.NewActivities(googleadk.Config{ + Models: map[string]googleadk.ModelFactory{ + chat.ModelName: func(context.Context, string) (model.LLM, error) { return fm, nil }, + }, + }) + require.NoError(t, err) + env.RegisterActivityWithOptions(acts.InvokeModel, activity.RegisterOptions{Name: googleadk.InvokeModelActivityName}) + + env.RegisterDelayedCallback(func() { + env.UpdateWorkflow(chat.SendMessageUpdateName, "msg-1", failOnReject(t), "Hi there!") + }, time.Second) + + env.ExecuteWorkflow(chat.ChatWorkflow, chat.ChatInput{MaxTurns: 1}) + + require.True(t, env.IsWorkflowCompleted()) + // After serving the single allowed turn, the workflow must continue-as-new. + err = env.GetWorkflowError() + require.Error(t, err, "MaxTurns=1 must trigger a continue-as-new") + var canErr *workflow.ContinueAsNewError + require.ErrorAs(t, err, &canErr, "the workflow must end by continuing-as-new to bound history") +} diff --git a/googleadk/humanintheloop/README.md b/googleadk/humanintheloop/README.md new file mode 100644 index 00000000..01e9ad4a --- /dev/null +++ b/googleadk/humanintheloop/README.md @@ -0,0 +1,75 @@ +### Google ADK human-in-the-loop (durable approval) + +A **human-in-the-loop** Google ADK agent running durably on Temporal with the +[`googleadk`](https://pkg.go.dev/go.temporal.io/sdk/contrib/googleadk) contrib +integration. The agent has a sensitive `delete_resource` function tool that must +not run without a human's approval. + +How it works: + +1. The model calls `delete_resource`. On its **first** invocation the tool sees + `ctx.ToolConfirmation() == nil`, calls `ctx.RequestConfirmation("Delete …?", nil)`, + and returns without doing the delete — so ADK pauses the agent. +2. `ApprovalWorkflow` detects the pause via `googleadk.PendingConfirmations` and + **durably blocks on a Temporal signal** (`googleadk.ConfirmationSignalName`, + read with `workflow.GetSignalChannel`) carrying a + `googleadk.ConfirmationDecision`. +3. When the decision arrives, the workflow resumes the run with + `googleadk.ConfirmationResponse(decision)`. ADK re-dispatches the original + tool call, which now sees a confirmation and performs the delete (or is blocked + if denied). + +**This is the differentiator: the wait for the human is durable.** The workflow +can sit blocked for minutes or days and survive worker restarts — no state is lost. +When the approval signal finally arrives, the agent resumes exactly where it paused. + +### Notes + +- **`delete_resource` runs in-workflow and only *simulates* the delete** (it returns + a status map) to keep the demo deterministic. A real destructive operation does I/O + and must not run in the workflow — expose it with `googleadk.ActivityAsTool` so it + runs worker-side under Temporal's retry/timeout policy. The confirmation gate is + identical either way: the tool still calls `ctx.RequestConfirmation(...)` before + doing the work. +- **The workflow handles one pending confirmation per resume pass.** This is also + the recommended pattern: as the `googleadk.ConfirmationResponse` docs note, + resuming several decisions in one pass can re-dispatch the approved tool calls + in an order that is not replay-stable, so when the confirmed tools dispatch + Activities, answer one decision per `Run` pass (additional pending + confirmations surface again on the next pass). + +### Prerequisites + +- A running [Temporal server](https://github.com/temporalio/samples-go/tree/main/#how-to-use) + (e.g. `temporal server start-dev`). +- A Gemini API key from , exported worker-side. + +### Steps to run this sample + +1) Start a Temporal server (see prerequisites). + +2) In a second terminal, start the worker: +```bash +export GEMINI_API_KEY=... +go run googleadk/humanintheloop/worker/main.go +``` + +3) In a third terminal, run the starter: +```bash +go run googleadk/humanintheloop/starter/main.go +``` + +The starter asks the agent to delete a resource; the workflow pauses awaiting +approval, and the starter then sends an approval signal (via +`client.SignalWorkflow`) to demonstrate the resume. In a real system the signal +would come from an operator clicking "approve" in a UI, possibly much later. + +### Test without a live LLM + +`workflow_test.go` scripts the model to call `delete_resource`, uses +`env.RegisterDelayedCallback` to deliver the approval through the **real** Temporal +signal, and asserts the delete completes only after approval (plus a denial case). +No API key or network needed: +```bash +go test ./googleadk/humanintheloop/... +``` diff --git a/googleadk/humanintheloop/starter/main.go b/googleadk/humanintheloop/starter/main.go new file mode 100644 index 00000000..eef35692 --- /dev/null +++ b/googleadk/humanintheloop/starter/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "context" + "log" + "time" + + "go.temporal.io/sdk/client" + "go.temporal.io/sdk/contrib/envconfig" + + "go.temporal.io/sdk/contrib/googleadk" + + humanintheloop "github.com/temporalio/samples-go/googleadk/humanintheloop" +) + +func main() { + // The client is a heavyweight object that should be created once per process. + c, err := client.Dial(envconfig.MustLoadDefaultClientOptions()) + if err != nil { + log.Fatalln("Unable to create client", err) + } + defer c.Close() + + workflowOptions := client.StartWorkflowOptions{ + ID: "google-adk-hitl_workflowID", + TaskQueue: humanintheloop.TaskQueue, + } + + request := "Please delete the resource named prod-db." + we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, humanintheloop.ApprovalWorkflow, request) + if err != nil { + log.Fatalln("Unable to execute workflow", err) + } + log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) + + // The agent will call delete_resource, which pauses awaiting human approval. + // The workflow is now durably blocked on the confirmation signal — it would + // stay blocked indefinitely (surviving worker restarts) until a decision + // arrives. Here we simulate a human approving after a short delay. + log.Println("Waiting for the agent to pause on confirmation, then approving...") + time.Sleep(3 * time.Second) + + decision := googleadk.ConfirmationDecision{Confirmed: true} + if err := c.SignalWorkflow(context.Background(), we.GetID(), we.GetRunID(), googleadk.ConfirmationSignalName, decision); err != nil { + log.Fatalln("Unable to signal approval", err) + } + log.Println("Sent approval signal") + + // Synchronously wait for the workflow completion. + var res humanintheloop.Result + if err := we.Get(context.Background(), &res); err != nil { + log.Fatalln("Unable to get workflow result", err) + } + log.Printf("Approved=%v answer=%q", res.Approved, res.Answer) +} diff --git a/googleadk/humanintheloop/worker/main.go b/googleadk/humanintheloop/worker/main.go new file mode 100644 index 00000000..4b1360e3 --- /dev/null +++ b/googleadk/humanintheloop/worker/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "context" + "log" + + "go.temporal.io/sdk/client" + "go.temporal.io/sdk/contrib/envconfig" + "go.temporal.io/sdk/worker" + + "google.golang.org/adk/v2/model" + "google.golang.org/adk/v2/model/gemini" + + "go.temporal.io/sdk/contrib/googleadk" + + humanintheloop "github.com/temporalio/samples-go/googleadk/humanintheloop" +) + +func main() { + // The client and worker are heavyweight objects that should be created once per process. + c, err := client.Dial(envconfig.MustLoadDefaultClientOptions()) + if err != nil { + log.Fatalln("Unable to create client", err) + } + defer c.Close() + + w := worker.New(c, humanintheloop.TaskQueue, worker.Options{}) + + w.RegisterWorkflow(humanintheloop.ApprovalWorkflow) + // The delete_resource tool is an in-workflow function tool (not an + // ActivityAsTool), so there is no tool activity to register here — only the + // model Activity below. + + acts, err := googleadk.NewActivities(googleadk.Config{ + Models: map[string]googleadk.ModelFactory{ + humanintheloop.ModelName: func(ctx context.Context, name string) (model.LLM, error) { + // nil config reads GEMINI_API_KEY / GOOGLE_API_KEY from the env. + return gemini.NewModel(ctx, name, nil) + }, + }, + }) + if err != nil { + log.Fatalln("Unable to build googleadk activities", err) + } + acts.Register(w) + + if err := w.Run(worker.InterruptCh()); err != nil { + log.Fatalln("Unable to start worker", err) + } +} diff --git a/googleadk/humanintheloop/workflow.go b/googleadk/humanintheloop/workflow.go new file mode 100644 index 00000000..eb239e2e --- /dev/null +++ b/googleadk/humanintheloop/workflow.go @@ -0,0 +1,164 @@ +// Package humanintheloop demonstrates a human-in-the-loop (HITL) Google ADK +// (adk-go) agent running durably on Temporal with the +// go.temporal.io/sdk/contrib/googleadk integration. The agent has a sensitive +// delete_resource tool that pauses for human approval; the Workflow durably waits +// for a Temporal signal carrying the human's decision before letting the tool run. +// +// This is the key differentiator over a plain agent loop: the wait for the human +// is durable. The Workflow can be idle for days and survive worker restarts — when +// the approval signal finally arrives, the agent resumes exactly where it paused. +package humanintheloop + +import ( + "go.temporal.io/sdk/workflow" + + "google.golang.org/adk/v2/agent" + "google.golang.org/adk/v2/agent/llmagent" + "google.golang.org/adk/v2/runner" + "google.golang.org/adk/v2/session" + "google.golang.org/adk/v2/tool" + "google.golang.org/adk/v2/tool/functiontool" + "google.golang.org/genai" + + "go.temporal.io/sdk/contrib/googleadk" +) + +const ( + // TaskQueue is the task queue the worker listens on and the starter targets. + TaskQueue = "google-adk-hitl" + + // ModelName is the Gemini model name the agent ships in-workflow. + ModelName = "gemini-2.0-flash" + + // DeleteToolName is the name of the sensitive function tool the model calls. + DeleteToolName = "delete_resource" +) + +// DeleteArgs is the argument schema the model fills in for the delete tool. +type DeleteArgs struct { + Resource string `json:"resource"` +} + +// Result is the serializable output of ApprovalWorkflow. +type Result struct { + // Approved reports the human's decision. + Approved bool + // Answer is the agent's final text after the decision was applied. + Answer string +} + +// deleteResource is the sensitive function tool. On its first invocation there is +// no confirmation yet, so it requests one (via ctx.RequestConfirmation) and +// returns without doing the work — this pauses the agent. On the resumed +// invocation ADK supplies a ToolConfirmation, so the delete proceeds. +// +// NOTE: this runs in-workflow and only simulates the delete (it returns a status +// map), which keeps the sample deterministic. A real destructive operation does +// I/O and must NOT run in the workflow — expose it with googleadk.ActivityAsTool +// so it runs worker-side under Temporal's retry/timeout policy. The confirmation +// gate is identical either way: request confirmation first, do the work only once +// confirmed. +func deleteResource(tctx agent.Context, args DeleteArgs) (map[string]any, error) { + if tctx.ToolConfirmation() == nil { + if err := tctx.RequestConfirmation("Delete "+args.Resource+"?", nil); err != nil { + return nil, err + } + return map[string]any{"status": "awaiting confirmation"}, nil + } + return map[string]any{"status": "deleted", "resource": args.Resource}, nil +} + +// ApprovalWorkflow runs the agent and, when the sensitive tool pauses awaiting a +// human decision, durably blocks on a Temporal signal named +// googleadk.ConfirmationSignalName carrying a googleadk.ConfirmationDecision. Once +// the decision arrives it resumes the agent with googleadk.ConfirmationResponse, +// so the tool runs (or is blocked) according to the human's choice. +// @@@SNIPSTART googleadk-hitl-workflow +func ApprovalWorkflow(ctx workflow.Context, request string) (Result, error) { + delTool, err := functiontool.New[DeleteArgs, map[string]any]( + functiontool.Config{ + Name: DeleteToolName, + Description: "Delete a named resource. Requires human confirmation before it runs.", + }, + deleteResource, + ) + if err != nil { + return Result{}, err + } + + root, err := llmagent.New(llmagent.Config{ + Name: "assistant", + Description: "an assistant that can delete resources with human approval", + Model: googleadk.NewModel(ModelName), + Instruction: "Use the delete_resource tool when the user asks to delete something.", + Tools: []tool.Tool{delTool}, + }) + if err != nil { + return Result{}, err + } + + r, err := runner.New(runner.Config{ + AppName: "hitl", + Agent: root, + SessionService: session.InMemoryService(), + AutoCreateSession: true, + }) + if err != nil { + return Result{}, err + } + + adkCtx := googleadk.NewContext(ctx) + msg := genai.NewContentFromText(request, genai.RoleUser) + + var res Result + // Drive the run in passes: each Run call is one pass over the same session. A + // pass either completes (no pending confirmation) or pauses awaiting a human. + for { + var events []*session.Event + for ev, err := range r.Run(adkCtx, "user-1", "session-1", msg, agent.RunConfig{}) { + if err != nil { + return Result{}, err + } + if ev == nil { + continue + } + events = append(events, ev) + if ev.Content != nil { + for _, p := range ev.Content.Parts { + if p != nil && p.Text != "" { + res.Answer = p.Text + } + } + } + } + + pending := googleadk.PendingConfirmations(events) + if len(pending) == 0 { + // The agent finished without (further) confirmations needed. + return res, nil + } + + // The agent paused. Durably wait for the human's decision to arrive as a + // Temporal signal. This is the whole point: the workflow can sit here for + // as long as it takes — across worker restarts — without losing state. + // + // This handles one pending confirmation per pass — the recommended + // pattern (see the googleadk.ConfirmationResponse docs): resuming + // several decisions at once can re-dispatch the approved tool calls in + // an order that is not replay-stable. Any other pending confirmations + // simply surface again on the next pass. + var decision googleadk.ConfirmationDecision + workflow.GetSignalChannel(ctx, googleadk.ConfirmationSignalName).Receive(ctx, &decision) + res.Approved = decision.Confirmed + + // Match the decision to the pending confirmation and resume the run with + // it as the next message. ADK re-dispatches (or blocks) the original tool + // call based on Confirmed. + if decision.FunctionCallID == "" { + decision.FunctionCallID = pending[0].FunctionCallID + } + msg = googleadk.ConfirmationResponse(decision) + } +} + +// @@@SNIPEND diff --git a/googleadk/humanintheloop/workflow_test.go b/googleadk/humanintheloop/workflow_test.go new file mode 100644 index 00000000..1a74b7e7 --- /dev/null +++ b/googleadk/humanintheloop/workflow_test.go @@ -0,0 +1,101 @@ +package humanintheloop_test + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.temporal.io/sdk/activity" + "go.temporal.io/sdk/testsuite" + + "google.golang.org/adk/v2/model" + + "go.temporal.io/sdk/contrib/googleadk" + + humanintheloop "github.com/temporalio/samples-go/googleadk/humanintheloop" +) + +// scriptedModelFactory returns a ModelFactory yielding a single shared FakeModel +// so its scripted responses advance turn by turn across Activity invocations. +func scriptedModelFactory(responses ...*model.LLMResponse) googleadk.ModelFactory { + fm := googleadk.NewFakeModel(responses...) + return func(context.Context, string) (model.LLM, error) { return fm, nil } +} + +// TestApprovalWorkflow proves the durable human-in-the-loop wait: the agent calls +// the sensitive delete_resource tool, which pauses the workflow awaiting a human +// decision. A delayed callback delivers an approval via the real Temporal signal +// (googleadk.ConfirmationSignalName), after which the workflow resumes and the +// delete runs. This exercises the actual signal round-trip, not a two-pass loop. +func TestApprovalWorkflow(t *testing.T) { + var s testsuite.WorkflowTestSuite + env := s.NewTestWorkflowEnvironment() + + env.RegisterWorkflow(humanintheloop.ApprovalWorkflow) + + // Scripted model: turn 1 (before approval) calls delete_resource; turn 2 + // (after the resume) produces the final text confirming the delete. + acts, err := googleadk.NewActivities(googleadk.Config{ + Models: map[string]googleadk.ModelFactory{ + humanintheloop.ModelName: scriptedModelFactory( + googleadk.FunctionCallResponse("call-1", humanintheloop.DeleteToolName, map[string]any{"resource": "prod-db"}), + googleadk.TextResponse("Deleted prod-db."), + ), + }, + }) + require.NoError(t, err) + env.RegisterActivityWithOptions(acts.InvokeModel, activity.RegisterOptions{Name: googleadk.InvokeModelActivityName}) + + // Approve after a delay, via the real signal the workflow blocks on. Until this + // fires the workflow is durably waiting; the delete cannot have run yet. + env.RegisterDelayedCallback(func() { + env.SignalWorkflow(googleadk.ConfirmationSignalName, googleadk.ConfirmationDecision{Confirmed: true}) + }, time.Second) + + env.ExecuteWorkflow(humanintheloop.ApprovalWorkflow, "Please delete the resource named prod-db.") + + require.True(t, env.IsWorkflowCompleted()) + require.NoError(t, env.GetWorkflowError()) + + var res humanintheloop.Result + require.NoError(t, env.GetWorkflowResult(&res)) + assert.True(t, res.Approved, "the workflow must record the human's approval") + // The final answer is produced only on the resumed pass — after approval. + assert.Contains(t, res.Answer, "Deleted") +} + +// TestApprovalWorkflowDenied proves denial also flows through: the human denies, +// the workflow records it, and the delete does not report success. +func TestApprovalWorkflowDenied(t *testing.T) { + var s testsuite.WorkflowTestSuite + env := s.NewTestWorkflowEnvironment() + + env.RegisterWorkflow(humanintheloop.ApprovalWorkflow) + + acts, err := googleadk.NewActivities(googleadk.Config{ + Models: map[string]googleadk.ModelFactory{ + humanintheloop.ModelName: scriptedModelFactory( + googleadk.FunctionCallResponse("call-1", humanintheloop.DeleteToolName, map[string]any{"resource": "prod-db"}), + googleadk.TextResponse("Okay, I did not delete prod-db."), + ), + }, + }) + require.NoError(t, err) + env.RegisterActivityWithOptions(acts.InvokeModel, activity.RegisterOptions{Name: googleadk.InvokeModelActivityName}) + + env.RegisterDelayedCallback(func() { + env.SignalWorkflow(googleadk.ConfirmationSignalName, googleadk.ConfirmationDecision{Confirmed: false}) + }, time.Second) + + env.ExecuteWorkflow(humanintheloop.ApprovalWorkflow, "Please delete the resource named prod-db.") + + require.True(t, env.IsWorkflowCompleted()) + require.NoError(t, env.GetWorkflowError()) + + var res humanintheloop.Result + require.NoError(t, env.GetWorkflowResult(&res)) + assert.False(t, res.Approved, "the workflow must record the human's denial") +} diff --git a/googleadk/multiagent/README.md b/googleadk/multiagent/README.md new file mode 100644 index 00000000..0d7e37a2 --- /dev/null +++ b/googleadk/multiagent/README.md @@ -0,0 +1,55 @@ +### Google ADK multi-agent (coordinator + specialists) + +A **multi-agent** Google ADK system running durably on Temporal with the +[`googleadk`](https://pkg.go.dev/go.temporal.io/sdk/contrib/googleadk) contrib +integration. A `coordinator` root agent delegates the user's request to one of two +specialist **SubAgents**: + +- a `weather` specialist that owns the `get_weather` tool (an ordinary Temporal + activity exposed via `googleadk.ActivityAsTool`), and +- a `jokes` specialist. + +The coordinator picks a specialist by emitting ADK's built-in `transfer_to_agent` +call. **That transfer is resolved in-workflow** — it is not a separate Temporal +workflow or a network round-trip — so the whole delegation is deterministic and +replayable. Only the per-agent model calls and the `get_weather` tool cross the +Activity boundary. + +Each agent is given a **distinct model name** (`gemini-2.0-flash-coordinator`, +`-weather`, `-jokes`) so tests can register and script a separate `FakeModel` per +agent; in production all three names resolve to the same real Gemini model behind +`googleadk.InvokeModel`. + +### Prerequisites + +- A running [Temporal server](https://github.com/temporalio/samples-go/tree/main/#how-to-use) + (e.g. `temporal server start-dev`). +- A Gemini API key from , exported worker-side. + +### Steps to run this sample + +1) Start a Temporal server (see prerequisites). + +2) In a second terminal, start the worker: +```bash +export GEMINI_API_KEY=... +go run googleadk/multiagent/worker/main.go +``` + +3) In a third terminal, run the starter: +```bash +go run googleadk/multiagent/starter/main.go +``` + +The starter asks a weather question; the coordinator transfers to the `weather` +specialist, which calls `get_weather` and answers. In the Temporal UI you will see +one `googleadk.InvokeModel` Activity per agent turn plus the `get_weather` Activity. + +### Test without a live LLM + +`workflow_test.go` scripts each agent's `FakeModel` (coordinator transfers to +`weather`; the weather specialist calls `get_weather` then answers) and asserts the +transfer took effect, so it needs no API key or network: +```bash +go test ./googleadk/multiagent/... +``` diff --git a/googleadk/multiagent/starter/main.go b/googleadk/multiagent/starter/main.go new file mode 100644 index 00000000..c6df4c6e --- /dev/null +++ b/googleadk/multiagent/starter/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "context" + "log" + + "go.temporal.io/sdk/client" + "go.temporal.io/sdk/contrib/envconfig" + + multiagent "github.com/temporalio/samples-go/googleadk/multiagent" +) + +func main() { + // The client is a heavyweight object that should be created once per process. + c, err := client.Dial(envconfig.MustLoadDefaultClientOptions()) + if err != nil { + log.Fatalln("Unable to create client", err) + } + defer c.Close() + + workflowOptions := client.StartWorkflowOptions{ + ID: "google-adk-multiagent_workflowID", + TaskQueue: multiagent.TaskQueue, + } + + question := "What's the weather in San Francisco?" + we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, multiagent.MultiAgentWorkflow, question) + if err != nil { + log.Fatalln("Unable to execute workflow", err) + } + + log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) + + // Synchronously wait for the workflow completion. + var answer string + if err := we.Get(context.Background(), &answer); err != nil { + log.Fatalln("Unable to get workflow result", err) + } + log.Println("Agent answer:", answer) +} diff --git a/googleadk/multiagent/worker/main.go b/googleadk/multiagent/worker/main.go new file mode 100644 index 00000000..9ee1a97b --- /dev/null +++ b/googleadk/multiagent/worker/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "context" + "log" + + "go.temporal.io/sdk/activity" + "go.temporal.io/sdk/client" + "go.temporal.io/sdk/contrib/envconfig" + "go.temporal.io/sdk/worker" + + "google.golang.org/adk/v2/model" + "google.golang.org/adk/v2/model/gemini" + + "go.temporal.io/sdk/contrib/googleadk" + + multiagent "github.com/temporalio/samples-go/googleadk/multiagent" +) + +func main() { + // The client and worker are heavyweight objects that should be created once per process. + c, err := client.Dial(envconfig.MustLoadDefaultClientOptions()) + if err != nil { + log.Fatalln("Unable to create client", err) + } + defer c.Close() + + w := worker.New(c, multiagent.TaskQueue, worker.Options{}) + + w.RegisterWorkflow(multiagent.MultiAgentWorkflow) + // Register GetWeather under the tool name the ActivityAsTool dispatches, so the + // weather specialist's get_weather call resolves to this activity. + w.RegisterActivityWithOptions(multiagent.GetWeather, activity.RegisterOptions{Name: multiagent.WeatherToolName}) + + // Register the integration's model Activity. Every agent in the tree uses a + // distinct model name so they can be scripted independently in tests; here + // they all resolve to the same real Gemini model. The API key is read + // worker-side and never crosses into the workflow. + gemModel := func(ctx context.Context, name string) (model.LLM, error) { + // nil config reads GEMINI_API_KEY / GOOGLE_API_KEY from the env. + return gemini.NewModel(ctx, "gemini-2.0-flash", nil) + } + acts, err := googleadk.NewActivities(googleadk.Config{ + Models: map[string]googleadk.ModelFactory{ + multiagent.CoordinatorModelName: gemModel, + multiagent.WeatherModelName: gemModel, + multiagent.JokesModelName: gemModel, + }, + }) + if err != nil { + log.Fatalln("Unable to build googleadk activities", err) + } + acts.Register(w) + + if err := w.Run(worker.InterruptCh()); err != nil { + log.Fatalln("Unable to start worker", err) + } +} diff --git a/googleadk/multiagent/workflow.go b/googleadk/multiagent/workflow.go new file mode 100644 index 00000000..818ff7d5 --- /dev/null +++ b/googleadk/multiagent/workflow.go @@ -0,0 +1,146 @@ +// Package multiagent demonstrates a multi-agent Google ADK (adk-go) system +// running durably on Temporal with the go.temporal.io/sdk/contrib/googleadk +// integration. A "coordinator" root agent delegates to one of two specialist +// SubAgents — a weather specialist (which owns the get_weather ActivityAsTool) +// and a jokes specialist — via ADK's built-in transfer_to_agent mechanism. +// +// The entire multi-agent orchestration, including the transfer_to_agent hop, runs +// inside the Workflow; only the model calls (one per agent turn) and the +// get_weather tool run as Temporal Activities. +package multiagent + +import ( + "context" + + "go.temporal.io/sdk/workflow" + + "google.golang.org/adk/v2/agent" + "google.golang.org/adk/v2/agent/llmagent" + "google.golang.org/adk/v2/runner" + "google.golang.org/adk/v2/session" + "google.golang.org/adk/v2/tool" + "google.golang.org/genai" + + "go.temporal.io/sdk/contrib/googleadk" +) + +const ( + // TaskQueue is the task queue the worker listens on and the starter targets. + TaskQueue = "google-adk-multiagent" + + // CoordinatorModelName, WeatherModelName, and JokesModelName are distinct + // model names so the worker (and the test) can register a separate + // ModelFactory per agent and script each one independently. In production + // they all resolve to the same Gemini model behind the InvokeModel Activity. + CoordinatorModelName = "gemini-2.0-flash-coordinator" + WeatherModelName = "gemini-2.0-flash-weather" + JokesModelName = "gemini-2.0-flash-jokes" + + // WeatherToolName is both the tool name the model sees and the Activity name + // the tool dispatches, so the worker must register GetWeather under this name. + WeatherToolName = "get_weather" +) + +// GetWeatherInput is the argument schema the model fills in for the weather tool. +type GetWeatherInput struct { + City string `json:"city"` +} + +// GetWeatherOutput is the weather tool's result, handed back to the model. +type GetWeatherOutput struct { + City string `json:"city"` + Conditions string `json:"conditions"` +} + +// GetWeather is an ordinary Temporal activity exposed to the weather specialist +// as a tool via googleadk.ActivityAsTool. It runs durably worker-side whenever +// the specialist calls it. A real implementation would call a weather API here. +func GetWeather(ctx context.Context, in GetWeatherInput) (GetWeatherOutput, error) { + return GetWeatherOutput{City: in.City, Conditions: "sunny, 72°F"}, nil +} + +// MultiAgentWorkflow builds a coordinator agent with two specialist SubAgents and +// runs the question through the tree. The coordinator decides which specialist to +// delegate to (emitting a transfer_to_agent call, which ADK resolves in-workflow); +// the chosen specialist then answers. The workflow returns the final text. +// @@@SNIPSTART googleadk-multiagent-workflow +func MultiAgentWorkflow(ctx workflow.Context, question string) (string, error) { + weatherTool, err := googleadk.ActivityAsTool(GetWeather, googleadk.ActivityToolOptions{ + Name: WeatherToolName, + Description: "Get the current weather for a city.", + }) + if err != nil { + return "", err + } + + // The weather specialist owns the get_weather tool. + weather, err := llmagent.New(llmagent.Config{ + Name: "weather", + Description: "answers questions about the current weather in a city", + Model: googleadk.NewModel(WeatherModelName), + Instruction: "You are a weather specialist. Use the get_weather tool to answer weather questions.", + Tools: []tool.Tool{weatherTool}, + }) + if err != nil { + return "", err + } + + // The jokes specialist just tells jokes. + jokes, err := llmagent.New(llmagent.Config{ + Name: "jokes", + Description: "tells a light-hearted joke", + Model: googleadk.NewModel(JokesModelName), + Instruction: "You are a comedian. Respond with a short, friendly joke.", + }) + if err != nil { + return "", err + } + + // The coordinator delegates to whichever specialist fits the question. ADK + // wires the parent/child relationship from SubAgents and exposes the built-in + // transfer_to_agent tool automatically. + coordinator, err := llmagent.New(llmagent.Config{ + Name: "coordinator", + Description: "routes the user's request to the right specialist", + Model: googleadk.NewModel(CoordinatorModelName), + Instruction: "You are a router. Delegate weather questions to the weather agent " + + "and requests for a joke to the jokes agent. Do not answer directly.", + SubAgents: []agent.Agent{weather, jokes}, + }) + if err != nil { + return "", err + } + + r, err := runner.New(runner.Config{ + AppName: "multiagent", + Agent: coordinator, + SessionService: session.InMemoryService(), + AutoCreateSession: true, + }) + if err != nil { + return "", err + } + + adkCtx := googleadk.NewContext(ctx) + msg := genai.NewContentFromText(question, genai.RoleUser) + + var answer string + for ev, err := range r.Run(adkCtx, "user-1", "session-1", msg, agent.RunConfig{}) { + if err != nil { + return "", err + } + if ev == nil || ev.Content == nil { + continue + } + // Keep the last non-empty text produced by any agent in the tree; after a + // transfer_to_agent hop this is the specialist's answer. + for _, p := range ev.Content.Parts { + if p != nil && p.Text != "" { + answer = p.Text + } + } + } + return answer, nil +} + +// @@@SNIPEND diff --git a/googleadk/multiagent/workflow_test.go b/googleadk/multiagent/workflow_test.go new file mode 100644 index 00000000..739e3127 --- /dev/null +++ b/googleadk/multiagent/workflow_test.go @@ -0,0 +1,82 @@ +package multiagent_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.temporal.io/sdk/activity" + "go.temporal.io/sdk/converter" + "go.temporal.io/sdk/testsuite" + + "google.golang.org/adk/v2/model" + + "go.temporal.io/sdk/contrib/googleadk" + + multiagent "github.com/temporalio/samples-go/googleadk/multiagent" +) + +// scriptedModelFactory returns a ModelFactory yielding a single shared FakeModel +// so its scripted responses advance turn by turn across Activity invocations +// (turn 1 = first response, turn 2 = second, ...). +func scriptedModelFactory(responses ...*model.LLMResponse) googleadk.ModelFactory { + fm := googleadk.NewFakeModel(responses...) + return func(context.Context, string) (model.LLM, error) { return fm, nil } +} + +// TestMultiAgentWorkflow drives the coordinator/specialist tree through scripted +// FakeModels — no live LLM. The coordinator emits a transfer_to_agent call to the +// weather specialist; the specialist then calls get_weather and answers. The test +// asserts the specialist's answer surfaced and that the get_weather tool ran (as a +// real activity), proving the transfer took effect. +func TestMultiAgentWorkflow(t *testing.T) { + var s testsuite.WorkflowTestSuite + env := s.NewTestWorkflowEnvironment() + + env.RegisterWorkflow(multiagent.MultiAgentWorkflow) + env.RegisterActivityWithOptions(multiagent.GetWeather, activity.RegisterOptions{Name: multiagent.WeatherToolName}) + + // Track whether the get_weather activity ran — it only does if the coordinator + // successfully transferred to the weather specialist and the specialist called + // the tool. + var weatherRan bool + env.SetOnActivityStartedListener(func(info *activity.Info, _ context.Context, _ converter.EncodedValues) { + if info.ActivityType.Name == multiagent.WeatherToolName { + weatherRan = true + } + }) + + // Per-agent scripted models keyed by their distinct model names. + // coordinator: turn 1 delegates to the weather specialist. + // weather specialist: turn 1 calls get_weather, turn 2 answers. + // jokes specialist: never invoked in this scenario. + acts, err := googleadk.NewActivities(googleadk.Config{ + Models: map[string]googleadk.ModelFactory{ + multiagent.CoordinatorModelName: scriptedModelFactory( + googleadk.FunctionCallResponse("t1", "transfer_to_agent", map[string]any{"agent_name": "weather"}), + ), + multiagent.WeatherModelName: scriptedModelFactory( + googleadk.FunctionCallResponse("call-1", multiagent.WeatherToolName, map[string]any{"city": "San Francisco"}), + googleadk.TextResponse("It's sunny, 72°F in San Francisco."), + ), + multiagent.JokesModelName: scriptedModelFactory( + googleadk.TextResponse("Why did the cloud break up with the fog? It needed space."), + ), + }, + }) + require.NoError(t, err) + env.RegisterActivityWithOptions(acts.InvokeModel, activity.RegisterOptions{Name: googleadk.InvokeModelActivityName}) + + env.ExecuteWorkflow(multiagent.MultiAgentWorkflow, "What's the weather in San Francisco?") + + require.True(t, env.IsWorkflowCompleted()) + require.NoError(t, env.GetWorkflowError()) + + var answer string + require.NoError(t, env.GetWorkflowResult(&answer)) + // The final answer came from the weather specialist after the transfer. + assert.Contains(t, answer, "sunny") + assert.True(t, weatherRan, "the transfer must reach the weather specialist so get_weather runs") +} diff --git a/googleadk/starter/main.go b/googleadk/starter/main.go new file mode 100644 index 00000000..5e8c5360 --- /dev/null +++ b/googleadk/starter/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "context" + "log" + + "go.temporal.io/sdk/client" + "go.temporal.io/sdk/contrib/envconfig" + + adk "github.com/temporalio/samples-go/googleadk" +) + +func main() { + // The client is a heavyweight object that should be created once per process. + c, err := client.Dial(envconfig.MustLoadDefaultClientOptions()) + if err != nil { + log.Fatalln("Unable to create client", err) + } + defer c.Close() + + workflowOptions := client.StartWorkflowOptions{ + ID: "google-adk_workflowID", + TaskQueue: adk.TaskQueue, + } + + // @@@SNIPSTART googleadk-hello-starter + question := "What's the weather in San Francisco?" + we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, adk.AgentWorkflow, question) + if err != nil { + log.Fatalln("Unable to execute workflow", err) + } + + log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID()) + + // Synchronously wait for the workflow completion. + var answer string + if err := we.Get(context.Background(), &answer); err != nil { + log.Fatalln("Unable to get workflow result", err) + } + log.Println("Agent answer:", answer) + // @@@SNIPEND +} diff --git a/googleadk/worker/main.go b/googleadk/worker/main.go new file mode 100644 index 00000000..d001ad9b --- /dev/null +++ b/googleadk/worker/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "context" + "log" + + "go.temporal.io/sdk/activity" + "go.temporal.io/sdk/client" + "go.temporal.io/sdk/contrib/envconfig" + "go.temporal.io/sdk/worker" + + "google.golang.org/adk/v2/model" + "google.golang.org/adk/v2/model/gemini" + + "go.temporal.io/sdk/contrib/googleadk" + + adk "github.com/temporalio/samples-go/googleadk" +) + +func main() { + // The client and worker are heavyweight objects that should be created once per process. + c, err := client.Dial(envconfig.MustLoadDefaultClientOptions()) + if err != nil { + log.Fatalln("Unable to create client", err) + } + defer c.Close() + + // @@@SNIPSTART googleadk-hello-worker + w := worker.New(c, adk.TaskQueue, worker.Options{}) + + w.RegisterWorkflow(adk.AgentWorkflow) + // Register GetWeather under the tool name the ActivityAsTool dispatches, so the + // agent's get_weather call resolves to this activity. + w.RegisterActivityWithOptions(adk.GetWeather, activity.RegisterOptions{Name: adk.WeatherToolName}) + + // Register the integration's model Activity. The real Gemini model lives here, + // behind the Activity boundary; the API key is read worker-side from the env + // and never crosses into the workflow. Disable the model SDK's own retries so + // Temporal's RetryPolicy is the single source of truth. + acts, err := googleadk.NewActivities(googleadk.Config{ + Models: map[string]googleadk.ModelFactory{ + adk.ModelName: func(ctx context.Context, name string) (model.LLM, error) { + // nil config reads GEMINI_API_KEY / GOOGLE_API_KEY from the env. + return gemini.NewModel(ctx, name, nil) + }, + }, + }) + if err != nil { + log.Fatalln("Unable to build googleadk activities", err) + } + acts.Register(w) + + if err := w.Run(worker.InterruptCh()); err != nil { + log.Fatalln("Unable to start worker", err) + } + // @@@SNIPEND +} diff --git a/googleadk/workflow.go b/googleadk/workflow.go new file mode 100644 index 00000000..7793e76b --- /dev/null +++ b/googleadk/workflow.go @@ -0,0 +1,117 @@ +// Package adk demonstrates running a Google ADK (adk-go) agent durably on +// Temporal with the go.temporal.io/sdk/contrib/googleadk integration. The agent's +// orchestration loop runs inside the workflow; the model call runs as a Temporal +// Activity, and the get_weather tool is an existing Temporal activity exposed to +// the agent via googleadk.ActivityAsTool. +package adk + +import ( + "context" + + "go.temporal.io/sdk/workflow" + + "google.golang.org/adk/v2/agent" + "google.golang.org/adk/v2/agent/llmagent" + "google.golang.org/adk/v2/runner" + "google.golang.org/adk/v2/session" + "google.golang.org/adk/v2/tool" + "google.golang.org/genai" + + "go.temporal.io/sdk/contrib/googleadk" +) + +const ( + // TaskQueue is the task queue the worker listens on and the starter targets. + TaskQueue = "google-adk" + + // ModelName is the Gemini model the agent uses. The workflow only ships this + // name; the real model is reconstructed worker-side by the ModelFactory the + // worker registers (see worker/main.go). + ModelName = "gemini-2.0-flash" + + // WeatherToolName is both the tool name the model sees and the Activity name + // the tool dispatches, so the worker must register GetWeather under this name. + WeatherToolName = "get_weather" +) + +// GetWeatherInput is the argument schema the model fills in for the weather tool. +type GetWeatherInput struct { + City string `json:"city"` +} + +// GetWeatherOutput is the weather tool's result, handed back to the model. +type GetWeatherOutput struct { + City string `json:"city"` + Conditions string `json:"conditions"` +} + +// GetWeather is an ordinary Temporal activity. Exposed to the agent as a tool via +// googleadk.ActivityAsTool, it runs durably worker-side (retried, timed-out, +// visible in the UI) whenever the model calls it — the recommended pattern for a +// tool that does I/O. A real implementation would call a weather API here. +// @@@SNIPSTART googleadk-hello-tool +func GetWeather(ctx context.Context, in GetWeatherInput) (GetWeatherOutput, error) { + return GetWeatherOutput{City: in.City, Conditions: "sunny, 72°F"}, nil +} + +// @@@SNIPEND + +// AgentWorkflow runs a native ADK agent durably. The model call inside r.Run is +// dispatched to the InvokeModel Activity by googleadk.NewModel, and the +// get_weather tool call is dispatched to the GetWeather Activity by +// googleadk.ActivityAsTool. +// @@@SNIPSTART googleadk-hello-workflow +func AgentWorkflow(ctx workflow.Context, question string) (string, error) { + weatherTool, err := googleadk.ActivityAsTool(GetWeather, googleadk.ActivityToolOptions{ + Name: WeatherToolName, + Description: "Get the current weather for a city.", + }) + if err != nil { + return "", err + } + + // Build the agent the ordinary ADK way. NewModel is a model.LLM that carries + // only the model name in-workflow; the real Gemini client lives worker-side. + root, err := llmagent.New(llmagent.Config{ + Name: "assistant", + Description: "a helpful weather assistant", + Model: googleadk.NewModel(ModelName), + Instruction: "Answer the user's question. Use the get_weather tool when asked about the weather.", + Tools: []tool.Tool{weatherTool}, + }) + if err != nil { + return "", err + } + + r, err := runner.New(runner.Config{ + AppName: "weather", + Agent: root, + SessionService: session.InMemoryService(), + AutoCreateSession: true, + }) + if err != nil { + return "", err + } + + // NewContext bridges the workflow.Context into the context ADK reads its + // determinism/executor seams from. Pass it straight to Run. + adkCtx := googleadk.NewContext(ctx) + msg := genai.NewContentFromText(question, genai.RoleUser) + + var answer string + for ev, err := range r.Run(adkCtx, "user-1", "session-1", msg, agent.RunConfig{}) { + if err != nil { + return "", err + } + if ev != nil && ev.Content != nil { + for _, p := range ev.Content.Parts { + if p != nil && p.Text != "" { + answer = p.Text + } + } + } + } + return answer, nil +} + +// @@@SNIPEND diff --git a/googleadk/workflow_test.go b/googleadk/workflow_test.go new file mode 100644 index 00000000..28cbfab9 --- /dev/null +++ b/googleadk/workflow_test.go @@ -0,0 +1,53 @@ +package adk_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.temporal.io/sdk/activity" + "go.temporal.io/sdk/testsuite" + + "google.golang.org/adk/v2/model" + + "go.temporal.io/sdk/contrib/googleadk" + + adk "github.com/temporalio/samples-go/googleadk" +) + +// TestAgentWorkflow drives AgentWorkflow through a scripted FakeModel — no live +// LLM. The model first calls the get_weather tool, then answers with text; the +// test asserts the tool ran (as a real activity) and its result reached the +// answer. +func TestAgentWorkflow(t *testing.T) { + var s testsuite.WorkflowTestSuite + env := s.NewTestWorkflowEnvironment() + + env.RegisterWorkflow(adk.AgentWorkflow) + env.RegisterActivityWithOptions(adk.GetWeather, activity.RegisterOptions{Name: adk.WeatherToolName}) + + // Scripted model: turn 1 requests get_weather, turn 2 produces the final text. + fm := googleadk.NewFakeModel( + googleadk.FunctionCallResponse("call-1", adk.WeatherToolName, map[string]any{"city": "San Francisco"}), + googleadk.TextResponse("It's sunny, 72°F in San Francisco."), + ) + acts, err := googleadk.NewActivities(googleadk.Config{ + Models: map[string]googleadk.ModelFactory{ + adk.ModelName: func(context.Context, string) (model.LLM, error) { return fm, nil }, + }, + }) + require.NoError(t, err) + // Register the integration's InvokeModel activity by its stable name. + env.RegisterActivityWithOptions(acts.InvokeModel, activity.RegisterOptions{Name: googleadk.InvokeModelActivityName}) + + env.ExecuteWorkflow(adk.AgentWorkflow, "What's the weather in San Francisco?") + + require.True(t, env.IsWorkflowCompleted()) + require.NoError(t, env.GetWorkflowError()) + + var answer string + require.NoError(t, env.GetWorkflowResult(&answer)) + assert.Contains(t, answer, "sunny") +} diff --git a/workflowcheck.config.yaml b/workflowcheck.config.yaml new file mode 100644 index 00000000..0000d9a6 --- /dev/null +++ b/workflowcheck.config.yaml @@ -0,0 +1,16 @@ +# Configuration for workflowcheck (go.temporal.io/sdk/contrib/tools/workflowcheck), +# run by the Makefile's workflowcheck target. +# +# The functions below are ones the checker cannot statically prove +# deterministic but that are deterministic by construction. The googleadk +# samples drive Google ADK agents in-workflow through +# go.temporal.io/sdk/contrib/googleadk, whose NewContext installs +# Temporal-deterministic time/UUID/task-fan-out providers and dispatches model +# calls and I/O tools as Activities; the ADK internals the checker flags +# (channels, map iteration, reflection) run on the workflow dispatcher with +# those seams. Determinism is replay-tested in the contrib package. +decls: + "(*google.golang.org/adk/v2/runner.Runner).Run": false + "go.temporal.io/sdk/contrib/googleadk.ExportSession": false + "go.temporal.io/sdk/contrib/googleadk.PendingConfirmations": false + "google.golang.org/adk/v2/tool/functiontool.New": false