feat: add models for streaming messages
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using AnthropicClient.Models;
|
||||
|
||||
namespace AnthropicClient.Json;
|
||||
|
||||
class ContentDeltaConverter : JsonConverter<ContentDelta>
|
||||
{
|
||||
public override ContentDelta Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
using var jsonDocument = JsonDocument.ParseValue(ref reader);
|
||||
var root = jsonDocument.RootElement;
|
||||
var type = root.GetProperty("type").GetString();
|
||||
return type switch
|
||||
{
|
||||
ContentDeltaType.TextDelta => JsonSerializer.Deserialize<TextDelta>(root.GetRawText(), options)!,
|
||||
ContentDeltaType.JsonDelta => JsonSerializer.Deserialize<JsonDelta>(root.GetRawText(), options)!,
|
||||
_ => throw new JsonException($"Unknown content type: {type}")
|
||||
};
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, ContentDelta value, JsonSerializerOptions options)
|
||||
{
|
||||
JsonSerializer.Serialize(writer, value, value.GetType(), options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using AnthropicClient.Models;
|
||||
|
||||
namespace AnthropicClient.Json;
|
||||
|
||||
class EventDataConverter : JsonConverter<EventData>
|
||||
{
|
||||
public override EventData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
using var jsonDocument = JsonDocument.ParseValue(ref reader);
|
||||
var root = jsonDocument.RootElement;
|
||||
var type = root.GetProperty("type").GetString();
|
||||
return type switch
|
||||
{
|
||||
EventType.Ping => JsonSerializer.Deserialize<PingEventData>(root.GetRawText(), options)!,
|
||||
EventType.Error => JsonSerializer.Deserialize<ErrorEventData>(root.GetRawText(), options)!,
|
||||
EventType.MessageStart => JsonSerializer.Deserialize<MessageStartEventData>(root.GetRawText(), options)!,
|
||||
EventType.MessageDelta => JsonSerializer.Deserialize<MessageDeltaEventData>(root.GetRawText(), options)!,
|
||||
EventType.MessageStop => JsonSerializer.Deserialize<MessageStopEventData>(root.GetRawText(), options)!,
|
||||
EventType.ContentBlockStart => JsonSerializer.Deserialize<ContentStartEventData>(root.GetRawText(), options)!,
|
||||
EventType.ContentBlockDelta => JsonSerializer.Deserialize<ContentDeltaEventData>(root.GetRawText(), options)!,
|
||||
EventType.ContentBlockStop => JsonSerializer.Deserialize<ContentStopEventData>(root.GetRawText(), options)!,
|
||||
_ => throw new JsonException($"Unknown content type: {type}")
|
||||
};
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, EventData value, JsonSerializerOptions options)
|
||||
{
|
||||
JsonSerializer.Serialize(writer, value, value.GetType(), options);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ static class JsonSerializationOptions
|
||||
new ContentConverter(),
|
||||
new ToolChoiceConverter(),
|
||||
new ErrorConverter(),
|
||||
new EventDataConverter(),
|
||||
new ContentDeltaConverter(),
|
||||
},
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public record AnthropicEvent
|
||||
{
|
||||
public string Type { get; init; } = string.Empty;
|
||||
public EventData Data { get; init; } = default!;
|
||||
|
||||
[JsonConstructor]
|
||||
internal AnthropicEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public AnthropicEvent(string type, EventData data)
|
||||
{
|
||||
Type = type;
|
||||
Data = data;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using AnthropicClient.Utils;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
/// <summary>
|
||||
@@ -9,66 +7,8 @@ namespace AnthropicClient.Models;
|
||||
/// </summary>
|
||||
public class ChatMessageRequest : MessageRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the model ID to use for the request.
|
||||
/// </summary>
|
||||
public string Model { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system ID to use for the request.
|
||||
/// </summary>
|
||||
public string? System { get; init; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the messages to send to the model.
|
||||
/// </summary>
|
||||
public List<ChatMessage> Messages { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum number of tokens to generate.
|
||||
/// </summary>
|
||||
[JsonPropertyName("max_tokens")]
|
||||
public int MaxTokens { get; init; } = 1024;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the metadata to include with the request.
|
||||
/// </summary>
|
||||
public Dictionary<string, object>? Metadata { get; init; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the prompt stop sequences.
|
||||
/// </summary>
|
||||
[JsonPropertyName("stop_sequences")]
|
||||
public List<string> StopSequences { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the temperature to use for the request.
|
||||
/// </summary>
|
||||
public decimal Temperature { get; init; } = 0.0m;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the top-K value to use for the request.
|
||||
/// </summary>
|
||||
public int? TopK { get; init; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the top-P value to use for the request.
|
||||
/// </summary>
|
||||
public decimal? TopP { get; init; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tool choice mode to use for the request.
|
||||
/// </summary>
|
||||
[JsonPropertyName("tool_choice")]
|
||||
public ToolChoice? ToolChoice { get; init; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tools to use for the request.
|
||||
/// </summary>
|
||||
public List<Tool>? Tools { get; init; } = null;
|
||||
|
||||
[JsonConstructor]
|
||||
internal ChatMessageRequest() : base(false) { }
|
||||
internal ChatMessageRequest() : base() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ChatMessageRequest"/> class.
|
||||
@@ -100,40 +40,19 @@ public class ChatMessageRequest : MessageRequest
|
||||
decimal? topP = null,
|
||||
ToolChoice? toolChoice = null,
|
||||
List<Tool>? tools = null
|
||||
) : base(false)
|
||||
) : base(
|
||||
model,
|
||||
messages,
|
||||
maxTokens,
|
||||
system,
|
||||
metadata,
|
||||
temperature,
|
||||
topK,
|
||||
topP,
|
||||
toolChoice,
|
||||
tools,
|
||||
false
|
||||
)
|
||||
{
|
||||
ArgumentValidator.ThrowIfNull(model, nameof(model));
|
||||
ArgumentValidator.ThrowIfNull(messages, nameof(messages));
|
||||
|
||||
if (AnthropicModels.IsValidModel(model) is false)
|
||||
{
|
||||
throw new ArgumentException($"Invalid model ID: {model}");
|
||||
}
|
||||
|
||||
if (messages.Count < 1)
|
||||
{
|
||||
throw new ArgumentException("Messages must contain at least one message");
|
||||
}
|
||||
|
||||
if (maxTokens < 1)
|
||||
{
|
||||
throw new ArgumentException($"Invalid max tokens: {maxTokens}");
|
||||
}
|
||||
|
||||
if (temperature < 0.0m || temperature > 1.0m)
|
||||
{
|
||||
throw new ArgumentException($"Invalid temperature: {temperature}");
|
||||
}
|
||||
|
||||
Model = model;
|
||||
Messages = messages;
|
||||
MaxTokens = maxTokens;
|
||||
System = system;
|
||||
Metadata = metadata;
|
||||
Temperature = temperature;
|
||||
TopK = topK;
|
||||
TopP = topP;
|
||||
ToolChoice = toolChoice;
|
||||
Tools = tools;
|
||||
}
|
||||
}
|
||||
@@ -10,42 +10,42 @@ public class ChatResponse
|
||||
/// <summary>
|
||||
/// Gets the ID of the chat response.
|
||||
/// </summary>
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string Id { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the model used for the chat response.
|
||||
/// </summary>
|
||||
public string Model { get; set; } = string.Empty;
|
||||
public string Model { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the role of the chat response.
|
||||
/// </summary>
|
||||
public string Role { get; set; } = string.Empty;
|
||||
public string Role { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the stop reason of the chat response.
|
||||
/// </summary>
|
||||
[JsonPropertyName("stop_reason")]
|
||||
public string StopReason { get; set; } = string.Empty;
|
||||
public string StopReason { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the stop sequence of the chat response.
|
||||
/// </summary>
|
||||
[JsonPropertyName("stop_sequence")]
|
||||
public string StopSequence { get; set; } = string.Empty;
|
||||
public string StopSequence { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the chat response.
|
||||
/// </summary>
|
||||
public string Type { get; set; } = string.Empty;
|
||||
public string Type { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the usage of the chat response.
|
||||
/// </summary>
|
||||
public ChatUsage Usage { get; set; } = new();
|
||||
public ChatUsage Usage { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the contents of the chat response.
|
||||
/// </summary>
|
||||
public List<Content> Content { get; set; } = [];
|
||||
public List<Content> Content { get; init; } = [];
|
||||
}
|
||||
@@ -11,11 +11,11 @@ public class ChatUsage
|
||||
/// Gets the number of input tokens used.
|
||||
/// </summary>
|
||||
[JsonPropertyName("input_tokens")]
|
||||
public int InputTokens { get; set; }
|
||||
public int InputTokens { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of output tokens used.
|
||||
/// </summary>
|
||||
[JsonPropertyName("output_tokens")]
|
||||
public int OutputTokens { get; set; }
|
||||
public int OutputTokens { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public abstract class ContentDelta
|
||||
{
|
||||
public string Type { get; init; }
|
||||
|
||||
protected ContentDelta(string type)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public class ContentDeltaEventData : EventData
|
||||
{
|
||||
public int Index { get; init; }
|
||||
public ContentDelta Delta { get; init; } = default!;
|
||||
|
||||
[JsonConstructor]
|
||||
internal ContentDeltaEventData() : base(EventType.ContentBlockDelta)
|
||||
{
|
||||
}
|
||||
|
||||
public ContentDeltaEventData(int index, ContentDelta delta) : base(EventType.ContentBlockDelta)
|
||||
{
|
||||
Index = index;
|
||||
Delta = delta;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public static class ContentDeltaType
|
||||
{
|
||||
public const string TextDelta = "text_delta";
|
||||
public const string JsonDelta = "input_json_delta";
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public class ContentStartEventData : EventData
|
||||
{
|
||||
public int Index { get; init; }
|
||||
|
||||
[JsonPropertyName("content_block")]
|
||||
public Content ContentBlock { get; init; }
|
||||
|
||||
[JsonConstructor]
|
||||
internal ContentStartEventData() : base(EventType.ContentBlockStart)
|
||||
{
|
||||
}
|
||||
|
||||
public ContentStartEventData(int index, Content contentBlock) : base(EventType.ContentBlockStart)
|
||||
{
|
||||
Index = index;
|
||||
ContentBlock = contentBlock;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public class ContentStopEventData : EventData
|
||||
{
|
||||
public int Index { get; init; }
|
||||
|
||||
[JsonConstructor]
|
||||
internal ContentStopEventData() : base(EventType.ContentBlockStop)
|
||||
{
|
||||
}
|
||||
|
||||
public ContentStopEventData(int index) : base(EventType.ContentBlockStop)
|
||||
{
|
||||
Index = index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public class ErrorEventData : EventData
|
||||
{
|
||||
public Error Error { get; init; } = default!;
|
||||
|
||||
[JsonConstructor]
|
||||
internal ErrorEventData() : base(EventType.Error)
|
||||
{
|
||||
}
|
||||
|
||||
public ErrorEventData(Error error) : base(EventType.Error)
|
||||
{
|
||||
Error = error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public abstract class EventData
|
||||
{
|
||||
public string Type { get; init; }
|
||||
|
||||
protected EventData(string type)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public static class EventType
|
||||
{
|
||||
public const string Error = "error";
|
||||
public const string Ping = "ping";
|
||||
public const string MessageStart = "message_start";
|
||||
public const string MessageDelta = "message_delta";
|
||||
public const string MessageStop = "message_stop";
|
||||
public const string ContentBlockStart = "content_block_start";
|
||||
public const string ContentBlockDelta = "content_block_delta";
|
||||
public const string ContentBlockStop = "content_block_stop";
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public class JsonDelta : ContentDelta
|
||||
{
|
||||
[JsonPropertyName("partial_json")]
|
||||
public string PartialJson { get; init; } = string.Empty;
|
||||
|
||||
[JsonConstructor]
|
||||
internal JsonDelta() : base(ContentDeltaType.JsonDelta)
|
||||
{
|
||||
}
|
||||
|
||||
public JsonDelta(string partialJson) : base(ContentDeltaType.JsonDelta)
|
||||
{
|
||||
PartialJson = partialJson;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public class MessageDeltaEventData : EventData
|
||||
{
|
||||
public MessageDelta Delta { get; init; }
|
||||
public ChatUsage Usage { get; init; }
|
||||
|
||||
[JsonConstructor]
|
||||
internal MessageDeltaEventData() : base(EventType.MessageDelta)
|
||||
{
|
||||
}
|
||||
|
||||
public MessageDeltaEventData(MessageDelta delta, ChatUsage usage) : base(EventType.MessageDelta)
|
||||
{
|
||||
Delta = delta;
|
||||
Usage = usage;
|
||||
}
|
||||
}
|
||||
|
||||
public class MessageDelta
|
||||
{
|
||||
[JsonPropertyName("stop_reason")]
|
||||
public string StopReason { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("stop_sequence")]
|
||||
public string StopSequence { get; init; } = string.Empty;
|
||||
|
||||
[JsonConstructor]
|
||||
internal MessageDelta()
|
||||
{
|
||||
}
|
||||
|
||||
public MessageDelta(string stopReason, string stopSequence)
|
||||
{
|
||||
StopReason = stopReason;
|
||||
StopSequence = stopSequence;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using AnthropicClient.Utils;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
/// <summary>
|
||||
@@ -5,18 +9,139 @@ namespace AnthropicClient.Models;
|
||||
/// </summary>
|
||||
public abstract class MessageRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the model ID to use for the request.
|
||||
/// </summary>
|
||||
public string Model { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system ID to use for the request.
|
||||
/// </summary>
|
||||
public string? System { get; init; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the messages to send to the model.
|
||||
/// </summary>
|
||||
public List<ChatMessage> Messages { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum number of tokens to generate.
|
||||
/// </summary>
|
||||
[JsonPropertyName("max_tokens")]
|
||||
public int MaxTokens { get; init; } = 1024;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the metadata to include with the request.
|
||||
/// </summary>
|
||||
public Dictionary<string, object>? Metadata { get; init; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the prompt stop sequences.
|
||||
/// </summary>
|
||||
[JsonPropertyName("stop_sequences")]
|
||||
public List<string> StopSequences { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the temperature to use for the request.
|
||||
/// </summary>
|
||||
public decimal Temperature { get; init; } = 0.0m;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the top-K value to use for the request.
|
||||
/// </summary>
|
||||
public int? TopK { get; init; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the top-P value to use for the request.
|
||||
/// </summary>
|
||||
public decimal? TopP { get; init; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tool choice mode to use for the request.
|
||||
/// </summary>
|
||||
[JsonPropertyName("tool_choice")]
|
||||
public ToolChoice? ToolChoice { get; init; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tools to use for the request.
|
||||
/// </summary>
|
||||
public List<Tool>? Tools { get; init; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the message should be streamed.
|
||||
/// </summary>
|
||||
public bool Stream { get; init; }
|
||||
|
||||
[JsonConstructor]
|
||||
internal MessageRequest() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MessageRequest"/> class.
|
||||
/// </summary>
|
||||
/// <param name="model">The model ID to use for the request.</param>
|
||||
/// <param name="messages">The messages to send to the model.</param>
|
||||
/// <param name="maxTokens">The maximum number of tokens to generate.</param>
|
||||
/// <param name="system">The system ID to use for the request.</param>
|
||||
/// <param name="metadata">The metadata to include with the request.</param>
|
||||
/// <param name="temperature">The temperature to use for the request.</param>
|
||||
/// <param name="topK">The top-K value to use for the request.</param>
|
||||
/// <param name="topP">The top-P value to use for the request.</param>
|
||||
/// <param name="toolChoice">The tool choice mode to use for the request.</param>
|
||||
/// <param name="tools">The tools to use for the request.</param>
|
||||
/// <param name="stream">A value indicating whether the message should be streamed.</param>
|
||||
/// <exception cref="ArgumentException">Thrown when the model ID is invalid.</exception>
|
||||
/// <exception cref="ArgumentNullException">Thrown when the model or messages is null.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when the messages contain no messages.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when the max tokens is less than one.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when the temperature is less than zero or greater than one.</exception>
|
||||
/// <returns>A new instance of the <see cref="MessageRequest"/> class.</returns>
|
||||
public MessageRequest(bool stream = false)
|
||||
protected MessageRequest(
|
||||
string model,
|
||||
List<ChatMessage> messages,
|
||||
int maxTokens = 1024,
|
||||
string? system = null,
|
||||
Dictionary<string, object>? metadata = null,
|
||||
decimal temperature = 0.0m,
|
||||
int? topK = null,
|
||||
decimal? topP = null,
|
||||
ToolChoice? toolChoice = null,
|
||||
List<Tool>? tools = null,
|
||||
bool stream = false
|
||||
)
|
||||
{
|
||||
ArgumentValidator.ThrowIfNull(model, nameof(model));
|
||||
ArgumentValidator.ThrowIfNull(messages, nameof(messages));
|
||||
|
||||
if (AnthropicModels.IsValidModel(model) is false)
|
||||
{
|
||||
throw new ArgumentException($"Invalid model ID: {model}");
|
||||
}
|
||||
|
||||
if (messages.Count < 1)
|
||||
{
|
||||
throw new ArgumentException("Messages must contain at least one message");
|
||||
}
|
||||
|
||||
if (maxTokens < 1)
|
||||
{
|
||||
throw new ArgumentException($"Invalid max tokens: {maxTokens}");
|
||||
}
|
||||
|
||||
if (temperature < 0.0m || temperature > 1.0m)
|
||||
{
|
||||
throw new ArgumentException($"Invalid temperature: {temperature}");
|
||||
}
|
||||
|
||||
Model = model;
|
||||
Messages = messages;
|
||||
MaxTokens = maxTokens;
|
||||
System = system;
|
||||
Metadata = metadata;
|
||||
Temperature = temperature;
|
||||
TopK = topK;
|
||||
TopP = topP;
|
||||
ToolChoice = toolChoice;
|
||||
Tools = tools;
|
||||
Stream = stream;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public class MessageStartEventData : EventData
|
||||
{
|
||||
public ChatMessage Message { get; init; } = new();
|
||||
|
||||
[JsonConstructor]
|
||||
internal MessageStartEventData() : base(EventType.MessageStart)
|
||||
{
|
||||
}
|
||||
|
||||
public MessageStartEventData(ChatMessage message) : base(EventType.MessageStart)
|
||||
{
|
||||
Message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public class MessageStopEventData : EventData
|
||||
{
|
||||
public MessageStopEventData() : base(EventType.MessageStop)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public class PingEventData : EventData
|
||||
{
|
||||
public PingEventData() : base(EventType.Ping)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a chat message request.
|
||||
/// </summary>
|
||||
public class StreamChatMessageRequest : MessageRequest
|
||||
{
|
||||
[JsonConstructor]
|
||||
internal StreamChatMessageRequest() : base() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StreamChatMessageRequest"/> class.
|
||||
/// </summary>
|
||||
/// <param name="model">The model ID to use for the request.</param>
|
||||
/// <param name="messages">The messages to send to the model.</param>
|
||||
/// <param name="maxTokens">The maximum number of tokens to generate.</param>
|
||||
/// <param name="system">The system ID to use for the request.</param>
|
||||
/// <param name="metadata">The metadata to include with the request.</param>
|
||||
/// <param name="temperature">The temperature to use for the request.</param>
|
||||
/// <param name="topK">The top-K value to use for the request.</param>
|
||||
/// <param name="topP">The top-P value to use for the request.</param>
|
||||
/// <param name="toolChoice">The tool choice mode to use for the request.</param>
|
||||
/// <param name="tools">The tools to use for the request.</param>
|
||||
/// <exception cref="ArgumentException">Thrown when the model ID is invalid.</exception>
|
||||
/// <exception cref="ArgumentNullException">Thrown when the model or messages is null.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when the messages contain no messages.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when the max tokens is less than one.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when the temperature is less than zero or greater than one.</exception>
|
||||
/// <returns>A new instance of the <see cref="StreamChatMessageRequest"/> class.</returns>
|
||||
public StreamChatMessageRequest(
|
||||
string model,
|
||||
List<ChatMessage> messages,
|
||||
int maxTokens = 1024,
|
||||
string? system = null,
|
||||
Dictionary<string, object>? metadata = null,
|
||||
decimal temperature = 0.0m,
|
||||
int? topK = null,
|
||||
decimal? topP = null,
|
||||
ToolChoice? toolChoice = null,
|
||||
List<Tool>? tools = null
|
||||
) : base(
|
||||
model,
|
||||
messages,
|
||||
maxTokens,
|
||||
system,
|
||||
metadata,
|
||||
temperature,
|
||||
topK,
|
||||
topP,
|
||||
toolChoice,
|
||||
tools,
|
||||
true
|
||||
)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AnthropicClient.Models;
|
||||
|
||||
public class TextDelta : ContentDelta
|
||||
{
|
||||
public string Text { get; set; } = string.Empty;
|
||||
|
||||
[JsonConstructor]
|
||||
internal TextDelta() : base(ContentDeltaType.TextDelta)
|
||||
{
|
||||
}
|
||||
|
||||
public TextDelta(string text) : base(ContentDeltaType.TextDelta)
|
||||
{
|
||||
Text = text;
|
||||
}
|
||||
}
|
||||
@@ -13,12 +13,12 @@ public class ToolResultContent : Content
|
||||
/// Gets the tool use ID of the content.
|
||||
/// </summary>
|
||||
[JsonPropertyName("tool_use_id")]
|
||||
public string ToolUseId { get; set; } = string.Empty;
|
||||
public string ToolUseId { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content of the tool result.
|
||||
/// </summary>
|
||||
public string Content { get; set; } = string.Empty;
|
||||
public string Content { get; init; } = string.Empty;
|
||||
|
||||
[JsonConstructor]
|
||||
internal ToolResultContent() : base(ContentType.ToolResult) { }
|
||||
|
||||
@@ -8,17 +8,17 @@ public class ToolUseContent : Content
|
||||
/// <summary>
|
||||
/// Gets the ID of the tool use.
|
||||
/// </summary>
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string Id { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the tool.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Name { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the input of the tool.
|
||||
/// </summary>
|
||||
public Dictionary<string, object?> Input { get; set; } = [];
|
||||
public Dictionary<string, object?> Input { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ToolUseContent"/> class.
|
||||
|
||||
@@ -18,4 +18,24 @@ public class ClientTests(
|
||||
result.IsSuccess.Should().BeTrue();
|
||||
result.Value.Should().BeOfType<ChatResponse>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateChatMessage_WhenCalledWithStreamRequest_IteratesOverChatResponse()
|
||||
{
|
||||
var request = new StreamChatMessageRequest(
|
||||
model: AnthropicModels.Claude3Haiku,
|
||||
messages: [new(MessageRole.User, [new TextContent("Hello!")])]
|
||||
);
|
||||
|
||||
var response = _client.CreateChatMessageAsync(request);
|
||||
|
||||
var events = new List<AnthropicEvent>();
|
||||
|
||||
await foreach (var e in response)
|
||||
{
|
||||
events.Add(e);
|
||||
}
|
||||
|
||||
events.Should().NotBeEmpty();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user