home / skills / sstobo / convex-skills / convex-agents-files

convex-agents-files skill

/convex-agents-files

This skill helps agents manage file uploads, attachments, and image processing across conversations to enable visual analysis and file workflows.

npx playbooks add skill sstobo/convex-skills --skill convex-agents-files

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
3.8 KB
---
name: "Convex Agents Files"
description: "Handles file uploads, image attachments, and media processing in agent conversations. Use this when agents analyze images, process documents, or generate files."
---

## Purpose

Files and images let agents understand and generate visual content. Covers uploading, storing, attaching to messages, and managing file lifecycle.

## When to Use This Skill

- Users upload images for agent analysis
- Agents need to process documents or files
- Agents generate images with DALL-E
- Building file-based workflows
- Implementing file cleanup and tracking

## Upload and Store a File

```typescript
import { storeFile } from "@convex-dev/agent";

export const uploadFile = action({
  args: { fileData: v.string(), filename: v.string(), mimeType: v.string() },
  handler: async (ctx, { fileData, filename, mimeType }) => {
    const bytes = Buffer.from(fileData, "base64");

    const { file } = await storeFile(
      ctx,
      components.agent,
      new Blob([bytes], { type: mimeType }),
      { filename, sha256: "hash" }
    );

    return {
      fileId: file.fileId,
      url: file.url,
      storageId: file.storageId,
    };
  },
});
```

## Send File with Message (2-Step)

Upload first, then send message with attachment:

```typescript
import { saveMessage, getFile } from "@convex-dev/agent";

// Step 1: Save message with file
export const submitFileQuestion = mutation({
  args: { threadId: v.string(), fileId: v.string(), question: v.string() },
  handler: async (ctx, { threadId, fileId, question }) => {
    const { imagePart, filePart } = await getFile(ctx, components.agent, fileId);

    const { messageId } = await saveMessage(ctx, components.agent, {
      threadId,
      message: {
        role: "user",
        content: [
          imagePart ?? filePart,
          { type: "text", text: question },
        ],
      },
      metadata: { fileIds: [fileId] },
    });

    return { messageId };
  },
});

// Step 2: Generate response
export const generateFileResponse = action({
  args: { threadId: v.string(), promptMessageId: v.string() },
  handler: async (ctx, { threadId, promptMessageId }) => {
    const { thread } = await myAgent.continueThread(ctx, { threadId });
    await thread.generateText({ promptMessageId });
  },
});
```

## Inline File Saving (Action Only)

Pass file directly in generation:

```typescript
export const analyzeImageInline = action({
  args: { threadId: v.string(), imageData: v.string(), question: v.string() },
  handler: async (ctx, { threadId, imageData, question }) => {
    const { thread } = await myAgent.continueThread(ctx, { threadId });

    await thread.generateText({
      message: {
        role: "user",
        content: [
          {
            type: "image",
            image: Buffer.from(imageData, "base64"),
            mimeType: "image/png",
          },
          { type: "text", text: question },
        ],
      },
    });
  },
});
```

## Generate and Save Images

```typescript
export const generateAndSaveImage = action({
  args: { threadId: v.string(), prompt: v.string() },
  handler: async (ctx, { threadId, prompt }) => {
    const { image } = await generateImage({
      model: openai.image("dall-e-2"),
      prompt,
    });

    const { file } = await storeFile(ctx, components.agent, image, {
      filename: `generated-${Date.now()}.png`,
    });

    return { fileId: file.fileId };
  },
});
```

## Key Principles

- **Automatic storage**: Files > 64KB stored automatically
- **File tracking**: Metadata tracks which messages reference files
- **URL generation**: Signed URLs prevent unauthorized access
- **MIME type inference**: Auto-detected if not provided
- **Cleanup**: Files tracked for garbage collection

## Next Steps

- See **messages** for message management
- See **streaming** for streamed file processing
- See **fundamentals** for agent setup

Overview

This skill handles file uploads, image attachments, and media processing inside agent conversations. It provides methods to upload and store files, attach files to messages, process inline media during generation, and generate images programmatically. Use it when agents must analyze images, process documents, or produce downloadable media.

How this skill works

The skill exposes functions to store files, retrieve file parts (image or generic file parts), and attach those parts to messages so agents can reference them during generation. It supports two-step workflows (upload then send) and inline actions that pass binary data directly into a generation request. Generated images from models can be saved back into storage and tracked via file IDs and signed URLs.

When to use it

  • When users upload images for agent analysis or visual QA
  • When agents must process or extract data from documents and binary files
  • When generating images (for example with DALL·E) and saving them as files
  • When building file-based workflows that require message attachments and tracking
  • When implementing file lifecycle management and cleanup

Best practices

  • Upload large files to storage first, then reference by fileId to avoid re-sending data
  • Include MIME type when possible; rely on auto-detection only if necessary
  • Attach files to messages with metadata.fileIds to enable tracking and garbage collection
  • Use signed URLs from the skill for temporary external access rather than exposing raw storage paths
  • Prefer inline image passing for quick analysis flows and two-step upload+send for persistent storage

Example use cases

  • User uploads a photo for an agent to describe or extract text from
  • Agent processes a PDF report, extracts tables, and returns a summary with the source file attached
  • Generate images from prompts, save them to storage, and return fileIds/URLs for download
  • Create a chat thread where multiple files are referenced and later garbage-collected once no longer needed
  • Inline image analysis: pass base64 image in a single action to get an immediate model response

FAQ

How do I attach an uploaded file to a message?

Upload the file with the storeFile helper, get the fileId, then retrieve the file parts with getFile and include imagePart or filePart in the message content. Save the message with metadata.fileIds to enable tracking.

When should I use inline file passing versus uploading first?

Use inline passing for quick, ephemeral analysis during generation. Upload first when you need persistent storage, reuse of the file across messages, or lifecycle tracking and garbage collection.