🐛 | Fix misc bugs

This commit is contained in:
2026-05-19 02:13:10 +03:00
parent 4b27995f62
commit 57f41add49
2 changed files with 218 additions and 10 deletions

View File

@@ -117,19 +117,56 @@ async function callUnsloth(prompt) {
temperature: store.temperature, temperature: store.temperature,
}; };
const res = await fetch(url, { let res;
let fetchError = null;
try {
res = await fetch(url, {
method: "POST", method: "POST",
headers, headers,
body: JSON.stringify(body), body: JSON.stringify(body),
}); });
} catch (e) {
fetchError = e;
}
if (fetchError) {
// Network-level error (e.g. Failed to fetch, CORS, DNS, etc.)
throw new DetailedApiError({
message: fetchError.message || "Failed to fetch",
url,
prompt,
model: store.model,
apiUrl: store.apiUrl,
status: null,
responseBody: null,
timestamp: new Date().toISOString(),
cause: fetchError,
});
}
if (!res.ok) { if (!res.ok) {
let responseBody = null;
let detail = `API returned ${res.status}`; let detail = `API returned ${res.status}`;
try { try {
const err = await res.json(); responseBody = await res.text();
try {
const err = JSON.parse(responseBody);
detail = err.error?.message || err.detail || detail; detail = err.error?.message || err.detail || detail;
} catch {} } catch {}
throw new Error(detail); } catch {}
throw new DetailedApiError({
message: detail,
url,
prompt,
model: store.model,
apiUrl: store.apiUrl,
status: res.status,
responseBody,
timestamp: new Date().toISOString(),
cause: null,
});
} }
const data = await res.json(); const data = await res.json();
@@ -137,6 +174,172 @@ async function callUnsloth(prompt) {
return text; return text;
} }
// ── Detailed error class ─────────────────────────────────────────
class DetailedApiError extends Error {
constructor(details) {
super(details.message);
this.name = "DetailedApiError";
this.url = details.url;
this.prompt = details.prompt;
this.model = details.model;
this.apiUrl = details.apiUrl;
this.status = details.status;
this.responseBody = details.responseBody;
this.timestamp = details.timestamp;
this.cause = details.cause;
}
}
// ── Error detail modal ──────────────────────────────────────────
function ErrorDetailModal(props) {
const error = props.error;
return (
<ModalRoot>
<ModalHeader close={props.close}>Unsloth Chat - Error Details</ModalHeader>
<ModalBody>
<div style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
{/* Error message */}
<div>
<Header tag={HeaderTags.HeadingSM} margin={false}>Error</Header>
<div style={{
marginTop: "4px",
padding: "8px 12px",
background: "var(--info-danger-background)",
color: "var(--text-danger)",
borderRadius: "4px",
fontSize: "14px",
wordBreak: "break-word",
}}>
{error.message}
</div>
</div>
{/* Status code */}
<div>
<Header tag={HeaderTags.HeadingSM} margin={false}>HTTP Status</Header>
<Text tag={TextTags.textSM} style={{ marginTop: "4px", display: "block" }}>
{error.status !== null ? error.status : "N/A (network error)"}
</Text>
</div>
{/* API URL used */}
<div>
<Header tag={HeaderTags.HeadingSM} margin={false}>Request URL</Header>
<Text tag={TextTags.textSM} style={{
marginTop: "4px",
display: "block",
wordBreak: "break-all",
fontFamily: "var(--font-code, monospace)",
fontSize: "12px",
}}>
{error.url}
</Text>
</div>
{/* Configured API URL */}
<div>
<Header tag={HeaderTags.HeadingSM} margin={false}>Configured API URL</Header>
<Text tag={TextTags.textSM} style={{
marginTop: "4px",
display: "block",
wordBreak: "break-all",
fontFamily: "var(--font-code, monospace)",
fontSize: "12px",
}}>
{error.apiUrl}
</Text>
</div>
{/* Model */}
<div>
<Header tag={HeaderTags.HeadingSM} margin={false}>Model</Header>
<Text tag={TextTags.textSM} style={{ marginTop: "4px", display: "block" }}>
{error.model}
</Text>
</div>
{/* Prompt */}
<div>
<Header tag={HeaderTags.HeadingSM} margin={false}>Prompt</Header>
<div style={{
marginTop: "4px",
padding: "8px 12px",
background: "var(--background-secondary)",
borderRadius: "4px",
fontSize: "13px",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
maxHeight: "120px",
overflowY: "auto",
}}>
{error.prompt}
</div>
</div>
{/* Response body */}
{error.responseBody && (
<div>
<Header tag={HeaderTags.HeadingSM} margin={false}>Response Body</Header>
<div style={{
marginTop: "4px",
padding: "8px 12px",
background: "var(--background-secondary)",
borderRadius: "4px",
fontSize: "12px",
fontFamily: "var(--font-code, monospace)",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
maxHeight: "200px",
overflowY: "auto",
}}>
{error.responseBody}
</div>
</div>
)}
{/* Cause (network error details) */}
{error.cause && (
<div>
<Header tag={HeaderTags.HeadingSM} margin={false}>Network Error</Header>
<div style={{
marginTop: "4px",
padding: "8px 12px",
background: "var(--background-secondary)",
borderRadius: "4px",
fontSize: "12px",
fontFamily: "var(--font-code, monospace)",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
}}>
{error.cause.toString()}
</div>
</div>
)}
{/* Timestamp */}
<div>
<Text tag={TextTags.textXS} style={{ color: "var(--text-muted)", display: "block" }}>
Occurred at: {error.timestamp}
</Text>
</div>
</div>
</ModalBody>
<ModalFooter>
<div style={{ display: "flex", gap: "8px", justifyContent: "flex-end", width: "100%" }}>
<Button
look={ButtonLooks.FILLED}
color={ButtonColors.PRIMARY}
onClick={props.close}
>
Close
</Button>
</div>
</ModalFooter>
</ModalRoot>
);
}
// ── The prompt modal ──────────────────────────────────────────── // ── The prompt modal ────────────────────────────────────────────
function PromptModal(props) { function PromptModal(props) {
const [prompt, setPrompt] = shelter.solid.createSignal(""); const [prompt, setPrompt] = shelter.solid.createSignal("");
@@ -184,7 +387,11 @@ function PromptModal(props) {
props.close(); props.close();
} catch (e) { } catch (e) {
if (e instanceof DetailedApiError) {
openModal((p) => <ErrorDetailModal close={p.close} error={e} />);
} else {
setError(e.message || "An error occurred"); setError(e.message || "An error occurred");
}
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -238,6 +445,7 @@ function PromptModal(props) {
color={ButtonColors.PRIMARY} color={ButtonColors.PRIMARY}
onClick={handleSubmit} onClick={handleSubmit}
disabled={loading() || !prompt().trim()} disabled={loading() || !prompt().trim()}
style={{ minWidth: "180px" }}
> >
{loading() ? "Generating..." : "Generate & Insert"} {loading() ? "Generating..." : "Generate & Insert"}
</Button> </Button>

View File

@@ -1,6 +1,6 @@
{ {
"name": "Unsloth Chat", "name": "Unsloth Chat",
"author": "You", "author": "NikkeDoy",
"description": "Adds a button to the chatbar that lets you fill text from a local LLM via Unsloth Studio API.", "description": "Adds a button to the chatbar that lets you fill text from a local LLM via Unsloth Studio API.",
"hash": "" "hash": ""
} }