Artifacts
Artifact references, the clients that retrieve their bytes, and helpers for tools that produce files.
#Artifacts
Files that tools produce during a run, such as a DOCX report or a PNG chart, come back as immutable artifact references. This page covers the reference models, the two clients that retrieve the bytes, and the helpers for tools that produce files.
#How artifacts reach you
A completed run returns its generated files on response.artifacts, a tuple of ArtifactRef values. The same references ride on the assistant message, and one message carries at most 32.
Every reference names one revision of one file:
artifact_ididentifies the revision. Editing a file creates a new revision with a newartifact_id.logical_output_idstays the same across revisions of the same output.revisionstarts at 1. Revision 2 and later setsupersedes_artifact_idto the revision before it.sha256andsize_bytes(ordinary references) let you check the bytes you download.
A filename does not identify a file. Use logical_output_id to follow an output across revisions.
There are two custody types, told apart by the custody field:
ArtifactRef is the union of the two, discriminated on custody. Use pydantic.TypeAdapter(ArtifactRef) to validate a stored reference of either kind.
from maivn import ArtifactRef, OrdinaryArtifactRef, PrivateArtifactRef#OrdinaryArtifactRef
#PrivateArtifactRef
A private reference describes a file held in the private vault. It carries no filename or content.
#Ordinary files: client.artifacts
client.artifacts lists, reads and downloads ordinary artifacts for the project your API key belongs to. Each method has an async twin with an a prefix.
from pathlib import Path
from maivn import Client
client = Client(api_key='...')
page = client.artifacts.list(limit=20)
for item in page.artifacts:
print(item.artifact_id, item.revision, item.display_filename, item.byte_size)
Path('downloads').mkdir(exist_ok=True)
for ref in response.artifacts:
if ref.custody == 'ordinary':
saved = client.artifacts.download_to(ref, Path('downloads') / ref.display_filename)
print(saved.filename, saved.size_bytes, saved.sha256)download, download_to and the preview methods raise ValueError when the response fails verification. When you pass a reference, a different revision argument also raises ValueError, because the reference already fixes the revision.
Pagination is cursor based. Pass page.next_cursor as cursor until it is None.
#Result models
#Private files: client.private_artifacts
client.private_artifacts is a PrivateArtifactsClient. Private bytes move between your application and the same base_url as every other call. The SDK requires HTTPS for that address, with plain HTTP allowed only on the local loopback host.
For a reference you already hold, pass the reference and the session_id of the run that produced it. The client obtains the authorization and a transient retrieval proof for you.
from pathlib import Path
Path('downloads').mkdir(exist_ok=True)
for index, ref in enumerate(response.artifacts):
if ref.custody == 'vault_private':
path = client.private_artifacts.download_to(
ref,
Path('downloads') / f'private-{index}.bin',
session_id=response.session_id,
)Each method has an async twin with an a prefix. RedactedExportReceipt and PrivateWorkIntentStatus live in maivn_contracts.artifacts.
export_redacted depends on the Vault holding a redacted source for the file, so it can fail for a file that has none. Deletion and export waits are bounded by the client's timeout_seconds; when the wait expires the call raises PrivateArtifactTimeoutError. Chunked reads accept a chunk_bytes from 1 to 65536.
#Explicit retrieval workflow
The lower-level methods split retrieval into steps. Most code does not need them.
new_retrieval_proof()returns aTransientVaultClientProof.resolve(ref, *, session_id, proof, retrieval_part='private_original')returns the authorized work. Useretrieval_part='editable_source'for the source.retrieve_stream(work, *, proof, chunk_bytes=65536)yields the bytes in ranges.aretrieve_streamis the async generator.ingest_file(work, *, proof, output, source)uploads a private file and its source, and returns the newPrivateArtifactRef.
You can also pass the resolved work to download, download_to, export_redacted and delete instead of a reference. In that form you pass proof to the download methods and omit session_id.
TransientVaultClientProof is an opaque, process-local value. Its repr is redacted, it cannot be copied or pickled, and only its digest is sent in the work request. Create a fresh one for each retrieval.
#Editing a file
Editing a generated file means asking its toolset to render a new revision from the previous one. The server rejects a stale base revision and keeps earlier downloads. To resume an edit:
- Download the portable source of the exact revision with
client.artifacts.download_source(ref, session_id=...), orclient.private_artifacts.download_source(ref, session_id=...)for a private file. - Pass the bytes and the reference to the matching toolset's
resume_artifactorresume_private_artifact.
The toolset's next render carries that revision as its expected base.
Use the session_id from the authenticated SDK run for that thread, not a Studio session identifier. The source restores mAIvn composition sources, including embedded images. It is not a general importer for Office files.
#Producing files from a tool: maivn.files
A custom toolset can return a file that the SDK publishes for you. Annotate the tool's return type as GeneratedFile or GeneratedFiles and return the result of generated_file(path).
from pathlib import Path
from maivn import toolify, toolset
from maivn.files import GeneratedFile, generated_file
@toolset
class ReportTools:
def __init__(self, output_dir: Path) -> None:
self._output_dir = output_dir
def authorized_generated_file_roots(self) -> tuple[Path, ...]:
return (self._output_dir,)
@toolify(description='Write a one-line CSV report')
def write_report(self, title: str) -> GeneratedFile:
path = self._output_dir / 'report.csv'
path.write_text(f'title\n{title}\n', encoding='utf-8')
return generated_file(path)The toolset must define authorized_generated_file_roots() and return the directories the SDK may read from. A file outside those roots is refused.
Notes on generated_file:
- The snapshot is written under a
.maivn-generatedfolder next to your file. Your original file is never changed or removed. .docx,.pdf,.xlsx,.pptx,.pngand.jpgor.jpegkeep their typed checks. Every other extension is stored asapplication/octet-streamwith no inline preview.custody='vault_private'routes the file to the private vault. Passexpected_basefor an ordinary revision orprivate_expected_basefor a private one when you are producing the next revision. A single file cannot set both.- Pass
sourceand asource_formatsuch as'acme-sheet/1'together to attach editable source that only your own versioned adapter reads.source_formatwithoutsourceraisesValueError.
Private output needs a compatible private-file service and a configured retention policy. External webhook tool callbacks refuse private artifact references.
#Image inputs for toolsets: maivn.artifact_images
Toolsets that compose images, such as a document or presentation builder, receive the bytes of images the run already selected. They read them through maivn.artifact_images.
from maivn.artifact_images import (
ArtifactImageUnavailableError,
ResolvedArtifactImage,
resolve_artifact_image,
)await resolve_artifact_image(artifact_id) returns a ResolvedArtifactImage with content (bytes), artifact_ref, mime_type and private (whether the source was private). It only resolves images selected by the current invocation's trusted references. Outside a run, or for an image that is not selected, it raises ArtifactImageUnavailableError, a ValueError with a fixed message.
Keep these bytes local. Do not return them as a tool result, because the model would see them. Return an opaque handle for the composed file instead.
#Errors
Private retrieval and lifecycle calls raise PrivateArtifactError or one of its subclasses. The messages are fixed and carry no file content. Branch on the class or on exc.code.
Ordinary retrieval raises MaivnHTTPError for non-2xx responses and ValueError when verification fails. See Errors and exceptions.
#See also
- Client: where
client.artifactsandclient.private_artifactslive - Shared Data Models: the
InvokeResponse.artifactsfield - Private Data guide: the private-data store and redaction controls