From 9ad927d269c86f5f245b18497ca675a7dccf75d6 Mon Sep 17 00:00:00 2001 From: Ryan Tinianov Date: Tue, 24 Feb 2026 10:41:54 -0500 Subject: [PATCH] Add a hook to tag workflow SDK discovery. This will be used to track what SDKs are used --- pkg/workflows/wasm/host/module.go | 10 +++++++++ pkg/workflows/wasm/host/module_test.go | 30 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/pkg/workflows/wasm/host/module.go b/pkg/workflows/wasm/host/module.go index 493d209031..01f66eacea 100644 --- a/pkg/workflows/wasm/host/module.go +++ b/pkg/workflows/wasm/host/module.go @@ -79,6 +79,10 @@ type ModuleConfig struct { // Labeler is used to emit messages from the module. Labeler custmsg.MessageEmitter + // SdkLabeler is called with the discovered v2 import name after module creation. + // If nil, it defaults to a no-op. Used to add metrics labels (e.g. sdk=name). + SdkLabeler func(string) + // If Determinism is set, the module will override the random_get function in the WASI API with // the provided seed to ensure deterministic behavior. Determinism *DeterminismConfig @@ -174,6 +178,10 @@ func NewModule(ctx context.Context, modCfg *ModuleConfig, binary []byte, opts .. modCfg.Labeler = &unimplementedMessageEmitter{} } + if modCfg.SdkLabeler == nil { + modCfg.SdkLabeler = func(string) {} + } + if modCfg.TickInterval == 0 { modCfg.TickInterval = defaultTickInterval } @@ -307,6 +315,8 @@ func newModule(modCfg *ModuleConfig, binary []byte) (*module, error) { } } + modCfg.SdkLabeler(v2ImportName) + return &module{ engine: engine, module: mod, diff --git a/pkg/workflows/wasm/host/module_test.go b/pkg/workflows/wasm/host/module_test.go index ca5cfa547e..af5d4fc12c 100644 --- a/pkg/workflows/wasm/host/module_test.go +++ b/pkg/workflows/wasm/host/module_test.go @@ -3,6 +3,7 @@ package host import ( "context" "encoding/binary" + "strings" "sync" "testing" @@ -587,6 +588,35 @@ func Test_toEmissible(t *testing.T) { }) } +func Test_SdkLabeler(t *testing.T) { + t.Run("defaults to no-op when nil", func(t *testing.T) { + // ModuleConfig with nil SdkLabeler should not panic when creating a module + binary := createTestBinary(successBinaryCmd, successBinaryLocation, true, t) + mc := &ModuleConfig{ + Logger: logger.Test(t), + IsUncompressed: true, + Fetch: func(context.Context, *FetchRequest) (*FetchResponse, error) { return &FetchResponse{}, nil }, + } + _, err := NewModule(t.Context(), mc, binary) + require.NoError(t, err) + require.NotNil(t, mc.SdkLabeler, "SdkLabeler should be set to no-op") + }) + + t.Run("is called with v2ImportName after discovery", func(t *testing.T) { + binary := createTestBinary(nodagRandomBinaryCmd, nodagRandomBinaryLocation, true, t) + var capturedName string + mc := defaultNoDAGModCfg(t) + mc.SdkLabeler = func(name string) { + capturedName = name + } + m, err := NewModule(t.Context(), mc, binary) + require.NoError(t, err) + require.False(t, m.IsLegacyDAG(), "expected NoDAG module") + require.NotEmpty(t, capturedName, "SdkLabeler should have been called with v2 import name") + require.True(t, strings.HasPrefix(capturedName, v2ImportPrefix), "captured name should have v2 prefix") + }) +} + // CallAwaitRace validates that every call can be awaited. func Test_CallAwaitRace(t *testing.T) { ctx := t.Context()