// Copyright (c) Microsoft. All rights reserved. using System; using System.Diagnostics.CodeAnalysis; using Microsoft.Agents.AI.Workflows.Checkpointing; using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI.Workflows; /// /// Represents a request to an external input port. /// /// The port to invoke. /// A unique identifier for this request instance. /// The data contained in the request. public record ExternalRequest(RequestPortInfo PortInfo, string RequestId, PortableValue Data) { /// /// Attempts to retrieve the underlying data as the specified type. /// /// The type to which the data should be cast or converted. /// The data cast to the specified type, or null if the data cannot be cast to the specified type. public TValue? DataAs() => this.Data.As(); /// /// Determines whether the underlying data is of the specified type. /// /// The type to compare with the underlying data. /// true if the underlying data is of type TValue; otherwise, false. public bool DataIs() => this.Data.Is(); /// /// Determines whether the underlying data is of the specified type and outputs the value if it is. /// /// The type to compare with the underlying data. /// true if the underlying data is of type TValue; otherwise, false. public bool DataIs([NotNullWhen(true)] out TValue? value) => this.Data.Is(out value); /// /// Creates a new for the specified input port and data payload. /// /// The port to invoke. /// The data contained in the request. /// An optional unique identifier for this request instance. If null, a UUID will be generated. /// An instance containing the specified port, data, and request identifier. /// Thrown when the input data object does not match the expected request type. public static ExternalRequest Create(RequestPort port, [NotNull] object data, string? requestId = null) { if (!port.Request.IsInstanceOfType(Throw.IfNull(data))) { throw new InvalidOperationException( $"Message type {data.GetType().Name} is not assignable to the request type {port.Request.Name} of input port {port.Id}."); } requestId ??= Guid.NewGuid().ToString("N"); return new ExternalRequest(port.ToPortInfo(), requestId, new PortableValue(data)); } /// /// Creates a new for the specified input port and data payload. /// /// The type of request data. /// The input port that identifies the target endpoint for the request. Must not be null. /// The data payload to include in the request. Must not be null. /// An optional identifier for the request. If null, a default identifier may be assigned. /// An instance containing the specified port, data, and request identifier. public static ExternalRequest Create(RequestPort port, T data, string? requestId = null) => Create(port, (object)Throw.IfNull(data), requestId); /// /// Creates a new corresponding to the request, with the speicified data payload. /// /// The data contained in the response. /// An instance corresponding to this request with the specified data. /// Thrown when the input data object does not match the expected response type. public ExternalResponse CreateResponse(object data) { if (!Throw.IfNull(this.PortInfo).ResponseType.IsMatchPolymorphic(Throw.IfNull(data).GetType())) { throw new InvalidOperationException( $"Message type {data.GetType().Name} does not match expected response type {this.PortInfo.ResponseType.TypeName} of input port {this.PortInfo.PortId}."); } return new ExternalResponse(this.PortInfo, this.RequestId, new PortableValue(data)); } internal ExternalResponse RewrapResponse(ExternalResponse response) { return new ExternalResponse(this.PortInfo, this.RequestId, response.Data); } /// /// Creates a new corresponding to the request, with the speicified data payload. /// /// The type of the response data. /// The data contained in the response. /// An instance corresponding to this request with the specified data. public ExternalResponse CreateResponse(T data) => this.CreateResponse((object)Throw.IfNull(data)); }