version 18 fixes

This commit is contained in:
Jacob Dubin
2026-04-25 22:44:57 -05:00
parent 06af262192
commit 773e768898
14 changed files with 3372 additions and 37 deletions

View File

@@ -234,7 +234,9 @@ Useful external references:
- The same bundle also confirmed some `OPENJIBO_AUDIO_RECEIVED` noise was still coming from an older running build, because the current `.NET` source no longer emits that synthetic websocket event. When a live session still shows it, operator workflow should treat that as a rebuild/restart sanity-check clue before assuming a new regression.
- Spoken `cancel alarm` should map into stock `@be/clock` `delete` semantics, not generic chat. The current cloud path now mirrors that local intent so voice cancel can follow the same lane as the robot's clock skill.
- The `jibo test 20` bundle suggests gallery itself is mostly okay in the latest pass, but clock and protocol polish still matter: stock `CLIENT_NLU intent="set"` with only `domain="alarm"` should stay on the local clarification path instead of defaulting the cloud payload to `7:00`, and stock `CLIENT_NLU intent="cancel"` on `clock/alarm_timer_query_menu` should reuse the last active clock domain so delete actually lands on alarm/timer instead of generic chat.
- The same `jibo test 20` robot logs also showed `OPENJIBO_TURN_PENDING` and `OPENJIBO_CONTEXT_ACK` are just unknown-event noise on stock OS 1.9, so the compatibility layer now keeps that turn state internally and stops sending those synthetic websocket event types to the robot.
- The same `jibo test 20` robot logs also showed `OPENJIBO_TURN_PENDING` and `OPENJIBO_CONTEXT_ACK` are just unknown-event noise on stock OS 1.9, so the compatibility layer now keeps that turn state internally and stops sending those synthetic websocket event types to the robot.
- The `jibo test 21` bundle confirms the first gallery path is healthy enough to open `@be/gallery`, ask the stock `shared/yes_no` follow-up, and hand into `@be/create` for a photo; the remaining alarm pain in that round was mostly the transcript collapsing to `set an alarm for suddenly` / `set an alarm for...`, which means the right fix is to keep `alarm_clarify` local by handing straight into `@be/clock` with `intent="set"` and `domain="alarm"` instead of asking the clarification through Nimbus-only cloud speech.
- The same bundle also showed stock OS 1.9 still logs fallback `OPENJIBO_ACK` packets as unknown-event noise, so the websocket compatibility layer now drops unrecognized inbound message types silently instead of replying with a synthetic ack the robot does not understand.
- That bundle still contains real `ffmpeg` / `whisper.cpp` failures in the buffered-audio STT seam, and it also includes a genuine `jibo-server-service` broken-pipe / server-connection-lost episode, so not every freeze in that round should be blamed on cloud turn routing alone.
## MCP-Like Ideas

View File

@@ -6,7 +6,7 @@
This is the production-oriented path for restoring device connectivity and creating a foundation for future runtime, AI, and OTA work.
Current spoken cloud version: `Open Jibo Cloud version 1.0.17.`
Current spoken cloud version: `Open Jibo Cloud version 1.0.18.`
Release hygiene reminder:

View File

@@ -59,8 +59,8 @@ public sealed class JiboInteractionService(
"alarm_delete" => BuildClockLaunchDecision("alarm_delete", "alarm", "delete", "Canceling the alarm."),
"timer_value" => BuildTimerValueDecision(lowered, isTimerValueTurn, clientEntities),
"alarm_value" => BuildAlarmValueDecision(lowered, isAlarmValueTurn, referenceLocalTime, clientEntities),
"timer_clarify" => new JiboInteractionDecision("timer_clarify", "How long should I set the timer for?"),
"alarm_clarify" => new JiboInteractionDecision("alarm_clarify", "What time should I set the alarm for?"),
"timer_clarify" => BuildClockClarifyDecision("timer_clarify", "timer", "How long should I set the timer for?"),
"alarm_clarify" => BuildClockClarifyDecision("alarm_clarify", "alarm", "What time should I set the alarm for?"),
"photo_gallery" => BuildPhotoGalleryLaunchDecision(),
"snapshot" => BuildPhotoCreateDecision("snapshot", "Taking a picture.", "createOnePhoto"),
"photobooth" => BuildPhotoCreateDecision("photobooth", "Starting photobooth.", "createSomePhotos"),
@@ -539,6 +539,20 @@ public sealed class JiboInteractionService(
return BuildClockLaunchDecision($"{domain}_menu", domain, "menu", replyText);
}
private static JiboInteractionDecision BuildClockClarifyDecision(string intentName, string domain, string replyText)
{
return new JiboInteractionDecision(
intentName,
replyText,
"@be/clock",
new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
{
["skillId"] = "@be/clock",
["domain"] = domain,
["clockIntent"] = "set"
});
}
private static JiboInteractionDecision BuildTimerValueDecision(
string loweredTranscript,
bool allowImplicit,

View File

@@ -72,21 +72,7 @@ public sealed class JiboWebSocketService(
return replies;
}
return
[
new WebSocketReply
{
Text = JsonSerializer.Serialize(new
{
type = "OPENJIBO_ACK",
data = new
{
messageType = parsedType,
sessionId = session.SessionId
}
})
}
];
return [];
}
private static string ReadMessageType(string? text)

View File

@@ -2,7 +2,7 @@ namespace Jibo.Cloud.Application.Services;
public static class OpenJiboCloudBuildInfo
{
public const string Version = "1.0.17";
public const string Version = "1.0.18";
public static string VersionWords => Version.Replace(".", " dot ");

View File

@@ -517,6 +517,8 @@ public sealed class WebSocketTurnFinalizationService(
!string.Equals(plan.IntentName, "clock_menu", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(plan.IntentName, "timer_menu", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(plan.IntentName, "alarm_menu", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(plan.IntentName, "timer_clarify", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(plan.IntentName, "alarm_clarify", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(plan.IntentName, "timer_value", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(plan.IntentName, "alarm_value", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(plan.IntentName, "photo_gallery", StringComparison.OrdinalIgnoreCase) &&

View File

@@ -498,7 +498,9 @@ public sealed class JiboInteractionServiceTests
});
Assert.Equal("alarm_clarify", decision.IntentName);
Assert.Null(decision.SkillName);
Assert.Equal("@be/clock", decision.SkillName);
Assert.Equal("alarm", decision.SkillPayload!["domain"]);
Assert.Equal("set", decision.SkillPayload["clockIntent"]);
Assert.Equal("What time should I set the alarm for?", decision.ReplyText);
}
@@ -540,7 +542,9 @@ public sealed class JiboInteractionServiceTests
});
Assert.Equal("alarm_clarify", decision.IntentName);
Assert.Null(decision.SkillName);
Assert.Equal("@be/clock", decision.SkillName);
Assert.Equal("alarm", decision.SkillPayload!["domain"]);
Assert.Equal("set", decision.SkillPayload["clockIntent"]);
}
[Fact]
@@ -578,7 +582,9 @@ public sealed class JiboInteractionServiceTests
});
Assert.Equal("timer_clarify", decision.IntentName);
Assert.Null(decision.SkillName);
Assert.Equal("@be/clock", decision.SkillName);
Assert.Equal("timer", decision.SkillPayload!["domain"]);
Assert.Equal("set", decision.SkillPayload["clockIntent"]);
Assert.Equal("How long should I set the timer for?", decision.ReplyText);
}

View File

@@ -649,7 +649,7 @@ public sealed class JiboWebSocketServiceTests
}
[Fact]
public async Task ClientAsr_SetAlarmWithoutTime_UsesClarificationSpeechInsteadOfClockRedirect()
public async Task ClientAsr_SetAlarmWithoutTime_RedirectsIntoClockSkillWithoutDefaultingTime()
{
await _service.HandleMessageAsync(new WebSocketMessageEnvelope
{
@@ -669,20 +669,20 @@ public sealed class JiboWebSocketServiceTests
Text = """{"type":"CLIENT_ASR","transID":"trans-clock-clarify-alarm","data":{"text":"set an alarm"}}"""
});
Assert.Equal(3, replies.Count);
Assert.Equal("SKILL_ACTION", ReadReplyType(replies[2]));
Assert.Equal(4, replies.Count);
Assert.Equal("LISTEN", ReadReplyType(replies[0]));
Assert.Equal("EOS", ReadReplyType(replies[1]));
Assert.Equal("SKILL_REDIRECT", ReadReplyType(replies[2]));
Assert.Equal("SKILL_ACTION", ReadReplyType(replies[3]));
using var skillPayload = JsonDocument.Parse(replies[2].Text!);
var esml = skillPayload.RootElement
.GetProperty("data")
.GetProperty("action")
.GetProperty("config")
.GetProperty("jcp")
.GetProperty("config")
.GetProperty("play")
.GetProperty("esml")
.GetString();
Assert.Contains("What time should I set the alarm for?", esml, StringComparison.Ordinal);
using var listenPayload = JsonDocument.Parse(replies[0].Text!);
Assert.Equal("set", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("intent").GetString());
Assert.Equal("alarm", listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("entities").GetProperty("domain").GetString());
Assert.False(listenPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("entities").TryGetProperty("time", out _));
using var redirectPayload = JsonDocument.Parse(replies[2].Text!);
Assert.Equal("@be/clock", redirectPayload.RootElement.GetProperty("data").GetProperty("match").GetProperty("skillID").GetString());
Assert.Equal("set", redirectPayload.RootElement.GetProperty("data").GetProperty("nlu").GetProperty("intent").GetString());
}
[Fact]