Skip to content

Commit 19be2a1

Browse files
committed
tweaks
1 parent 370d79c commit 19be2a1

3 files changed

Lines changed: 81 additions & 23 deletions

File tree

examples/cli/main.cpp

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -204,14 +204,14 @@ struct cli_args {
204204
bool quiet = false;
205205
bool list_devices = false; // --list-devices: print devices and exit
206206
bool batch_jsonl = false; // --batch-jsonl: output JSONL
207-
std::string output_path; // -o: write raw text here
208-
int repeat = 1;
209-
int n_threads = 0; // 0 = library default (all cores)
210-
int n_ctx = 0; // 0 = model's true max; >0 lowers the cap
211-
transcribe_kv_type kv_type = TRANSCRIBE_KV_TYPE_AUTO;
212-
transcribe_backend_request backend = TRANSCRIBE_BACKEND_AUTO;
213-
int gpu_device = 0; // --device N: 0 = auto, >0 = registry index
214-
transcribe_timestamp_kind timestamps = TRANSCRIBE_TIMESTAMPS_AUTO;
207+
std::string output_path; // -o/--output: write raw text here
208+
int repeat = 1;
209+
int n_threads = 0; // 0 = library default (all cores)
210+
int n_ctx = 0; // 0 = model's true max; >0 lowers the cap
211+
transcribe_kv_type kv_type = TRANSCRIBE_KV_TYPE_AUTO;
212+
transcribe_backend_request backend = TRANSCRIBE_BACKEND_AUTO;
213+
int gpu_device = 0; // --device N: 0 = auto, >0 = registry index
214+
transcribe_timestamp_kind timestamps = TRANSCRIBE_TIMESTAMPS_AUTO;
215215

216216
// Whisper-family knobs. Ignored for non-Whisper models.
217217
std::string initial_prompt; // --initial-prompt TEXT
@@ -696,18 +696,21 @@ void log_cb(transcribe_log_level level, const char * msg, void * userdata) {
696696
std::fprintf(stderr, "%s %s%s", prefix, msg, (msg && *msg && msg[std::strlen(msg) - 1] == '\n') ? "" : "\n");
697697
}
698698

699-
// Write transcription text to the output file if -o was given.
700-
// Appends so it works for both single-file and batch mode.
701-
static void write_output_file(const std::string & path, const char * text) {
702-
if (path.empty() || text == nullptr || text[0] == '\0') {
703-
return;
699+
bool write_output_file(std::ofstream * output, const std::string & path, const char * text) {
700+
if (output == nullptr) {
701+
return true;
704702
}
705-
std::ofstream fout(path, std::ios::binary | std::ios::app);
706-
if (!fout) {
707-
std::fprintf(stderr, "error: cannot open %s for writing\n", path.c_str());
708-
} else {
709-
fout << text << '\n';
703+
const char * value = text != nullptr ? text : "";
704+
*output << value;
705+
if (value[0] == '\0' || value[std::strlen(value) - 1] != '\n') {
706+
*output << '\n';
710707
}
708+
output->flush();
709+
if (!*output) {
710+
std::fprintf(stderr, "error: cannot write %s\n", path.c_str());
711+
return false;
712+
}
713+
return true;
711714
}
712715

713716
} // namespace
@@ -735,6 +738,18 @@ int main(int argc, char ** argv) {
735738
transcribe_log_set(log_cb, nullptr);
736739
}
737740

741+
std::ofstream output_file;
742+
std::ofstream * output = nullptr;
743+
if (!args.output_path.empty()) {
744+
output_file.open(args.output_path, std::ios::binary | std::ios::trunc);
745+
if (!output_file) {
746+
std::fprintf(stderr, "error: cannot open %s for writing\n", args.output_path.c_str());
747+
return EXIT_FAILURE;
748+
}
749+
output = &output_file;
750+
}
751+
bool output_ok = true;
752+
738753
// Batch mode: --batch reads a file list, one wav path per line. Loads
739754
// the model ONCE and reuses the context across all files. Outputs one
740755
// JSONL line per file to stdout when --batch-jsonl is set, otherwise
@@ -979,7 +994,7 @@ int main(int argc, char ** argv) {
979994
std::printf(" ERROR: %s\n", transcribe_status_string(ust));
980995
}
981996
}
982-
write_output_file(args.output_path, text);
997+
output_ok = write_output_file(output, args.output_path, text) && output_ok;
983998
std::fflush(stdout);
984999
}
9851000
}
@@ -1118,7 +1133,7 @@ int main(int argc, char ** argv) {
11181133
std::printf(" ERROR: %s\n", transcribe_status_string(run_st));
11191134
}
11201135
}
1121-
write_output_file(args.output_path, text);
1136+
output_ok = write_output_file(output, args.output_path, text) && output_ok;
11221137
std::fflush(stdout);
11231138
}
11241139
}
@@ -1132,7 +1147,7 @@ int main(int argc, char ** argv) {
11321147
transcribe_model_free(model);
11331148
// OUTPUT_TRUNCATED is result-bearing and does not fail the batch, but
11341149
// hard per-utterance failures must remain visible to automation.
1135-
return n_fail > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1150+
return n_fail > 0 || !output_ok ? EXIT_FAILURE : EXIT_SUCCESS;
11361151
}
11371152

