Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20)

project(cpp-workspace VERSION 0.3.0 LANGUAGES CXX)
project(cpp-workspace VERSION 0.4.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand All @@ -21,6 +21,7 @@ add_executable(cpp-workspace-app
src/utils/text_utils.cpp
src/utils/system_utils.cpp
src/utils/embedding_utils.cpp
src/utils/retrieving_utils.cpp
src/utils/http_utils.hpp
)

Expand Down
38 changes: 27 additions & 11 deletions src/interface/user_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,15 +361,15 @@ void UserInterface::draw_input_area() {
*/
void UserInterface::draw_output_area() {
// Clear the output area
for (int i = 0; i < layout.output_height; i++) {
for (int i = 0; i < layout.output_height; i += 1) {
mvhline(layout.output_y + i, 1, ' ', COLS - 2);
}

int current_line = layout.input_y - 2; // Start from bottom
int max_content_width = COLS - 4;

// Iterate backwards through buffer (newest first)
for (auto it = output_buffer.rbegin(); it != output_buffer.rend(); ++it) {
for (auto it = output_buffer.rbegin(); it != output_buffer.rend(); it += 1) {
if (current_line <= layout.output_y) {
break;
}
Expand All @@ -387,7 +387,7 @@ void UserInterface::draw_output_area() {
}

// Render from bottom to top (reverse order)
for (auto line_it = display_lines.rbegin(); line_it != display_lines.rend(); ++line_it) {
for (auto line_it = display_lines.rbegin(); line_it != display_lines.rend(); line_it += 1) {
if (current_line <= layout.output_y) break;

if (type == UserInterface::MESSAGE_TYPE_ERROR) {
Expand Down Expand Up @@ -712,8 +712,10 @@ void UserInterface::end_session(int exit_code) {
* @param embedding_model Name of the embedding model in use.
* @param prompt Initial prompt or query string provided by the user.
* @param stream Boolean flag indicating whether streaming mode is enabled.
* @param rag Whether to run the program in the Retrieval Augmented Generation mode.
* @param interactive Boolean flag indicating whether interactive mode is enabled.
* @param dark_theme Boolean flag indicating whether dark theme is active.
* @param skip_embedding Whether to skip vectorizing documents and creating embeddings.
*
* Constructs a multi-line diagnostic string containing all provided configuration parameters.
* Each parameter is formatted with a 🛈 (information) icon prefix for visual consistency.
Expand All @@ -735,14 +737,16 @@ void UserInterface::end_session(int exit_code) {
* models, modes, and theme are in effect.
*/
void UserInterface::print_args(const std::string& model, const std::string& embedding_model, const std::string& prompt,
bool stream, bool interactive, bool dark_theme) {
bool stream, bool rag, bool interactive, bool dark_theme, bool skip_embedding) {
std::string buffer;
buffer += "🛈 model = " + model + "\n";
buffer += "🛈 embedding_model = " + embedding_model + "\n";
buffer += "🛈 prompt = " + prompt + "\n";
buffer += "🛈 stream = " + std::string(stream ? "true" : "false") + "\n";
buffer += "🛈 rag = " + std::string(rag ? "true" : "false") + "\n";
buffer += "🛈 interactive = " + std::string(interactive ? "true" : "false") + "\n";
buffer += "🛈 dark_theme = " + std::string(dark_theme ? "true" : "false") + "\n";
buffer += "🛈 skip_embedding = " + std::string(skip_embedding ? "true" : "false") + "\n";

this->print(buffer, UserInterface::MESSAGE_TYPE_NORMAL);
}
Expand All @@ -754,8 +758,10 @@ void UserInterface::print_args(const std::string& model, const std::string& embe
* @param embedding_model Name of the embedding model in use.
* @param prompt Initial prompt or query string provided by the user.
* @param stream Boolean flag indicating whether streaming mode is enabled.
* @param rag Whether to run the program in the Retrieval Augmented Generation mode.
* @param interactive Boolean flag indicating whether interactive mode is enabled.
* @param dark_theme Boolean flag indicating whether dark theme is active.
* @param skip_embedding Whether to skip vectorizing documents and creating embeddings.
*
* @return void
*
Expand All @@ -779,15 +785,17 @@ void UserInterface::print_args(const std::string& model, const std::string& embe
* initialization or after cleanup, to avoid conflicts with the ncurses output buffer.
*/
void UserInterface::print_args_noninteractive(const std::string& model, const std::string& embedding_model,
const std::string& prompt, bool stream, bool interactive,
bool dark_theme) {
const std::string& prompt, bool stream, bool rag, bool interactive,
bool dark_theme, bool skip_embedding) {
printf("\n");
printf("🛈 model = %s\n", model.c_str());
printf("🛈 embedding_model = %s\n", embedding_model.c_str());
printf("🛈 prompt = %s\n", prompt.c_str());
printf("🛈 stream = %s\n", stream ? "true" : "false");
printf("🛈 rag = %s\n", rag ? "true" : "false");
printf("🛈 interactive = %s\n", interactive ? "true" : "false");
printf("🛈 dark_theme = %s\n", dark_theme ? "true" : "false");
printf("🛈 skip_embedding = %s\n", skip_embedding ? "true" : "false");
printf("\n");
}

Expand Down Expand Up @@ -978,7 +986,7 @@ bool UserInterface::next_prompt(std::string* prompt) {
this->request_redraw();
}
} else if (ch != ERR && isprint(ch)) {
if (input_buffer.length() < COLS - 40) { // Leave room for prompt
if (input_buffer.length() < COLS - 40) {
input_buffer.push_back(ch);
input_cursor_x++;
this->request_redraw();
Expand Down Expand Up @@ -1030,16 +1038,22 @@ bool UserInterface::next_prompt_noninteractive(std::string* prompt) {
* @param text The message content to be displayed.
* @param type The message type (MESSAGE_TYPE_NORMAL, MESSAGE_TYPE_ERROR, MESSAGE_TYPE_SUCCESS) that controls styling.
* Defaults to MESSAGE_TYPE_NORMAL if not specified.
* @param stream In the stream mode, every chunk of text after the first one is appended to the last buffer entry.
* Defaults to 'false'.
*
* Thread-safe: acquires ui_mutex before adding the message to output_buffer.
* Automatically requests a redraw via request_redraw() to display the new message.
*
* @see draw_output_area()
*/
void UserInterface::print(const std::string& text, int type) {
void UserInterface::print(const std::string& text, int type, bool stream) {
{
std::lock_guard<std::mutex> lock(this->ui_mutex);
output_buffer.push_back({text, type});
if (stream == true && !output_buffer.empty()) {
output_buffer.back().first += text;
} else {
output_buffer.push_back({text, type});
}
}
this->request_redraw();
}
Expand Down Expand Up @@ -1070,11 +1084,13 @@ void UserInterface::print_success(const std::string& text) {
* @brief Queues a normal message (plain text) and requests a redraw.
*
* @param text The message content.
* @param stream In the stream mode, every chunk of text after the first one is appended to the last buffer entry.
* Defaults to 'false'.
*
* @see print()
*/
void UserInterface::print_message(const std::string& text) {
this->print(text, UserInterface::MESSAGE_TYPE_NORMAL);
void UserInterface::print_message(const std::string& text, bool stream) {
this->print(text, UserInterface::MESSAGE_TYPE_NORMAL, stream);
}

/**
Expand Down
9 changes: 5 additions & 4 deletions src/interface/user_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,11 @@ class UserInterface {
void end_session(int exit_code);

void print_args(const std::string& model, const std::string& embedding_model, const std::string& prompt,
bool stream, bool interactive, bool dark_theme);
bool stream, bool rag, bool interactive, bool dark_theme, bool skip_embedding);

static void print_args_noninteractive(const std::string& model, const std::string& embedding_model,
const std::string& prompt, bool stream, bool interactive, bool dark_theme);
const std::string& prompt, bool stream, bool rag, bool interactive,
bool dark_theme, bool skip_embedding);

void print_model_list(const std::vector<std::string>& models);

Expand All @@ -167,7 +168,7 @@ class UserInterface {

static bool next_prompt_noninteractive(std::string* prompt);

void print(const std::string& text, int type = UserInterface::MESSAGE_TYPE_NORMAL);
void print(const std::string& text, int type = UserInterface::MESSAGE_TYPE_NORMAL, bool stream = false);

void print_error(const std::string& text);

Expand All @@ -177,7 +178,7 @@ class UserInterface {

static void print_success_noninteractive(const std::string& text);

void print_message(const std::string& text);
void print_message(const std::string& text, bool stream = false);

static void print_message_noninteractive(const std::string& text);

Expand Down
78 changes: 51 additions & 27 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "storage/qdrant_client.hpp"
#include "utils/curl_utils.hpp"
#include "utils/embedding_utils.hpp"
#include "utils/retrieving_utils.hpp"
#include "utils/system_utils.hpp"
#include "utils/text_utils.hpp"
#include <iostream>
Expand All @@ -14,6 +15,7 @@ int main(int argc, const char* argv[]) {
std::string embedding_model = "embeddinggemma:latest";
std::string prompt = "Hey! What's up!";
bool stream = false;
bool rag = false;
bool interactive = false;
bool dark_theme = true;
bool skip_embedding = false;
Expand All @@ -32,6 +34,8 @@ int main(int argc, const char* argv[]) {
prompt = arg.substr(9);
} else if (arg.starts_with("--stream=") == true) {
stream = arg.substr(9) == "true" ? true : false;
} else if (arg.starts_with("--rag=") == true) {
rag = arg.substr(6) == "true" ? true : false;
} else if (arg.starts_with("--interactive=") == true) {
interactive = arg.substr(14) == "true" ? true : false;
} else if (arg.starts_with("--dark_theme=") == true) {
Expand All @@ -43,17 +47,23 @@ int main(int argc, const char* argv[]) {

UserInterface ui(dark_theme);
if (interactive == true) {
printf("\nInteractive mode debug info.\n");
printf("---\n");
printf("isatty(0)=%d, isatty(1)=%d, isatty(2)=%d\n", isatty(0), isatty(1), isatty(2));
printf("TERM=%s\n", getenv("TERM"));
printf("TERMINFO=%s\n", getenv("TERMINFO") ? getenv("TERMINFO") : "(not set)");
printf("LC_ALL=%s\n", getenv("LC_ALL") ? getenv("LC_ALL") : "(not set)");
printf("LANG=%s\n", getenv("LANG") ? getenv("LANG") : "(not set)");
printf("---\n");
message = std::format("\nInteractive mode debug info.\n"
"---\n"
"isatty(0)={}, isatty(1)={}, isatty(2)={}\n"
"TERM={}\n"
"TERMINFO={}\n"
"LC_ALL={}\n"
"LANG={}\n"
"---\n",
isatty(0), isatty(1), isatty(2), getenv("TERM") ? getenv("TERM") : "(not set)",
getenv("TERMINFO") ? getenv("TERMINFO") : "(not set)",
getenv("LC_ALL") ? getenv("LC_ALL") : "(not set)",
getenv("LANG") ? getenv("LANG") : "(not set)");

UserInterface::print_message_noninteractive(message);

ui.run();
ui.print_args(model, embedding_model, prompt, stream, interactive, dark_theme);
ui.print_args(model, embedding_model, prompt, stream, rag, interactive, dark_theme, skip_embedding);

while (prompt.length() == 0) {
done = ui.next_prompt(&prompt);
Expand All @@ -63,18 +73,19 @@ int main(int argc, const char* argv[]) {
}
}
} else {
UserInterface::print_args_noninteractive(model, embedding_model, prompt, stream, interactive, dark_theme);
UserInterface::print_args_noninteractive(model, embedding_model, prompt, stream, rag, interactive, dark_theme,
skip_embedding);

if (prompt.length() == 0) {
done = UserInterface::next_prompt_noninteractive(&prompt);

if (prompt.length() == 0) {
printf("\n🏁 Empty prompt. The session has ended.\n");
UserInterface::print_message_noninteractive("\n🏁 Empty prompt. The session has ended.\n");
exit(0);
}

if (done == true) {
printf("\n🏁 The session has ended.\n");
UserInterface::print_message_noninteractive("\n🏁 The session has ended.\n");
exit(0);
}
}
Expand All @@ -86,12 +97,16 @@ int main(int argc, const char* argv[]) {

QdrantClient storage_client(&ui, &curl, &system_utils);

struct OllamaClientConfig config {};
struct OllamaClientConfig ollama_config {};

OllamaClient ollama_client(&config, &ui, &curl, &system_utils);
OllamaClient ollama_client(&ollama_config, &ui, &curl, &system_utils);

EmbeddingUtils embedding_utils(&ui, &curl, &ollama_client, &storage_client);

struct RetrievingUtilsConfig retrieving_config {};

RetrievingUtils retrieving_utils(&retrieving_config, &ui, &curl, &ollama_client, &storage_client);

std::vector<std::string> models = ollama_client.query_model_list();
bool force_model_selection = true;
for (size_t i = 0; i < models.size(); i += 1) {
Expand All @@ -106,12 +121,13 @@ int main(int argc, const char* argv[]) {
ui.print_message(message);
ui.print_model_list(models);
ui.select_model(models, &model);
ui.print_args(model, embedding_model, prompt, stream, interactive, dark_theme);
ui.print_args(model, embedding_model, prompt, stream, rag, interactive, dark_theme, skip_embedding);
} else {
UserInterface::print_message_noninteractive(message);
UserInterface::print_model_list_noninteractive(models);
UserInterface::select_model_noninteractive(models, &model);
UserInterface::print_args_noninteractive(model, embedding_model, prompt, stream, interactive, dark_theme);
UserInterface::print_args_noninteractive(model, embedding_model, prompt, stream, rag, interactive,
dark_theme, skip_embedding);
}
}

Expand All @@ -128,29 +144,33 @@ int main(int argc, const char* argv[]) {
ui.print_message(message);
ui.print_model_list(models);
ui.select_model(models, &embedding_model);
ui.print_args(model, embedding_model, prompt, stream, interactive, dark_theme);
ui.print_args(model, embedding_model, prompt, stream, rag, interactive, dark_theme, skip_embedding);
} else {
UserInterface::print_message_noninteractive(message);
UserInterface::print_model_list_noninteractive(models);
UserInterface::select_model_noninteractive(models, &embedding_model);
UserInterface::print_args_noninteractive(model, embedding_model, prompt, stream, interactive,
dark_theme);
UserInterface::print_args_noninteractive(model, embedding_model, prompt, stream, rag, interactive,
dark_theme, skip_embedding);
}
}
}

config.model = model;
config.embedding_model = embedding_model;
config.prompt = prompt;
config.stream = stream;
ollama_config.model = model;
ollama_config.embedding_model = embedding_model;
ollama_config.prompt = prompt;
ollama_config.stream = stream;

if (skip_embedding == false) {
std::vector<std::string> urls = {
"https://raw.githubusercontent.com/rfprod/nx-ng-starter/refs/heads/main/README.md"};
embedding_utils.create_embedings(urls);
}

ollama_client.query_model();
if (rag == false) {
ollama_client.query_model();
} else {
retrieving_utils.run();
}

if (interactive == true) {
while (done == false) {
Expand All @@ -159,13 +179,17 @@ int main(int argc, const char* argv[]) {
continue;
}

config.prompt = prompt;
ollama_client.query_model();
ollama_config.prompt = prompt;
if (rag == false) {
ollama_client.query_model();
} else {
retrieving_utils.run();
}
ui.request_redraw();
}
ui.end_session(EXIT_SUCCESS);
} else {
printf("\n🏁 The session has ended.\n");
UserInterface::print_message_noninteractive("\n🏁 The session has ended.\n");
}

return 0;
Expand Down
Loading
Loading