Skip to content

Commit f0c35dc

Browse files
Robert WeberRobert Weber
authored andcommitted
Added LLM errors to final summary page
1 parent 4862df8 commit f0c35dc

4 files changed

Lines changed: 47 additions & 4 deletions

File tree

novelforge/llm/client.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,10 @@ def _call_single_provider(
503503

504504
# Retryable server errors
505505
if resp.status_code == 429 or resp.status_code >= 500:
506+
if resp.status_code >= 500:
507+
_llm_usage.http_errors_5xx = getattr(_llm_usage, "http_errors_5xx", 0) + 1
508+
else:
509+
_llm_usage.http_errors_4xx = getattr(_llm_usage, "http_errors_4xx", 0) + 1
506510
wait = _retry_delay(attempt)
507511
logger.warning(
508512
"LLM API [%s] returned %s – retry %d/%d in %.1fs",
@@ -532,6 +536,7 @@ def _call_single_provider(
532536
return content
533537

534538
except requests.exceptions.Timeout:
539+
_llm_usage.timeout_errors = getattr(_llm_usage, "timeout_errors", 0) + 1
535540
logger.warning(
536541
"LLM request timed out [%s] (attempt %d/%d)",
537542
provider.label, attempt, MAX_RETRIES,
@@ -555,6 +560,12 @@ def _call_single_provider(
555560
time.sleep(_retry_delay(attempt))
556561

557562
except requests.exceptions.RequestException as exc:
563+
status = getattr(getattr(exc, "response", None), "status_code", None)
564+
if status is not None:
565+
if status >= 500:
566+
_llm_usage.http_errors_5xx = getattr(_llm_usage, "http_errors_5xx", 0) + 1
567+
elif status >= 400:
568+
_llm_usage.http_errors_4xx = getattr(_llm_usage, "http_errors_4xx", 0) + 1
558569
typed_error = _classify_request_error(
559570
exc, provider,
560571
action=action, attempt=attempt,
@@ -629,6 +640,9 @@ def reset_llm_usage() -> None:
629640
_llm_usage.prompt_tokens = 0
630641
_llm_usage.completion_tokens = 0
631642
_llm_usage.call_count = 0
643+
_llm_usage.http_errors_4xx = 0
644+
_llm_usage.http_errors_5xx = 0
645+
_llm_usage.timeout_errors = 0
632646

633647

634648
def get_llm_usage() -> dict:
@@ -638,6 +652,9 @@ def get_llm_usage() -> dict:
638652
"completion_tokens": getattr(_llm_usage, "completion_tokens", 0),
639653
"total_tokens": getattr(_llm_usage, "prompt_tokens", 0) + getattr(_llm_usage, "completion_tokens", 0),
640654
"call_count": getattr(_llm_usage, "call_count", 0),
655+
"http_errors_4xx": getattr(_llm_usage, "http_errors_4xx", 0),
656+
"http_errors_5xx": getattr(_llm_usage, "http_errors_5xx", 0),
657+
"timeout_errors": getattr(_llm_usage, "timeout_errors", 0),
641658
}
642659
reset_llm_usage()
643660
return usage

novelforge/routes/generation/chapters.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,9 @@ def _set_step(step_label: str) -> None:
388388
"prompt_tokens": chapter_usage["prompt_tokens"],
389389
"completion_tokens": chapter_usage["completion_tokens"],
390390
"total_tokens": chapter_usage["total_tokens"],
391+
"http_errors_4xx": chapter_usage["http_errors_4xx"],
392+
"http_errors_5xx": chapter_usage["http_errors_5xx"],
393+
"timeout_errors": chapter_usage["timeout_errors"],
391394
})
392395

393396
progress_manager.update(token, {

static/js/script.js

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1737,6 +1737,7 @@ $(function () {
17371737

17381738
// Pre-compute per-chapter data and find max values
17391739
var totalWords = 0, totalTime = 0, totalCalls = 0, totalTokens = 0;
1740+
var totalErrors4xx = 0, totalErrors5xx = 0, totalTimeouts = 0;
17401741
var maxWords = 0, maxTime = 0;
17411742
var rows = [];
17421743

@@ -1745,10 +1746,14 @@ $(function () {
17451746
var timeSec = ch.generation_time_seconds || 0;
17461747
var calls = ch.llm_calls || 0;
17471748
var tokens = ch.total_tokens || 0;
1749+
var err4 = ch.http_errors_4xx || 0;
1750+
var err5 = ch.http_errors_5xx || 0;
1751+
var errT = ch.timeout_errors || 0;
17481752
totalWords += words; totalTime += timeSec; totalCalls += calls; totalTokens += tokens;
1753+
totalErrors4xx += err4; totalErrors5xx += err5; totalTimeouts += errT;
17491754
if (words > maxWords) maxWords = words;
17501755
if (timeSec > maxTime) maxTime = timeSec;
1751-
rows.push({ ch: ch, words: words, timeSec: timeSec, calls: calls, tokens: tokens });
1756+
rows.push({ ch: ch, words: words, timeSec: timeSec, calls: calls, tokens: tokens, errors: err4 + err5 + errT });
17521757
});
17531758

17541759
$.each(rows, function (_, r) {
@@ -1759,6 +1764,9 @@ $(function () {
17591764
var wordsBadge = (r.words === maxWords && chapters.length > 1) ? ' <span class="nf-stat-badge nf-stat-badge-words">longest</span>' : "";
17601765
var timeBadge = (r.timeSec === maxTime && maxTime > 0 && chapters.length > 1) ? ' <span class="nf-stat-badge nf-stat-badge-time">slowest</span>' : "";
17611766

1767+
var errorsStr = r.errors > 0 ? r.errors.toLocaleString() : "-";
1768+
var errorsCls = r.errors > 0 ? ' class="text-end text-danger"' : ' class="text-end"';
1769+
17621770
$tbody.append(
17631771
"<tr>" +
17641772
"<td>" + escapeHtml(r.ch.number) + "</td>" +
@@ -1767,6 +1775,7 @@ $(function () {
17671775
'<td class="text-end">' + timeStr + timeBadge + "</td>" +
17681776
'<td class="text-end">' + callsStr + "</td>" +
17691777
'<td class="text-end">' + tokensStr + "</td>" +
1778+
"<td" + errorsCls + ">" + errorsStr + "</td>" +
17701779
"</tr>"
17711780
);
17721781
});
@@ -1790,18 +1799,31 @@ $(function () {
17901799
if (totalTokens > 0) {
17911800
summaryItems.push({ label: "Total Tokens", value: totalTokens.toLocaleString(), icon: "bi-cpu" });
17921801
}
1802+
var totalLLMErrors = totalErrors4xx + totalErrors5xx + totalTimeouts;
1803+
if (totalLLMErrors > 0) {
1804+
var breakdown = [];
1805+
if (totalErrors4xx > 0) breakdown.push(totalErrors4xx + " 4xx");
1806+
if (totalErrors5xx > 0) breakdown.push(totalErrors5xx + " 5xx");
1807+
if (totalTimeouts > 0) breakdown.push(totalTimeouts + " timeout");
1808+
summaryItems.push({ label: "LLM Errors", value: totalLLMErrors.toLocaleString(), icon: "bi-exclamation-triangle", cls: "text-danger", tooltip: breakdown.join(", ") });
1809+
}
17931810

17941811
$.each(summaryItems, function (_, item) {
1812+
var iconCls = item.cls || "text-primary";
1813+
var tooltipAttr = item.tooltip ? ' data-bs-toggle="tooltip" title="' + escapeHtml(item.tooltip) + '"' : "";
17951814
$summary.append(
17961815
'<div class="col-6 col-md-4 col-lg-2">' +
1797-
'<div class="card text-center h-100">' +
1816+
'<div class="card text-center h-100"' + tooltipAttr + '>' +
17981817
'<div class="card-body py-2 px-1">' +
1799-
'<i class="bi ' + item.icon + ' text-primary mb-1 d-block"></i>' +
1800-
'<div class="fw-bold">' + item.value + "</div>" +
1818+
'<i class="bi ' + item.icon + ' ' + iconCls + ' mb-1 d-block"></i>' +
1819+
'<div class="fw-bold ' + iconCls + '">' + item.value + "</div>" +
18011820
'<small class="text-muted">' + item.label + "</small>" +
18021821
"</div></div></div>"
18031822
);
18041823
});
1824+
$summary.find('[data-bs-toggle="tooltip"]').each(function () {
1825+
new bootstrap.Tooltip(this);
1826+
});
18051827

18061828
$panel.removeClass("d-none");
18071829
}

templates/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ <h2 id="done-title" class="nf-literary nf-completion-title mb-2"></h2>
525525
<th scope="col" class="text-end">Time</th>
526526
<th scope="col" class="text-end">LLM Calls</th>
527527
<th scope="col" class="text-end">Tokens</th>
528+
<th scope="col" class="text-end">Errors</th>
528529
</tr>
529530
</thead>
530531
<tbody id="writing-stats-tbody">

0 commit comments

Comments
 (0)