Files
afs/apps/studio/src/ui/components/deployment_panel.cc
2025-12-30 10:25:25 -05:00

368 lines
9.9 KiB
C++

#include "deployment_panel.h"
#include <imgui.h>
#include <cstring>
namespace afs {
namespace studio {
namespace ui {
DeploymentPanel::DeploymentPanel() {
// Default test prompt
std::strncpy(test_prompt_buffer_.data(),
"Write a simple NOP instruction in 65816 assembly:",
test_prompt_buffer_.size() - 1);
}
void DeploymentPanel::Render(const ModelMetadata* selected_model) {
if (!selected_model) {
ImGui::TextDisabled("Select a model to view deployment options.");
return;
}
const auto& model = *selected_model;
// Header
ImGui::Text("%s", model.display_name.empty() ? model.model_id.c_str()
: model.display_name.c_str());
ImGui::Separator();
// Status display
if (is_busy_ || !status_message_.empty()) {
RenderDeploymentStatus();
}
// Quick actions
RenderQuickActions(model);
ImGui::Spacing();
ImGui::Separator();
// Ollama deployment section
if (ImGui::CollapsingHeader("Deploy to Ollama",
ImGuiTreeNodeFlags_DefaultOpen)) {
RenderOllamaDeployment(model);
}
// Conversion options
if (ImGui::CollapsingHeader("Format Conversion")) {
RenderConversionOptions(model);
}
// Test prompt
if (ImGui::CollapsingHeader("Test Model")) {
RenderTestPrompt(model);
}
}
void DeploymentPanel::RenderDeploymentStatus() {
if (is_busy_) {
ImGui::TextColored(ImVec4(0.2f, 0.7f, 0.9f, 1.0f), "%s",
current_operation_.c_str());
ImGui::ProgressBar(progress_, ImVec2(-1, 0));
}
if (!status_message_.empty()) {
bool is_error = !last_error_.empty();
ImVec4 color = is_error ? ImVec4(0.9f, 0.3f, 0.3f, 1.0f)
: ImVec4(0.3f, 0.9f, 0.3f, 1.0f);
ImGui::TextColored(color, "%s", status_message_.c_str());
}
if (!last_error_.empty()) {
ImGui::TextWrapped("Error: %s", last_error_.c_str());
}
ImGui::Spacing();
}
void DeploymentPanel::RenderQuickActions(const ModelMetadata& model) {
// Check current state
bool is_local = model.locations.count("mac") > 0;
bool has_gguf = false;
for (const auto& fmt : model.formats) {
if (fmt == "gguf") {
has_gguf = true;
break;
}
}
bool in_ollama = false;
for (const auto& backend : model.deployed_backends) {
if (backend == "ollama") {
in_ollama = true;
break;
}
}
// Quick action buttons
ImGui::Text("Quick Actions:");
// Pull button
ImGui::BeginDisabled(is_busy_ || is_local);
if (ImGui::Button("Pull to Mac")) {
is_busy_ = true;
current_operation_ = "Pulling model...";
progress_ = 0.1f;
status_message_.clear();
last_error_.clear();
// Execute pull
last_result_ = actions_.PullModel(model.model_id);
is_busy_ = false;
if (last_result_.status == ActionStatus::kCompleted) {
status_message_ = "Model pulled successfully";
} else {
status_message_ = "Pull failed";
last_error_ = last_result_.error;
}
}
ImGui::EndDisabled();
ImGui::SameLine();
if (!is_local) {
ImGui::TextDisabled("(not local)");
} else {
ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), "(local)");
}
ImGui::SameLine(0, 20);
// Convert button
ImGui::BeginDisabled(is_busy_ || !is_local || has_gguf);
if (ImGui::Button("Convert to GGUF")) {
is_busy_ = true;
current_operation_ = "Converting to GGUF...";
progress_ = 0.2f;
status_message_.clear();
last_error_.clear();
auto quant_opts = GetQuantizationOptions();
std::string quant = quant_opts[selected_quantization_].name;
last_result_ = actions_.ConvertToGGUF(model.model_id, quant);
is_busy_ = false;
if (last_result_.status == ActionStatus::kCompleted) {
status_message_ = "Conversion complete";
} else {
status_message_ = "Conversion failed";
last_error_ = last_result_.error;
}
}
ImGui::EndDisabled();
ImGui::SameLine();
if (has_gguf) {
ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), "(has GGUF)");
}
ImGui::SameLine(0, 20);
// Deploy button
ImGui::BeginDisabled(is_busy_ || in_ollama);
if (ImGui::Button("Deploy to Ollama")) {
is_busy_ = true;
current_operation_ = "Deploying to Ollama...";
progress_ = 0.3f;
status_message_.clear();
last_error_.clear();
std::string name(ollama_name_buffer_.data());
auto quant_opts = GetQuantizationOptions();
std::string quant = quant_opts[selected_quantization_].name;
last_result_ = actions_.DeployToOllama(model.model_id, name, quant);
is_busy_ = false;
if (last_result_.status == ActionStatus::kCompleted) {
status_message_ = "Deployed to Ollama";
} else {
status_message_ = "Deployment failed";
last_error_ = last_result_.error;
}
}
ImGui::EndDisabled();
ImGui::SameLine();
if (in_ollama) {
ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), "(in Ollama)");
}
}
void DeploymentPanel::RenderOllamaDeployment(const ModelMetadata& model) {
ImGui::Indent();
// Ollama name input
ImGui::Text("Ollama Model Name:");
ImGui::SameLine();
ImGui::SetNextItemWidth(200);
ImGui::InputTextWithHint("##OllamaName", model.model_id.c_str(),
ollama_name_buffer_.data(),
ollama_name_buffer_.size());
// Quantization selection
ImGui::Text("Quantization:");
auto quant_opts = GetQuantizationOptions();
for (int i = 0; i < static_cast<int>(quant_opts.size()); ++i) {
ImGui::SameLine();
if (ImGui::RadioButton(quant_opts[i].name.c_str(),
selected_quantization_ == i)) {
selected_quantization_ = i;
}
}
// Current quantization info
const auto& selected_quant = quant_opts[selected_quantization_];
ImGui::TextDisabled("%s (%.0f%% of F16 size)",
selected_quant.description.c_str(),
selected_quant.size_ratio * 100.0f);
// Status indicators
ImGui::Spacing();
bool ollama_running = actions_.IsOllamaRunning();
bool llama_available = actions_.IsLlamaCppAvailable();
ImGui::TextDisabled("Prerequisites:");
ImGui::BulletText("Ollama: %s",
ollama_running ? "Running" : "Not running");
if (!ollama_running) {
ImGui::SameLine();
ImGui::TextColored(ImVec4(0.9f, 0.6f, 0.2f, 1.0f), "(run: ollama serve)");
}
ImGui::BulletText("llama.cpp: %s",
llama_available ? "Available" : "Not found");
ImGui::Unindent();
}
void DeploymentPanel::RenderConversionOptions(const ModelMetadata& model) {
ImGui::Indent();
// Current formats
ImGui::Text("Available formats:");
for (const auto& fmt : model.formats) {
ImGui::SameLine();
ImGui::TextColored(ImVec4(0.4f, 0.7f, 0.9f, 1.0f), "[%s]", fmt.c_str());
}
if (model.formats.empty()) {
ImGui::SameLine();
ImGui::TextDisabled("(none)");
}
ImGui::Spacing();
// Conversion buttons
ImGui::BeginDisabled(is_busy_);
if (ImGui::Button("Convert to GGUF (Q4_K_M)")) {
is_busy_ = true;
current_operation_ = "Converting to GGUF Q4_K_M...";
last_result_ = actions_.ConvertToGGUF(model.model_id, "Q4_K_M");
is_busy_ = false;
status_message_ = last_result_.status == ActionStatus::kCompleted
? "Conversion complete"
: "Conversion failed";
if (last_result_.status != ActionStatus::kCompleted) {
last_error_ = last_result_.error;
}
}
ImGui::SameLine();
if (ImGui::Button("Convert to GGUF (Q5_K_M)")) {
is_busy_ = true;
current_operation_ = "Converting to GGUF Q5_K_M...";
last_result_ = actions_.ConvertToGGUF(model.model_id, "Q5_K_M");
is_busy_ = false;
status_message_ = last_result_.status == ActionStatus::kCompleted
? "Conversion complete"
: "Conversion failed";
if (last_result_.status != ActionStatus::kCompleted) {
last_error_ = last_result_.error;
}
}
ImGui::EndDisabled();
ImGui::Unindent();
}
void DeploymentPanel::RenderTestPrompt(const ModelMetadata& model) {
ImGui::Indent();
// Check if model is deployed
bool can_test = false;
std::string deployed_to;
for (const auto& backend : model.deployed_backends) {
if (backend == "ollama") {
can_test = true;
deployed_to = "ollama";
break;
}
}
if (!can_test) {
ImGui::TextDisabled("Deploy model to a backend first to test.");
ImGui::Unindent();
return;
}
// Test prompt input
ImGui::Text("Test Prompt:");
ImGui::SetNextItemWidth(-1);
ImGui::InputTextMultiline("##TestPrompt", test_prompt_buffer_.data(),
test_prompt_buffer_.size(), ImVec2(0, 60));
// Run test button
ImGui::BeginDisabled(is_busy_);
if (ImGui::Button("Run Test")) {
is_busy_ = true;
current_operation_ = "Testing model...";
status_message_.clear();
last_error_.clear();
test_output_.clear();
last_result_ = actions_.TestModel(model.model_id, DeploymentBackend::kOllama,
std::string(test_prompt_buffer_.data()));
is_busy_ = false;
if (last_result_.status == ActionStatus::kCompleted) {
status_message_ = "Test completed";
test_output_ = last_result_.output;
} else {
status_message_ = "Test failed";
last_error_ = last_result_.error;
}
}
ImGui::EndDisabled();
// Test output
if (!test_output_.empty()) {
ImGui::Spacing();
ImGui::Text("Response:");
ImGui::BeginChild("TestOutput", ImVec2(0, 100), true);
ImGui::TextWrapped("%s", test_output_.c_str());
ImGui::EndChild();
}
ImGui::Unindent();
}
void RenderDeploymentPanelWindow(DeploymentPanel& panel,
const ModelMetadata* model,
bool* open) {
if (!open || !*open) return;
ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Deployment", open)) {
ImGui::End();
return;
}
panel.Render(model);
ImGui::End();
}
} // namespace ui
} // namespace studio
} // namespace afs