Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 193 additions & 8 deletions docs/features/image-input.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Image Input

Send images to Copilot sessions by attaching them as file attachments. The runtime reads the file from disk, converts it to base64 internally, and sends it to the LLM as an image content block — no manual encoding required.
Send images to Copilot sessions as attachments. There are two ways to attach images:

- **File attachment** (`type: "file"`) — provide an absolute path; the runtime reads the file from disk, converts it to base64, and sends it to the LLM.
- **Blob attachment** (`type: "blob"`) — provide base64-encoded data directly; useful when the image is already in memory (e.g., screenshots, generated images, or data from an API).

## Overview

Expand All @@ -25,11 +28,12 @@ sequenceDiagram
| Concept | Description |
|---------|-------------|
| **File attachment** | An attachment with `type: "file"` and an absolute `path` to an image on disk |
| **Automatic encoding** | The runtime reads the image, converts it to base64, and sends it as an `image_url` block |
| **Blob attachment** | An attachment with `type: "blob"`, base64-encoded `data`, and a `mimeType` — no disk I/O needed |
| **Automatic encoding** | For file attachments, the runtime reads the image and converts it to base64 automatically |
| **Auto-resize** | The runtime automatically resizes or quality-reduces images that exceed model-specific limits |
| **Vision capability** | The model must have `capabilities.supports.vision = true` to process images |

## Quick Start
## Quick Start — File Attachment

Attach an image file to any message using the file attachment type. The path must be an absolute path to an image on disk.

