From 2ea7afe2e798ec24ffd7c4415012356c0e44fcef Mon Sep 17 00:00:00 2001 From: Jacob Dubin Date: Mon, 20 Apr 2026 22:13:37 -0500 Subject: [PATCH] added cloud versioning --- OpenJibo/docs/development-plan.md | 6 ++++++ OpenJibo/src/Jibo.Cloud/dotnet/README.md | 7 +++++++ .../dotnet/src/Jibo.Cloud.Api/Program.cs | 7 ++++++- .../Services/JiboInteractionService.cs | 13 +++++++++++++ .../Services/OpenJiboCloudBuildInfo.cs | 8 ++++++++ .../WebSockets/JiboInteractionServiceTests.cs | 15 +++++++++++++++ 6 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/OpenJiboCloudBuildInfo.cs diff --git a/OpenJibo/docs/development-plan.md b/OpenJibo/docs/development-plan.md index 5944bf4..bd2ffb4 100644 --- a/OpenJibo/docs/development-plan.md +++ b/OpenJibo/docs/development-plan.md @@ -8,6 +8,12 @@ It is intentionally broader than the current Node server. The Node server is a p Day-to-day feature sequencing now lives in [feature-backlog.md](/C:/Projects/JiboExperiments/OpenJibo/docs/feature-backlog.md). +Cloud release hygiene: + +- keep a visible OpenJibo Cloud version string +- expose it through diagnostics such as `/health` and the spoken `cloud version` command +- bump the shared version constant whenever we deploy a meaningful hosted-cloud change + ## Current Scope - stable .NET cloud scaffold diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/README.md b/OpenJibo/src/Jibo.Cloud/dotnet/README.md index b8ffc76..4e4768b 100644 --- a/OpenJibo/src/Jibo.Cloud/dotnet/README.md +++ b/OpenJibo/src/Jibo.Cloud/dotnet/README.md @@ -6,6 +6,13 @@ 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.10.` + +Release hygiene reminder: + +- bump [OpenJiboCloudBuildInfo.cs](/C:/Projects/JiboExperiments/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/OpenJiboCloudBuildInfo.cs) whenever we ship a meaningful hosted-cloud update +- keep the spoken version response and `/health` version field aligned from that single source of truth + ## Architecture The first implementation is a modular monolith: diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Api/Program.cs b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Api/Program.cs index a1f2b48..747bfb2 100644 --- a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Api/Program.cs +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Api/Program.cs @@ -125,7 +125,12 @@ app.Use(async (context, next) => await telemetrySink.RecordConnectionClosedAsync(closeEnvelope, closeSession, $"socket-loop-ended{(isPrematureClose ? "-prematurely" : string.Empty)}", context.RequestAborted); }); -app.MapGet("/health", () => Results.Json(new { ok = true, service = "OpenJibo Cloud Api" })); +app.MapGet("/health", () => Results.Json(new +{ + ok = true, + service = "OpenJibo Cloud Api", + version = OpenJiboCloudBuildInfo.Version +})); app.MapMethods("/{**path}", ["GET", "POST", "PUT"], async (HttpContext context, JiboCloudProtocolService service, IProtocolTelemetrySink telemetrySink, CancellationToken cancellationToken) => { diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.cs b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.cs index 9f4696a..fa15eac 100644 --- a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.cs +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/JiboInteractionService.cs @@ -29,6 +29,7 @@ public sealed class JiboInteractionService( "dance" => BuildDanceDecision(catalog), "time" => new JiboInteractionDecision("time", $"It is {DateTime.Now:h:mm tt}."), "date" => new JiboInteractionDecision("date", $"Today is {DateTime.Now:dddd, MMMM d}."), + "cloud_version" => new JiboInteractionDecision("cloud_version", OpenJiboCloudBuildInfo.SpokenVersion), "radio" => BuildRadioLaunchDecision(), "radio_genre" => BuildRadioGenreLaunchDecision(lowered), "hello" => new JiboInteractionDecision("hello", randomizer.Choose(catalog.GreetingReplies)), @@ -169,6 +170,18 @@ public sealed class JiboInteractionService( return "joke"; } + if (MatchesAny( + loweredTranscript, + "cloud version", + "open jibo cloud version", + "openjibo cloud version", + "what version is the cloud", + "what s the cloud version", + "what's the cloud version")) + { + return "cloud_version"; + } + if (TryResolveRadioGenre(loweredTranscript) is not null) { return "radio_genre"; diff --git a/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/OpenJiboCloudBuildInfo.cs b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/OpenJiboCloudBuildInfo.cs new file mode 100644 index 0000000..56822f5 --- /dev/null +++ b/OpenJibo/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Application/Services/OpenJiboCloudBuildInfo.cs @@ -0,0 +1,8 @@ +namespace Jibo.Cloud.Application.Services; + +public static class OpenJiboCloudBuildInfo +{ + public const string Version = "1.0.10"; + + public static string SpokenVersion => $"Open Jibo Cloud version {Version}."; +} diff --git a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs index f7de49e..8e519a3 100644 --- a/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs +++ b/OpenJibo/tests/Jibo.Cloud.Tests/WebSockets/JiboInteractionServiceTests.cs @@ -200,6 +200,21 @@ public sealed class JiboInteractionServiceTests Assert.DoesNotContain("future cloud integration", decision.ReplyText, StringComparison.OrdinalIgnoreCase); } + [Fact] + public async Task BuildDecisionAsync_CloudVersion_UsesSharedBuildInfo() + { + var service = CreateService(); + + var decision = await service.BuildDecisionAsync(new TurnContext + { + RawTranscript = "what's the cloud version", + NormalizedTranscript = "what's the cloud version" + }); + + Assert.Equal("cloud_version", decision.IntentName); + Assert.Equal(OpenJiboCloudBuildInfo.SpokenVersion, decision.ReplyText); + } + [Fact] public async Task BuildDecisionAsync_WordOfDayGuess_UsesStructuredClientNluGuess() {