2026-04-11 07:12:57 -05:00
|
|
|
using System.Text.Json;
|
|
|
|
|
using Jibo.Cloud.Domain.Models;
|
|
|
|
|
using Jibo.Runtime.Abstractions;
|
|
|
|
|
|
|
|
|
|
namespace Jibo.Cloud.Application.Services;
|
|
|
|
|
|
|
|
|
|
public sealed class ProtocolToTurnContextMapper
|
|
|
|
|
{
|
2026-04-18 00:54:56 -05:00
|
|
|
public static TurnContext MapListenMessage(WebSocketMessageEnvelope envelope, CloudSession session, string messageType)
|
2026-04-11 07:12:57 -05:00
|
|
|
{
|
2026-04-12 08:31:33 -05:00
|
|
|
var turnState = session.TurnState;
|
2026-04-11 21:50:26 -05:00
|
|
|
var protocolOperation = messageType.ToLowerInvariant();
|
|
|
|
|
var attributes = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
|
|
|
|
{
|
|
|
|
|
["messageType"] = messageType
|
|
|
|
|
};
|
2026-04-16 15:40:28 -05:00
|
|
|
var text = ExtractTranscript(envelope.Text, attributes);
|
2026-04-11 21:50:26 -05:00
|
|
|
|
2026-04-12 08:31:33 -05:00
|
|
|
if (!string.IsNullOrWhiteSpace(turnState.TransId))
|
2026-04-11 21:50:26 -05:00
|
|
|
{
|
2026-04-12 08:31:33 -05:00
|
|
|
attributes["transID"] = turnState.TransId;
|
2026-04-11 21:50:26 -05:00
|
|
|
}
|
|
|
|
|
|
2026-04-12 08:31:33 -05:00
|
|
|
if (!string.IsNullOrWhiteSpace(turnState.ContextPayload))
|
2026-04-11 21:50:26 -05:00
|
|
|
{
|
2026-04-12 08:31:33 -05:00
|
|
|
attributes["context"] = turnState.ContextPayload;
|
2026-04-11 21:50:26 -05:00
|
|
|
}
|
|
|
|
|
|
2026-04-12 08:31:33 -05:00
|
|
|
if (turnState.ListenRules.Count > 0)
|
2026-04-11 21:50:26 -05:00
|
|
|
{
|
2026-04-12 08:31:33 -05:00
|
|
|
attributes["listenRules"] = turnState.ListenRules;
|
2026-04-11 21:50:26 -05:00
|
|
|
}
|
2026-04-11 07:12:57 -05:00
|
|
|
|
2026-04-12 08:31:33 -05:00
|
|
|
if (turnState.BufferedAudioBytes > 0)
|
2026-04-11 22:11:08 -05:00
|
|
|
{
|
2026-04-12 08:31:33 -05:00
|
|
|
attributes["bufferedAudioBytes"] = turnState.BufferedAudioBytes;
|
|
|
|
|
attributes["bufferedAudioChunks"] = turnState.BufferedAudioChunkCount;
|
2026-04-16 15:40:28 -05:00
|
|
|
attributes["bufferedAudioFrames"] = turnState.BufferedAudioFrames.Select(frame => frame.ToArray()).ToArray();
|
2026-04-11 22:11:08 -05:00
|
|
|
}
|
|
|
|
|
|
2026-04-12 08:31:33 -05:00
|
|
|
if (!string.IsNullOrWhiteSpace(turnState.AudioTranscriptHint))
|
2026-04-11 22:11:08 -05:00
|
|
|
{
|
2026-04-12 08:31:33 -05:00
|
|
|
attributes["audioTranscriptHint"] = turnState.AudioTranscriptHint;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (turnState.FinalizeAttemptCount > 0)
|
|
|
|
|
{
|
|
|
|
|
attributes["finalizeAttemptCount"] = turnState.FinalizeAttemptCount;
|
2026-04-11 22:11:08 -05:00
|
|
|
}
|
|
|
|
|
|
2026-04-11 07:12:57 -05:00
|
|
|
return new TurnContext
|
|
|
|
|
{
|
|
|
|
|
SessionId = session.SessionId,
|
2026-04-11 21:50:26 -05:00
|
|
|
InputMode = session.FollowUpOpen ? TurnInputMode.FollowUp : TurnInputMode.DirectText,
|
2026-04-11 07:12:57 -05:00
|
|
|
SourceKind = TurnSourceKind.Api,
|
|
|
|
|
RawTranscript = text,
|
|
|
|
|
NormalizedTranscript = text?.Trim(),
|
|
|
|
|
DeviceId = session.DeviceId,
|
|
|
|
|
HostName = envelope.HostName,
|
|
|
|
|
RequestId = envelope.ConnectionId,
|
|
|
|
|
ProtocolService = "neo-hub",
|
2026-04-11 21:50:26 -05:00
|
|
|
ProtocolOperation = protocolOperation,
|
2026-04-11 07:12:57 -05:00
|
|
|
FirmwareVersion = session.Metadata.TryGetValue("firmwareVersion", out var firmwareVersion) ? firmwareVersion as string : null,
|
|
|
|
|
ApplicationVersion = session.Metadata.TryGetValue("applicationVersion", out var applicationVersion) ? applicationVersion as string : null,
|
2026-04-11 21:50:26 -05:00
|
|
|
IsFollowUpEligible = true,
|
|
|
|
|
Attributes = attributes
|
2026-04-11 07:12:57 -05:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-16 15:40:28 -05:00
|
|
|
private static string? ExtractTranscript(string? text, IDictionary<string, object?> attributes)
|
2026-04-11 07:12:57 -05:00
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(text))
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
using var document = JsonDocument.Parse(text);
|
|
|
|
|
var root = document.RootElement;
|
|
|
|
|
|
|
|
|
|
if (root.TryGetProperty("data", out var data))
|
|
|
|
|
{
|
|
|
|
|
if (data.TryGetProperty("text", out var transcript) && transcript.ValueKind == JsonValueKind.String)
|
|
|
|
|
{
|
|
|
|
|
return transcript.GetString();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 22:11:08 -05:00
|
|
|
if (data.TryGetProperty("asr", out var asr) &&
|
|
|
|
|
asr.ValueKind == JsonValueKind.Object &&
|
|
|
|
|
asr.TryGetProperty("text", out var asrText) &&
|
|
|
|
|
asrText.ValueKind == JsonValueKind.String)
|
|
|
|
|
{
|
|
|
|
|
return asrText.GetString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data.TryGetProperty("transcriptHint", out var transcriptHint) && transcriptHint.ValueKind == JsonValueKind.String)
|
|
|
|
|
{
|
|
|
|
|
return transcriptHint.GetString();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 07:12:57 -05:00
|
|
|
if (data.TryGetProperty("intent", out var intent) && intent.ValueKind == JsonValueKind.String)
|
2026-04-16 15:40:28 -05:00
|
|
|
{
|
|
|
|
|
attributes["clientIntent"] = intent.GetString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data.TryGetProperty("rules", out var rules) && rules.ValueKind == JsonValueKind.Array)
|
|
|
|
|
{
|
|
|
|
|
attributes["clientRules"] = rules.EnumerateArray()
|
|
|
|
|
.Where(item => item.ValueKind == JsonValueKind.String)
|
|
|
|
|
.Select(item => item.GetString() ?? string.Empty)
|
|
|
|
|
.Where(rule => !string.IsNullOrWhiteSpace(rule))
|
|
|
|
|
.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data.TryGetProperty("entities", out var entities) && entities.ValueKind == JsonValueKind.Object)
|
|
|
|
|
{
|
|
|
|
|
attributes["clientEntities"] = entities.Clone();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (intent.ValueKind == JsonValueKind.String)
|
2026-04-11 07:12:57 -05:00
|
|
|
{
|
|
|
|
|
return intent.GetString();
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-11 22:11:08 -05:00
|
|
|
|
|
|
|
|
return null;
|
2026-04-11 07:12:57 -05:00
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
return text;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|