a little code cleanup
This commit is contained in:
@@ -35,7 +35,7 @@ app.Use(async (context, next) =>
|
||||
return;
|
||||
}
|
||||
|
||||
if ((kind is "neo-hub-listen" or "neo-hub-proactive") && string.IsNullOrWhiteSpace(token))
|
||||
if (kind is "neo-hub-listen" or "neo-hub-proactive" && string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return;
|
||||
|
||||
@@ -9,11 +9,8 @@ public sealed class DefaultSttStrategySelector(IEnumerable<ISttStrategy> strateg
|
||||
public Task<ISttStrategy> SelectAsync(TurnContext turn, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var strategy = _strategies.FirstOrDefault(candidate => candidate.CanHandle(turn));
|
||||
if (strategy is null)
|
||||
{
|
||||
throw new InvalidOperationException("No STT strategy can handle the current turn.");
|
||||
}
|
||||
|
||||
return Task.FromResult(strategy);
|
||||
return strategy is null
|
||||
? throw new InvalidOperationException("No STT strategy can handle the current turn.")
|
||||
: Task.FromResult(strategy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,8 @@ public sealed class DefaultJiboRandomizer : IJiboRandomizer
|
||||
{
|
||||
public T Choose<T>(IReadOnlyList<T> items)
|
||||
{
|
||||
if (items.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot choose from an empty list.");
|
||||
}
|
||||
|
||||
return items[Random.Shared.Next(items.Count)];
|
||||
return items.Count == 0
|
||||
? throw new InvalidOperationException("Cannot choose from an empty list.")
|
||||
: items[Random.Shared.Next(items.Count)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ public sealed class JiboCloudProtocolService(ICloudStateStore stateStore)
|
||||
}).ToArray());
|
||||
}
|
||||
|
||||
private ProtocolDispatchResult HandleLog(string operation, ProtocolEnvelope envelope)
|
||||
private static ProtocolDispatchResult HandleLog(string operation, ProtocolEnvelope envelope)
|
||||
{
|
||||
return operation switch
|
||||
{
|
||||
@@ -393,22 +393,16 @@ public sealed class JiboCloudProtocolService(ICloudStateStore stateStore)
|
||||
|
||||
private ProtocolDispatchResult HandlePerson(string operation)
|
||||
{
|
||||
if (operation.Equals("ListHolidays", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ProtocolDispatchResult.Ok(stateStore.GetHolidays());
|
||||
}
|
||||
|
||||
return ProtocolDispatchResult.Ok(Array.Empty<object>());
|
||||
return ProtocolDispatchResult.Ok(operation.Equals("ListHolidays", StringComparison.OrdinalIgnoreCase)
|
||||
? stateStore.GetHolidays()
|
||||
: []);
|
||||
}
|
||||
|
||||
private ProtocolDispatchResult HandleBackup(string operation)
|
||||
{
|
||||
if (operation.Equals("List", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ProtocolDispatchResult.Ok(stateStore.GetBackups());
|
||||
}
|
||||
|
||||
return ProtocolDispatchResult.Ok(Array.Empty<object>());
|
||||
return operation.Equals("List", StringComparison.OrdinalIgnoreCase)
|
||||
? ProtocolDispatchResult.Ok(stateStore.GetBackups())
|
||||
: ProtocolDispatchResult.Ok(Array.Empty<object>());
|
||||
}
|
||||
|
||||
private ProtocolDispatchResult HandleKey(string operation, ProtocolEnvelope envelope)
|
||||
|
||||
@@ -80,12 +80,10 @@ public sealed class JiboInteractionService(
|
||||
return "Good afternoon. I am happy to be here.";
|
||||
}
|
||||
|
||||
if (lowered.Contains("good night", StringComparison.Ordinal))
|
||||
{
|
||||
return "Good night. Sleep tight.";
|
||||
}
|
||||
|
||||
return randomizer.Choose(catalog.GenericFallbackReplies).Replace("{transcript}", transcript, StringComparison.Ordinal);
|
||||
return lowered.Contains("good night", StringComparison.Ordinal)
|
||||
? "Good night. Sleep tight."
|
||||
: randomizer.Choose(catalog.GenericFallbackReplies)
|
||||
.Replace("{transcript}", transcript, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static string ResolveSemanticIntent(string loweredTranscript, string? clientIntent)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Text.Json;
|
||||
using Jibo.Cloud.Application.Abstractions;
|
||||
using Jibo.Cloud.Domain.Models;
|
||||
using Jibo.Runtime.Abstractions;
|
||||
|
||||
namespace Jibo.Cloud.Application.Services;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Jibo.Cloud.Application.Services;
|
||||
|
||||
public sealed class ProtocolToTurnContextMapper
|
||||
{
|
||||
public TurnContext MapListenMessage(WebSocketMessageEnvelope envelope, CloudSession session, string messageType)
|
||||
public static TurnContext MapListenMessage(WebSocketMessageEnvelope envelope, CloudSession session, string messageType)
|
||||
{
|
||||
var turnState = session.TurnState;
|
||||
var protocolOperation = messageType.ToLowerInvariant();
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Jibo.Cloud.Application.Services;
|
||||
|
||||
public sealed class ResponsePlanToSocketMessagesMapper
|
||||
{
|
||||
public IReadOnlyList<SocketReplyPlan> Map(ResponsePlan plan, TurnContext turn, CloudSession session, bool emitSkillActions)
|
||||
public static IReadOnlyList<SocketReplyPlan> Map(ResponsePlan plan, TurnContext turn, CloudSession session, bool emitSkillActions)
|
||||
{
|
||||
var speak = plan.Actions.OfType<SpeakAction>().FirstOrDefault();
|
||||
var skill = plan.Actions.OfType<InvokeNativeSkillAction>().FirstOrDefault();
|
||||
@@ -18,50 +18,50 @@ public sealed class ResponsePlanToSocketMessagesMapper
|
||||
var clientIntent = ReadAttribute(turn, "clientIntent");
|
||||
var rules = ReadRules(turn, messageType);
|
||||
var outboundIntent = string.Equals(messageType, "CLIENT_NLU", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(clientIntent)
|
||||
? clientIntent!
|
||||
? clientIntent
|
||||
: plan.IntentName ?? "unknown";
|
||||
var outboundAsrText = string.Equals(messageType, "CLIENT_NLU", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(clientIntent)
|
||||
? clientIntent!
|
||||
? clientIntent
|
||||
: transcript;
|
||||
var entities = ReadEntities(turn, messageType);
|
||||
var messages = new List<SocketReplyPlan>();
|
||||
|
||||
messages.Add(new SocketReplyPlan(JsonSerializer.Serialize(new
|
||||
var messages = new List<SocketReplyPlan>
|
||||
{
|
||||
type = "LISTEN",
|
||||
transID = transId,
|
||||
data = new
|
||||
new(JsonSerializer.Serialize(new
|
||||
{
|
||||
asr = new
|
||||
type = "LISTEN",
|
||||
transID = transId,
|
||||
data = new
|
||||
{
|
||||
confidence = 0.95,
|
||||
final = true,
|
||||
text = outboundAsrText
|
||||
},
|
||||
nlu = new
|
||||
{
|
||||
confidence = 0.95,
|
||||
intent = outboundIntent,
|
||||
rules,
|
||||
entities
|
||||
},
|
||||
match = new
|
||||
{
|
||||
intent = outboundIntent,
|
||||
rule = rules.FirstOrDefault() ?? string.Empty,
|
||||
score = 0.95
|
||||
asr = new
|
||||
{
|
||||
confidence = 0.95,
|
||||
final = true,
|
||||
text = outboundAsrText
|
||||
},
|
||||
nlu = new
|
||||
{
|
||||
confidence = 0.95,
|
||||
intent = outboundIntent,
|
||||
rules,
|
||||
entities
|
||||
},
|
||||
match = new
|
||||
{
|
||||
intent = outboundIntent,
|
||||
rule = rules.FirstOrDefault() ?? string.Empty,
|
||||
score = 0.95
|
||||
}
|
||||
}
|
||||
}
|
||||
})));
|
||||
|
||||
messages.Add(new SocketReplyPlan(JsonSerializer.Serialize(new
|
||||
{
|
||||
type = "EOS",
|
||||
ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
msgID = CreateHubMessageId(),
|
||||
transID = transId,
|
||||
data = new { }
|
||||
})));
|
||||
})),
|
||||
new(JsonSerializer.Serialize(new
|
||||
{
|
||||
type = "EOS",
|
||||
ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
msgID = CreateHubMessageId(),
|
||||
transID = transId,
|
||||
data = new { }
|
||||
}))
|
||||
};
|
||||
|
||||
if (emitSkillActions && speak is not null)
|
||||
{
|
||||
@@ -73,7 +73,7 @@ public sealed class ResponsePlanToSocketMessagesMapper
|
||||
return messages;
|
||||
}
|
||||
|
||||
public IReadOnlyList<SocketReplyPlan> MapFallback(CloudSession session, string transId, IReadOnlyList<string> rules)
|
||||
public static IReadOnlyList<SocketReplyPlan> MapFallback(CloudSession session, string transId, IReadOnlyList<string> rules)
|
||||
{
|
||||
return
|
||||
[
|
||||
@@ -149,7 +149,7 @@ public sealed class ResponsePlanToSocketMessagesMapper
|
||||
|
||||
return value switch
|
||||
{
|
||||
JsonElement jsonElement when jsonElement.ValueKind == JsonValueKind.Object => jsonElement,
|
||||
JsonElement { ValueKind: JsonValueKind.Object } jsonElement => jsonElement,
|
||||
IDictionary<string, object?> dictionary => dictionary,
|
||||
_ => new Dictionary<string, object?>()
|
||||
};
|
||||
|
||||
@@ -260,7 +260,7 @@ public sealed class WebSocketTurnFinalizationService(
|
||||
bool allowFallbackOnMissingTranscript,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var turn = turnContextMapper.MapListenMessage(envelope, session, messageType);
|
||||
var turn = ProtocolToTurnContextMapper.MapListenMessage(envelope, session, messageType);
|
||||
var finalizedTurn = await ResolveTranscriptAsync(turn, session, cancellationToken);
|
||||
var turnState = session.TurnState;
|
||||
if (string.IsNullOrWhiteSpace(finalizedTurn.NormalizedTranscript) &&
|
||||
@@ -278,7 +278,7 @@ public sealed class WebSocketTurnFinalizationService(
|
||||
session.LastTranscript = string.Empty;
|
||||
session.LastIntent = "heyJibo";
|
||||
session.LastListenType = "fallback";
|
||||
var fallbackReplies = replyMapper.MapFallback(session, turnState.TransId ?? session.LastTransId ?? string.Empty, turnState.ListenRules)
|
||||
var fallbackReplies = ResponsePlanToSocketMessagesMapper.MapFallback(session, turnState.TransId ?? session.LastTransId ?? string.Empty, turnState.ListenRules)
|
||||
.Select(map => new WebSocketReply { Text = map.Text, DelayMs = map.DelayMs })
|
||||
.ToArray();
|
||||
ResetBufferedAudio(session);
|
||||
@@ -318,7 +318,7 @@ public sealed class WebSocketTurnFinalizationService(
|
||||
turnState.AwaitingTurnCompletion = false;
|
||||
|
||||
var emitSkillActions = messageType != "CLIENT_NLU";
|
||||
var replies = replyMapper.Map(plan, finalizedTurn, session, emitSkillActions).Select(map => new WebSocketReply
|
||||
var replies = ResponsePlanToSocketMessagesMapper.Map(plan, finalizedTurn, session, emitSkillActions).Select(map => new WebSocketReply
|
||||
{
|
||||
Text = map.Text,
|
||||
DelayMs = map.DelayMs
|
||||
@@ -332,10 +332,11 @@ public sealed class WebSocketTurnFinalizationService(
|
||||
{
|
||||
var turnState = session.TurnState;
|
||||
return turnState.AwaitingTurnCompletion &&
|
||||
turnState.SawListen &&
|
||||
turnState.SawContext &&
|
||||
turnState.BufferedAudioChunkCount >= AutoFinalizeMinBufferedAudioChunks &&
|
||||
turnState.BufferedAudioBytes >= AutoFinalizeMinBufferedAudioBytes;
|
||||
turnState is
|
||||
{
|
||||
SawListen: true, SawContext: true, BufferedAudioChunkCount: >= AutoFinalizeMinBufferedAudioChunks,
|
||||
BufferedAudioBytes: >= AutoFinalizeMinBufferedAudioBytes
|
||||
};
|
||||
}
|
||||
|
||||
private static string? ExtractDataPayload(string? text)
|
||||
|
||||
@@ -32,11 +32,9 @@ public sealed class ExternalProcessRunner : IExternalProcessRunner
|
||||
var stdOut = await stdOutTask;
|
||||
var stdErr = await stdErrTask;
|
||||
|
||||
if (process.ExitCode != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"External process '{fileName}' failed with exit code {process.ExitCode}: {stdErr}");
|
||||
}
|
||||
|
||||
return new ExternalProcessResult(process.ExitCode, stdOut, stdErr);
|
||||
return process.ExitCode != 0
|
||||
? throw new InvalidOperationException(
|
||||
$"External process '{fileName}' failed with exit code {process.ExitCode}: {stdErr}")
|
||||
: new ExternalProcessResult(process.ExitCode, stdOut, stdErr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ public sealed class LocalWhisperCppBufferedAudioSttStrategy(
|
||||
byte[][] jagged => jagged,
|
||||
IReadOnlyList<byte[]> typed => typed,
|
||||
IEnumerable<byte[]> enumerable => enumerable.ToArray(),
|
||||
JsonElement jsonElement when jsonElement.ValueKind == JsonValueKind.Array => jsonElement.EnumerateArray()
|
||||
JsonElement { ValueKind: JsonValueKind.Array } jsonElement => jsonElement.EnumerateArray()
|
||||
.Where(static item => item.ValueKind == JsonValueKind.Array)
|
||||
.Select(static item => item.EnumerateArray().Select(static b => (byte)b.GetInt32()).ToArray())
|
||||
.ToArray(),
|
||||
@@ -128,12 +128,7 @@ public sealed class LocalWhisperCppBufferedAudioSttStrategy(
|
||||
.Where(static line => !string.IsNullOrWhiteSpace(line))
|
||||
.ToArray();
|
||||
|
||||
if (timecoded.Length > 0)
|
||||
{
|
||||
return string.Join(" ", timecoded).Trim();
|
||||
}
|
||||
|
||||
return string.Join(" ", lines).Trim();
|
||||
return timecoded.Length > 0 ? string.Join(" ", timecoded).Trim() : string.Join(" ", lines).Trim();
|
||||
}
|
||||
|
||||
private static void TryDelete(string path)
|
||||
|
||||
@@ -72,12 +72,9 @@ internal static class OggOpusAudioNormalizer
|
||||
}
|
||||
|
||||
var expectedLength = 27 + pageSegments + payloadLength;
|
||||
if (buffer.Length < expectedLength)
|
||||
{
|
||||
throw new InvalidOperationException("Buffered Ogg page payload was truncated.");
|
||||
}
|
||||
|
||||
return new ParsedOggPage(BinaryPrimitives.ReadUInt64LittleEndian(buffer.AsSpan(6, 8)));
|
||||
return buffer.Length < expectedLength
|
||||
? throw new InvalidOperationException("Buffered Ogg page payload was truncated.")
|
||||
: new ParsedOggPage(BinaryPrimitives.ReadUInt64LittleEndian(buffer.AsSpan(6, 8)));
|
||||
}
|
||||
|
||||
private static uint ComputeCrc(byte[] buffer)
|
||||
|
||||
@@ -22,7 +22,7 @@ internal static class CapturePathResolver
|
||||
}
|
||||
|
||||
var directory = new DirectoryInfo(Path.GetFullPath(startPath));
|
||||
if (!directory.Exists && directory.Parent is not null)
|
||||
if (directory is { Exists: false, Parent: not null })
|
||||
{
|
||||
directory = directory.Parent;
|
||||
}
|
||||
|
||||
@@ -42,22 +42,18 @@ public sealed class FileWebSocketTelemetrySink(
|
||||
|
||||
public Task RecordInboundAsync(WebSocketMessageEnvelope envelope, CloudSession session, string? messageType, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!options.Value.Enabled)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
return WriteRecordAsync(BuildRecord("message_in", envelope, session, messageType, "in", null, null), cancellationToken);
|
||||
return !options.Value.Enabled
|
||||
? Task.CompletedTask
|
||||
: WriteRecordAsync(BuildRecord("message_in", envelope, session, messageType, "in", null, null),
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
public Task RecordTurnEventAsync(WebSocketMessageEnvelope envelope, CloudSession session, string eventType, IReadOnlyDictionary<string, object?> details, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!options.Value.Enabled)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
return WriteRecordAsync(BuildRecord(eventType, envelope, session, null, "internal", null, details), cancellationToken);
|
||||
return !options.Value.Enabled
|
||||
? Task.CompletedTask
|
||||
: WriteRecordAsync(BuildRecord(eventType, envelope, session, null, "internal", null, details),
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
public async Task RecordOutboundAsync(WebSocketMessageEnvelope envelope, CloudSession session, IReadOnlyList<WebSocketReply> replies, CancellationToken cancellationToken = default)
|
||||
|
||||
@@ -177,6 +177,8 @@ while (!cts.IsCancellationRequested)
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
static string PickBestUtterance(List<AsrUtterance>? utterances)
|
||||
{
|
||||
if (utterances == null || utterances.Count == 0)
|
||||
@@ -201,7 +203,7 @@ static string NormalizeUtterance(string? text)
|
||||
|
||||
// Very light cleanup for occasional weird leading duplication like "wWhat"
|
||||
if (s.Length >= 2 && char.ToLowerInvariant(s[0]) == char.ToLowerInvariant(s[1]))
|
||||
s = s.Substring(1);
|
||||
s = s[1..];
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -216,10 +218,7 @@ static string BuildReply(string heard)
|
||||
if (text.Contains("hello") || text.Contains("hi"))
|
||||
return "Hello! I heard you loud and clear.";
|
||||
|
||||
if (text.Contains("your name"))
|
||||
return "I am Jibo, running with a local demo bridge.";
|
||||
|
||||
return $"You said: {heard}";
|
||||
return text.Contains("your name") ? "I am Jibo, running with a local demo bridge." : $"You said: {heard}";
|
||||
}
|
||||
|
||||
public sealed class AsrEvent
|
||||
|
||||
@@ -98,9 +98,12 @@ public sealed class FileWebSocketTelemetrySinkTests : IDisposable
|
||||
{
|
||||
Token = "token-relative",
|
||||
HostName = "neo-hub.jibo.com",
|
||||
Path = "/listen"
|
||||
Path = "/listen",
|
||||
TurnState =
|
||||
{
|
||||
TransId = "trans-relative"
|
||||
}
|
||||
};
|
||||
session.TurnState.TransId = "trans-relative";
|
||||
|
||||
await sink.RecordConnectionOpenedAsync(envelope, session);
|
||||
await sink.RecordOutboundAsync(envelope, session, [new WebSocketReply { Text = """{"type":"LISTEN"}""" }]);
|
||||
|
||||
@@ -105,8 +105,8 @@ public sealed class LocalWhisperCppBufferedAudioSttStrategyTests
|
||||
|
||||
if (string.Equals(fileName, "ffmpeg", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var outputPath = arguments.Last();
|
||||
File.WriteAllBytes(outputPath, [0x52, 0x49, 0x46, 0x46]);
|
||||
var outputPath = arguments[^1];
|
||||
File.WriteAllBytes(outputPath, "RIFF"u8);
|
||||
return Task.FromResult(new ExternalProcessResult(0, string.Empty, string.Empty));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user