Skip to content

RG-T117 More work with TTS#429

Merged
ucswift merged 3 commits into
masterfrom
develop
Jul 17, 2026
Merged

RG-T117 More work with TTS#429
ucswift merged 3 commits into
masterfrom
develop

Conversation

@ucswift

@ucswift ucswift commented Jul 17, 2026

Copy link
Copy Markdown
Member

Pull Request Description

This PR improves the quality and intelligibility of text-to-speech (TTS) audio generated for telephone dispatch calls.

Voice model change:

  • Switched the default English voice from the norman-medium model to the ryan-high model, and updated the Dockerfile to provision the new model.

TTS generation tuning:

  • Added 0.35 seconds of inter-sentence silence (previously 0) so that dispatch messages composed of short sentences have clear audible boundaries.
  • Added noise-scale (0.333) and noise-w (0.4) parameters to reduce prosody jitter, improving clarity of digits and short words.

Telephone audio filter improvements:

  • Raised the lowpass filter cutoff from 3000 Hz to 3400 Hz to preserve sibilants through the mulaw encoding used on phone lines.
  • Added loudnorm normalization to maintain a consistent perceived audio level across clips.

Cache invalidation:

  • Bumped the cache prefix from tts to tts2 to force regeneration of all cached audio with the new voice and settings.

All unit tests were updated to reflect the new model, generation parameters, and audio filter.

Summary by CodeRabbit

  • New Features

    • Updated English text-to-speech voice quality and audio tuning.
    • Added automatic retries for temporary text-to-speech service failures.
    • Increased default text-to-speech request capacity and queue limits.
  • Bug Fixes

    • Improved map initialization to prevent missing center or zoom errors.
    • Added safer handling when department information is unavailable.
    • Updated the default text-to-speech cache configuration.
    • Removed outdated map point-of-interest widget initialization.

@Resgrid-Bot

This comment has been minimized.

@request-info

request-info Bot commented Jul 17, 2026

Copy link
Copy Markdown

Thanks for opening this, but we'd appreciate a little more information. Could you update it with more details?

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ucswift, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 99f48308-ac7b-409f-954d-31dd8654a612

📥 Commits

Reviewing files that changed from the base of the PR and between 576e96f and e94318f.

📒 Files selected for processing (1)
  • Web/Resgrid.Web.Tts/Services/AudioProcessingService.cs
📝 Walkthrough

Walkthrough

The TTS service now uses updated cache and rate-limit defaults, downloads and selects Piper’s Ryan high English voice, adjusts audio generation parameters, and retries transient requests. Mapping flows add missing-department guards and an initial Leaflet map view, while AddPOI removes its widget initialization scripts.

Changes

TTS updates

