Skip to content
Merged
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
173 changes: 173 additions & 0 deletions Anthropic.SDK.Tests/TextEditorCodeExecutionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
using System.Text.Json;
using Anthropic.SDK.Extensions;
using Anthropic.SDK.Messaging;

namespace Anthropic.SDK.Tests;

[TestClass]
public class TextEditorCodeExecutionTests
{
[TestMethod]
public void TestTextEditorToolUseDeserialization()
{
var json = @"{
""type"": ""server_tool_use"",
""id"": ""srvtoolu_01E6F7G8H9I0J1K2L3M4N5O6"",
""name"": ""text_editor_code_execution"",
""input"": {
""command"": ""str_replace"",
""path"": ""config.json"",
""old_str"": ""\""debug\"": true"",
""new_str"": ""\""debug\"": false""
}
}";

var options = new JsonSerializerOptions
{
Converters = { ContentConverter.Instance }
};

var content = JsonSerializer.Deserialize<ServerToolUseContent>(json, options);

Assert.IsNotNull(content);
Assert.AreEqual(ContentType.server_tool_use, content.Type);
Assert.AreEqual("srvtoolu_01E6F7G8H9I0J1K2L3M4N5O6", content.Id);
Assert.AreEqual("text_editor_code_execution", content.Name);
Assert.IsNotNull(content.Input);
Assert.AreEqual("str_replace", content.Input.Command);
Assert.AreEqual("config.json", content.Input.Path);
Assert.AreEqual("\"debug\": true", content.Input.OldStr);
Assert.AreEqual("\"debug\": false", content.Input.NewStr);
}

[TestMethod]
public void TestTextEditorToolResultWithDiffDeserialization()
{
var json = @"{
""type"": ""text_editor_code_execution_tool_result"",
""tool_use_id"": ""srvtoolu_01E6F7G8H9I0J1K2L3M4N5O6"",
""content"": {
""type"": ""text_editor_code_execution_result"",
""oldStart"": 3,
""oldLines"": 1,
""newStart"": 3,
""newLines"": 1,
""lines"": [""- \""debug\"": true"", ""+ \""debug\"": false""]
}
}";

var options = new JsonSerializerOptions
{
Converters = { ContentConverter.Instance }
};

var content = JsonSerializer.Deserialize<TextEditorCodeExecutionToolResultContent>(json, options);

Assert.IsNotNull(content);
Assert.AreEqual(ContentType.text_editor_code_execution_tool_result, content.Type);
Assert.AreEqual("srvtoolu_01E6F7G8H9I0J1K2L3M4N5O6", content.ToolUseId);
Assert.IsNotNull(content.Content);

var result = content.Content as TextEditorCodeExecutionResultContent;
Assert.IsNotNull(result);
Assert.AreEqual(ContentType.text_editor_code_execution_result, result.Type);
Assert.AreEqual(3, result.OldStart);
Assert.AreEqual(1, result.OldLines);
Assert.AreEqual(3, result.NewStart);
Assert.AreEqual(1, result.NewLines);
Assert.IsNotNull(result.Lines);
Assert.AreEqual(2, result.Lines.Count);
Assert.AreEqual("- \"debug\": true", result.Lines[0]);
Assert.AreEqual("+ \"debug\": false", result.Lines[1]);
}

[TestMethod]
public void TestTextEditorCreateToolResultDeserialization()
{
var json = @"{
""type"": ""text_editor_code_execution_tool_result"",
""tool_use_id"": ""srvtoolu_01D5E6F7G8H9I0J1K2L3M4N5"",
""content"": {
""type"": ""text_editor_code_execution_result"",
""is_file_update"": false
}
}";

var options = new JsonSerializerOptions
{
Converters = { ContentConverter.Instance }
};

var content = JsonSerializer.Deserialize<TextEditorCodeExecutionToolResultContent>(json, options);

Assert.IsNotNull(content);
Assert.AreEqual(ContentType.text_editor_code_execution_tool_result, content.Type);
Assert.AreEqual("srvtoolu_01D5E6F7G8H9I0J1K2L3M4N5", content.ToolUseId);
Assert.IsNotNull(content.Content);

