fixes for test paths
This commit is contained in:
@@ -72,6 +72,7 @@ The current .NET pass covers only a narrow, explicitly synthetic subset of obser
|
|||||||
- `CLIENT_NLU` turn completion using remembered listen/session metadata
|
- `CLIENT_NLU` turn completion using remembered listen/session metadata
|
||||||
- `CLIENT_ASR` turn completion, including a synthetic STT seam for buffered-audio replay
|
- `CLIENT_ASR` turn completion, including a synthetic STT seam for buffered-audio replay
|
||||||
- `EOS` emission after completed turns
|
- `EOS` emission after completed turns
|
||||||
|
- delayed `SKILL_ACTION` emission after `EOS` on completed turn flows to better match the Node oracle timing
|
||||||
- first richer vertical slice for joke/chat `SKILL_ACTION` playback
|
- first richer vertical slice for joke/chat `SKILL_ACTION` playback
|
||||||
|
|
||||||
This does not yet mean parity for:
|
This does not yet mean parity for:
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ Current websocket scope is still intentionally narrow:
|
|||||||
- structured websocket telemetry and live-run fixture export
|
- structured websocket telemetry and live-run fixture export
|
||||||
- `CONTEXT` capture and follow-up turn state
|
- `CONTEXT` capture and follow-up turn state
|
||||||
- `EOS` completion
|
- `EOS` completion
|
||||||
|
- delayed `SKILL_ACTION` emission after `EOS` to preserve the current Node-observed turn sequence
|
||||||
- first skill vertical for joke/chat `SKILL_ACTION` playback
|
- first skill vertical for joke/chat `SKILL_ACTION` playback
|
||||||
- repo-root live-run capture support for both `captures/http/` and `captures/websocket/`
|
- repo-root live-run capture support for both `captures/http/` and `captures/websocket/`
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,11 @@ app.Use(async (context, next) =>
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reply.DelayMs > 0)
|
||||||
|
{
|
||||||
|
await Task.Delay(reply.DelayMs, context.RequestAborted);
|
||||||
|
}
|
||||||
|
|
||||||
var payload = Encoding.UTF8.GetBytes(reply.Text);
|
var payload = Encoding.UTF8.GetBytes(reply.Text);
|
||||||
await socket.SendAsync(payload, WebSocketMessageType.Text, true, context.RequestAborted);
|
await socket.SendAsync(payload, WebSocketMessageType.Text, true, context.RequestAborted);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Jibo.Cloud.Application.Services;
|
|||||||
|
|
||||||
public sealed class ResponsePlanToSocketMessagesMapper
|
public sealed class ResponsePlanToSocketMessagesMapper
|
||||||
{
|
{
|
||||||
public IReadOnlyList<string> Map(ResponsePlan plan, TurnContext turn, CloudSession session, bool emitSkillActions)
|
public IReadOnlyList<SocketReplyPlan> Map(ResponsePlan plan, TurnContext turn, CloudSession session, bool emitSkillActions)
|
||||||
{
|
{
|
||||||
var speak = plan.Actions.OfType<SpeakAction>().FirstOrDefault();
|
var speak = plan.Actions.OfType<SpeakAction>().FirstOrDefault();
|
||||||
var skill = plan.Actions.OfType<InvokeNativeSkillAction>().FirstOrDefault();
|
var skill = plan.Actions.OfType<InvokeNativeSkillAction>().FirstOrDefault();
|
||||||
@@ -15,9 +15,9 @@ public sealed class ResponsePlanToSocketMessagesMapper
|
|||||||
: session.LastTransId ?? string.Empty;
|
: session.LastTransId ?? string.Empty;
|
||||||
var transcript = turn.NormalizedTranscript ?? turn.RawTranscript ?? string.Empty;
|
var transcript = turn.NormalizedTranscript ?? turn.RawTranscript ?? string.Empty;
|
||||||
var rules = ReadRules(turn);
|
var rules = ReadRules(turn);
|
||||||
var messages = new List<string>();
|
var messages = new List<SocketReplyPlan>();
|
||||||
|
|
||||||
messages.Add(JsonSerializer.Serialize(new
|
messages.Add(new SocketReplyPlan(JsonSerializer.Serialize(new
|
||||||
{
|
{
|
||||||
type = "LISTEN",
|
type = "LISTEN",
|
||||||
transID = transId,
|
transID = transId,
|
||||||
@@ -43,9 +43,9 @@ public sealed class ResponsePlanToSocketMessagesMapper
|
|||||||
score = 0.95
|
score = 0.95
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
})));
|
||||||
|
|
||||||
messages.Add(JsonSerializer.Serialize(new
|
messages.Add(new SocketReplyPlan(JsonSerializer.Serialize(new
|
||||||
{
|
{
|
||||||
type = "EOS",
|
type = "EOS",
|
||||||
data = new
|
data = new
|
||||||
@@ -53,21 +53,23 @@ public sealed class ResponsePlanToSocketMessagesMapper
|
|||||||
sessionId = plan.SessionId,
|
sessionId = plan.SessionId,
|
||||||
transID = transId
|
transID = transId
|
||||||
}
|
}
|
||||||
}));
|
})));
|
||||||
|
|
||||||
if (emitSkillActions && speak is not null)
|
if (emitSkillActions && speak is not null)
|
||||||
{
|
{
|
||||||
messages.Add(JsonSerializer.Serialize(BuildSkillPayload(plan, turn, transId, speak, skill)));
|
messages.Add(new SocketReplyPlan(
|
||||||
|
JsonSerializer.Serialize(BuildSkillPayload(plan, turn, transId, speak, skill)),
|
||||||
|
DelayMs: 75));
|
||||||
}
|
}
|
||||||
|
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<string> MapFallback(CloudSession session, string transId, IReadOnlyList<string> rules)
|
public IReadOnlyList<SocketReplyPlan> MapFallback(CloudSession session, string transId, IReadOnlyList<string> rules)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
[
|
[
|
||||||
JsonSerializer.Serialize(new
|
new SocketReplyPlan(JsonSerializer.Serialize(new
|
||||||
{
|
{
|
||||||
type = "LISTEN",
|
type = "LISTEN",
|
||||||
transID = transId,
|
transID = transId,
|
||||||
@@ -93,8 +95,8 @@ public sealed class ResponsePlanToSocketMessagesMapper
|
|||||||
score = 0.95
|
score = 0.95
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
})),
|
||||||
JsonSerializer.Serialize(new
|
new SocketReplyPlan(JsonSerializer.Serialize(new
|
||||||
{
|
{
|
||||||
type = "EOS",
|
type = "EOS",
|
||||||
data = new
|
data = new
|
||||||
@@ -102,8 +104,8 @@ public sealed class ResponsePlanToSocketMessagesMapper
|
|||||||
sessionId = session.SessionId,
|
sessionId = session.SessionId,
|
||||||
transID = transId
|
transID = transId
|
||||||
}
|
}
|
||||||
}),
|
})),
|
||||||
JsonSerializer.Serialize(BuildGenericFallbackSkillPayload(transId))
|
new SocketReplyPlan(JsonSerializer.Serialize(BuildGenericFallbackSkillPayload(transId)), DelayMs: 75)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,4 +233,6 @@ public sealed class ResponsePlanToSocketMessagesMapper
|
|||||||
.Replace("\"", """, StringComparison.Ordinal)
|
.Replace("\"", """, StringComparison.Ordinal)
|
||||||
.Replace("'", "'", StringComparison.Ordinal);
|
.Replace("'", "'", StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed record SocketReplyPlan(string Text, int DelayMs = 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ public sealed class WebSocketTurnFinalizationService(
|
|||||||
session.LastIntent = "heyJibo";
|
session.LastIntent = "heyJibo";
|
||||||
session.LastListenType = "fallback";
|
session.LastListenType = "fallback";
|
||||||
var fallbackReplies = replyMapper.MapFallback(session, turnState.TransId ?? session.LastTransId ?? string.Empty, turnState.ListenRules)
|
var fallbackReplies = replyMapper.MapFallback(session, turnState.TransId ?? session.LastTransId ?? string.Empty, turnState.ListenRules)
|
||||||
.Select(text => new WebSocketReply { Text = text })
|
.Select(map => new WebSocketReply { Text = map.Text, DelayMs = map.DelayMs })
|
||||||
.ToArray();
|
.ToArray();
|
||||||
ResetBufferedAudio(session);
|
ResetBufferedAudio(session);
|
||||||
return fallbackReplies;
|
return fallbackReplies;
|
||||||
@@ -308,9 +308,10 @@ public sealed class WebSocketTurnFinalizationService(
|
|||||||
turnState.AwaitingTurnCompletion = false;
|
turnState.AwaitingTurnCompletion = false;
|
||||||
|
|
||||||
var emitSkillActions = messageType != "CLIENT_NLU";
|
var emitSkillActions = messageType != "CLIENT_NLU";
|
||||||
var replies = replyMapper.Map(plan, finalizedTurn, session, emitSkillActions).Select(text => new WebSocketReply
|
var replies = replyMapper.Map(plan, finalizedTurn, session, emitSkillActions).Select(map => new WebSocketReply
|
||||||
{
|
{
|
||||||
Text = text
|
Text = map.Text,
|
||||||
|
DelayMs = map.DelayMs
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
|
||||||
ResetBufferedAudio(session);
|
ResetBufferedAudio(session);
|
||||||
|
|||||||
@@ -3,5 +3,6 @@ namespace Jibo.Cloud.Domain.Models;
|
|||||||
public sealed class WebSocketReply
|
public sealed class WebSocketReply
|
||||||
{
|
{
|
||||||
public string? Text { get; init; }
|
public string? Text { get; init; }
|
||||||
|
public int DelayMs { get; init; }
|
||||||
public bool Close { get; init; }
|
public bool Close { get; init; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ public sealed class JiboWebSocketServiceTests
|
|||||||
Assert.Equal("LISTEN", ReadReplyType(replies[0]));
|
Assert.Equal("LISTEN", ReadReplyType(replies[0]));
|
||||||
Assert.Equal("EOS", ReadReplyType(replies[1]));
|
Assert.Equal("EOS", ReadReplyType(replies[1]));
|
||||||
Assert.Equal("SKILL_ACTION", ReadReplyType(replies[2]));
|
Assert.Equal("SKILL_ACTION", ReadReplyType(replies[2]));
|
||||||
|
Assert.Equal(75, replies[2].DelayMs);
|
||||||
|
|
||||||
using var listenPayload = JsonDocument.Parse(replies[0].Text!);
|
using var listenPayload = JsonDocument.Parse(replies[0].Text!);
|
||||||
Assert.Equal("hello jibo", listenPayload.RootElement.GetProperty("data").GetProperty("asr").GetProperty("text").GetString());
|
Assert.Equal("hello jibo", listenPayload.RootElement.GetProperty("data").GetProperty("asr").GetProperty("text").GetString());
|
||||||
@@ -124,6 +125,7 @@ public sealed class JiboWebSocketServiceTests
|
|||||||
Assert.Equal("LISTEN", ReadReplyType(replies[0]));
|
Assert.Equal("LISTEN", ReadReplyType(replies[0]));
|
||||||
Assert.Equal("EOS", ReadReplyType(replies[1]));
|
Assert.Equal("EOS", ReadReplyType(replies[1]));
|
||||||
Assert.Equal("SKILL_ACTION", ReadReplyType(replies[2]));
|
Assert.Equal("SKILL_ACTION", ReadReplyType(replies[2]));
|
||||||
|
Assert.Equal(75, replies[2].DelayMs);
|
||||||
|
|
||||||
using var listenPayload = JsonDocument.Parse(replies[0].Text!);
|
using var listenPayload = JsonDocument.Parse(replies[0].Text!);
|
||||||
Assert.Equal("tell me a joke", listenPayload.RootElement.GetProperty("data").GetProperty("asr").GetProperty("text").GetString());
|
Assert.Equal("tell me a joke", listenPayload.RootElement.GetProperty("data").GetProperty("asr").GetProperty("text").GetString());
|
||||||
@@ -180,6 +182,7 @@ public sealed class JiboWebSocketServiceTests
|
|||||||
Assert.Equal("LISTEN", ReadReplyType(replies[0]));
|
Assert.Equal("LISTEN", ReadReplyType(replies[0]));
|
||||||
Assert.Equal("EOS", ReadReplyType(replies[1]));
|
Assert.Equal("EOS", ReadReplyType(replies[1]));
|
||||||
Assert.Equal("SKILL_ACTION", ReadReplyType(replies[2]));
|
Assert.Equal("SKILL_ACTION", ReadReplyType(replies[2]));
|
||||||
|
Assert.Equal(75, replies[2].DelayMs);
|
||||||
|
|
||||||
using var listenPayload = JsonDocument.Parse(replies[0].Text!);
|
using var listenPayload = JsonDocument.Parse(replies[0].Text!);
|
||||||
Assert.Equal("heyJibo", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("intent").GetString());
|
Assert.Equal("heyJibo", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("intent").GetString());
|
||||||
@@ -324,6 +327,7 @@ public sealed class JiboWebSocketServiceTests
|
|||||||
Assert.Equal("LISTEN", ReadReplyType(finalizeReplies[0]));
|
Assert.Equal("LISTEN", ReadReplyType(finalizeReplies[0]));
|
||||||
Assert.Equal("EOS", ReadReplyType(finalizeReplies[1]));
|
Assert.Equal("EOS", ReadReplyType(finalizeReplies[1]));
|
||||||
Assert.Equal("SKILL_ACTION", ReadReplyType(finalizeReplies[2]));
|
Assert.Equal("SKILL_ACTION", ReadReplyType(finalizeReplies[2]));
|
||||||
|
Assert.Equal(75, finalizeReplies[2].DelayMs);
|
||||||
|
|
||||||
using var listenPayload = JsonDocument.Parse(finalizeReplies[0].Text!);
|
using var listenPayload = JsonDocument.Parse(finalizeReplies[0].Text!);
|
||||||
Assert.Equal("tell me a joke", listenPayload.RootElement.GetProperty("data").GetProperty("asr").GetProperty("text").GetString());
|
Assert.Equal("tell me a joke", listenPayload.RootElement.GetProperty("data").GetProperty("asr").GetProperty("text").GetString());
|
||||||
|
|||||||
Reference in New Issue
Block a user