diff --git a/backend/cpp/audio-cpp/Makefile b/backend/cpp/audio-cpp/Makefile index 262a95557fde..fe4996d0d324 100644 --- a/backend/cpp/audio-cpp/Makefile +++ b/backend/cpp/audio-cpp/Makefile @@ -9,7 +9,7 @@ # recipe is a make target (not a prepare.sh) so 'make purge && make' is a clean # rebuild and so the bump bot can see the pin. -AUDIO_CPP_VERSION?=9d6e7b39236e0151ad28a70fab0d538b84ce8718 +AUDIO_CPP_VERSION?=502b5b74bd26e9b4aed267d1776ecf131cae7215 AUDIO_CPP_REPO?=https://github.com/0xShug0/audio.cpp CURRENT_MAKEFILE_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) diff --git a/backend/cpp/audio-cpp/loaded_model.cpp b/backend/cpp/audio-cpp/loaded_model.cpp index 779a19ab61de..c194cf5aaff4 100644 --- a/backend/cpp/audio-cpp/loaded_model.cpp +++ b/backend/cpp/audio-cpp/loaded_model.cpp @@ -23,22 +23,24 @@ namespace audiocpp_backend { // -------------------------------------------------------------------------- // Enum coupling // -// audiocpp_backend::Task mirrors engine::runtime::VoiceTaskKind positionally so +// audiocpp_backend::Task mirrors the VoiceTaskKind values LocalAI can expose so // capability_routing can stay stdlib-only and testable without an audio.cpp -// checkout. Nothing about that mirroring is enforced by the type system, and a -// drift is silent in the worst possible way: every unit still compiles, every -// test still passes, and the backend runs a different task than the one the -// caller asked for. +// checkout. audio.cpp can also expose engine-only task kinds for which LocalAI +// has no RPC; those are deliberately filtered in to_capabilities below. +// Nothing about the mirrored subset is enforced by the type system, and a drift +// is silent in the worst possible way: every unit still compiles, every test +// still passes, and the backend runs a different task than the one the caller +// asked for. // // Two mechanisms pin it, and both are needed because they catch different edits: // // 1. The assertions below pin every enumerator's value on both sides. An // insertion or a reorder anywhere before the last member shifts the values // after it and fails the build here. -// 2. An enumerator APPENDED after the last one shifts nothing, so no value -// assertion can see it. What sees it is the switch in from_engine_task, -// which covers the engine enum with no `default:` label. CMakeLists.txt -// compiles this file with -Werror=switch so that omission is an error +// 2. An enumerator APPENDED after the mirrored subset shifts nothing, so no +// mirror assertion can see it. The final engine assertion and the switch +// in from_engine_task cover the full upstream enum. CMakeLists.txt compiles +// this file with -Werror=switch so an unclassified addition is an error // rather than a warning nobody reads. // // Neither mechanism catches a pure RENAME of an upstream enumerator, but that @@ -67,12 +69,14 @@ static_assert(kEngine(engine::runtime::VoiceTaskKind::SpeechToSpeech) == 8, "Voi static_assert(kEngine(engine::runtime::VoiceTaskKind::Alignment) == 9, "VoiceTaskKind drifted"); static_assert(kEngine(engine::runtime::VoiceTaskKind::VoiceDesign) == 10, "VoiceTaskKind drifted"); static_assert(kEngine(engine::runtime::VoiceTaskKind::SpeakerRecognition) == 11, "VoiceTaskKind drifted"); -// The last member. Pinning it pins the member count too, as long as the -// enumerators stay contiguous and unassigned, which upstream's declaration is. -static_assert(kEngine(engine::runtime::VoiceTaskKind::Svc) == 12, +static_assert(kEngine(engine::runtime::VoiceTaskKind::Svc) == 12, "VoiceTaskKind drifted"); +// The last upstream member. Pinning it pins the member count too, as long as +// the enumerators stay contiguous and unassigned, which upstream's declaration +// is. Midi is not mirrored because LocalAI has no MIDI RPC. +static_assert(kEngine(engine::runtime::VoiceTaskKind::Midi) == 13, "engine::runtime::VoiceTaskKind gained, lost or reordered a member. " - "audiocpp_backend::Task mirrors it positionally: update capability_routing.h, " - "to_engine_task and from_engine_task together, then move this pin."); + "Classify the new task as a LocalAI capability or an engine-only task, " + "then update the conversions and move this pin."); static_assert(kMirror(Task::Vad) == 0, "Task drifted from VoiceTaskKind"); static_assert(kMirror(Task::Asr) == 1, "Task drifted from VoiceTaskKind"); @@ -263,6 +267,8 @@ Task from_engine_task(engine::runtime::VoiceTaskKind kind) { case K::VoiceDesign: return Task::VoiceDesign; case K::SpeakerRecognition: return Task::SpeakerRecognition; case K::Svc: return Task::Svc; + case K::Midi: + throw CapabilityError("audio-cpp: LocalAI has no RPC for the MIDI task"); } return Task::Vad; } @@ -291,6 +297,13 @@ Capabilities to_capabilities(const std::string &family, caps.family = family; caps.tasks.reserve(set.supported_tasks.size()); for (const auto &supported : set.supported_tasks) { + // MuScriptor exposes Midi in addition to Asr. LocalAI has no MIDI RPC, + // so advertising it as any mirrored Task would make capability routing + // claim a surface the backend cannot serve. Keep the ASR capability and + // omit only the engine-specific one. + if (supported.task == engine::runtime::VoiceTaskKind::Midi) { + continue; + } TaskCapability capability; capability.task = from_engine_task(supported.task); capability.modes.reserve(supported.modes.size());