| 89 | std::optional<std::string> json_optional_string( |
| 90 | const engine::io::json::Value & object, |
| 91 | const std::string & key) { |
| 92 | const auto * value = object.find(key); |
| 93 | if (value == nullptr || value->is_null()) { |
| 94 | return std::nullopt; |
| 95 | } |
| 96 | return value->as_string(); |
| 97 | } |
| 98 | |
| 99 | std::optional<float> json_optional_float( |
| 100 | const engine::io::json::Value & object, |
| 101 | const std::string & key) { |
| 102 | const auto * value = object.find(key); |
| 103 | if (value == nullptr || value->is_null()) { |
| 104 | return std::nullopt; |
| 105 | } |
| 106 | return value->as_f32(); |
| 107 | } |
| 108 | |
| 109 | engine::runtime::TaskRequest build_request_from_json( |
| 110 | const engine::io::json::Value & value, |
| 111 | const std::filesystem::path & base_dir) { |
| 112 | engine::runtime::TaskRequest request; |
| 113 | const std::string language = json_optional_string(value, "language").value_or(""); |
| 114 | if (const auto text = json_optional_string(value, "text")) { |
| 115 | request.text_input = engine::runtime::Transcript{*text, language}; |
| 116 | } |
| 117 | if (const auto audio = json_optional_string(value, "audio")) { |
| 118 | request.audio_input = read_audio_buffer(resolve_case_path(base_dir, *audio)); |
| 119 | } |
| 120 | |
| 121 | engine::runtime::VoiceCondition voice; |
| 122 | bool has_voice = false; |
| 123 | if (const auto voice_id = json_optional_string(value, "voice_id")) { |
| 124 | engine::runtime::VoiceReference reference; |
| 125 | reference.cached_voice_id = *voice_id; |
| 126 | voice.speaker = std::move(reference); |
| 127 | has_voice = true; |
| 128 | } |
| 129 | if (const auto voice_ref = json_optional_string(value, "voice_ref")) { |
| 130 | if (!voice.speaker.has_value()) { |
| 131 | voice.speaker = engine::runtime::VoiceReference{}; |
| 132 | } |
| 133 | voice.speaker->audio = read_audio_buffer(resolve_case_path(base_dir, *voice_ref)); |
| 134 | has_voice = true; |
| 135 | } |
| 136 | |
| 137 | engine::runtime::StyleCondition style; |
| 138 | if (const auto style_language = json_optional_string(value, "style_language")) { |
| 139 | style.language = *style_language; |
| 140 | has_voice = true; |
| 141 | } |
| 142 | if (const auto emotion = json_optional_string(value, "emotion")) { |
| 143 | style.emotion = *emotion; |
| 144 | has_voice = true; |
| 145 | } |
| 146 | if (const auto speaking_rate = json_optional_float(value, "speaking_rate")) { |
| 147 | style.speaking_rate = *speaking_rate; |
| 148 | has_voice = true; |
no test coverage detected