OutputMeta enables usage-based pricing by reporting what your app processes and generates.
Basic Structure
1from inferencesh import BaseAppOutput, OutputMeta, TextMeta, ImageMeta, VideoMeta, AudioMeta23class AppOutput(BaseAppOutput):4 result: File = Field(description="Generated output")5 # output_meta is inherited from BaseAppOutputMetaItem Types
| Type | Class | Key Fields |
|---|---|---|
| Text | TextMeta | tokens, cached_tokens, cache_write_tokens |
| Image | ImageMeta | width, height, resolution_mp, steps, count |
| Video | VideoMeta | width, height, resolution, resolution_mp, seconds, fps |
| Audio | AudioMeta | seconds |
Examples
LLM/Text Generation
Fill in LLMUsage from the provider's response and build the token report from it, so the usage your app shows and the tokens it bills are the same numbers:
1from inferencesh import OutputMeta2from inferencesh.models.llm import LLMUsage34usage = LLMUsage(5 prompt_tokens=response.usage.prompt_tokens, # every input token, cached ones included6 cached_tokens=details.cached_tokens, # read from the prompt cache7 cache_write_tokens=details.cache_write_tokens, # written to the prompt cache8 completion_tokens=response.usage.completion_tokens,9)1011return AppOutput(12 response=generated_text,13 usage=usage,14 output_meta=OutputMeta.from_llm_usage(usage),15)prompt_tokens counts every input token, the way OpenAI, xAI and OpenRouter report it. cached_tokens and cache_write_tokens are parts of it, so a pricing formula can bill them at the provider's cache rates (see pricing). Anthropic reports cache reads and writes outside input_tokens: add them back into prompt_tokens.
For streaming responses, take the counts from the final chunk that carries usage.
Image Generation
1return AppOutput(2 image=File(path=output_path),3 output_meta=OutputMeta(4 outputs=[ImageMeta(5 width=1024,6 height=1024,7 resolution_mp=1.05,8 steps=20,9 count=110 )]11 )12)Video Generation
Prefer probing the output file instead of hardcoding dimensions and duration:
1from inferencesh import VideoMeta, OutputMeta23return AppOutput(4 video=File(path=output_path),5 output_meta=OutputMeta(6 outputs=[7 VideoMeta.from_file(8 output_path,9 resolution="720p",10 resolution_mp=0.92,11 )12 ]13 )14)VideoMeta.from_file() and probe_video() ship in inferencesh ≥ v0.7.10. They call ffprobe (from the ffmpeg apt package — add ffmpeg to packages.txt).
Audio Generation
1return AppOutput(2 audio=File(path=output_path),3 output_meta=OutputMeta(4 outputs=[AudioMeta(seconds=30.0)]5 )6)Probing video files (Python)
When your app writes a video to disk, use the SDK helpers to populate VideoMeta from the actual file:
1from inferencesh import probe_video, VideoMeta23# Low-level: returns a dict (empty on failure)4info = probe_video("/path/to/output.mp4")5# keys: width, height, fps, nb_frames, seconds67# High-level: builds VideoMeta with probed fields8meta = VideoMeta.from_file(9 "/path/to/output.mp4",10 resolution="1080p", # optional kwargs pass through11 extra={"model": "kling"},12)Duration calculation: seconds is nb_frames / fps (frame-accurate), not container metadata. This matches how upstream video APIs (e.g. BytePlus) bill on frame count.
Failure behavior: If ffprobe is missing, times out (10s), or cannot read a video stream, probe_video() returns {} and VideoMeta.from_file() falls back to zero values for probed fields. Pass explicit resolution or other kwargs when you need non-zero metadata on probe failure.
Both helpers are exported from inferencesh and inferencesh.models.output_meta.
Custom Data
Use extra for app-specific pricing factors:
1output_meta=OutputMeta(2 outputs=[ImageMeta(3 width=1024,4 height=1024,5 extra={6 "model": "sdxl-turbo",7 "lora_count": 28 }9 )]10)Public API visibility
output_meta is pricing metadata for the platform. App authors should always set it on task output, but it is not returned on public surfaces:
| Surface | What you see |
|---|---|
Task output (REST, WebSocket, SDK) | output_meta is extracted at write time; only your app's user-facing fields remain in output |
App output_schema (version DTO, app store, Grid) | output_meta is stripped from schemas at deploy and on read |
Your app code still returns output_meta in AppOutput / run results — the backend stores it for usage-based billing and CEL pricing formulas. Do not rely on clients reading output_meta from completed tasks or published schemas.
Best Practices
- Always populate
output_metaif usage varies per request - Use accurate token counts from the actual tokenizer
- Report actual dimensions — for video, use
VideoMeta.from_file()instead of hardcoding width, height, fps, or seconds - Include relevant
extradata for pricing flexibility
Next
→ Usage-based pricing — CEL formulas that read output_meta
→ Secrets — API keys and sensitive values