CrewAI video generation with a Sume Agent Completions tool

Give a CrewAI agent a BaseTool that hands a video brief to Sume Agent Completions with a spend cap, then reads the agent.run for the finished video.

5 min readSume
All posts

For CrewAI video generation with Sume, give an agent a BaseTool whose _run method sends the crew's brief to POST https://api.sume.com/v1/agent/completions with the required generation_spend_cap_usd and returns the agent.run id; a second tool reads the run until the video is ready.

The Sume facts come from Agent Completions and Authentication, and the CrewAI facts from its Create Custom Tools page, all read on 2026-09-27. Sume has no official CrewAI integration: this is a plain HTTPS call from your tool. Run the Sume video agent from your backend is the endpoint reference.

Can I plug Agent Completions in as a CrewAI LLM?

Not as a chat model. The request borrows OpenAI's messages[] shape, but an Agent Completion is not a synchronous chat completion. A real agent turn opens a sandbox, calls tools, and may generate media, so the create call returns 202 with a run receipt, not choices[]. Sume's docs list streaming and a synchronous OpenAI-compatible choices[] response as not available yet. Wrap it as a tool instead: your crew plans the work, and Sume's agent makes the video.

How do I write the tool?

A CrewAI tool can subclass BaseTool with a name, a description the agent reads, an args_schema for input validation, and a _run method. CrewAI also supports _arun for non-blocking I/O such as HTTP requests. The key below is a hash of the brief, so a retry with the same brief returns the original run instead of starting a second one:

import hashlib, os, requests
from typing import Type
from crewai.tools import BaseTool
from pydantic import BaseModel, Field

API = "https://api.sume.com/v1"
AUTH = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}

class VideoBrief(BaseModel):
    brief: str = Field(..., description="What the video must show, in plain words.")

class StartSumeVideo(BaseTool):
    name: str = "Start Sume video"
    description: str = "Hands a video brief to the Sume agent. Returns a run id; the video takes minutes."
    args_schema: Type[BaseModel] = VideoBrief

    def _run(self, brief: str) -> str:
        key = "crew-" + hashlib.sha256(brief.encode()).hexdigest()[:40]
        body = {"instruction": "Make the video described in the input file.",
                "input": {"brief": brief}, "generation_spend_cap_usd": 10}
        res = requests.post(f"{API}/agent/completions", json=body,
                            headers={**AUTH, "Idempotency-Key": key}, timeout=30)
        res.raise_for_status()
        return res.json()["data"]["id"]

How does each part of the tool map to the API?

The spend cap replaces the interactive spend-approval prompt that a backend caller does not get. Set it to the most you will spend on one run, using the metered rates on API pricing.

From Agent Completions, read 2026-09-27.
Tool partSume requestRule from the docs
The brief argumentinput.briefWritten whole to /workspace/inputs/sume-action-input.json. Treated as data, never as instructions.
The fixed taskinstructionSend exactly one of instruction or messages, never both.
The cap of 10generation_spend_cap_usdRequired, with no default. Omitting it is 400 invalid_request.
The brief's hashIdempotency-Key headerA replay returns the original receipt with idempotency_hit: true. The same key with a different payload is 409 idempotency_conflict.
The return valuedata.idThe agent.run id your second tool reads.

How does the crew get the finished video?

Read GET /v1/agent-runs/{id}. Statuses are queued, processing, completed, failed, and canceled. A completed run fills output: by default the agent's closing text in output.text and generated media in output.images, output.videos, output.audio, and output.files, as durable media.sume.com HTTPS URLs. Instead of polling, your server can pass communication.webhook_url, which is notified when the run reaches a terminal status; to stop a run in flight, call POST /v1/agent-runs/{id}/cancel.

CrewAI's docs recommend a typed output when a tool returns structured data, and an explicit result_schema when it returns a dictionary; the agent then receives JSON with named fields instead of guessing from text. In the same module:

from crewai.tools import tool

class RunStatus(BaseModel):
    status: str = Field(description="queued, processing, completed, failed, or canceled")
    videos: list = Field(description="output.videos once the run has completed")

@tool("Check Sume video", result_schema=RunStatus)
def check_sume_video(run_id: str) -> dict[str, object]:
    """Reads a Sume agent run. While it is queued or processing, check again later."""
    run = requests.get(f"{API}/agent-runs/{run_id}", headers=AUTH, timeout=30).json()["data"]
    return {"status": run["status"], "videos": (run.get("output") or {}).get("videos") or []}

Which key does the tool need?

The key needs agent_completions:write to create and cancel runs and agent_completions:read to read them. Keep it on a trusted server or in a CI secret store, never in frontend JavaScript. Older keys without these scopes, service-account keys, and what Agent Completions does not offer yet are covered in Run the Sume video agent from your backend.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume