Add artifact trigger automation: TYPE_ARTIFACT + ArtifactTrigger - #7749
Conversation
Third TriggerAutomationSpec automation type: fire a run when a new version of the named artifact is created. ArtifactTrigger carries the watched artifact name, an optional exact-version pin, and the task input the artifact's value binds to at fire time (mirroring Schedule.kickoff_time_input_arg). Also adds RUN_SOURCE_ARTIFACT_TRIGGER so artifact-triggered runs carry their provenance. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR extends Flyte’s IDL surface to represent artifact-triggered automations (triggering a run when a new version of an artifact is created) and to record run provenance for artifact-triggered runs, then regenerates Go/Python/TS bindings accordingly.
Changes:
- Add
TriggerAutomationSpecType.TYPE_ARTIFACT = 3plusArtifactTrigger{artifact_name, version, input_arg}underTriggerAutomationSpec.automation(oneof). - Add
RunSource.RUN_SOURCE_ARTIFACT_TRIGGER = 4to identify artifact-triggered runs. - Regenerate Go/Python/TS generated code for the updated protos.
Reviewed changes
Copilot reviewed 6 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| flyteidl2/task/common.proto | Introduces ArtifactTrigger message and TYPE_ARTIFACT automation type; extends TriggerAutomationSpec oneof. |
| flyteidl2/workflow/run_definition.proto | Adds RUN_SOURCE_ARTIFACT_TRIGGER to RunSource. |
| gen/go/flyteidl2/task/common.pb.go | Regenerated Go bindings for the new ArtifactTrigger message and updated oneof/enum wiring. |
| gen/go/flyteidl2/task/common.pb.validate.go | Regenerated validation stubs for ArtifactTrigger and updated oneof validation handling. |
| gen/go/flyteidl2/workflow/run_definition.pb.go | Regenerated Go bindings for the new RunSource enum value. |
| gen/python/flyteidl2/task/common_pb2.py | Regenerated Python runtime module for new automation type/message. |
| gen/python/flyteidl2/task/common_pb2.pyi | Regenerated Python type stubs for new automation type/message. |
| gen/python/flyteidl2/workflow/run_definition_pb2.py | Regenerated Python runtime module for new RunSource value. |
| gen/python/flyteidl2/workflow/run_definition_pb2.pyi | Regenerated Python type stubs for new RunSource value. |
| gen/ts/flyteidl2/task/common_pb.ts | Regenerated TS bindings for ArtifactTrigger, updated oneof union, and new enum value. |
| gen/ts/flyteidl2/workflow/run_definition_pb.ts | Regenerated TS bindings for the new RunSource enum value. |
Files not reviewed (5)
- gen/go/flyteidl2/task/common.pb.go: Generated file
- gen/go/flyteidl2/task/common.pb.validate.go: Generated file
- gen/go/flyteidl2/workflow/run_definition.pb.go: Generated file
- gen/python/flyteidl2/task/common_pb2.py: Generated file
- gen/python/flyteidl2/workflow/run_definition_pb2.py: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func (m *ArtifactTrigger) validate(all bool) error { | ||
| if m == nil { | ||
| return nil | ||
| } | ||
|
|
||
| var errors []error | ||
|
|
||
| // no validation rules for ArtifactName | ||
|
|
||
| // no validation rules for Version | ||
|
|
||
| // no validation rules for InputArg |
…ifacts (#7760) Replaces the implicit metadata-key contract (magic "_ua"/"_uap" keys in Literal.metadata carrying JSON payloads that SDKs and backends had to keep in sync) with typed fields, split by lifecycle: - core.ArtifactVersionId (ArtifactKey + version), core.ArtifactCard (hoisted from artifact.Card, same shape), and core.ArtifactInfo (description, user_metadata, card) — the descriptive payload shared by the production declaration and the stored spec, deliberately excluding identity so the two can never drift. - core.Literal.artifact_id: the value's artifact identity, stamped once at registration and on artifact-bound inputs; travels with the value through every copy and participates in cache-key hashing by design. - task.ProducedArtifact + Outputs.produced_artifacts: the transient registration declaration (output name, artifact name, optional version, info, declared type) — an action-scoped instruction that never enters the value stream. Field 3 on Outputs because outputs.pb blobs are sometimes cross-decoded as task.Inputs, where field 2 is context. - artifact.ArtifactSpec now nests the descriptive payload as core.ArtifactInfo (value/type/source stay top-level); artifact.Card is removed in favor of core.ArtifactCard. Co-authored-by: Ketan Umare <kumare3@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 34 changed files in this pull request and generated no new comments.
Files not reviewed (14)
- gen/go/flyteidl2/artifact/artifact.pb.go: Generated file
- gen/go/flyteidl2/artifact/artifact.pb.validate.go: Generated file
- gen/go/flyteidl2/core/artifact_id.pb.go: Generated file
- gen/go/flyteidl2/core/artifact_id.pb.validate.go: Generated file
- gen/go/flyteidl2/core/literals.pb.go: Generated file
- gen/go/flyteidl2/core/literals.pb.validate.go: Generated file
- gen/go/flyteidl2/task/common.pb.go: Generated file
- gen/go/flyteidl2/task/common.pb.validate.go: Generated file
- gen/go/flyteidl2/workflow/run_definition.pb.go: Generated file
- gen/python/flyteidl2/artifact/artifact_pb2.py: Generated file
- gen/python/flyteidl2/core/artifact_id_pb2.py: Generated file
- gen/python/flyteidl2/core/literals_pb2.py: Generated file
- gen/python/flyteidl2/task/common_pb2.py: Generated file
- gen/python/flyteidl2/workflow/run_definition_pb2.py: Generated file
Suppressed comments (3)
flyteidl2/artifact/artifact.proto:96
- ArtifactSpec reuses tag number 3 with a different wire type: it used to be
string description = 3(and tags 4/5 for metadata/card), but is nowcore.ArtifactInfo info = 3. Reusing a tag with a different type breaks protobuf wire compatibility (old data will be mis-decoded, and old clients won’t understand new payloads). Consider reserving the removed tags (3–5) and addinginfounder a new, unused tag number (e.g. 7), keepingsource = 6.
// User-provided specification of an artifact.
message ArtifactSpec {
// The typed value of the artifact. Offloaded data (files, directories,
// dataframes) must already reside in blob storage; the literal references
// it by uri.
core.Literal value = 1 [(buf.validate.field).required = true];
// The Flyte type of the value.
core.LiteralType type = 2 [(buf.validate.field).required = true];
// Descriptive payload (description, user metadata, card). Shared with the
// production declaration (task.ProducedArtifact.info), so task-produced
// artifacts carry it verbatim.
core.ArtifactInfo info = 3;
// Optional provenance: what produced this artifact. Clients (including the
// SDK, which stamps this automatically when publishing from inside a
// running task) declare it at creation time; it is immutable afterwards.
ArtifactSource source = 6;
}
flyteidl2/core/artifact_id.proto:24
- ArtifactVersionId is intended to be the minimal identity for an artifact version (and is embedded in core.Literal.artifact_id), but it has no validation constraints. As written, it allows an empty
versionand a missingkey, which undermines the “identity” semantics and can propagate partially-specified IDs through cache hashing and APIs. Consider adding(buf.validate.field).required = trueonkeyand(buf.validate.field).string.min_len = 1(and possibly a max_len consistent with other version fields) onversion.
// Identity of one artifact version. Unlike the legacy ArtifactID below, this
// carries no partition machinery — it is the minimal identity stamped onto
// values (core.Literal.artifact_id) and referenced by v2 artifact APIs.
message ArtifactVersionId {
ArtifactKey key = 1;
string version = 2;
}
flyteidl2/task/common.proto:152
- ProducedArtifact’s comment says the producing SDK is authoritative for the output’s declared Flyte type and that it’s copied into ArtifactSpec.type to make the declaration self-contained, but
type = 5is not marked required (and is generated as optional in TS/Python). This allows ProducedArtifact declarations without a type, which defeats the stated self-contained contract. Consider adding(buf.validate.field).required = truetotype(and possibly clarifying whetherinfois optional) so backends/consumers can rely on the presence of the declared type.
// Declares that a named output of the producing action is a new artifact
// version. The output's literal (in Outputs.literals) is the artifact's value;
// org/project/domain come from the producing action's scope at registration.
message ProducedArtifact {
// Name of the NamedLiteral in Outputs.literals this declaration refers to.
string output = 1 [(buf.validate.field).string.min_len = 1];
// Artifact name to register under.
string name = 2 [(buf.validate.field).string.min_len = 1];
// Optional explicit version. Empty means the backend derives one
// (<run>-<action>-<attempt>).
string version = 3;
// Descriptive payload, copied verbatim into ArtifactSpec.info.
flyteidl2.core.ArtifactInfo info = 4;
// The declared Flyte type of the output, copied into ArtifactSpec.type.
// The producing SDK is authoritative for this.
flyteidl2.core.LiteralType type = 5;
}
| string artifact_name = 1 [(buf.validate.field).string.min_len = 1]; | ||
|
|
||
| // Optional: fire only when exactly this version is created. Empty means any new version. | ||
| string version = 2; |
There was a problem hiding this comment.
it this really needed?
# Conflicts: # flyteidl2/workflow/run_definition.proto # gen/go/flyteidl2/workflow/run_definition.pb.go # gen/python/flyteidl2/workflow/run_definition_pb2.py # gen/python/flyteidl2/workflow/run_definition_pb2.pyi # gen/rust/src/flyteidl2.workflow.rs # gen/ts/flyteidl2/workflow/run_definition_pb.ts
…tifact-trigger-protos
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 34 changed files in this pull request and generated no new comments.
Files not reviewed (14)
- gen/go/flyteidl2/artifact/artifact.pb.go: Generated file
- gen/go/flyteidl2/artifact/artifact.pb.validate.go: Generated file
- gen/go/flyteidl2/core/artifact_id.pb.go: Generated file
- gen/go/flyteidl2/core/artifact_id.pb.validate.go: Generated file
- gen/go/flyteidl2/core/literals.pb.go: Generated file
- gen/go/flyteidl2/core/literals.pb.validate.go: Generated file
- gen/go/flyteidl2/task/common.pb.go: Generated file
- gen/go/flyteidl2/task/common.pb.validate.go: Generated file
- gen/go/flyteidl2/workflow/run_definition.pb.go: Generated file
- gen/python/flyteidl2/artifact/artifact_pb2.py: Generated file
- gen/python/flyteidl2/core/artifact_id_pb2.py: Generated file
- gen/python/flyteidl2/core/literals_pb2.py: Generated file
- gen/python/flyteidl2/task/common_pb2.py: Generated file
- gen/python/flyteidl2/workflow/run_definition_pb2.py: Generated file
Suppressed comments (3)
gen/go/flyteidl2/task/common.pb.validate.go:629
ArtifactTrigger.artifact_nameis marked with(buf.validate.field).string.min_len = 1in the proto, but the generatedArtifactTrigger.Validate()implementation currently has no field-level validation (it explicitly says "no validation rules for ArtifactName"). If server-side request validation relies on these generatedValidate()methods, emptyartifact_namevalues could be accepted and lead to triggers that never match. Consider ensuring buf.validate rules are actually enforced in Go (e.g. via protovalidate runtime validation or by fixing the validation codegen).
flyteidl2/workflow/run_definition.proto:486- The PR description states
RunSource.RUN_SOURCE_ARTIFACT_TRIGGER = 4, but the proto definesRUN_SOURCE_LOCAL = 4and the newly addedRUN_SOURCE_ARTIFACT_TRIGGER = 5. This should be reconciled (most likely update the PR description/docs) to avoid consumers implementing the wrong numeric value.
// The run is orchestrated outside the platform (e.g. on a user's machine) and its state is
// reported via LocalRunService.
RUN_SOURCE_LOCAL = 4;
RUN_SOURCE_ARTIFACT_TRIGGER = 5;
}
gen/go/flyteidl2/task/common.pb.validate.go:1326
ProducedArtifact.outputandProducedArtifact.nameare marked with(buf.validate.field).string.min_len = 1in the proto, but the generatedProducedArtifact.Validate()does not enforce any field-level rules ("no validation rules for Output/Name"). If theseValidate()methods are used for request validation, invalid artifact declarations (empty output/name) may be accepted and fail later in registration/consumption.
Summary
Adds the third
TriggerAutomationSpecautomation type for artifact triggers: fire a run when a new version of a named artifact is created.TriggerAutomationSpecType.TYPE_ARTIFACT = 3ArtifactTrigger{artifact_name, version, input_arg}in theTriggerAutomationSpec.automationoneof —versionoptionally pins an exact version (empty = any new version);input_argnames the task input that receives the triggering artifact's value at fire time, mirroringSchedule.kickoff_time_input_argRunSource.RUN_SOURCE_ARTIFACT_TRIGGER = 4so artifact-triggered runs carry their provenanceBackend consumption lands separately (union cloud); the SDK side adds
flyte.OnArtifact(...)+ aflyte.TriggeredArtifactinput sentinel on top of these protos.🤖 Generated with Claude Code
main