cli: add --output option (#25484)
This commit is contained in:
+1
-1
@@ -2849,7 +2849,7 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
|||||||
params.out_file = value;
|
params.out_file = value;
|
||||||
}
|
}
|
||||||
).set_examples({LLAMA_EXAMPLE_IMATRIX, LLAMA_EXAMPLE_CVECTOR_GENERATOR, LLAMA_EXAMPLE_EXPORT_LORA, LLAMA_EXAMPLE_TTS, LLAMA_EXAMPLE_FINETUNE,
|
).set_examples({LLAMA_EXAMPLE_IMATRIX, LLAMA_EXAMPLE_CVECTOR_GENERATOR, LLAMA_EXAMPLE_EXPORT_LORA, LLAMA_EXAMPLE_TTS, LLAMA_EXAMPLE_FINETUNE,
|
||||||
LLAMA_EXAMPLE_RESULTS, LLAMA_EXAMPLE_EXPORT_GRAPH_OPS}));
|
LLAMA_EXAMPLE_RESULTS, LLAMA_EXAMPLE_EXPORT_GRAPH_OPS, LLAMA_EXAMPLE_CLI}));
|
||||||
add_opt(common_arg(
|
add_opt(common_arg(
|
||||||
{"-ofreq", "--output-frequency"}, "N",
|
{"-ofreq", "--output-frequency"}, "N",
|
||||||
string_format("output the imatrix every N iterations (default: %d)", params.n_out_freq),
|
string_format("output the imatrix every N iterations (default: %d)", params.n_out_freq),
|
||||||
|
|||||||
@@ -162,6 +162,14 @@ bool cli_context::init() {
|
|||||||
|
|
||||||
fetch_server_props();
|
fetch_server_props();
|
||||||
|
|
||||||
|
if (!params.out_file.empty()) {
|
||||||
|
output_file.emplace(params.out_file);
|
||||||
|
if (!output_file->is_open()) {
|
||||||
|
ui::show_error(string_format("failed to open output file '%s'", params.out_file.c_str()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +331,14 @@ bool cli_context::stage_media_file(const std::string & fname, const std::string
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cli_context::generate_completion(std::string & assistant_content, cli_timings & timings) {
|
void cli_context::write_output_file(const std::string & content) {
|
||||||
|
if (output_file) {
|
||||||
|
(*output_file) << content;
|
||||||
|
output_file->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cli_context::generate_completion(generated_content & content_out, cli_timings & timings) {
|
||||||
json body = {
|
json body = {
|
||||||
{"messages", impl->messages},
|
{"messages", impl->messages},
|
||||||
{"stream", true},
|
{"stream", true},
|
||||||
@@ -364,13 +379,14 @@ bool cli_context::generate_completion(std::string & assistant_content, cli_timin
|
|||||||
if (delta.contains("reasoning_content") && delta.at("reasoning_content").is_string()) {
|
if (delta.contains("reasoning_content") && delta.at("reasoning_content").is_string()) {
|
||||||
const std::string text = delta.at("reasoning_content").get<std::string>();
|
const std::string text = delta.at("reasoning_content").get<std::string>();
|
||||||
if (!text.empty()) {
|
if (!text.empty()) {
|
||||||
|
content_out.reasoning += text;
|
||||||
a.push(ui::ASSISTANT_DISPLAY_MODE_REASONING, text);
|
a.push(ui::ASSISTANT_DISPLAY_MODE_REASONING, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (delta.contains("content") && delta.at("content").is_string()) {
|
if (delta.contains("content") && delta.at("content").is_string()) {
|
||||||
const std::string text = delta.at("content").get<std::string>();
|
const std::string text = delta.at("content").get<std::string>();
|
||||||
if (!text.empty()) {
|
if (!text.empty()) {
|
||||||
assistant_content += text;
|
content_out.content += text;
|
||||||
a.push(ui::ASSISTANT_DISPLAY_MODE_CONTENT, text);
|
a.push(ui::ASSISTANT_DISPLAY_MODE_CONTENT, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -520,10 +536,12 @@ int cli_context::run() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ui::show_message(string_format("Loaded media from '%s'", fname.c_str()));
|
ui::show_message(string_format("Loaded media from '%s'", fname.c_str()));
|
||||||
|
write_output_file(string_format("User: Added media: %s\n", fname.c_str()));
|
||||||
continue;
|
continue;
|
||||||
} else if (string_starts_with(buffer, "/read ")) {
|
} else if (string_starts_with(buffer, "/read ")) {
|
||||||
std::string fname = string_strip(buffer.substr(6));
|
std::string fname = string_strip(buffer.substr(6));
|
||||||
add_text_file(fname);
|
add_text_file(fname);
|
||||||
|
write_output_file(string_format("User: Added text file: %s\n", fname.c_str()));
|
||||||
continue;
|
continue;
|
||||||
} else if (string_starts_with(buffer, "/glob ")) {
|
} else if (string_starts_with(buffer, "/glob ")) {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
@@ -568,9 +586,11 @@ int cli_context::run() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!add_text_file((rel_path / rel).string())) {
|
const std::string full_path = (curdir / rel).string();
|
||||||
|
if (!add_text_file(full_path)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
write_output_file(string_format("User: Added text file: %s\n", full_path.c_str()));
|
||||||
|
|
||||||
if (++count >= FILE_GLOB_MAX_RESULTS) {
|
if (++count >= FILE_GLOB_MAX_RESULTS) {
|
||||||
ui::show_error(string_format("Maximum number of globbed files allowed (%zu) reached.", FILE_GLOB_MAX_RESULTS));
|
ui::show_error(string_format("Maximum number of globbed files allowed (%zu) reached.", FILE_GLOB_MAX_RESULTS));
|
||||||
@@ -586,16 +606,34 @@ int cli_context::run() {
|
|||||||
// generate response
|
// generate response
|
||||||
if (add_user_msg) {
|
if (add_user_msg) {
|
||||||
push_user_message(cur_msg);
|
push_user_message(cur_msg);
|
||||||
|
write_output_file(string_format("User:\n%s\n\n", cur_msg.c_str()));
|
||||||
cur_msg.clear();
|
cur_msg.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
cli_timings timings;
|
cli_timings timings;
|
||||||
std::string assistant_content;
|
generated_content content;
|
||||||
generate_completion(assistant_content, timings);
|
generate_completion(content, timings);
|
||||||
|
|
||||||
impl->messages.push_back({
|
impl->messages.push_back({
|
||||||
{"role", "assistant"},
|
{"role", "assistant"},
|
||||||
{"content", assistant_content}
|
{"content", content.content}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (output_file) {
|
||||||
|
std::string out_content = "Assistant:\n";
|
||||||
|
if (!content.reasoning.empty()) {
|
||||||
|
out_content += "[Start thinking]\n\n";
|
||||||
|
out_content += content.reasoning;
|
||||||
|
out_content += "[End thinking]\n\n";
|
||||||
|
}
|
||||||
|
out_content += content.content;
|
||||||
|
if (!out_content.empty() && out_content.back() != '\n') {
|
||||||
|
out_content += "\n";
|
||||||
|
}
|
||||||
|
out_content += "\n";
|
||||||
|
write_output_file(out_content);
|
||||||
|
}
|
||||||
|
|
||||||
if (params.show_timings) {
|
if (params.show_timings) {
|
||||||
ui::show_info(string_format(
|
ui::show_info(string_format(
|
||||||
"\n[ Prompt: %.1f t/s | Generation: %.1f t/s ]",
|
"\n[ Prompt: %.1f t/s | Generation: %.1f t/s ]",
|
||||||
@@ -619,4 +657,8 @@ void cli_context::shutdown() {
|
|||||||
server->stop();
|
server->stop();
|
||||||
server.reset();
|
server.reset();
|
||||||
}
|
}
|
||||||
|
if (output_file) {
|
||||||
|
output_file->close();
|
||||||
|
output_file.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-1
@@ -9,6 +9,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
struct cli_timings {
|
struct cli_timings {
|
||||||
double prompt_per_second = 0.0;
|
double prompt_per_second = 0.0;
|
||||||
@@ -32,6 +33,8 @@ struct cli_context {
|
|||||||
bool has_audio = false;
|
bool has_audio = false;
|
||||||
bool has_video = false;
|
bool has_video = false;
|
||||||
|
|
||||||
|
std::optional<std::ofstream> output_file;
|
||||||
|
|
||||||
cli_context(const common_params & params);
|
cli_context(const common_params & params);
|
||||||
~cli_context();
|
~cli_context();
|
||||||
|
|
||||||
@@ -49,7 +52,11 @@ struct cli_context {
|
|||||||
static std::atomic<bool> & interrupted();
|
static std::atomic<bool> & interrupted();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool generate_completion(std::string & assistant_content, cli_timings & timings);
|
struct generated_content {
|
||||||
|
std::string reasoning;
|
||||||
|
std::string content;
|
||||||
|
};
|
||||||
|
bool generate_completion(generated_content & content_out, cli_timings & timings);
|
||||||
void fetch_server_props();
|
void fetch_server_props();
|
||||||
void add_system_prompt();
|
void add_system_prompt();
|
||||||
void push_user_message(const std::string & text);
|
void push_user_message(const std::string & text);
|
||||||
@@ -62,5 +69,8 @@ private:
|
|||||||
// "image", "audio", "video"; returns false if the file cannot be read
|
// "image", "audio", "video"; returns false if the file cannot be read
|
||||||
bool stage_media_file(const std::string & fname, const std::string & type);
|
bool stage_media_file(const std::string & fname, const std::string & type);
|
||||||
|
|
||||||
|
// no-op if output file is not set
|
||||||
|
void write_output_file(const std::string & content);
|
||||||
|
|
||||||
std::unique_ptr<cli_context_impl> impl;
|
std::unique_ptr<cli_context_impl> impl;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user