🐛 | Fix misc bugs
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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": ""
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user