var result = content.Content as TextEditorCodeExecutionResultContent;
Assert.IsNotNull(result);
Assert.AreEqual(ContentType.text_editor_code_execution_result, result.Type);
Assert.AreEqual(false, result.IsFileUpdate);
}

[TestMethod]
public void TestTextEditorToolResultErrorDeserialization()
{
var json = @"{
""type"": ""text_editor_code_execution_tool_result"",
""tool_use_id"": ""srvtoolu_01VfmxgZ46TiHbmXgy928hQR"",
""content"": {
""type"": ""text_editor_code_execution_tool_result_error"",
""error_code"": ""unavailable""
}
}";

var options = new JsonSerializerOptions
{
Converters = { ContentConverter.Instance }
};

var content = JsonSerializer.Deserialize<TextEditorCodeExecutionToolResultContent>(json, options);

Assert.IsNotNull(content);
Assert.AreEqual(ContentType.text_editor_code_execution_tool_result, content.Type);
Assert.AreEqual("srvtoolu_01VfmxgZ46TiHbmXgy928hQR", content.ToolUseId);
Assert.IsNotNull(content.Content);

var error = content.Content as TextEditorCodeExecutionToolResultErrorContent;
Assert.IsNotNull(error);
Assert.AreEqual(ContentType.text_editor_code_execution_tool_result_error, error.Type);
Assert.AreEqual("unavailable", error.ErrorCode);
}

[TestMethod]
public void TestPauseTurnStopReason()
{
var json = @"{
""id"": ""msg_01XFDUDYJgAACzvnptvVoYEL"",
""type"": ""message"",
""role"": ""assistant"",
""content"": [{""type"": ""text"", ""text"": ""Processing...""}],
""model"": ""claude-3-5-sonnet-20241022"",
""stop_reason"": ""pause_turn"",
""stop_sequence"": null,
""usage"": {
""input_tokens"": 10,
""output_tokens"": 20
}
}";

var options = new JsonSerializerOptions
{
Converters = { ContentConverter.Instance }
};

var response = JsonSerializer.Deserialize<MessageResponse>(json, options);

Assert.IsNotNull(response);
Assert.AreEqual("pause_turn", response.StopReason);
Assert.AreEqual("msg_01XFDUDYJgAACzvnptvVoYEL", response.Id);
Assert.AreEqual("assistant", response.Role.ToString().ToLower());
}
}
6 changes: 6 additions & 0 deletions Anthropic.SDK/Extensions/ContentConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ public override ContentBase Read(ref Utf8JsonReader reader, Type typeToConvert,
return JsonSerializer.Deserialize<BashCodeExecutionOutputContent>(root.GetRawText(), options);
case "bash_code_execution_tool_result_error":
return JsonSerializer.Deserialize<BashCodeExecutionToolResultErrorContent>(root.GetRawText(), options);
case "text_editor_code_execution_tool_result":
return JsonSerializer.Deserialize<TextEditorCodeExecutionToolResultContent>(root.GetRawText(), options);
case "text_editor_code_execution_result":
return JsonSerializer.Deserialize<TextEditorCodeExecutionResultContent>(root.GetRawText(), options);
case "text_editor_code_execution_tool_result_error":
return JsonSerializer.Deserialize<TextEditorCodeExecutionToolResultErrorContent>(root.GetRawText(), options);
// Add cases for other types as necessary
default:
throw new JsonException($"Unknown type {type}");
Expand Down
153 changes: 153 additions & 0 deletions Anthropic.SDK/Messaging/Content.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,41 @@ public class ServerToolUseContent : ContentBase

public class ServerToolInput
{
/// <summary>
/// Query for web_search tool
/// </summary>
[JsonPropertyName("query")]
public string Query { get; set; }

/// <summary>
/// Command for text_editor_code_execution or bash_code_execution tools
/// </summary>
[JsonPropertyName("command")]
public string Command { get; set; }

/// <summary>
/// File path for text_editor_code_execution tool
/// </summary>
[JsonPropertyName("path")]
public string Path { get; set; }

/// <summary>
/// Old string to replace (for str_replace command)
/// </summary>
[JsonPropertyName("old_str")]
public string OldStr { get; set; }

/// <summary>
/// New string to replace with (for str_replace command)
/// </summary>
[JsonPropertyName("new_str")]
public string NewStr { get; set; }

/// <summary>
/// File text content (for create command)
/// </summary>
[JsonPropertyName("file_text")]
public string FileText { get; set; }
}


Expand Down Expand Up @@ -589,4 +622,124 @@ public class BashCodeExecutionToolResultErrorContent : ContentBase
[JsonPropertyName("error_code")]
public string ErrorCode { get; set; }
}

