Jeandre de Bruyn
12/10/2025, 6:15 AMMarvin
12/10/2025, 6:16 AMMarvin
12/10/2025, 6:19 AMkey)
- Works with matplotlib and most libraries that can save to PNG
from prefect import flow, task
from prefect.artifacts import create_markdown_artifact
import io, base64
import matplotlib.pyplot as plt
@task
def plot_as_markdown():
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 9])
ax.set_title("Demo plot")
buf = io.BytesIO()
fig.savefig(buf, format="png", dpi=120, bbox_inches="tight")
plt.close(fig)
buf.seek(0)
b64 = base64.b64encode(buf.read()).decode()
md = f"!<data:image/png;base64,{b64}|plot>"
create_markdown_artifact(
markdown=md,
key="demo-plot", # lowercase, numbers, dashes only
description="Matplotlib figure embedded as Markdown"
)
@flow
def demo_flow():
plot_as_markdown()
if __name__ == "__main__":
demo_flow()
2) Image artifact (requires a public URL)
- Use create_image_artifact(image_url=...)
- You’ll need to host the image (e.g., S3 with public-read, static web server)
- Then pass the public URL to the artifact
from prefect import flow, task
from prefect.artifacts import create_image_artifact
import io
import matplotlib.pyplot as plt
import boto3
@task
def plot_and_publish():
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 9])
buf = io.BytesIO()
fig.savefig(buf, format="png")
plt.close(fig)
buf.seek(0)
# Upload to a publicly accessible location (example: S3)
s3 = boto3.client("s3")
bucket, key = "my-public-bucket", "plots/demo.png"
s3.upload_fileobj(
buf, bucket, key,
ExtraArgs={"ContentType": "image/png", "ACL": "public-read"}
)
url = f"https://{bucket}.<http://s3.amazonaws.com/{key}|s3.amazonaws.com/{key}>"
create_image_artifact(
image_url=url,
key="demo-plot",
description="Matplotlib figure hosted on S3"
)
@flow
def demo_flow():
plot_and_publish()
Notes
- The key is important if you want the artifact to show up on the global Artifacts page; it must be lowercase letters, numbers, and dashes.
- Without a key, you’ll still see the artifact under that run’s Artifacts tab.
- If you have an interactive Plotly chart, host the HTML and use a link artifact:
from prefect.artifacts import create_link_artifact
create_link_artifact(link=html_url, link_text="Interactive chart", key="plotly-demo")
Where to view in the UI
- Flow/Task run details → Artifacts tab
- Global Artifacts page (requires key) to see the latest version and history of that artifact
Docs
- Concepts: Artifacts concepts
- How-to: Create artifacts
- API: prefect.artifacts API
Would you prefer the Markdown-embedded route (no hosting) or hosting images (e.g., S3)? Also, which plotting library are you using?