Skip to content

workflow: set a run's attributes from a workflow or a step - #303

Merged
fantix merged 6 commits into
mainfrom
fantix/set-attributes
Aug 26, 2026
Merged

workflow: set a run's attributes from a workflow or a step#303
fantix merged 6 commits into
mainfrom
fantix/set-attributes

Conversation

@fantix

@fantix fantix commented Aug 15, 2026

Copy link
Copy Markdown
Member

This PR adds 3 new public APIs:

  1. vercel.workflow.set_attributes() writes plaintext user key/value attributes onto the run
  2. vercel.workflow.remove_attributes() drops attributes by keys
  3. vercel.workflow.Run.attributes() fetches the current attributes of the run

1 and 2 can only be called within a workflow run or a step, while 3 is only available on the client-side. In other words, workflows and steps themselves can only write attributes, for the caller to read.

For example, a workflow (or a step):

from vercel.workflow import remove_attributes, set_attributes

@app.workflow
async def renew_subscription(customer_id: str) -> None:
    await set_attributes(customer=customer_id, phase="charging")
    await charge_customer(customer_id)
    await set_attributes(phase="done")
    await remove_attributes("customer")

And the caller:

from vercel.workflow import start

run = await start(renew_subscription, "cus_123")
await run.return_value()

assert await run.attributes() == {"phase": "done"}

@fantix
fantix marked this pull request as draft August 15, 2026 17:48
@fantix
fantix force-pushed the fantix/set-attributes branch from d7a8ce6 to a6e3005 Compare August 20, 2026 03:45
@fantix
fantix changed the base branch from main to fantix/retryable-error August 20, 2026 03:46
Base automatically changed from fantix/retryable-error to main August 24, 2026 16:24
@fantix
fantix force-pushed the fantix/set-attributes branch from a6e3005 to 1b96fc9 Compare August 24, 2026 16:24
if not context.suspensions and events_created:
# We captured a _SuspendException but there is no suspension - this is likely caused
# by a disposed hook that cleared its suspensions. Just retry if event log changed.
if attributes_written or (not context.suspensions and events_created):

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

attributes_written is future-insufficient; AttrSetEvent is not the only event that - once written - is immediately eligible for another in-process replay. HookConflictEvent, StepFailedEvent are examples (each leading to a separate bug) of this branch. I shall promote attributes_written into a replay_ready flag or even a FlushResult class in a following PR.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #327