Expand Down Expand Up @@ -215,9 +219,190 @@ await session.SendAsync(new MessageOptions

</details>

## Quick Start — Blob Attachment

When you already have image data in memory (e.g., a screenshot captured by your app, or an image fetched from an API), use a blob attachment to send it directly without writing to disk.

<details open>
<summary><strong>Node.js / TypeScript</strong></summary>

```typescript
import { CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient();
await client.start();

const session = await client.createSession({
model: "gpt-4.1",
onPermissionRequest: async () => ({ kind: "approved" }),
});

const base64ImageData = "..."; // your base64-encoded image
await session.send({
prompt: "Describe what you see in this image",
attachments: [
{
type: "blob",
data: base64ImageData,
mimeType: "image/png",
displayName: "screenshot.png",
},
],
});
```

</details>

<details>
<summary><strong>Python</strong></summary>

```python
from copilot import CopilotClient
from copilot.types import PermissionRequestResult

client = CopilotClient()
await client.start()

session = await client.create_session({
"model": "gpt-4.1",
"on_permission_request": lambda req, inv: PermissionRequestResult(kind="approved"),
})

base64_image_data = "..." # your base64-encoded image
await session.send({
"prompt": "Describe what you see in this image",
"attachments": [
{
"type": "blob",
"data": base64_image_data,
"mimeType": "image/png",
"displayName": "screenshot.png",
},
],
})
```

</details>

<details>
<summary><strong>Go</strong></summary>

<!-- docs-validate: hidden -->
```go
package main

import (
"context"
copilot "github.com/github/copilot-sdk/go"
)

func main() {
ctx := context.Background()
client := copilot.NewClient(nil)
client.Start(ctx)

session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
Model: "gpt-4.1",
OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (copilot.PermissionRequestResult, error) {
return copilot.PermissionRequestResult{Kind: copilot.PermissionRequestResultKindApproved}, nil
},
})

base64ImageData := "..."
mimeType := "image/png"
displayName := "screenshot.png"
session.Send(ctx, copilot.MessageOptions{
Prompt: "Describe what you see in this image",
Attachments: []copilot.Attachment{
{
Type: copilot.Blob,
Data: &base64ImageData,
MIMEType: &mimeType,
DisplayName: &displayName,
},
},
})
}
```
<!-- /docs-validate: hidden -->

```go
mimeType := "image/png"
displayName := "screenshot.png"
session.Send(ctx, copilot.MessageOptions{
Prompt: "Describe what you see in this image",
Attachments: []copilot.Attachment{
{
Type: copilot.Blob,
Data: &base64ImageData, // base64-encoded string
MIMEType: &mimeType,
DisplayName: &displayName,
},
},
})
```

</details>

<details>
<summary><strong>.NET</strong></summary>

<!-- docs-validate: hidden -->
```csharp
using GitHub.Copilot.SDK;

public static class BlobAttachmentExample
{
public static async Task Main()
{
await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-4.1",
OnPermissionRequest = (req, inv) =>
Task.FromResult(new PermissionRequestResult { Kind = PermissionRequestResultKind.Approved }),
});

var base64ImageData = "...";
await session.SendAsync(new MessageOptions
{
Prompt = "Describe what you see in this image",
Attachments = new List<UserMessageDataAttachmentsItem>
{
new UserMessageDataAttachmentsItemBlob
{
Data = base64ImageData,
MimeType = "image/png",
DisplayName = "screenshot.png",
},
},
});
}
}
```
<!-- /docs-validate: hidden -->

```csharp
await session.SendAsync(new MessageOptions
{
Prompt = "Describe what you see in this image",
Attachments = new List<UserMessageDataAttachmentsItem>
{
new UserMessageDataAttachmentsItemBlob
{
Data = base64ImageData,
MimeType = "image/png",
DisplayName = "screenshot.png",
},
},
});
```

</details>

## Supported Formats

Supported image formats include JPG, PNG, GIF, and other common image types. The runtime reads the image from disk and converts it as needed before sending to the LLM. Use PNG or JPEG for best results, as these are the most widely supported formats.
Supported image formats include JPG, PNG, GIF, and other common image types. For file attachments, the runtime reads the image from disk and converts it as needed. For blob attachments, you provide the base64 data and MIME type directly. Use PNG or JPEG for best results, as these are the most widely supported formats.

The model's `capabilities.limits.vision.supported_media_types` field lists the exact MIME types it accepts.

Expand Down Expand Up @@ -283,10 +468,10 @@ These image blocks appear in `tool.execution_complete` event results. See the [S
|-----|---------|
| **Use PNG or JPEG directly** | Avoids conversion overhead — these are sent to the LLM as-is |
| **Keep images reasonably sized** | Large images may be quality-reduced, which can lose important details |
| **Use absolute paths** | The runtime reads files from disk; relative paths may not resolve correctly |
| **Check vision support first** | Sending images to a non-vision model wastes tokens on the file path without visual understanding |
| **Multiple images are supported** | Attach several file attachments in one message, up to the model's `max_prompt_images` limit |
| **Images are not base64 in your code** | You provide a file path — the runtime handles encoding, resizing, and format conversion |
| **Use absolute paths for file attachments** | The runtime reads files from disk; relative paths may not resolve correctly |
| **Use blob attachments for in-memory data** | When you already have base64 data (e.g., screenshots, API responses), blob avoids unnecessary disk I/O |
| **Check vision support first** | Sending images to a non-vision model wastes tokens without visual understanding |
| **Multiple images are supported** | Attach several attachments in one message, up to the model's `max_prompt_images` limit |
| **SVG is not supported** | SVG files are text-based and excluded from image processing |

## See Also
Expand Down
2 changes: 1 addition & 1 deletion docs/features/streaming-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ The user sent a message. Recorded for the session timeline.
|------------|------|----------|-------------|
| `content` | `string` | ✅ | The user's message text |
| `transformedContent` | `string` | | Transformed version after preprocessing |
| `attachments` | `Attachment[]` | | File, directory, selection, or GitHub reference attachments |
| `attachments` | `Attachment[]` | | File, directory, selection, blob, or GitHub reference attachments |
| `source` | `string` | | Message source identifier |
| `agentMode` | `string` | | Agent mode: `"interactive"`, `"plan"`, `"autopilot"`, or `"shell"` |
| `interactionId` | `string` | | CAPI interaction ID |
Expand Down
20 changes: 17 additions & 3 deletions dotnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,21 +265,35 @@ session.On(evt =>

## Image Support

The SDK supports image attachments via the `Attachments` parameter. You can attach images by providing their file path:
The SDK supports image attachments via the `Attachments` parameter. You can attach images by providing their file path, or by passing base64-encoded data directly using a blob attachment:

```csharp
// File attachment — runtime reads from disk
await session.SendAsync(new MessageOptions
{
Prompt = "What's in this image?",
Attachments = new List<UserMessageDataAttachmentsItem>
{
new UserMessageDataAttachmentsItem
new UserMessageDataAttachmentsItemFile
{
Type = UserMessageDataAttachmentsItemType.File,
Path = "/path/to/image.jpg"
}
}
});

// Blob attachment — provide base64 data directly
await session.SendAsync(new MessageOptions
{
Prompt = "What's in this image?",
Attachments = new List<UserMessageDataAttachmentsItem>
{
new UserMessageDataAttachmentsItemBlob
{
Data = base64ImageData,
MimeType = "image/png",
}
}
});
```

Supported image formats include JPG, PNG, GIF, and other common image types. The agent's `view` tool can also read images directly from the filesystem, so you can also ask questions like:
Expand Down
18 changes: 18 additions & 0 deletions dotnet/src/Generated/SessionEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1870,13 +1870,30 @@ public partial class UserMessageDataAttachmentsItemGithubReference : UserMessage
public required string Url { get; set; }
}

public partial class UserMessageDataAttachmentsItemBlob : UserMessageDataAttachmentsItem
{
[JsonIgnore]
public override string Type => "blob";

[JsonPropertyName("data")]
public required string Data { get; set; }

[JsonPropertyName("mimeType")]
public required string MimeType { get; set; }

[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("displayName")]
public string? DisplayName { get; set; }
}

[JsonPolymorphic(
TypeDiscriminatorPropertyName = "type",
UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)]
[JsonDerivedType(typeof(UserMessageDataAttachmentsItemFile), "file")]
[JsonDerivedType(typeof(UserMessageDataAttachmentsItemDirectory), "directory")]
[JsonDerivedType(typeof(UserMessageDataAttachmentsItemSelection), "selection")]
[JsonDerivedType(typeof(UserMessageDataAttachmentsItemGithubReference), "github_reference")]
[JsonDerivedType(typeof(UserMessageDataAttachmentsItemBlob), "blob")]
public partial class UserMessageDataAttachmentsItem
{
[JsonPropertyName("type")]
Expand Down Expand Up @@ -2365,6 +2382,7 @@ public enum PermissionCompletedDataResultKind
[JsonSerializable(typeof(UserInputRequestedEvent))]
[JsonSerializable(typeof(UserMessageData))]
[JsonSerializable(typeof(UserMessageDataAttachmentsItem))]
[JsonSerializable(typeof(UserMessageDataAttachmentsItemBlob))]
[JsonSerializable(typeof(UserMessageDataAttachmentsItemDirectory))]
[JsonSerializable(typeof(UserMessageDataAttachmentsItemDirectoryLineRange))]
[JsonSerializable(typeof(UserMessageDataAttachmentsItemFile))]
Expand Down
16 changes: 15 additions & 1 deletion go/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,10 @@ Event types: `SessionLifecycleCreated`, `SessionLifecycleDeleted`, `SessionLifec

## Image Support

The SDK supports image attachments via the `Attachments` field in `MessageOptions`. You can attach images by providing their file path:
The SDK supports image attachments via the `Attachments` field in `MessageOptions`. You can attach images by providing their file path, or by passing base64-encoded data directly using a blob attachment:

```go
// File attachment — runtime reads from disk
_, err = session.Send(context.Background(), copilot.MessageOptions{
Prompt: "What's in this image?",
Attachments: []copilot.Attachment{
Expand All @@ -190,6 +191,19 @@ _, err = session.Send(context.Background(), copilot.MessageOptions{
},
},
})

// Blob attachment — provide base64 data directly
mimeType := "image/png"
_, err = session.Send(context.Background(), copilot.MessageOptions{
Prompt: "What's in this image?",
Attachments: []copilot.Attachment{
{
Type: copilot.Blob,
Data: &base64ImageData,
MIMEType: &mimeType,
},
},
})
```

Supported image formats include JPG, PNG, GIF, and other common image types. The agent's `view` tool can also read images directly from the filesystem, so you can also ask questions like:
Expand Down
5 changes: 5 additions & 0 deletions go/generated_session_events.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading