Improve weather and news diagnostics for report skills

This commit is contained in:
Jacob Dubin
2026-05-11 19:59:15 -05:00
parent c0e9b41cd1
commit 67c738fae3
16 changed files with 63295 additions and 46 deletions

View File

@@ -20,4 +20,9 @@ public sealed record NewsHeadline(
public sealed record NewsBriefingSnapshot(
IReadOnlyList<NewsHeadline> Headlines,
string? SourceName = null);
string? SourceName = null,
string? ProviderStatus = null,
string? ProviderMessage = null,
int? ProviderHttpStatusCode = null,
string? ProviderEndpoint = null,
string? ProviderErrorCode = null);

View File

@@ -719,7 +719,7 @@ public sealed class JiboInteractionService(
: snapshot.Summary.Trim().TrimEnd('.');
var location = string.IsNullOrWhiteSpace(snapshot.LocationName)
? "your area"
: snapshot.LocationName;
: NormalizeLocationForSpeech(snapshot.LocationName);
if (weatherDate.ForecastDayOffset > 0)
{
@@ -758,6 +758,10 @@ public sealed class JiboInteractionService(
{
location = "your area";
}
else
{
location = NormalizeLocationForSpeech(location);
}
var unit = snapshots[0].Snapshot.UseCelsius ? "Celsius" : "Fahrenheit";
var referenceDate = (referenceLocalTime ?? DateTimeOffset.UtcNow).Date;
@@ -1076,6 +1080,12 @@ public sealed class JiboInteractionService(
return BuildProviderNewsDecision(snapshot, preferredCategories, requestedHeadlineCount);
}
var providerStatus = ResolveNewsProviderStatus(snapshot);
var providerMessage = snapshot?.ProviderMessage;
var providerEndpoint = snapshot?.ProviderEndpoint;
var providerHttpStatusCode = snapshot?.ProviderHttpStatusCode;
var providerErrorCode = snapshot?.ProviderErrorCode;
var fallbackBriefingWhenEmpty = randomizer.Choose(catalog.NewsBriefings);
return BuildNewsDecision(
fallbackBriefingWhenEmpty,
@@ -1083,10 +1093,14 @@ public sealed class JiboInteractionService(
preferredCategories.Count > 0 ? preferredCategories : null,
headlineCount: null,
providerDiagnostics: BuildNewsProviderDiagnostics(
"provider_empty",
providerStatus,
preferredCategories,
requestedHeadlineCount,
snapshot?.Headlines.Count ?? 0));
snapshot?.Headlines.Count ?? 0,
providerMessage,
providerHttpStatusCode,
providerEndpoint,
providerErrorCode));
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
@@ -1208,7 +1222,11 @@ public sealed class JiboInteractionService(
string status,
IReadOnlyList<string> preferredCategories,
int requestedHeadlineCount,
int? resolvedHeadlineCount = null)
int? resolvedHeadlineCount = null,
string? providerMessage = null,
int? providerHttpStatusCode = null,
string? providerEndpoint = null,
string? providerErrorCode = null)
{
var diagnostics = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
{
@@ -1224,9 +1242,41 @@ public sealed class JiboInteractionService(
diagnostics["news_provider_resolved_headlines"] = resolvedHeadlineCount.Value;
}
if (!string.IsNullOrWhiteSpace(providerMessage))
{
diagnostics["news_provider_message"] = providerMessage;
}
if (providerHttpStatusCode is not null)
{
diagnostics["news_provider_http_status"] = providerHttpStatusCode.Value;
}
if (!string.IsNullOrWhiteSpace(providerEndpoint))
{
diagnostics["news_provider_endpoint"] = providerEndpoint;
}
if (!string.IsNullOrWhiteSpace(providerErrorCode))
{
diagnostics["news_provider_error_code"] = providerErrorCode;
}
return diagnostics;
}
private static string ResolveNewsProviderStatus(NewsBriefingSnapshot? snapshot)
{
var providerStatus = snapshot?.ProviderStatus?.Trim().ToLowerInvariant();
return providerStatus switch
{
"success" => "provider_success",
"exception" => "provider_exception",
"http_error" or "api_error" or "schema_error" => "provider_error",
_ => "provider_empty"
};
}
private static string BuildNewsLeadIn(string? sourceName, IReadOnlyList<string> preferredCategories)
{
var categoryLeadIn = preferredCategories.Count switch
@@ -1249,11 +1299,35 @@ public sealed class JiboInteractionService(
}
// Expand "AI" so Nimbus TTS does not collapse it to a single "aye" sound.
return Regex.Replace(
var normalized = Regex.Replace(
text,
@"\bA\.?\s*I\.?\b",
"artificial intelligence",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
return NormalizeLocationForSpeech(normalized);
}
private static string NormalizeLocationForSpeech(string text)
{
if (string.IsNullOrWhiteSpace(text))
{
return text;
}
return Regex.Replace(
text,
@"\b(?<token>[A-Z]{2,3})\b",
static match =>
{
var token = match.Groups["token"].Value;
if (!SpokenAbbreviationTokens.Contains(token))
{
return token;
}
return string.Join(".", token.ToCharArray()) + ".";
},
RegexOptions.CultureInvariant);
}
private List<string> ResolvePreferredNewsCategories(TurnContext turn, string transcript)
@@ -4486,6 +4560,66 @@ public sealed class JiboInteractionService(
"our neighbourhood"
};
private static readonly HashSet<string> SpokenAbbreviationTokens = new(StringComparer.Ordinal)
{
"US",
"USA",
"UK",
"UAE",
"EU",
"DC",
"AL",
"AK",
"AZ",
"AR",
"CA",
"CO",
"CT",
"DE",
"FL",
"GA",
"HI",
"ID",
"IL",
"IN",
"IA",
"KS",
"KY",
"LA",
"ME",
"MD",
"MA",
"MI",
"MN",
"MS",
"MO",
"MT",
"NE",
"NV",
"NH",
"NJ",
"NM",
"NY",
"NC",
"ND",
"OH",
"OK",
"OR",
"PA",
"RI",
"SC",
"SD",
"TN",
"TX",
"UT",
"VT",
"VA",
"WA",
"WV",
"WI",
"WY"
};
private const string GreetingRouteMetadataKey = "greetingsRoute";
private const string GreetingSpeakerMetadataKey = "greetingsSpeaker";
private const string LastProactiveGreetingUtcMetadataKey = "greetingsLastProactiveUtc";

View File

@@ -808,7 +808,7 @@ public sealed class ResponsePlanToSocketMessagesMapper
var weatherHiLoView = BuildWeatherHiLoView(skillPayload);
if (weatherHiLoView is not null)
{
var resolvedGuiConfig = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
var resolvedGuiContext = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
{
["type"] = "Javascript",
["data"] = weatherHiLoView,
@@ -825,7 +825,15 @@ public sealed class ResponsePlanToSocketMessagesMapper
jcpConfig["gui"] = legacyGuiConfig;
jcpConfig["display"] = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
{
["view"] = resolvedGuiConfig
["view"] = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
{
// Legacy fields used by existing tests and tooling.
["type"] = "Javascript",
["data"] = weatherHiLoView,
["pause"] = true,
// Pegasus-style view context used by on-robot weather cards.
["context"] = resolvedGuiContext
}
};
jcpConfig["timeout"] = 6;
@@ -1182,7 +1190,7 @@ public sealed class ResponsePlanToSocketMessagesMapper
{
id = "hiNumLabel",
type = "Label",
text = high.Value.ToString(),
text = $"{high.Value}°",
style = new
{
fontSize = "160",
@@ -1214,7 +1222,7 @@ public sealed class ResponsePlanToSocketMessagesMapper
{
id = "loNumLabel",
type = "Label",
text = low.Value.ToString(),
text = $"{low.Value}°",
style = new
{
fontSize = "160",

View File

@@ -690,6 +690,10 @@ public sealed partial class WebSocketTurnFinalizationService(
invokedSkillAction.Payload.TryGetValue("news_provider_resolved_headlines", out var resolvedHeadlines);
invokedSkillAction.Payload.TryGetValue("news_provider_preferred_categories", out var preferredCategories);
invokedSkillAction.Payload.TryGetValue("news_source", out var newsSource);
invokedSkillAction.Payload.TryGetValue("news_provider_message", out var providerMessage);
invokedSkillAction.Payload.TryGetValue("news_provider_http_status", out var providerHttpStatus);
invokedSkillAction.Payload.TryGetValue("news_provider_endpoint", out var providerEndpoint);
invokedSkillAction.Payload.TryGetValue("news_provider_error_code", out var providerErrorCode);
await sink.RecordTurnDiagnosticAsync(
"news_provider_trace",
@@ -701,7 +705,11 @@ public sealed partial class WebSocketTurnFinalizationService(
["requestedHeadlines"] = requestedHeadlines,
["resolvedHeadlines"] = resolvedHeadlines,
["preferredCategories"] = preferredCategories,
["source"] = newsSource
["source"] = newsSource,
["providerMessage"] = providerMessage,
["providerHttpStatus"] = providerHttpStatus,
["providerEndpoint"] = providerEndpoint,
["providerErrorCode"] = providerErrorCode
}),
cancellationToken);
}

View File

@@ -51,6 +51,30 @@ public sealed class NewsApiBriefingProvider(
var headlines = new List<NewsHeadline>(requestedHeadlineCount);
var seenTitles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
string? failureStatus = null;
string? failureMessage = null;
int? failureStatusCode = null;
string? failureEndpoint = null;
string? failureErrorCode = null;
void CaptureFailure(
string status,
string? message,
int? statusCode,
Uri? endpoint,
string? errorCode = null)
{
if (!string.IsNullOrWhiteSpace(failureStatus))
{
return;
}
failureStatus = status;
failureMessage = message;
failureStatusCode = statusCode;
failureEndpoint = endpoint is null ? null : SanitizeEndpoint(endpoint);
failureErrorCode = errorCode;
}
foreach (var category in categories)
{
@@ -59,6 +83,11 @@ public sealed class NewsApiBriefingProvider(
if (!response.IsSuccessStatusCode)
{
var responseBody = await TryReadResponseBodySnippetAsync(response, cancellationToken);
CaptureFailure(
"http_error",
$"Category '{category}' returned {(int)response.StatusCode} {response.ReasonPhrase}.",
(int)response.StatusCode,
uri);
logger.LogWarning(
"NewsAPI request failed for category {Category}. StatusCode={StatusCode} Reason={ReasonPhrase} Body={Body}",
category,
@@ -74,6 +103,12 @@ public sealed class NewsApiBriefingProvider(
statusNode.ValueKind == JsonValueKind.String &&
!string.Equals(statusNode.GetString(), "ok", StringComparison.OrdinalIgnoreCase))
{
CaptureFailure(
"api_error",
ReadString(document.RootElement, "message"),
null,
uri,
ReadString(document.RootElement, "code"));
logger.LogWarning(
"NewsAPI returned non-ok status for category {Category}. Status={Status} Code={Code} Message={Message}",
category,
@@ -85,6 +120,11 @@ public sealed class NewsApiBriefingProvider(
if (!document.RootElement.TryGetProperty("articles", out var articles) ||
articles.ValueKind != JsonValueKind.Array)
{
CaptureFailure(
"schema_error",
$"Category '{category}' response did not include an articles array.",
null,
uri);
logger.LogWarning("NewsAPI response missing articles array for category {Category}.", category);
continue;
}
@@ -107,7 +147,10 @@ public sealed class NewsApiBriefingProvider(
if (headlines.Count >= requestedHeadlineCount)
{
var snapshot = new NewsBriefingSnapshot(headlines, "NewsAPI");
var snapshot = new NewsBriefingSnapshot(
headlines,
"NewsAPI",
ProviderStatus: "success");
SetCachedValue(briefingCache, cacheKey, snapshot, options.CacheTtlSeconds);
logger.LogInformation(
"NewsAPI request succeeded. Categories={Categories} HeadlineCount={HeadlineCount}",
@@ -157,12 +200,22 @@ public sealed class NewsApiBriefingProvider(
}
else
{
CaptureFailure(
"schema_error",
"Uncategorized fallback response did not include an articles array.",
null,
broadUri);
logger.LogWarning("NewsAPI uncategorized fallback response missing articles array.");
}
}
else
{
var fallbackBody = await TryReadResponseBodySnippetAsync(broadResponse, cancellationToken);
CaptureFailure(
"http_error",
$"Uncategorized fallback returned {(int)broadResponse.StatusCode} {broadResponse.ReasonPhrase}.",
(int)broadResponse.StatusCode,
broadUri);
logger.LogWarning(
"NewsAPI uncategorized fallback failed. StatusCode={StatusCode} Reason={ReasonPhrase} Body={Body}",
(int)broadResponse.StatusCode,
@@ -210,12 +263,22 @@ public sealed class NewsApiBriefingProvider(
}
else
{
CaptureFailure(
"schema_error",
"Everything fallback response did not include an articles array.",
null,
everythingUri);
logger.LogWarning("NewsAPI everything fallback response missing articles array.");
}
}
else
{
var everythingBody = await TryReadResponseBodySnippetAsync(everythingResponse, cancellationToken);
CaptureFailure(
"http_error",
$"Everything fallback returned {(int)everythingResponse.StatusCode} {everythingResponse.ReasonPhrase}.",
(int)everythingResponse.StatusCode,
everythingUri);
logger.LogWarning(
"NewsAPI everything fallback failed. StatusCode={StatusCode} Reason={ReasonPhrase} Body={Body}",
(int)everythingResponse.StatusCode,
@@ -226,15 +289,26 @@ public sealed class NewsApiBriefingProvider(
if (headlines.Count == 0)
{
SetCachedValue(briefingCache, cacheKey, null, options.FailureCacheTtlSeconds);
var emptySnapshot = new NewsBriefingSnapshot(
Array.Empty<NewsHeadline>(),
"NewsAPI",
ProviderStatus: failureStatus ?? "empty",
ProviderMessage: failureMessage ?? "NewsAPI returned no usable headlines.",
ProviderHttpStatusCode: failureStatusCode,
ProviderEndpoint: failureEndpoint,
ProviderErrorCode: failureErrorCode);
SetCachedValue(briefingCache, cacheKey, emptySnapshot, options.FailureCacheTtlSeconds);
logger.LogWarning(
"NewsAPI returned no usable headlines. Categories={Categories} RequestedHeadlineCount={RequestedHeadlineCount}",
string.Join(",", categories),
requestedHeadlineCount);
return null;
return emptySnapshot;
}
var populatedSnapshot = new NewsBriefingSnapshot(headlines, "NewsAPI");
var populatedSnapshot = new NewsBriefingSnapshot(
headlines,
"NewsAPI",
ProviderStatus: "success");
SetCachedValue(briefingCache, cacheKey, populatedSnapshot, options.CacheTtlSeconds);
logger.LogInformation(
"NewsAPI request partially filled headlines. Categories={Categories} HeadlineCount={HeadlineCount} RequestedHeadlineCount={RequestedHeadlineCount}",
@@ -246,11 +320,16 @@ public sealed class NewsApiBriefingProvider(
catch (Exception exception)
{
logger.LogWarning(exception, "NewsAPI lookup failed.");
var exceptionSnapshot = new NewsBriefingSnapshot(
Array.Empty<NewsHeadline>(),
"NewsAPI",
ProviderStatus: "exception",
ProviderMessage: exception.Message);
if (!string.IsNullOrWhiteSpace(cacheKey))
{
SetCachedValue(briefingCache, cacheKey, null, options.FailureCacheTtlSeconds);
SetCachedValue(briefingCache, cacheKey, exceptionSnapshot, options.FailureCacheTtlSeconds);
}
return null;
return exceptionSnapshot;
}
}
@@ -365,6 +444,26 @@ public sealed class NewsApiBriefingProvider(
return string.IsNullOrWhiteSpace(trimmed) ? null : trimmed;
}
private static string SanitizeEndpoint(Uri uri)
{
var path = uri.GetLeftPart(UriPartial.Path);
if (string.IsNullOrWhiteSpace(uri.Query))
{
return path;
}
var filtered = uri.Query
.TrimStart('?')
.Split('&', StringSplitOptions.RemoveEmptyEntries)
.Where(static pair =>
{
var key = pair.Split('=', 2)[0];
return !string.Equals(key, "apiKey", StringComparison.OrdinalIgnoreCase);
});
var safeQuery = string.Join("&", filtered);
return string.IsNullOrWhiteSpace(safeQuery) ? path : $"{path}?{safeQuery}";
}
private string BuildCacheKey(IReadOnlyList<string> categories, int requestedHeadlineCount)
{
var categoryKey = string.Join(",", categories.Select(category => category.Trim().ToLowerInvariant()));

View File

@@ -1267,7 +1267,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Boston, US", "light rain", 61, 65, 54, "rain", false)
Snapshot = new WeatherReportSnapshot("Boston, U.S.", "light rain", 61, 65, 54, "rain", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1284,7 +1284,7 @@ public sealed class JiboInteractionServiceTests
Assert.Equal("personal_report_delivered", decision.IntentName);
Assert.Contains("Great, alex. Here is your personal report.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.Contains("Right now in Boston, US, it is light rain and 61 degrees Fahrenheit.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.Contains("Right now in Boston, U.S., it is light rain and 61 degrees Fahrenheit.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.Contains("That is your personal report.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.NotNull(decision.ContextUpdates);
Assert.Equal("idle", decision.ContextUpdates![PersonalReportStateKey]);
@@ -1447,7 +1447,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Boston, US", "light rain", 61, 65, 54, "rain", false)
Snapshot = new WeatherReportSnapshot("Boston, U.S.", "light rain", 61, 65, 54, "rain", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1471,7 +1471,7 @@ public sealed class JiboInteractionServiceTests
Assert.Equal(54, decision.SkillPayload["weather_low"]);
Assert.Equal("F", decision.SkillPayload["weather_unit"]);
Assert.Equal("Normal", decision.SkillPayload["weather_theme"]);
Assert.Equal("Right now in Boston, US, it is light rain and 61 degrees Fahrenheit.", decision.ReplyText);
Assert.Equal("Right now in Boston, U.S., it is light rain and 61 degrees Fahrenheit.", decision.ReplyText);
Assert.NotNull(provider.LastRequest);
Assert.False(provider.LastRequest!.IsTomorrow);
Assert.Equal(0, provider.LastRequest.ForecastDayOffset);
@@ -1482,7 +1482,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Chicago, US", "mostly cloudy", 72, 74, 60, "cloudy", false)
Snapshot = new WeatherReportSnapshot("Chicago, U.S.", "mostly cloudy", 72, 74, 60, "cloudy", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1496,7 +1496,7 @@ public sealed class JiboInteractionServiceTests
Assert.Equal("Chicago", provider.LastRequest?.LocationQuery);
Assert.True(provider.LastRequest?.IsTomorrow);
Assert.Equal(1, provider.LastRequest?.ForecastDayOffset);
Assert.Equal("Tomorrow in Chicago, US, expect mostly cloudy with a high near 74 degrees Fahrenheit and a low around 60 degrees Fahrenheit.", decision.ReplyText);
Assert.Equal("Tomorrow in Chicago, U.S., expect mostly cloudy with a high near 74 degrees Fahrenheit and a low around 60 degrees Fahrenheit.", decision.ReplyText);
}
[Fact]
@@ -1504,7 +1504,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Seattle, US", "light rain", 58, 61, 52, "rain", false)
Snapshot = new WeatherReportSnapshot("Seattle, U.S.", "light rain", 58, 61, 52, "rain", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1518,7 +1518,7 @@ public sealed class JiboInteractionServiceTests
Assert.Equal("Seattle", provider.LastRequest?.LocationQuery);
Assert.False(provider.LastRequest?.IsTomorrow);
Assert.Equal(0, provider.LastRequest?.ForecastDayOffset);
Assert.Equal("Right now in Seattle, US, it is light rain and 58 degrees Fahrenheit.", decision.ReplyText);
Assert.Equal("Right now in Seattle, U.S., it is light rain and 58 degrees Fahrenheit.", decision.ReplyText);
}
[Fact]
@@ -1548,7 +1548,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Redmond, US", "clear sky", 63, 66, 52, "sunny", false)
Snapshot = new WeatherReportSnapshot("Redmond, U.S.", "clear sky", 63, 66, 52, "sunny", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1562,7 +1562,7 @@ public sealed class JiboInteractionServiceTests
Assert.Equal("Redmond Oregon", provider.LastRequest?.LocationQuery);
Assert.False(provider.LastRequest?.IsTomorrow);
Assert.Equal(0, provider.LastRequest?.ForecastDayOffset);
Assert.Equal("Right now in Redmond, US, it is clear sky and 63 degrees Fahrenheit.", decision.ReplyText);
Assert.Equal("Right now in Redmond, U.S., it is clear sky and 63 degrees Fahrenheit.", decision.ReplyText);
}
[Fact]
@@ -1570,7 +1570,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("New York, US", "partly cloudy", 71, 76, 61, "cloudy", false)
Snapshot = new WeatherReportSnapshot("New York, U.S.", "partly cloudy", 71, 76, 61, "cloudy", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1584,7 +1584,7 @@ public sealed class JiboInteractionServiceTests
Assert.Equal("New York City", provider.LastRequest?.LocationQuery);
Assert.True(provider.LastRequest?.IsTomorrow);
Assert.Equal(1, provider.LastRequest?.ForecastDayOffset);
Assert.Equal("Tomorrow in New York, US, expect partly cloudy with a high near 76 degrees Fahrenheit and a low around 61 degrees Fahrenheit.", decision.ReplyText);
Assert.Equal("Tomorrow in New York, U.S., expect partly cloudy with a high near 76 degrees Fahrenheit and a low around 61 degrees Fahrenheit.", decision.ReplyText);
}
[Fact]
@@ -1592,7 +1592,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Kansas City, US", "clear sky", 72, 79, 63, "sunny", false)
Snapshot = new WeatherReportSnapshot("Kansas City, U.S.", "clear sky", 72, 79, 63, "sunny", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1606,7 +1606,7 @@ public sealed class JiboInteractionServiceTests
Assert.Null(provider.LastRequest?.LocationQuery);
Assert.True(provider.LastRequest?.IsTomorrow);
Assert.Equal(1, provider.LastRequest?.ForecastDayOffset);
Assert.Equal("Tomorrow in Kansas City, US, expect clear sky with a high near 79 degrees Fahrenheit and a low around 63 degrees Fahrenheit.", decision.ReplyText);
Assert.Equal("Tomorrow in Kansas City, U.S., expect clear sky with a high near 79 degrees Fahrenheit and a low around 63 degrees Fahrenheit.", decision.ReplyText);
}
[Fact]
@@ -1614,7 +1614,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Chicago, US", "mostly cloudy", 70, 75, 62, "cloudy", false)
Snapshot = new WeatherReportSnapshot("Chicago, U.S.", "mostly cloudy", 70, 75, 62, "cloudy", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1632,7 +1632,7 @@ public sealed class JiboInteractionServiceTests
Assert.Equal("Chicago", provider.LastRequest?.LocationQuery);
Assert.Null(provider.LastRequest?.Latitude);
Assert.Null(provider.LastRequest?.Longitude);
Assert.Equal("Right now in Chicago, US, it is mostly cloudy and 70 degrees Fahrenheit.", decision.ReplyText);
Assert.Equal("Right now in Chicago, U.S., it is mostly cloudy and 70 degrees Fahrenheit.", decision.ReplyText);
}
[Theory]
@@ -1652,7 +1652,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Test City, US", "light rain", 62, 66, 55, "rain", false)
Snapshot = new WeatherReportSnapshot("Test City, U.S.", "light rain", 62, 66, 55, "rain", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1676,7 +1676,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Portland, US", "scattered clouds", 64, 68, 53, "cloudy", false)
Snapshot = new WeatherReportSnapshot("Portland, U.S.", "scattered clouds", 64, 68, 53, "cloudy", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1697,7 +1697,7 @@ public sealed class JiboInteractionServiceTests
Assert.Equal("weather", decision.IntentName);
Assert.Equal(2, provider.LastRequest?.ForecastDayOffset);
Assert.False(provider.LastRequest?.IsTomorrow);
Assert.Equal("On Monday in Portland, US, expect scattered clouds with a high near 68 degrees Fahrenheit and a low around 53 degrees Fahrenheit.", decision.ReplyText);
Assert.Equal("On Monday in Portland, U.S., expect scattered clouds with a high near 68 degrees Fahrenheit and a low around 53 degrees Fahrenheit.", decision.ReplyText);
}
[Fact]
@@ -1705,7 +1705,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Chicago, US", "light rain", 59, 63, 51, "rain", false)
Snapshot = new WeatherReportSnapshot("Chicago, U.S.", "light rain", 59, 63, 51, "rain", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1722,7 +1722,7 @@ public sealed class JiboInteractionServiceTests
Assert.Equal("weather", decision.IntentName);
Assert.Equal("Chicago", provider.LastRequest?.LocationQuery);
Assert.Equal(1, provider.LastRequest?.ForecastDayOffset);
Assert.Equal("On Tuesday in Chicago, US, expect light rain with a high near 63 degrees Fahrenheit and a low around 51 degrees Fahrenheit.", decision.ReplyText);
Assert.Equal("On Tuesday in Chicago, U.S., expect light rain with a high near 63 degrees Fahrenheit and a low around 51 degrees Fahrenheit.", decision.ReplyText);
}
[Fact]
@@ -1730,7 +1730,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Chicago, US", "light rain", 59, 63, 51, "rain", false)
Snapshot = new WeatherReportSnapshot("Chicago, U.S.", "light rain", 59, 63, 51, "rain", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1779,7 +1779,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Seattle, US", "light rain", 58, 61, 52, "rain", false)
Snapshot = new WeatherReportSnapshot("Seattle, U.S.", "light rain", 58, 61, 52, "rain", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1808,7 +1808,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Seattle, US", "light rain", 58, 61, 52, "rain", false)
Snapshot = new WeatherReportSnapshot("Seattle, U.S.", "light rain", 58, 61, 52, "rain", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1824,7 +1824,7 @@ public sealed class JiboInteractionServiceTests
Assert.Equal("weather", decision.IntentName);
Assert.Contains("next five-day forecast", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.Contains("Seattle, US", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.Contains("Seattle, U.S.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.Contains("Temperatures are in Fahrenheit.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.NotNull(provider.LastRequest);
Assert.Equal("Seattle", provider.LastRequest!.LocationQuery);
@@ -1836,7 +1836,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Seattle, US", "light rain", 58, 61, 52, "rain", false)
Snapshot = new WeatherReportSnapshot("Seattle, U.S.", "light rain", 58, 61, 52, "rain", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1852,7 +1852,7 @@ public sealed class JiboInteractionServiceTests
Assert.Equal("weather", decision.IntentName);
Assert.Contains("next five-day forecast", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.Contains("Seattle, US", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.Contains("Seattle, U.S.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.Contains("Temperatures are in Fahrenheit.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.NotNull(provider.LastRequest);
Assert.Null(provider.LastRequest!.LocationQuery);
@@ -1864,7 +1864,7 @@ public sealed class JiboInteractionServiceTests
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Chicago, US", "mostly cloudy", 72, 74, 60, "cloudy", false)
Snapshot = new WeatherReportSnapshot("Chicago, U.S.", "mostly cloudy", 72, 74, 60, "cloudy", false)
};
var service = CreateService(weatherReportProvider: provider);
@@ -1881,7 +1881,7 @@ public sealed class JiboInteractionServiceTests
Assert.Equal("weather", decision.IntentName);
Assert.Equal("Chicago", provider.LastRequest?.LocationQuery);
Assert.Equal(2, provider.LastRequest?.ForecastDayOffset);
Assert.Equal("The day after tomorrow in Chicago, US, expect mostly cloudy with a high near 74 degrees Fahrenheit and a low around 60 degrees Fahrenheit.", decision.ReplyText);
Assert.Equal("The day after tomorrow in Chicago, U.S., expect mostly cloudy with a high near 74 degrees Fahrenheit and a low around 60 degrees Fahrenheit.", decision.ReplyText);
}
[Fact]

View File

@@ -2232,6 +2232,9 @@ public sealed class JiboWebSocketServiceTests
Assert.Equal(
"weatherTempView",
display.GetProperty("view").GetProperty("data").GetProperty("viewConfig").GetProperty("id").GetString());
Assert.Equal(
"weatherTempView",
display.GetProperty("view").GetProperty("context").GetProperty("data").GetProperty("viewConfig").GetProperty("id").GetString());
Assert.True(jcpConfig.TryGetProperty("views", out var views));
var weatherHiLo = views.GetProperty("weatherHiLo");