11381153
// Single-file mode.
@@ -1365,7 +1380,7 @@ int main(int argc, char ** argv) {
13651380
if (result_present) {
13661381
const char * text = transcribe_full_text(ctx);
13671382
std::printf("text: %s\n", (text && *text) ? text : "(empty)");
1368-
write_output_file(args.output_path, text);
1383+
output_ok = write_output_file(output, args.output_path, text) && output_ok;
13691384

13701385
// A truncated decode hit the model's context/output budget before
13711386
// end-of-stream; the text above is incomplete.
@@ -1443,7 +1458,7 @@ int main(int argc, char ** argv) {
14431458
transcribe_session_free(ctx);
14441459
transcribe_model_free(model);
14451460

1446-
if (run_st != TRANSCRIBE_OK) {
1461+
if (run_st != TRANSCRIBE_OK || !output_ok) {
14471462
return EXIT_FAILURE;
14481463
}
14491464
} else {

tests/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,4 +1234,11 @@ if(TRANSCRIBE_BUILD_EXAMPLES)
12341234
${CMAKE_SOURCE_DIR}/samples/jfk.wav)
12351235
set_tests_properties(transcribe_cli_smoke PROPERTIES
12361236
PASS_REGULAR_EXPRESSION "duration:")
1237+
1238+
add_test(NAME transcribe_cli_output_smoke
1239+
COMMAND ${CMAKE_COMMAND}
1240+
-DCLI=$<TARGET_FILE:transcribe-cli>
1241+
-DWAV=${CMAKE_SOURCE_DIR}/samples/jfk.wav
1242+
-DTEST_DIR=${CMAKE_CURRENT_BINARY_DIR}/cli-output-smoke
1243+
-P ${CMAKE_CURRENT_SOURCE_DIR}/cli_output_smoke.cmake)
12371244
endif()

tests/cli_output_smoke.cmake

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
foreach(_var CLI WAV TEST_DIR)
2+
if(NOT DEFINED ${_var} OR "${${_var}}" STREQUAL "")
3+
message(FATAL_ERROR "${_var} is required")
4+
endif()
5+
endforeach()
6+
7+
file(REMOVE_RECURSE "${TEST_DIR}")
8+
file(MAKE_DIRECTORY "${TEST_DIR}")
9+
10+
set(_output "${TEST_DIR}/transcript.txt")
11+
file(WRITE "${_output}" "stale output\n")
12+
execute_process(
13+
COMMAND "${CLI}" -q -o "${_output}" "${WAV}"
14+
RESULT_VARIABLE _result
15+
OUTPUT_VARIABLE _stdout
16+
ERROR_VARIABLE _stderr)
17+
if(NOT _result EQUAL 0)
18+
message(FATAL_ERROR "output command failed (${_result}):\n${_stdout}\n${_stderr}")
19+
endif()
20+
file(SIZE "${_output}" _output_size)
21+
if(NOT _output_size EQUAL 0)
22+
message(FATAL_ERROR "--output did not truncate stale output")
23+
endif()
24+
25+
set(_bad_output "${TEST_DIR}/missing/transcript.txt")
26+
execute_process(
27+
COMMAND "${CLI}" -q -o "${_bad_output}" "${WAV}"
28+
RESULT_VARIABLE _bad_result
29+
OUTPUT_VARIABLE _bad_stdout
30+
ERROR_VARIABLE _bad_stderr)
31+
if(_bad_result EQUAL 0)
32+
message(FATAL_ERROR "unwritable output path unexpectedly succeeded")
33+
endif()
34+
if(EXISTS "${_bad_output}")
35+
message(FATAL_ERROR "unwritable output path unexpectedly created a file")
36+
endif()

0 commit comments

Comments
 (0)