/// <summary>
/// Text Editor Code Execution Tool Result Content
/// </summary>
public class TextEditorCodeExecutionToolResultContent : ContentBase
{
/// <summary>
/// Type of Content (text_editor_code_execution_tool_result, pre-set)
/// </summary>
[JsonPropertyName("type")]
public override ContentType Type => ContentType.text_editor_code_execution_tool_result;

/// <summary>
/// Tool Use Id
/// </summary>
[JsonPropertyName("tool_use_id")]
public string ToolUseId { get; set; }

/// <summary>
/// Content - can be either TextEditorCodeExecutionResultContent or TextEditorCodeExecutionToolResultErrorContent
/// </summary>
[JsonPropertyName("content")]
public ContentBase Content { get; set; }
}

/// <summary>
/// Text Editor Code Execution Result Content
/// </summary>
public class TextEditorCodeExecutionResultContent : ContentBase
{
/// <summary>
/// Type of Content (text_editor_code_execution_result, pre-set)
/// </summary>
[JsonPropertyName("type")]
public override ContentType Type => ContentType.text_editor_code_execution_result;

/// <summary>
/// Whether file already existed (for create operations)
/// </summary>
[JsonPropertyName("is_file_update")]
public bool? IsFileUpdate { get; set; }

/// <summary>
/// File type (for view operations)
/// </summary>
[JsonPropertyName("file_type")]
public string FileType { get; set; }

/// <summary>
/// Content of the file (for view operations)
/// </summary>
[JsonPropertyName("content")]
public string Content { get; set; }

/// <summary>
/// Number of lines (for view operations)
/// </summary>
[JsonPropertyName("numLines")]
public int? NumLines { get; set; }

/// <summary>
/// Start line number (for view operations)
/// </summary>
[JsonPropertyName("startLine")]
public int? StartLine { get; set; }

/// <summary>
/// Total lines in file (for view operations)
/// </summary>
[JsonPropertyName("totalLines")]
public int? TotalLines { get; set; }

/// <summary>
/// Old start line (for edit operations)
/// </summary>
[JsonPropertyName("oldStart")]
public int? OldStart { get; set; }

/// <summary>
/// Old number of lines (for edit operations)
/// </summary>
[JsonPropertyName("oldLines")]
public int? OldLines { get; set; }

/// <summary>
/// New start line (for edit operations)
/// </summary>
[JsonPropertyName("newStart")]
public int? NewStart { get; set; }

/// <summary>
/// New number of lines (for edit operations)
/// </summary>
[JsonPropertyName("newLines")]
public int? NewLines { get; set; }

/// <summary>
/// Diff lines (for edit operations)
/// </summary>
[JsonPropertyName("lines")]
public List<string> Lines { get; set; }
}

/// <summary>
/// Text Editor Code Execution Tool Result Error Content
/// </summary>
public class TextEditorCodeExecutionToolResultErrorContent : ContentBase
{
/// <summary>
/// Type of Content (text_editor_code_execution_tool_result_error, pre-set)
/// </summary>
[JsonPropertyName("type")]
public override ContentType Type => ContentType.text_editor_code_execution_tool_result_error;

/// <summary>
/// Error code describing the failure
/// </summary>
[JsonPropertyName("error_code")]
public string ErrorCode { get; set; }
}
}
8 changes: 7 additions & 1 deletion Anthropic.SDK/Messaging/ContentType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ public enum ContentType

bash_code_execution_output,

bash_code_execution_tool_result_error
bash_code_execution_tool_result_error,

text_editor_code_execution_tool_result,

text_editor_code_execution_result,

text_editor_code_execution_tool_result_error
}
}