Comment thread src/vercel-workflow/vercel/workflow/_internal/worlds/local.py Outdated
Comment on lines +11 to +12
ATTRIBUTE_KEY_MAX_LENGTH = 256
"""Max length of an attribute key, in UTF-16 code units."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hate JS.

@fantix
fantix requested a deployment to ci August 26, 2026 15:38 — with GitHub Actions Abandoned
@fantix
fantix requested a deployment to ci August 26, 2026 15:38 — with GitHub Actions Abandoned
@fantix
fantix requested a deployment to ci August 26, 2026 15:38 — with GitHub Actions Abandoned
@fantix
fantix requested a deployment to ci August 26, 2026 15:38 — with GitHub Actions Abandoned
@fantix
fantix requested a deployment to ci August 26, 2026 15:38 — with GitHub Actions Abandoned
@fantix
fantix merged commit 0bbad70 into main Aug 26, 2026
14 checks passed
@fantix
fantix deleted the fantix/set-attributes branch August 26, 2026 18:40
@scotttrinh scotttrinh mentioned this pull request Aug 26, 2026
scotttrinh added a commit that referenced this pull request Aug 26, 2026
vercel-internal-core
--------------------

0.1.3 - 2026-08-26
------------------

Internal
--------

- Support disabling HTTP timeouts for selected SDK operations while preserving the client default elsewhere. (#307)

vercel-connect
--------------

0.1.1 - 2026-08-26
------------------

- Update dependencies.

vercel-queue
------------

0.8.1 - 2026-08-26
------------------

Documentation
-------------

- Remove documentation and examples for `asgi_app` in preparation for its removal. (#309)

vercel-sandbox
--------------

0.5.0 - 2026-08-26
------------------

Features
--------

- Add sync and async `fork_sandbox(...)` support for creating a sandbox from an existing named sandbox with optional configuration overrides. (#257)
- Add `region` and `failover_regions` configuration for sandbox creation, forks, and updates, plus multi-region snapshot availability reporting. (#308)

Bug Fixes
---------

- Allow Sandbox process waits and log streams to remain idle longer than the session HTTP timeout. (#307)

vercel-workflow
---------------

0.10.0 - 2026-08-26
-------------------

Breaking Changes
----------------

- Use type annotations on workflows and step to allow passing Pydantic models and dataclasses. (#317)
- This is a breaking change, because type annotations will now be enforced. Passing a `dict` when the declaration expects a `list` will fail. (#317)
- Pydantic models and dataclasses can no longer be passed to `@serializable` or `register_serializable()`. Annotate the workflow or step parameter or return value with their type instead. (#317)

Features
--------

- `get_workflow_metadata()` returns the current run's `WorkflowInfo` (run id, workflow name, start time, deployment URL, and feature flags), callable from a workflow body or a step body — mirroring the JS SDK's `getWorkflowMetadata()`. (#320)
- One current limitation is that `started_at` is `None` from inside a step. (#320)
- `BaseHook.wait()` accepts `metadata` to record on the hook, and `get_hook_by_token()` reads it back for a resumer. (#301)
- A step can raise `RetryableError` to control when its next attempt runs. (#302)
- Accept `specVersion` 7 sealed noop event logs. (#319)
- A workflow or step can attach plaintext metadata to its run with `set_attributes()`. (#303)
- Add a `share_sandboxes` parameter to `SandboxPolicy` to enable reusing already created sandboxes instead of creating a new one on each invocation. This speeds up workflows but means that modifications to global state may persist between invocations. (#310)
- Expose unstable API to serve workflow HTTP endpoint from your own web framework. (#294)
- Added semi-internal manifest API for TS tools and e2e test. (#296)

Bug Fixes
---------

- Fix failing or even crashing cipher calls inside the workflow sandbox. (#305)
- Support resuming hooks with payload in the queue message. (#300)
- Fixed nulls rejected by server, requiring Pydantic 2.12 or newer. (#321)
- Fixed workflow and step calls with both positional-or-keyword parameters and `*args` failing during replay because their arguments were recorded in an unbindable shape. (#312)

Internal
--------

- Construct the protocol models by Python field name. (#322)

vercel
------

0.11.0 - 2026-08-26
-------------------

Features
--------

- Expose `get_deadline()` for reading the current Function invocation deadline. (#306)
- Answer workflow health checks for both queue-based transport and HTTP. (#292)
- Add support to read the sealed (`encp`) workflow payloads (X25519 + AES-GCM) an outside writer addresses to a run, under the `encryption` extra. (#297)

Bug Fixes
---------

- Remove upper bounds on aggregate Sandbox and Workflow dependencies so sibling releases cannot make the `vercel` package un-installable. (#334)
- Start a workflow run even when its queue message arrives before the `run_created` event has landed. (#284)

Internal
--------

- The Workflows implementation now ships in the separate `vercel-workflow` distribution, which `vercel` depends on, so `vercel.workflow` imports keep working without installing anything extra. (#299)

vercel-apscheduler
------------------

0.3.0 - 2026-08-26
------------------

Breaking Changes
----------------

- The managed Redis backend was removed. The integration now always runs on its managed job store (Vercel Runtime Cache); a configured default `RedisJobStore` is rejected at import, `VERCEL_APSCHEDULER_BACKEND` accepts only `cache`, and the `redis` dependency is gone. The scheduler's durable identity now always derives from the builder-assigned subscriber id (previously the Redis `jobs_key`); the `scheduler_id` option still pins an identity explicitly. (#286)

vercel-celery
-------------

0.7.5 - 2026-08-26
------------------

- Update dependencies.

vercel-django-tasks
-------------------

0.7.0 - 2026-08-26
------------------

Features
--------

- Add a Vercel Queues backend for Django Tasks and use it by default when no task backends are configured. (#291)

vercel-dramatiq
---------------

0.7.4 - 2026-08-26
------------------

- Update dependencies.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants