Skip to content

Bind Python temporal and UUID arguments to their native Go types - #71809

Draft
jason810496 wants to merge 2 commits into
apache:mainfrom
jason810496:feature/go-sdk/temporal-uuid-arg-binding
Draft

Bind Python temporal and UUID arguments to their native Go types#71809
jason810496 wants to merge 2 commits into
apache:mainfrom
jason810496:feature/go-sdk/temporal-uuid-arg-binding

Conversation

@jason810496

@jason810496jason810496 commented Aug 19, 2026

Copy link
Copy Markdown
Member

Why

Stub annotations already carry temporal and UUID formats, but Go tasks receive the values as strings and must parse them again.

What

Bind each JSON Schema format to its native Go type:

Python annotationwire formatGo parameter type
datetime, pendulum.DateTimedate-timetime.Time
datedatetime.Time (midnight)
timetimetime.Time (zero date)
timedelta, pendulum.Durationdurationtime.Duration
UUIDuuiduuid.UUID
// The stub is annotated (when: datetime, window: timedelta, trace_id: UUID)// and called with the formatted strings those annotations imply.funcViaTemporalArgs(
ctx sdk.TIRunContext, log*slog.Logger,
when time.Time, window time.Duration, traceID uuid.UUID,
) (any, error)
  • Convert nested slices, maps, and struct fields recursively, with path-aware decode errors.
  • Reject incompatible Go types before the task runs. Nullable values require nil-capable types.
  • Keep plain string and custom UnmarshalJSON types as escape hatches.
  • Parse fixed ISO 8601 durations and reject variable-length year or month components.

Values cross runtimes in JSON form. The example currently passes formatted strings; native Python datetime, timedelta, and UUID call arguments require the separate #71536 Core change.


Was generative AI tooling used to co-author this PR?
  • Yes, with help of Claude Code (Opus 5) and Codex (GPT-5) following the guidelines

Drafted-by: Codex (GPT-5) (no human review before posting)

A Dag author who annotates a stub parameter `datetime`, `timedelta` or `UUID`
means a moment, a span or an identifier -- not the string those become on the
wire. Without this the Go task receives that string and has to parse it itself,
re-deriving a contract the annotation already stated, and getting it wrong in
ways the runtime could have caught.
The annotation is what pydantic turns into the JSON-schema `format` the
argument spec carries, so it is also what decides which Go type can receive the
value: `date-time`/`date`/`time` fill a time.Time, `duration` a time.Duration,
`uuid` a uuid.UUID. Reading it as the contract means a Go parameter the declared
Python type cannot fill now fails before the task body runs rather than binding
something the author never meant -- including a plain int declared as a
time.Duration, which would otherwise be read as a count of nanoseconds.
These values are rewritten into a spelling encoding/json can read by one walk
over the target type, so a duration is reached wherever it sits: the argument
itself, a slice element, a map value or a struct field. A single walk is what
keeps those cases consistent; reaching only the top level would leave a
`list[timedelta]` or a struct's `timedelta` field failing against a schema that
describes them perfectly well.
@jason810496
jason810496force-pushed the feature/go-sdk/temporal-uuid-arg-binding branch from b3a0bf5 to e3abe8cCompareAugust 21, 2026 03:16
containsNativeSeen omitted uuidType, so containers of uuid.UUID
(slices, maps, struct fields) skipped format validation entirely —
a schema declaring the wrong format on a nested UUID was silently
accepted. Add uuidType to the identity check and add an early
return in normalizeNative for native types that are self-decoding
(uuid.UUID via TextUnmarshaler) so they do not fall through to the
container-recursion switch.
Cache containsNative and nativeStructFields results in sync.Maps
keyed by reflect.Type — both depend only on static type information
but were recomputed from scratch on every Resolve call.
Restore a test case for anyOf(string, null) → *string that was
inadvertently dropped when the nullable matrix cases were changed
to exercise temporal types.
@jason810496
jason810496force-pushed the feature/go-sdk/temporal-uuid-arg-binding branch from e3abe8c to 0342c29CompareAugust 21, 2026 06:14
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jason810496