Layer / File(s) Summary
Cache, rate limits, and voice defaults
Core/Resgrid.Config/TtsConfig.cs, Web/Resgrid.Web.Tts/Configuration/*, Web/Resgrid.Web.Tts/Dockerfile, Web/Resgrid.Web.Tts/Services/AudioProcessingService.cs
TTS cache prefixes change to tts2, default permit and queue limits increase, and the Ryan high English Piper voice becomes the downloaded and mapped model.
Audio generation parameters
Web/Resgrid.Web.Tts/Services/AudioProcessingService.cs
Piper silence and noise parameters are updated, and the telephone audio filter cutoff changes to 3400 Hz.
Transient TTS request retries
Core/Resgrid.Services/TtsAudioService.cs
Speech URL and static-prompt requests gain bounded exponential-backoff retries for transient failures while preserving cancellation and existing failure handling.

Mapping safeguards

Layer / File(s) Summary
Department validation and map fallback
Core/Resgrid.Services/DepartmentSettingsService.cs, Web/Resgrid.Web/Areas/User/Controllers/MappingController.cs, Web/Resgrid.Web/Areas/User/Apps/src/components/map/LeafletMapView.tsx, Web/Resgrid.Web/Areas/User/Views/Mapping/AddPOI.cshtml
Mapping actions handle missing departments, map initialization sets a fallback view, and AddPOI removes its client-side widget initialization block.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested reviewers: github-actions

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is related to the change set, but it is too vague to clearly describe the main TTS updates. Rename it to a specific summary like "Update TTS voice, audio tuning, cache prefix, and rate limits".
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Web/Resgrid.Web.Tts/Services/AudioProcessingService.cs`:
- Around line 208-217: The Piper command arguments in AudioProcessingService
must use underscore-form flags for v1.2.0. Update the argument names in the
affected startInfo configuration from --sentence-silence, --noise-scale, and
--noise-w to --sentence_silence, --noise_scale, and --noise_w while preserving
their existing values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6c316118-09a6-466b-9070-75bfb76a29f6

📥 Commits

Reviewing files that changed from the base of the PR and between d6a062c and de9772d.

⛔ Files ignored due to path filters (1)
  • Tests/Resgrid.Tests/Web/Tts/TtsServiceTests.cs is excluded by !**/Tests/**
📒 Files selected for processing (4)
  • Core/Resgrid.Config/TtsConfig.cs
  • Web/Resgrid.Web.Tts/Configuration/TtsOptions.cs
  • Web/Resgrid.Web.Tts/Dockerfile
  • Web/Resgrid.Web.Tts/Services/AudioProcessingService.cs

Comment on lines +208 to +217
// 0.35s of silence between sentences — dispatch messages are strings of
// short sentences and need audible boundaries to stay intelligible.
startInfo.ArgumentList.Add("--sentence-silence");
startInfo.ArgumentList.Add("0.0");
startInfo.ArgumentList.Add("0.35");
// Lower generation noise than the Piper defaults (0.667/0.8): reduces
// prosody jitter that makes digits and short words sound mumbled.
startInfo.ArgumentList.Add("--noise-scale");
startInfo.ArgumentList.Add("0.333");
startInfo.ArgumentList.Add("--noise-w");
startInfo.ArgumentList.Add("0.4");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Download Piper v1.2.0 binary and check its help output for argument names.

curl -fsSL "https://github.com/rhasspy/piper/releases/download/v1.2.0/piper_amd64.tar.gz" -o piper.tar.gz
tar -xzf piper.tar.gz
./piper/piper --help | grep -E 'sentence|noise'

Repository: Resgrid/Core

Length of output: 1266


Use Piper’s underscore flags here.
--sentence-silence, --noise-scale, and --noise-w are not recognized by Piper v1.2.0; use --sentence_silence, --noise_scale, and --noise_w instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Web/Resgrid.Web.Tts/Services/AudioProcessingService.cs` around lines 208 -
217, The Piper command arguments in AudioProcessingService must use
underscore-form flags for v1.2.0. Update the argument names in the affected
startInfo configuration from --sentence-silence, --noise-scale, and --noise-w to
--sentence_silence, --noise_scale, and --noise_w while preserving their existing
values.

public static string FfmpegExecutable = "ffmpeg";
public static string TempDirectory = "";
public static string CachePrefix = "tts";
public static string CachePrefix = "tts2";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Bug high

WHAT: The k8s deployment.yaml (line 28) sets RESGRID__TtsConfig__CachePrefix: tts, which overrides the new code default tts2 via the env-var → TtsConfig → TtsOptions chain in ServiceCollectionExtensions.cs:60, defeating the cache-busting intent in production. WHY: Non-English voice model names (Spanish, German, French, etc.) did not change in this PR, so their SHA256 cache hashes are identical to before; the prefix change from ttstts2 was the only mechanism to invalidate their stale cache entries, but the env var override keeps it at tts, causing non-English voices to serve old audio generated with sentence-silence 0.0, lowpass 3000, and no loudnorm/noise parameters indefinitely. HOW: Update the k8s deployment.yaml RESGRID__TtsConfig__CachePrefix value from tts to tts2 to match the code default, or remove the env var override so the code default takes effect.

public static string CachePrefix = "tts2";
// Also update Web/Resgrid.Web.Tts/k8s/deployment.yaml line 28:
//   RESGRID__TtsConfig__CachePrefix: tts2
Prompt for LLM

File Core/Resgrid.Config/TtsConfig.cs:

Line 35:

WHAT: The k8s deployment.yaml (line 28) sets `RESGRID__TtsConfig__CachePrefix: tts`, which overrides the new code default `tts2` via the env-var → TtsConfig → TtsOptions chain in ServiceCollectionExtensions.cs:60, defeating the cache-busting intent in production. WHY: Non-English voice model names (Spanish, German, French, etc.) did not change in this PR, so their SHA256 cache hashes are identical to before; the prefix change from `tts` → `tts2` was the only mechanism to invalidate their stale cache entries, but the env var override keeps it at `tts`, causing non-English voices to serve old audio generated with sentence-silence 0.0, lowpass 3000, and no loudnorm/noise parameters indefinitely. HOW: Update the k8s deployment.yaml `RESGRID__TtsConfig__CachePrefix` value from `tts` to `tts2` to match the code default, or remove the env var override so the code default takes effect.

Suggested Code:

public static string CachePrefix = "tts2";
// Also update Web/Resgrid.Web.Tts/k8s/deployment.yaml line 28:
//   RESGRID__TtsConfig__CachePrefix: tts2

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread Web/Resgrid.Web.Tts/Services/AudioProcessingService.cs Outdated
Comment on lines +208 to +217
// 0.35s of silence between sentences — dispatch messages are strings of
// short sentences and need audible boundaries to stay intelligible.
startInfo.ArgumentList.Add("--sentence-silence");
startInfo.ArgumentList.Add("0.0");
startInfo.ArgumentList.Add("0.35");
// Lower generation noise than the Piper defaults (0.667/0.8): reduces
// prosody jitter that makes digits and short words sound mumbled.
startInfo.ArgumentList.Add("--noise-scale");
startInfo.ArgumentList.Add("0.333");
startInfo.ArgumentList.Add("--noise-w");
startInfo.ArgumentList.Add("0.4");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Kody Rules low

Violates rule 'Replace magic numbers with named constants': the new piper synthesis parameters 0.35 (sentence silence), 0.333 (noise scale), and 0.4 (noise width) on lines 211, 215, 217 are meaningful numeric literals that should be extracted into named constants. The same class already extracts similar tuning values (SpeedReferenceWpm=175f, MinLengthScale=0.25f, MaxLengthScale=3.0f), establishing the expected pattern.

// Named constants at the top of the class:
		private const float SentenceSilence = 0.35f;
		private const float NoiseScale = 0.333f;
		private const float NoiseWidth = 0.4f;

// In CreatePiperStartInfo:
			startInfo.ArgumentList.Add("--sentence-silence");
			startInfo.ArgumentList.Add(SentenceSilence.ToString("0.00", CultureInfo.InvariantCulture));
			startInfo.ArgumentList.Add("--noise-scale");
			startInfo.ArgumentList.Add(NoiseScale.ToString("0.000", CultureInfo.InvariantCulture));
			startInfo.ArgumentList.Add("--noise-w");
			startInfo.ArgumentList.Add(NoiseWidth.ToString("0.0", CultureInfo.InvariantCulture));
Prompt for LLM

File Web/Resgrid.Web.Tts/Services/AudioProcessingService.cs:

Line 208 to 217:

Violates rule 'Replace magic numbers with named constants': the new piper synthesis parameters 0.35 (sentence silence), 0.333 (noise scale), and 0.4 (noise width) on lines 211, 215, 217 are meaningful numeric literals that should be extracted into named constants. The same class already extracts similar tuning values (SpeedReferenceWpm=175f, MinLengthScale=0.25f, MaxLengthScale=3.0f), establishing the expected pattern.

Suggested Code:

// Named constants at the top of the class:
		private const float SentenceSilence = 0.35f;
		private const float NoiseScale = 0.333f;
		private const float NoiseWidth = 0.4f;

// In CreatePiperStartInfo:
			startInfo.ArgumentList.Add("--sentence-silence");
			startInfo.ArgumentList.Add(SentenceSilence.ToString("0.00", CultureInfo.InvariantCulture));
			startInfo.ArgumentList.Add("--noise-scale");
			startInfo.ArgumentList.Add(NoiseScale.ToString("0.000", CultureInfo.InvariantCulture));
			startInfo.ArgumentList.Add("--noise-w");
			startInfo.ArgumentList.Add(NoiseWidth.ToString("0.0", CultureInfo.InvariantCulture));

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

@Resgrid-Bot

This comment has been minimized.

Comment on lines +76 to +77
public static int RateLimitPermitLimit = 600;
public static int RateLimitQueueLimit = 60;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Bug high

Environment variable overrides in Web/Resgrid.Web.Tts/k8s/deployment.yaml silently negate the rate-limit increase, as ConfigProcessor.LoadAndProcessEnvVariables (Program.cs:17) loads these values into static TtsConfig fields that ApplyRateLimitOptions (ServiceCollectionExtensions.cs:77-78) passes to FixedWindowRateLimiter at Program.cs:86-87, causing futile retry-on-429 attempts. Update the RESGRID__TtsConfig__RateLimitPermitLimit and RESGRID__TtsConfig__RateLimitQueueLimit overrides at lines 35-36 to match the intended 600/60 code defaults.

// Code defaults are correct; but deployment.yaml must be updated to match,
// otherwise the env override keeps production at 60/10:
//   RESGRID__TtsConfig__RateLimitPermitLimit: "600"
//   RESGRID__TtsConfig__RateLimitQueueLimit: "60"
Prompt for LLM

File Core/Resgrid.Config/TtsConfig.cs:

Line 76 to 77:

WHAT: The rate-limit raise to 600 permits / 60 queue is silently overridden in the production k8s deployment, so it never takes effect. WHY: deployment.yaml still sets RESGRID__TtsConfig__RateLimitPermitLimit:"60" and RateLimitQueueLimit:"10"; ConfigProcessor.LoadAndProcessEnvVariables (Program.cs:17) loads those into the static TtsConfig fields, which ApplyRateLimitOptions (ServiceCollectionExtensions.cs:77-78) copies into RateLimitOptions, which Program.cs:86-87 feeds to the FixedWindowRateLimiter — env vars take precedence over the code defaults, so production still throttles at 60/10. Combined with the new retry-on-429 logic, retries within the same 60s fixed window are largely futile (permits don't free until window reset), so the dispatch fan-out failures the PR targets can still occur. HOW: update Web/Resgrid.Web.Tts/k8s/deployment.yaml lines 35-36 to RESGRID__TtsConfig__RateLimitPermitLimit:"600" and RESGRID__TtsConfig__RateLimitQueueLimit:"60" (or remove those overrides so the code defaults apply).

Suggested Code:

// Code defaults are correct; but deployment.yaml must be updated to match,
// otherwise the env override keeps production at 60/10:
//   RESGRID__TtsConfig__RateLimitPermitLimit: "600"
//   RESGRID__TtsConfig__RateLimitQueueLimit: "60"

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines +307 to +308
if (department == null)
return new Coordinates() { Latitude = 39.14086268299356, Longitude = -119.7583809782715 };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Kody Rules low

Magic number violation identified at line 308 and lines 371-372, where the default map center coordinates 39.14086268299356 and -119.7583809782715 are hardcoded as raw literals. Extract these fallback values into static readonly fields, following the existing cache key pattern at lines 17-25 to eliminate duplication across PersonnelController, UnitsController, MappingController v4, and LeafletMapView.tsx.

Kody rule violation: Replace magic numbers with named constants

private static readonly double DefaultLatitude = 39.14086268299356;
private static readonly double DefaultLongitude = -119.7583809782715;

// ...

if (department == null)
    return new Coordinates() { Latitude = DefaultLatitude, Longitude = DefaultLongitude };
Prompt for LLM

File Core/Resgrid.Services/DepartmentSettingsService.cs:

Line 307 to 308:

Violates rule 'Replace magic numbers with named constants': the default map center coordinates 39.14086268299356 and -119.7583809782715 are hardcoded as raw literals at line 308 and again at lines 371-372 within the same method. These are meaningful fallback values (default location when no department coordinates exist) that also appear identically in PersonnelController, UnitsController, and MappingController v4. They should be extracted to named static readonly constants, following the existing pattern in this class (e.g., lines 17-25 define static string constants for cache keys).

Suggested Code:

private static readonly double DefaultLatitude = 39.14086268299356;
private static readonly double DefaultLongitude = -119.7583809782715;

// ...

if (department == null)
    return new Coordinates() { Latitude = DefaultLatitude, Longitude = DefaultLongitude };

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

{
model.Department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId);

if (model.Department == null)
@Resgrid-Bot

Resgrid-Bot commented Jul 17, 2026

Copy link
Copy Markdown

Kody Review Complete

Great news! 🎉
No issues were found that match your current review configurations.

Keep up the excellent work! 🚀

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

@ucswift
ucswift merged commit 04cc0fb into master Jul 17, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants