Skip to content
Open
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
29 changes: 29 additions & 0 deletions .github/ISSUE_TEMPLATE/runtime_error_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
name: Runtime error report
about: Report a runtime/model/space error from HARP
title: "HARP runtime error report"
labels: bug
assignees: ""
---

## Summary
<!-- Briefly describe the error -->

## Endpoint
<!-- Space/model endpoint if available -->

## User Notes
<!-- What were you doing when this happened? -->

## Environment
<!-- HARP version, local time, log file path -->

## Reproduction
1.
2.
3.

## Expected Behavior

## Actual Behavior

16 changes: 15 additions & 1 deletion src/MainComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ void MainComponent::openSettingsWindow()
DialogWindow::LaunchOptions options;
options.dialogTitle = "Settings";
options.dialogBackgroundColour = Colours::darkgrey;
options.content.setOwned(new SettingsWindow());
options.content.setOwned(new SettingsWindow([this] { restoreViewDefaults(); }));

options.useNativeTitleBar = true;
options.resizable = true;
Expand All @@ -216,6 +216,20 @@ void MainComponent::openSettingsWindow()
options.launchAsync();
}

void MainComponent::restoreViewDefaults()
{
// Default: status area shown — toggle if currently hidden
if (! showStatusArea)
viewStatusAreaCallback();

// Default: media clipboard shown — toggle if currently hidden
if (! showMediaClipboard)
viewMediaClipboardCallback();

// showWelcomePopup default (true) is already restored by clearing settings;
// it will show on the next launch automatically.
}

/* --View-- */

void MainComponent::viewStatusAreaCallback()
Expand Down
1 change: 1 addition & 0 deletions src/MainComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class MainComponent : public Component,
// View
void viewStatusAreaCallback();
void viewMediaClipboardCallback();
void restoreViewDefaults();

// Help
void openAboutWindow();
Expand Down
189 changes: 145 additions & 44 deletions src/ModelTab.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "utils/Errors.h"
#include "utils/Logging.h"
#include "utils/Settings.h"
#include "utils/Tutorial.h"

using namespace juce;
Expand Down Expand Up @@ -324,79 +325,178 @@ class ModelTab : public Component, private ChangeListener, public ChangeBroadcas

void openErrorPopup(const Error error, std::function<void()> onExit = {})
{
MessageBoxOptions errorPopup =
MessageBoxOptions()
.withIconType(AlertWindow::WarningIcon)
.withTitle("Error") // TODO - Name of error family would be nice here
// error ? toUserMessage(*error) : "An unknown error occurred."
.withMessage(toUserMessage(error));

std::optional<String> openablePath = getOpenablePath(error);
String errorMessage = toUserMessage(error);

if (openablePath.has_value())
constexpr int buttonOpenURL = 1;
constexpr int buttonOpenLogs = 2;
constexpr int buttonSendReport = 3;
constexpr int buttonOk = 0;

String popupMessage = errorMessage
+ "\n\nOptional: add context below before opening the issue.";

if (errorPopupWindow != nullptr)
{
errorPopup = errorPopup.withButton("Open URL");
removeChildComponent(errorPopupWindow.get());
errorPopupWindow.reset();
}

errorPopup = errorPopup.withButton("Open Logs").withButton("Ok");
errorPopupWindow = std::make_unique<AlertWindow>("Error", popupMessage, AlertWindow::WarningIcon);
AlertWindow* alertWindow = errorPopupWindow.get();

auto alertCallback = [this, error, openablePath, onExit, errorPopup](int choice)
auto bindButtonCallback = [alertWindow](const String& buttonText, std::function<void()> callback)
{
DBG_AND_LOG("ModelTab::loadModelCallback::alertCallback: Chose button index: " << choice
<< ".");

enum Choice
for (int i = 0; i < alertWindow->getNumChildComponents(); ++i)
{
OpenURL,
OpenLogs,
OK
};

/*
TODO - The button indices assigned by MessageBoxOptions do not follow the order in which
they were added. This should be fixed in JUCE v8. The following is a temporary workaround.

See https://forum.juce.com/t/wrong-callback-value-for-alertwindow-showokcancelbox/55671/2
if (auto* button = dynamic_cast<TextButton*>(alertWindow->getChildComponent(i)))
{
if (button->getButtonText() == buttonText)
{
button->onClick = std::move(callback);
return;
}
}
}
};

When this is fixed, errorPopup can be removed from the argument list.
*/
if (openablePath.has_value())
{
alertWindow->addButton("Open URL", buttonOpenURL);
bindButtonCallback("Open URL", [this, openablePath, onExit]
{
std::map<int, int> observedButtonIndicesMap = {};
URL(*openablePath).launchInDefaultBrowser();

if (errorPopup.getNumButtons() == 3)
if (onExit)
{
observedButtonIndicesMap.insert({ 1, Choice::OpenURL });
onExit();
}

observedButtonIndicesMap.insert(
{ errorPopup.getNumButtons() - 1, Choice::OpenLogs });
if (errorPopupWindow != nullptr)
{
removeChildComponent(errorPopupWindow.get());
errorPopupWindow.reset();
}
});
}

observedButtonIndicesMap.insert({ 0, Choice::OK });
alertWindow->addButton("Open Logs", buttonOpenLogs);
bindButtonCallback("Open Logs", [this, onExit]
{
HARPLogger::getInstance()->getLogFile().revealToUser();

choice = observedButtonIndicesMap[choice];
if (onExit)
{
onExit();
}

if (choice == Choice::OpenURL)
if (errorPopupWindow != nullptr)
{
URL(*openablePath).launchInDefaultBrowser();
removeChildComponent(errorPopupWindow.get());
errorPopupWindow.reset();
}
else if (choice == Choice::OpenLogs)
});

alertWindow->addButton("Open GitHub Issue", buttonSendReport);
bindButtonCallback("Open GitHub Issue", [this, error, errorMessage, onExit]
{
if (errorPopupWindow != nullptr)
{
HARPLogger::getInstance()->getLogFile().revealToUser();
openGitHubIssue(
error, errorMessage, errorPopupWindow->getTextEditorContents("errorReportNotes"));
}
else

if (onExit)
{
// Nothing to do
onExit();
}

if (errorPopupWindow != nullptr)
{
removeChildComponent(errorPopupWindow.get());
errorPopupWindow.reset();
}
});

alertWindow->addButton("Ok", buttonOk);
bindButtonCallback("Ok", [this, onExit]
{
if (onExit)
{
// Perform optional state cleanup
onExit();
}
};

AlertWindow::showAsync(errorPopup, alertCallback);
if (errorPopupWindow != nullptr)
{
removeChildComponent(errorPopupWindow.get());
errorPopupWindow.reset();
}
});

alertWindow->addTextEditor(
"errorReportNotes",
"",
"What were you doing when this happened? (optional)");

addAndMakeVisible(*errorPopupWindow);
errorPopupWindow->setAlwaysOnTop(true);

int popupWidth = jmax(560, jmin(getWidth() - 24, 1100));
int popupHeight = 280;
errorPopupWindow->setBounds(getLocalBounds().withSizeKeepingCentre(popupWidth, popupHeight));
errorPopupWindow->toFront(true);
}

void openGitHubIssue(const Error& error, const String& errorMessage, const String& notes)
{
static const String issueBaseUrl = "https://github.com/TEAMuP-dev/HARP/issues/new";
static const String issueTemplate = "runtime_error_report.md";

File logFile = HARPLogger::getInstance()->getLogFile();

String issueTitle = "HARP runtime error report";
if (const auto* gradioError = std::get_if<GradioError>(&error))
{
if (gradioError->reason.isNotEmpty())
{
issueTitle = "HARP: " + gradioError->reason;
}
else if (gradioError->type == GradioError::Type::QuotaExceeded)
{
issueTitle = "HARP: Hugging Face quota exceeded";
}
}

String body;
body << "## Summary\n";
body << errorMessage << "\n\n";

if (const auto* gradioError = std::get_if<GradioError>(&error))
{
if (gradioError->endpointPath.isNotEmpty())
{
body << "## Endpoint\n";
body << gradioError->endpointPath << "\n\n";
}
}

body << "## User Notes\n";
body << (notes.isNotEmpty() ? notes : "(none)") << "\n\n";
body << "## Environment\n";
body << "- HARP version: " << JUCE_APPLICATION_VERSION_STRING << "\n";
body << "- Time (local): " << Time::getCurrentTime().toString(true, true) << "\n";
body << "- Log file: " << logFile.getFullPathName() << "\n\n";
body << "## Reproduction\n";
body << "1. \n";
body << "2. \n";
body << "3. \n";

String escapedTitle = URL::addEscapeChars(issueTitle, true);
String escapedBody = URL::addEscapeChars(body, true);
String escapedTemplate = URL::addEscapeChars(issueTemplate, true);
URL(issueBaseUrl + "?template=" + escapedTemplate + "&title=" + escapedTitle + "&body="
+ escapedBody)
.launchInDefaultBrowser();
}

void loadModelCallback()
Expand Down Expand Up @@ -609,4 +709,5 @@ class ModelTab : public Component, private ChangeListener, public ChangeBroadcas
ThreadPool processingThreadPool { 10 };

std::atomic<uint64_t> currentProcessID { 0 };
};
std::unique_ptr<AlertWindow> errorPopupWindow;
};
3 changes: 3 additions & 0 deletions src/clients/Client.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "../utils/Errors.h"
#include "../utils/Labels.h"
#include "../utils/Logging.h"
#include "../utils/Messages.h"
#include "../utils/Settings.h"

using namespace juce;
Expand Down Expand Up @@ -292,6 +293,8 @@ class Client
String getCommonHeaders() const { return getAuthorizationHeader() + acceptHeader; }
String getJSONHeaders() const { return getCommonHeaders() + contentTypeJSONHeader; }

SharedResourcePointer<StatusMessage> statusMessage;

private:
String getAuthorizationHeader() const
{
Expand Down
Loading
Loading