feat: initial implementation of creating a file via the Files API
This commit is contained in:
@@ -201,6 +201,72 @@ if (response.IsFailure)
|
||||
Console.WriteLine("Model Id: {0}", response.Value.Id);
|
||||
```
|
||||
|
||||
### Files API
|
||||
|
||||
The `AnthropicApiClient` provides support for the Anthropic Files API, which allows you to upload and manage files for use with the Anthropic API.
|
||||
|
||||
#### Create a File
|
||||
|
||||
You can create a file using the Files API in several ways:
|
||||
|
||||
##### From a Byte Array
|
||||
|
||||
```csharp
|
||||
using AnthropicClient;
|
||||
using AnthropicClient.Models;
|
||||
using System.Text;
|
||||
|
||||
// Create a file from a byte array
|
||||
var content = "This is a sample text file for the Anthropic Files API.";
|
||||
var fileBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var request = new CreateFileRequest(fileBytes, "sample.txt", "text/plain");
|
||||
var response = await client.CreateFileAsync(request);
|
||||
|
||||
if (response.IsSuccess)
|
||||
{
|
||||
Console.WriteLine("File created successfully from byte array!");
|
||||
Console.WriteLine("File ID: {0}", response.Value.Id);
|
||||
Console.WriteLine("File Name: {0}", response.Value.FileName);
|
||||
Console.WriteLine("File Type: {0}", response.Value.FileType);
|
||||
Console.WriteLine("File Size: {0} bytes", response.Value.SizeBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Failed to create file");
|
||||
Console.WriteLine("Error Type: {0}", response.Error.Error.Type);
|
||||
Console.WriteLine("Error Message: {0}", response.Error.Error.Message);
|
||||
}
|
||||
```
|
||||
|
||||
##### From a Stream
|
||||
|
||||
```csharp
|
||||
using AnthropicClient;
|
||||
using AnthropicClient.Models;
|
||||
using System.Text;
|
||||
|
||||
// Create a file from a stream
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes("Stream content"));
|
||||
var request = new CreateFileRequest(stream, "stream-file.txt", "text/plain");
|
||||
var response = await client.CreateFileAsync(request);
|
||||
|
||||
if (response.IsSuccess)
|
||||
{
|
||||
Console.WriteLine("File created successfully from stream!");
|
||||
Console.WriteLine("File ID: {0}", response.Value.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Failed to create file");
|
||||
Console.WriteLine("Error Type: {0}", response.Error.Error.Type);
|
||||
Console.WriteLine("Error Message: {0}", response.Error.Error.Message);
|
||||
}
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> The Files API has certain limitations on file size, supported file types, and usage quotas. Please refer to the [Anthropic API Documentation](https://docs.anthropic.com/en/docs/build-with-claude/files) for the most up-to-date information on these limitations.
|
||||
|
||||
### Create a message
|
||||
|
||||
The `AnthropicApiClient` exposes a method named `CreateMessageAsync` that can be used to create a message. The method requires a `MessageRequest` or a `StreamMessageRequest` instance as a parameter. The `MessageRequest` class is used to create a message whose response is not streamed and the `StreamMessageRequest` class is used to create a message whose response is streamed. The `MessageRequest` instance's properties can be set to configure how the message is created.
|
||||
|
||||
@@ -18,6 +18,7 @@ public class AnthropicApiClient : IAnthropicApiClient
|
||||
private string CountTokensEndpoint => $"{MessagesEndpoint}/count_tokens";
|
||||
private string MessageBatchesEndpoint => $"{MessagesEndpoint}/batches";
|
||||
private const string ModelsEndpoint = "models";
|
||||
private const string FilesEndpoint = "files";
|
||||
private const string JsonContentType = "application/json";
|
||||
private const string EventPrefix = "event:";
|
||||
private const string DataPrefix = "data:";
|
||||
@@ -380,6 +381,13 @@ public class AnthropicApiClient : IAnthropicApiClient
|
||||
return await CreateResultAsync<AnthropicModel>(response);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<AnthropicResult<AnthropicFile>> CreateFileAsync(CreateFileRequest request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var response = await SendFileRequestAsync(FilesEndpoint, request, cancellationToken);
|
||||
return await CreateResultAsync<AnthropicFile>(response);
|
||||
}
|
||||
|
||||
private async IAsyncEnumerable<AnthropicResult<Page<T>>> GetAllPagesAsync<T>(string endpoint, int limit = 20, [EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
var pagingRequest = new PagingRequest(limit: limit);
|
||||
@@ -462,6 +470,17 @@ public class AnthropicApiClient : IAnthropicApiClient
|
||||
return await _httpClient.PostAsync(endpoint, requestContent, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> SendFileRequestAsync(string endpoint, CreateFileRequest request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var multipartContent = new MultipartFormDataContent();
|
||||
|
||||
using var fileContent = new ByteArrayContent(request.File);
|
||||
fileContent.Headers.ContentType = new MediaTypeHeaderValue(request.FileType);
|
||||
multipartContent.Add(fileContent, "file", request.FileName);
|
||||
|
||||
return await _httpClient.PostAsync(endpoint, multipartContent, cancellationToken);
|
||||
}
|
||||
|
||||
private string Serialize<T>(T obj) => JsonSerializer.Serialize(obj, JsonSerializationOptions.DefaultOptions);
|
||||
private T? Deserialize<T>(string json) => JsonSerializer.Deserialize<T>(json, JsonSerializationOptions.DefaultOptions);
|
||||
}
|
||||
@@ -111,4 +111,12 @@ public interface IAnthropicApiClient
|
||||
/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the response as an <see cref="AnthropicResult{T}"/> where T is <see cref="AnthropicModel"/>.</returns>
|
||||
Task<AnthropicResult<AnthropicModel>> GetModelAsync(string modelId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a file asynchronously using the Files API.
|
||||
/// </summary>
|
||||
/// <param name="request">The file creation request.</param>
|
||||
/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the response as an <see cref="AnthropicResult{T}"/> where T is <see cref="AnthropicFile"/>.</returns>
|
||||
Task<AnthropicResult<AnthropicFile>> CreateFileAsync(CreateFileRequest request, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a file object from the Anthropic Files API.
|
||||
/// </summary>
|
||||
public class AnthropicFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique object identifier.
|
||||
/// The format and length of IDs may change over time.
|
||||
/// </summary>
|
||||
[JsonPropertyName("id")]
|
||||
public string Id { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Object type.
|
||||
/// For files, this is always "file".
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; init; } = "file";
|
||||
|
||||
/// <summary>
|
||||
/// Original filename of the uploaded file.
|
||||
/// </summary>
|
||||
[JsonPropertyName("file_name")]
|
||||
public string FileName { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// RFC 3339 datetime string representing when the file was created.
|
||||
/// </summary>
|
||||
[JsonPropertyName("created_at")]
|
||||
public string CreatedAt { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Size of the file in bytes.
|
||||
/// </summary>
|
||||
[JsonPropertyName("size_bytes")]
|
||||
public long SizeBytes { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// MIME type of the file.
|
||||
/// </summary>
|
||||
[JsonPropertyName("file_type")]
|
||||
public string FileType { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the file can be downloaded.
|
||||
/// </summary>
|
||||
[JsonPropertyName("downloadable")]
|
||||
public bool Downloadable { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using AnthropicClient.Utils;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a request to create a file via the Anthropic Files API.
|
||||
/// </summary>
|
||||
public class CreateFileRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The file content as a byte array.
|
||||
/// </summary>
|
||||
public byte[] File { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The original filename of the file being uploaded.
|
||||
/// </summary>
|
||||
public string FileName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The MIME type of the file.
|
||||
/// </summary>
|
||||
public string FileType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CreateFileRequest"/> class.
|
||||
/// </summary>
|
||||
/// <param name="file">The file content as a byte array.</param>
|
||||
/// <param name="fileName">The original filename of the file being uploaded.</param>
|
||||
/// <param name="fileType">The MIME type of the file.</param>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="file"/>, <paramref name="fileName"/>, or <paramref name="fileType"/> is null.</exception>
|
||||
public CreateFileRequest(byte[] file, string fileName, string fileType)
|
||||
{
|
||||
ArgumentValidator.ThrowIfNull(file, nameof(file));
|
||||
ArgumentValidator.ThrowIfNullOrWhitespace(fileName, nameof(fileName));
|
||||
ArgumentValidator.ThrowIfNullOrWhitespace(fileType, nameof(fileType));
|
||||
|
||||
File = file;
|
||||
FileName = fileName;
|
||||
FileType = fileType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CreateFileRequest"/> class from a stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream containing the file content.</param>
|
||||
/// <param name="fileName">The original filename of the file being uploaded.</param>
|
||||
/// <param name="fileType">The MIME type of the file.</param>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="stream"/>, <paramref name="fileName"/>, or <paramref name="fileType"/> is null.</exception>
|
||||
public CreateFileRequest(Stream stream, string fileName, string fileType)
|
||||
{
|
||||
ArgumentValidator.ThrowIfNull(stream, nameof(stream));
|
||||
ArgumentValidator.ThrowIfNullOrWhitespace(fileName, nameof(fileName));
|
||||
ArgumentValidator.ThrowIfNullOrWhitespace(fileType, nameof(fileType));
|
||||
|
||||
using var memoryStream = new MemoryStream();
|
||||
stream.CopyToAsync(memoryStream);
|
||||
var fileContent = memoryStream.ToArray();
|
||||
|
||||
File = fileContent;
|
||||
FileName = fileName;
|
||||
FileType = fileType;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user