fix(solutions): VoiceAgent knobs the loader parses are dropped by the expansion

Merged
AY
1mo ago
+77-0

Description

A solution YAML can set barge-in, and the setting goes nowhere.

config_loader.cpp:506-509 parses both knobs and puts them on VoiceAgentConfig:

if (auto n = node.find("enable_barge_in"))
    cfg->set_enable_barge_in(to_bool(n->scalar));
if (auto n = node.find("barge_in_threshold_ms"))
    cfg->set_barge_in_threshold_ms(to_int(n->scalar));

expand_voice_agent then builds the PipelineSpec and never mentions either one. Since operator params are the only converter-to-operator channel, a host factory cannot see them. enable_barge_in: false in a config file is read, stored, and discarded.

The asymmetry is what makes this a bug rather than a design choice. Every other knob the loader parses for this solution is forwarded:

parsed by config_loaderforwarded by expand_voice_agent
sample_rate_hzyes, vad
chunk_msyes, vad
system_promptyes, llm
max_context_tokensyes, llm
temperatureyes, llm
emit_partialsyes, tts
enable_barge_inno
barge_in_threshold_msno

This is not a knob with no meaning behind it either: the barge-in path is implemented in voice_agent_feed_abi.cpp (kBargeInEchoMargin, kBargeInMinFrames, barge_in_armed, per-frame arming). solutions.proto:63 documents the flag as "Unset means enabled", so false is the only way to turn it off and it is exactly the value that gets lost.

Both now ride the vad op:

if (cfg.has_enable_barge_in()) {
    (*vad->mutable_params())["enable_barge_in"] = cfg.enable_barge_in() ? "true" : "false";
}
if (cfg.barge_in_threshold_ms() > 0) {
    (*vad->mutable_params())["barge_in_threshold_ms"] = std::to_string(cfg.barge_in_threshold_ms());
}

has_enable_barge_in() rather than the value, because the field is optional and the documented default is enabled, so reading the value would turn "unset" into an explicit false and silently disable barge-in for everyone.

Two judgement calls I want to flag rather than bury.

First, vad and not tts. Barge-in is detected from user speech arriving during playback, and the existing implementation does that arming in the VAD/feed path, so the threshold is a VAD-domain value and it sits naturally beside sample_rate_hz/chunk_ms. If you would rather both landed on tts next to emit_partials, that is a one-line move and I will make it.

Second, there is no in-tree consumer to point at, and that is expected here rather than a gap in this PR. The header of this file says these expansions "intentionally reference engine-backed operator type names without installing factories for them", so operators are host-supplied and interpret params themselves. emit_partials has no in-tree reader either, for the same reason. This PR makes the two knobs reachable on the same terms as every other one.

I did not touch audio_source / audio_file_path, which the loader also parses without forwarding. Those describe how the host feeds audio into the pipeline rather than how an operator behaves, so they are a separate question and not obviously the same defect.

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Refactoring

Testing

  • Lint passes locally
  • Added/updated tests for changes

Added voice_agent_barge_in_params_reach_the_vad_operator to core/tests/test_solution_runner.cpp, beside the existing voice_agent_solution_compiles. It sets enable_barge_in = false (the value that cannot survive a value-read) plus a threshold, then asserts both land on the vad operator's params.

$ cmake -B build -DRAC_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Debug && cmake --build build -j 10
build exit=0, 0 errors
$ ctest --test-dir build -j 10
100% tests passed out of 101

Fails on the unfixed code. Verified by removing only the forwarding block and confirming the relink happened first:

[100%] Linking CXX executable test_solution_runner
[FAIL] core/tests/test_solution_runner.cpp:1101 enabled != params.end()

On lint: unchecked because core/scripts/lint-cpp.sh needs the repo's pinned clang-format and only Apple clang-format 21 is available here. Both files this PR touches are completely clean under it (zero diff hunks), so there is no formatting drift either way.

The edited block is inside #if defined(RAC_HAVE_PROTOBUF); protobuf-off builds do not compile it.

No platform boxes ticked: commons-only, exercised through the C++ suite on macOS.

Labels

SDKs:

  • Commons - Changes to shared native code (core)

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Documentation updated (if needed)

Summary by CodeRabbit

  • New Features

    • Voice agent configurations now support barge-in controls, including enablement and response threshold settings.
    • Reasoning output preferences are now honored, allowing configured reasoning content to be included in generated responses.
  • Bug Fixes

    • Explicitly disabled barge-in settings are now preserved when configuring voice agents.