From c5e2f6a46d225676ad1d5f260f175868375e3a97 Mon Sep 17 00:00:00 2001 From: nick863 <30440255+nick863@users.noreply.github.com> Date: Tue, 17 Jun 2025 11:41:24 -0700 Subject: [PATCH] Add thread message delete operation --- .../Azure.AI.Agents.Persistent/CHANGELOG.md | 1 + .../api/Azure.AI.Agents.Persistent.net8.0.cs | 2 + ...ure.AI.Agents.Persistent.netstandard2.0.cs | 2 + sdk/ai/Azure.AI.Agents.Persistent/assets.json | 2 +- .../samples/README.md | 1 + ...20_PersistentAgents_FileSearch_Steaming.md | 2 +- .../src/Custom/ThreadMessages.cs | 52 ++++++ .../src/Generated/MessageAttachment.cs | 5 +- .../MessageDeletionStatus.Serialization.cs | 158 ++++++++++++++++++ .../src/Generated/MessageDeletionStatus.cs | 85 ++++++++++ .../Generated/MessageDeletionStatusObject.cs | 48 ++++++ .../src/Generated/ThreadMessages.cs | 131 +++++++++++++++ .../tests/PersistentAgentsTests.cs | 12 ++ .../tsp-location.yaml | 2 +- 14 files changed, 499 insertions(+), 4 deletions(-) create mode 100644 sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageDeletionStatus.Serialization.cs create mode 100644 sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageDeletionStatus.cs create mode 100644 sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageDeletionStatusObject.cs diff --git a/sdk/ai/Azure.AI.Agents.Persistent/CHANGELOG.md b/sdk/ai/Azure.AI.Agents.Persistent/CHANGELOG.md index f0807ea558ab..ea790e23831e 100644 --- a/sdk/ai/Azure.AI.Agents.Persistent/CHANGELOG.md +++ b/sdk/ai/Azure.AI.Agents.Persistent/CHANGELOG.md @@ -3,6 +3,7 @@ ## 1.1.0-beta.3 (Unreleased) ### Features Added +- Added delete operation for `ThreadMessages`. ### Breaking Changes diff --git a/sdk/ai/Azure.AI.Agents.Persistent/api/Azure.AI.Agents.Persistent.net8.0.cs b/sdk/ai/Azure.AI.Agents.Persistent/api/Azure.AI.Agents.Persistent.net8.0.cs index 4b2b0c3e2591..2ca1bcb3431e 100644 --- a/sdk/ai/Azure.AI.Agents.Persistent/api/Azure.AI.Agents.Persistent.net8.0.cs +++ b/sdk/ai/Azure.AI.Agents.Persistent/api/Azure.AI.Agents.Persistent.net8.0.cs @@ -2371,6 +2371,8 @@ protected ThreadMessages() { } public virtual System.Threading.Tasks.Task> CreateMessageAsync(string threadId, Azure.AI.Agents.Persistent.MessageRole role, System.Collections.Generic.IEnumerable contentBlocks, System.Collections.Generic.IEnumerable attachments = null, System.Collections.Generic.IReadOnlyDictionary metadata = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> CreateMessageAsync(string threadId, Azure.AI.Agents.Persistent.MessageRole role, string content, System.Collections.Generic.IEnumerable attachments = null, System.Collections.Generic.IReadOnlyDictionary metadata = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task CreateMessageAsync(string threadId, Azure.Core.RequestContent content, Azure.RequestContext context = null) { throw null; } + public virtual Azure.Response DeleteMessage(string threadId, string messageId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task> DeleteMessageAsync(string threadId, string messageId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response GetMessage(string threadId, string messageId, Azure.RequestContext context) { throw null; } public virtual Azure.Response GetMessage(string threadId, string messageId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task GetMessageAsync(string threadId, string messageId, Azure.RequestContext context) { throw null; } diff --git a/sdk/ai/Azure.AI.Agents.Persistent/api/Azure.AI.Agents.Persistent.netstandard2.0.cs b/sdk/ai/Azure.AI.Agents.Persistent/api/Azure.AI.Agents.Persistent.netstandard2.0.cs index 8f341b968fe1..e11bb4d52e6e 100644 --- a/sdk/ai/Azure.AI.Agents.Persistent/api/Azure.AI.Agents.Persistent.netstandard2.0.cs +++ b/sdk/ai/Azure.AI.Agents.Persistent/api/Azure.AI.Agents.Persistent.netstandard2.0.cs @@ -2371,6 +2371,8 @@ protected ThreadMessages() { } public virtual System.Threading.Tasks.Task> CreateMessageAsync(string threadId, Azure.AI.Agents.Persistent.MessageRole role, System.Collections.Generic.IEnumerable contentBlocks, System.Collections.Generic.IEnumerable attachments = null, System.Collections.Generic.IReadOnlyDictionary metadata = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> CreateMessageAsync(string threadId, Azure.AI.Agents.Persistent.MessageRole role, string content, System.Collections.Generic.IEnumerable attachments = null, System.Collections.Generic.IReadOnlyDictionary metadata = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task CreateMessageAsync(string threadId, Azure.Core.RequestContent content, Azure.RequestContext context = null) { throw null; } + public virtual Azure.Response DeleteMessage(string threadId, string messageId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task> DeleteMessageAsync(string threadId, string messageId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response GetMessage(string threadId, string messageId, Azure.RequestContext context) { throw null; } public virtual Azure.Response GetMessage(string threadId, string messageId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task GetMessageAsync(string threadId, string messageId, Azure.RequestContext context) { throw null; } diff --git a/sdk/ai/Azure.AI.Agents.Persistent/assets.json b/sdk/ai/Azure.AI.Agents.Persistent/assets.json index 58e232a05adb..611809ab3c4d 100644 --- a/sdk/ai/Azure.AI.Agents.Persistent/assets.json +++ b/sdk/ai/Azure.AI.Agents.Persistent/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "net", "TagPrefix": "net/ai/Azure.AI.Agents.Persistent", - "Tag": "net/ai/Azure.AI.Agents.Persistent_8e1aef00a7" + "Tag": "net/ai/Azure.AI.Agents.Persistent_812e51862e" } diff --git a/sdk/ai/Azure.AI.Agents.Persistent/samples/README.md b/sdk/ai/Azure.AI.Agents.Persistent/samples/README.md index 1c2bbce40e49..be856708d320 100644 --- a/sdk/ai/Azure.AI.Agents.Persistent/samples/README.md +++ b/sdk/ai/Azure.AI.Agents.Persistent/samples/README.md @@ -32,5 +32,6 @@ description: Samples for the Azure.AI.Agents.Persistent client library. | [Sample17_PersistentAgents_ImageUrlInputs](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/ai/Azure.AI.Agents.Persistent/samples/Sample17_PersistentAgents_ImageUrlInputs.md) | Sample using agents with Image URL as an input. | | [Sample18_PersistentAgents_ImageFileInputs](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/ai/Azure.AI.Agents.Persistent/samples/Sample18_PersistentAgents_ImageFileInputs.md) | Sample using agents with Image Fileas an input. | | [Sample19_PersistentAgents_VectorStoreFile_WithSteps](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/ai/Azure.AI.Agents.Persistent/samples/Sample19_PersistentAgents_VectorStoreFile_WithSteps.md) | Sample file search using `VectorStoreFile` and agents. | +| [Sample20_PersistentAgents_FileSearch_Steaming](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/ai/Azure.AI.Agents.Persistent/samples/Sample20_PersistentAgents_FileSearch_Steaming.md) | Sample file search with agent and streaming. | diff --git a/sdk/ai/Azure.AI.Agents.Persistent/samples/Sample20_PersistentAgents_FileSearch_Steaming.md b/sdk/ai/Azure.AI.Agents.Persistent/samples/Sample20_PersistentAgents_FileSearch_Steaming.md index 82144563d403..6907e2893d8f 100644 --- a/sdk/ai/Azure.AI.Agents.Persistent/samples/Sample20_PersistentAgents_FileSearch_Steaming.md +++ b/sdk/ai/Azure.AI.Agents.Persistent/samples/Sample20_PersistentAgents_FileSearch_Steaming.md @@ -1,4 +1,4 @@ -# Sample file search with agent in Azure.AI.Agents.Persistent. +# Sample file search with agent and streaming in Azure.AI.Agents.Persistent. In this example we will create the local file, upload it to the newly created `VectorStore`, which will be used in the file search. In this example we will stream the result. diff --git a/sdk/ai/Azure.AI.Agents.Persistent/src/Custom/ThreadMessages.cs b/sdk/ai/Azure.AI.Agents.Persistent/src/Custom/ThreadMessages.cs index 4b7877328784..fc9bf1e89a85 100644 --- a/sdk/ai/Azure.AI.Agents.Persistent/src/Custom/ThreadMessages.cs +++ b/sdk/ai/Azure.AI.Agents.Persistent/src/Custom/ThreadMessages.cs @@ -361,5 +361,57 @@ internal virtual Pageable GetMessages(string threadId, string runId, HttpMessage FirstPageRequest(int? pageSizeHint) => CreateGetMessagesRequest(threadId, runId, limit, order, after, before, context); return GeneratorPageableHelpers.CreatePageable(FirstPageRequest, null, e => BinaryData.FromString(e.GetRawText()), ClientDiagnostics, _pipeline, "ThreadMessagesClient.GetMessages", "data", null, context); } + + /// Deletes a thread message. + /// The ID of the thread to delete. + /// The ID of the message to delete. + /// The cancellation token to use. + /// is null. + /// is an empty string, and was expected to be non-empty. + public virtual Response DeleteMessage( + string threadId, + string messageId, + CancellationToken cancellationToken = default) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("PersistentAgentsClient.DeleteThread"); + scope.Start(); + Response baseResponse + = InternalDeleteMessage( + threadId:threadId, + messageId: messageId, + cancellationToken: cancellationToken); + bool simplifiedValue = + baseResponse.GetRawResponse() != null + && !baseResponse.GetRawResponse().IsError + && baseResponse.Value != null + && baseResponse.Value.Deleted; + return Response.FromValue(simplifiedValue, baseResponse.GetRawResponse()); + } + + /// Deletes a thread message. + /// The ID of the thread to delete. + /// The ID of the message to delete. + /// The cancellation token to use. + /// is null. + /// is an empty string, and was expected to be non-empty. + public virtual async Task> DeleteMessageAsync( + string threadId, + string messageId, + CancellationToken cancellationToken = default) + { + using DiagnosticScope scope = ClientDiagnostics.CreateScope("PersistentAgentsClient.DeleteThread"); + scope.Start(); + Response baseResponse + = await InternalDeleteMessageAsync( + threadId: threadId, + messageId: messageId, + cancellationToken: cancellationToken).ConfigureAwait(false); + bool simplifiedValue = + baseResponse.GetRawResponse() != null + && !baseResponse.GetRawResponse().IsError + && baseResponse.Value != null + && baseResponse.Value.Deleted; + return Response.FromValue(simplifiedValue, baseResponse.GetRawResponse()); + } } } diff --git a/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageAttachment.cs b/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageAttachment.cs index ec4296155e12..19e226ba2846 100644 --- a/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageAttachment.cs +++ b/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageAttachment.cs @@ -91,7 +91,10 @@ internal MessageAttachment() /// Supported types: /// /// - /// + /// + /// + /// + /// /// /// /// diff --git a/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageDeletionStatus.Serialization.cs b/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageDeletionStatus.Serialization.cs new file mode 100644 index 000000000000..2f491b6aec06 --- /dev/null +++ b/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageDeletionStatus.Serialization.cs @@ -0,0 +1,158 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; +using Azure.Core; + +namespace Azure.AI.Agents.Persistent +{ + internal partial class MessageDeletionStatus : IUtf8JsonSerializable, IJsonModel + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModel)this).Write(writer, ModelSerializationExtensions.WireOptions); + + void IJsonModel.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + writer.WriteStartObject(); + JsonModelWriteCore(writer, options); + writer.WriteEndObject(); + } + + /// The JSON writer. + /// The client options for reading and writing models. + protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(MessageDeletionStatus)} does not support writing '{format}' format."); + } + + writer.WritePropertyName("id"u8); + writer.WriteStringValue(Id); + writer.WritePropertyName("deleted"u8); + writer.WriteBooleanValue(Deleted); + writer.WritePropertyName("object"u8); + writer.WriteStringValue(Object.ToString()); + if (options.Format != "W" && _serializedAdditionalRawData != null) + { + foreach (var item in _serializedAdditionalRawData) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value, ModelSerializationExtensions.JsonDocumentOptions)) + { + JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + MessageDeletionStatus IJsonModel.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if (format != "J") + { + throw new FormatException($"The model {nameof(MessageDeletionStatus)} does not support reading '{format}' format."); + } + + using JsonDocument document = JsonDocument.ParseValue(ref reader); + return DeserializeMessageDeletionStatus(document.RootElement, options); + } + + internal static MessageDeletionStatus DeserializeMessageDeletionStatus(JsonElement element, ModelReaderWriterOptions options = null) + { + options ??= ModelSerializationExtensions.WireOptions; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + string id = default; + bool deleted = default; + MessageDeletionStatusObject @object = default; + IDictionary serializedAdditionalRawData = default; + Dictionary rawDataDictionary = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("id"u8)) + { + id = property.Value.GetString(); + continue; + } + if (property.NameEquals("deleted"u8)) + { + deleted = property.Value.GetBoolean(); + continue; + } + if (property.NameEquals("object"u8)) + { + @object = new MessageDeletionStatusObject(property.Value.GetString()); + continue; + } + if (options.Format != "W") + { + rawDataDictionary.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + serializedAdditionalRawData = rawDataDictionary; + return new MessageDeletionStatus(id, deleted, @object, serializedAdditionalRawData); + } + + BinaryData IPersistableModel.Write(ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + return ModelReaderWriter.Write(this, options, AzureAIAgentsPersistentContext.Default); + default: + throw new FormatException($"The model {nameof(MessageDeletionStatus)} does not support writing '{options.Format}' format."); + } + } + + MessageDeletionStatus IPersistableModel.Create(BinaryData data, ModelReaderWriterOptions options) + { + var format = options.Format == "W" ? ((IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + + switch (format) + { + case "J": + { + using JsonDocument document = JsonDocument.Parse(data, ModelSerializationExtensions.JsonDocumentOptions); + return DeserializeMessageDeletionStatus(document.RootElement, options); + } + default: + throw new FormatException($"The model {nameof(MessageDeletionStatus)} does not support reading '{options.Format}' format."); + } + } + + string IPersistableModel.GetFormatFromOptions(ModelReaderWriterOptions options) => "J"; + + /// Deserializes the model from a raw response. + /// The response to deserialize the model from. + internal static MessageDeletionStatus FromResponse(Response response) + { + using var document = JsonDocument.Parse(response.Content, ModelSerializationExtensions.JsonDocumentOptions); + return DeserializeMessageDeletionStatus(document.RootElement); + } + + /// Convert into a . + internal virtual RequestContent ToRequestContent() + { + var content = new Utf8JsonRequestContent(); + content.JsonWriter.WriteObjectValue(this, ModelSerializationExtensions.WireOptions); + return content; + } + } +} diff --git a/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageDeletionStatus.cs b/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageDeletionStatus.cs new file mode 100644 index 000000000000..cd1b8a8efa59 --- /dev/null +++ b/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageDeletionStatus.cs @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; + +namespace Azure.AI.Agents.Persistent +{ + /// The status of a thread message deletion operation. + internal partial class MessageDeletionStatus + { + /// + /// Keeps track of any properties unknown to the library. + /// + /// To assign an object to the value of this property use . + /// + /// + /// To assign an already formatted json string to this property use . + /// + /// + /// Examples: + /// + /// + /// BinaryData.FromObjectAsJson("foo") + /// Creates a payload of "foo". + /// + /// + /// BinaryData.FromString("\"foo\"") + /// Creates a payload of "foo". + /// + /// + /// BinaryData.FromObjectAsJson(new { key = "value" }) + /// Creates a payload of { "key": "value" }. + /// + /// + /// BinaryData.FromString("{\"key\": \"value\"}") + /// Creates a payload of { "key": "value" }. + /// + /// + /// + /// + private IDictionary _serializedAdditionalRawData; + + /// Initializes a new instance of . + /// The ID of the resource specified for deletion. + /// A value indicating whether deletion was successful. + /// is null. + internal MessageDeletionStatus(string id, bool deleted) + { + Argument.AssertNotNull(id, nameof(id)); + + Id = id; + Deleted = deleted; + } + + /// Initializes a new instance of . + /// The ID of the resource specified for deletion. + /// A value indicating whether deletion was successful. + /// The object type, which is always 'thread.message.deleted'. + /// Keeps track of any properties unknown to the library. + internal MessageDeletionStatus(string id, bool deleted, MessageDeletionStatusObject @object, IDictionary serializedAdditionalRawData) + { + Id = id; + Deleted = deleted; + Object = @object; + _serializedAdditionalRawData = serializedAdditionalRawData; + } + + /// Initializes a new instance of for deserialization. + internal MessageDeletionStatus() + { + } + + /// The ID of the resource specified for deletion. + public string Id { get; } + /// A value indicating whether deletion was successful. + public bool Deleted { get; } + /// The object type, which is always 'thread.message.deleted'. + public MessageDeletionStatusObject Object { get; } = MessageDeletionStatusObject.ThreadMessageDeleted; + } +} diff --git a/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageDeletionStatusObject.cs b/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageDeletionStatusObject.cs new file mode 100644 index 000000000000..95ff4a3a1168 --- /dev/null +++ b/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/MessageDeletionStatusObject.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.AI.Agents.Persistent +{ + /// The MessageDeletionStatusObject. + internal readonly partial struct MessageDeletionStatusObject : IEquatable + { + private readonly string _value; + + /// Initializes a new instance of . + /// is null. + public MessageDeletionStatusObject(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string ThreadMessageDeletedValue = "thread.message.deleted"; + + /// thread.message.deleted. + public static MessageDeletionStatusObject ThreadMessageDeleted { get; } = new MessageDeletionStatusObject(ThreadMessageDeletedValue); + /// Determines if two values are the same. + public static bool operator ==(MessageDeletionStatusObject left, MessageDeletionStatusObject right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(MessageDeletionStatusObject left, MessageDeletionStatusObject right) => !left.Equals(right); + /// Converts a to a . + public static implicit operator MessageDeletionStatusObject(string value) => new MessageDeletionStatusObject(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is MessageDeletionStatusObject other && Equals(other); + /// + public bool Equals(MessageDeletionStatusObject other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0; + /// + public override string ToString() => _value; + } +} diff --git a/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/ThreadMessages.cs b/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/ThreadMessages.cs index 9e2ef80a7334..f462fbf5b453 100644 --- a/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/ThreadMessages.cs +++ b/sdk/ai/Azure.AI.Agents.Persistent/src/Generated/ThreadMessages.cs @@ -430,6 +430,120 @@ public virtual Response UpdateMessage(string threadId, string messageId, Request } } + /// Deletes an existing message on an existing thread. + /// Identifier of the thread. + /// Identifier of the message. + /// The cancellation token to use. + /// or is null. + /// or is an empty string, and was expected to be non-empty. + internal virtual async Task> InternalDeleteMessageAsync(string threadId, string messageId, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(threadId, nameof(threadId)); + Argument.AssertNotNullOrEmpty(messageId, nameof(messageId)); + + RequestContext context = FromCancellationToken(cancellationToken); + Response response = await InternalDeleteMessageAsync(threadId, messageId, context).ConfigureAwait(false); + return Response.FromValue(MessageDeletionStatus.FromResponse(response), response); + } + + /// Deletes an existing message on an existing thread. + /// Identifier of the thread. + /// Identifier of the message. + /// The cancellation token to use. + /// or is null. + /// or is an empty string, and was expected to be non-empty. + internal virtual Response InternalDeleteMessage(string threadId, string messageId, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(threadId, nameof(threadId)); + Argument.AssertNotNullOrEmpty(messageId, nameof(messageId)); + + RequestContext context = FromCancellationToken(cancellationToken); + Response response = InternalDeleteMessage(threadId, messageId, context); + return Response.FromValue(MessageDeletionStatus.FromResponse(response), response); + } + + /// + /// [Protocol Method] Deletes an existing message on an existing thread. + /// + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// + /// Please try the simpler convenience overload with strongly typed models first. + /// + /// + /// + /// + /// Identifier of the thread. + /// Identifier of the message. + /// The request context, which can override default behaviors of the client pipeline on a per-call basis. + /// or is null. + /// or is an empty string, and was expected to be non-empty. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual async Task InternalDeleteMessageAsync(string threadId, string messageId, RequestContext context) + { + Argument.AssertNotNullOrEmpty(threadId, nameof(threadId)); + Argument.AssertNotNullOrEmpty(messageId, nameof(messageId)); + + using var scope = ClientDiagnostics.CreateScope("ThreadMessages.InternalDeleteMessage"); + scope.Start(); + try + { + using HttpMessage message = CreateInternalDeleteMessageRequest(threadId, messageId, context); + return await _pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// + /// [Protocol Method] Deletes an existing message on an existing thread. + /// + /// + /// + /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. + /// + /// + /// + /// + /// Please try the simpler convenience overload with strongly typed models first. + /// + /// + /// + /// + /// Identifier of the thread. + /// Identifier of the message. + /// The request context, which can override default behaviors of the client pipeline on a per-call basis. + /// or is null. + /// or is an empty string, and was expected to be non-empty. + /// Service returned a non-success status code. + /// The response returned from the service. + internal virtual Response InternalDeleteMessage(string threadId, string messageId, RequestContext context) + { + Argument.AssertNotNullOrEmpty(threadId, nameof(threadId)); + Argument.AssertNotNullOrEmpty(messageId, nameof(messageId)); + + using var scope = ClientDiagnostics.CreateScope("ThreadMessages.InternalDeleteMessage"); + scope.Start(); + try + { + using HttpMessage message = CreateInternalDeleteMessageRequest(threadId, messageId, context); + return _pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + internal HttpMessage CreateCreateMessageRequest(string threadId, RequestContent content, RequestContext context) { var message = _pipeline.CreateMessage(context, ResponseClassifier200); @@ -520,6 +634,23 @@ internal HttpMessage CreateUpdateMessageRequest(string threadId, string messageI return message; } + internal HttpMessage CreateInternalDeleteMessageRequest(string threadId, string messageId, RequestContext context) + { + var message = _pipeline.CreateMessage(context, ResponseClassifier200); + var request = message.Request; + request.Method = RequestMethod.Delete; + var uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/threads/", false); + uri.AppendPath(threadId, true); + uri.AppendPath("/messages/", false); + uri.AppendPath(messageId, true); + uri.AppendQuery("api-version", _apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + private static RequestContext DefaultRequestContext = new RequestContext(); internal static RequestContext FromCancellationToken(CancellationToken cancellationToken = default) { diff --git a/sdk/ai/Azure.AI.Agents.Persistent/tests/PersistentAgentsTests.cs b/sdk/ai/Azure.AI.Agents.Persistent/tests/PersistentAgentsTests.cs index cead4a3bb908..8c7dc4ab483a 100644 --- a/sdk/ai/Azure.AI.Agents.Persistent/tests/PersistentAgentsTests.cs +++ b/sdk/ai/Azure.AI.Agents.Persistent/tests/PersistentAgentsTests.cs @@ -448,6 +448,18 @@ public async Task TestListMessage() } Assert.AreEqual(0, ids.Count); Assert.AreEqual(2, (await msgResp.ToListAsync()).Count); + // Delete message. + bool wasDeleted = await client.Messages.DeleteMessageAsync(threadId: thread.Id, messageId: msg1.Id); + Assert.IsTrue(wasDeleted); + ids.Add(msg1.Id); + ids.Add(msg2.Id); + msgResp = client.Messages.GetMessagesAsync(thread.Id); + await foreach (PersistentThreadMessage msg in msgResp) + { + ids.Remove(msg.Id); + } + Assert.AreEqual(1, ids.Count); + Assert.That(ids.Contains(msg1.Id)); } [RecordedTest] diff --git a/sdk/ai/Azure.AI.Agents.Persistent/tsp-location.yaml b/sdk/ai/Azure.AI.Agents.Persistent/tsp-location.yaml index 55cc339ea283..2988954ea2b9 100644 --- a/sdk/ai/Azure.AI.Agents.Persistent/tsp-location.yaml +++ b/sdk/ai/Azure.AI.Agents.Persistent/tsp-location.yaml @@ -1,4 +1,4 @@ directory: specification/ai/Azure.AI.Agents -commit: 92d30ce3b5d901b4dab77bb3d7784e49e5456519 +commit: 5be7fb743c2c097ce0d6420969ee26acee1730ac repo: Azure/azure-rest-api-specs additionalDirectories: