Pydantic AI Workspace sandboxes: fetch Sume artifacts
Pydantic AI's Workspace abstraction runs tools locally or in sandboxes. Inside a sandbox, fetch Sume artifact URLs, and give Sume inputs as public HTTPS URLs.

Pydantic AI's releases for September and October 2026 describe a new Workspace abstraction so tools work locally or in sandboxes such as E2B, Modal and Sprites. The Sume side is simple: media you send to Sume must be a public HTTPS URL, so a file that exists only inside a sandbox cannot be an input. Results come back as Sume media URLs you can download.
What the release says
From the Pydantic AI releases page, read on 2026-10-03: a Workspace abstraction lets the same tools run locally or in a sandbox, with E2B, Modal and Sprites named. See the release notes for the API; this post uses no Pydantic AI code.
Inputs: public HTTPS only
Sume generation requests accept media as public HTTPS URLs. Localhost, private-network, non-HTTPS, signed or private URLs and mismatched content types are rejected before submission. A path such as /workspace/clip.mp4, or a URL that only resolves inside the sandbox, will be refused.
| Input location | Accepted? |
|---|---|
| Public HTTPS URL | Yes |
| Sume-hosted media.sume.com artifact | Yes |
| localhost or private network | No, rejected |
| Signed or private URL | No, rejected |
To get a sandbox file to Sume, host it at a public HTTPS location first, or use the asset flow: sume assets upload-url on the CLI, or assets_upload_url then assets_complete on MCP. Hosted MCP cannot read files from your laptop, and the same is true of a remote sandbox.
Outputs: download from the result
Completed jobs return artifact objects whose url is a Sume-owned media URL. Sume mirrors generated outputs there before exposing them, and the docs tell integrations to store the Sume URL, not raw provider URLs. Treat the URL as opaque.
import requests
def fetch_artifact(job_id: str, api_key: str, dest: str) -> str:
headers = {"Authorization": f"Bearer {api_key}"}
r = requests.get(f"https://api.sume.com/v1/jobs/{job_id}/result",
headers=headers, timeout=30)
r.raise_for_status()
url = r.json()["result"]["artifacts"][0]["url"]
data = requests.get(url, timeout=120)
data.raise_for_status()
with open(dest, "wb") as f:
f.write(data.content)
return destKeep the key out of the sandbox
Prefer to call Sume from the host process and pass only the artifact URL or file into the sandbox. If the sandbox must call the API, give it a key scoped to what it needs, and never log the key or signed URLs.
Sources
Related posts
More in Developers
- pytest and respx: fake a Sume job that is queued, then completed
Test your Sume poll loop with respx: a queued, a processing and a completed response in order, with sleep injected so the test finishes in milliseconds.
- Python 3.10 is end of life: a stdlib Sume webhook verifier
Python 3.10 has reached end of life. A standard-library verifier for Sume's signed webhooks that refuses an empty secret and accepts rotated signatures.
- Python 3.14.8 urllib credential fix: a stdlib Sume job poll script
Python 3.14.8 fixed urllib HTTPPasswordMgr handing credentials across schemes. Upgrade, and call Sume with an x-api-key header from a plain stdlib script.
- Log x-sume-request-id on every Sume call in Python
Capture the x-sume-request-id response header in a requests hook, attach it to each log line with logging.Filter and contextvars, and quote it to support.
Written by Sume