Skip to content
Merged
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
8 changes: 5 additions & 3 deletions src/components/AsciiSpinner.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<!--
Text spinner for in-flight rows in the manual librarian-run strips
(`src/screens/Memories.svelte`). Cycles the classic terminal bar
frames `- \ | /` on a timer.
Text spinner for in-flight wiki-agent surfaces: the librarian-run
strip's step rows (`src/screens/Wiki.svelte`) and the Skipped
panel's Retrying button (`src/components/WikiSkippedPanel.svelte`).
The memory strip uses `SleepSpinner.svelte` instead. Cycles the
classic terminal bar frames `- \ | /` on a timer.

Why a JS timer instead of the CSS `transform: rotate()` trick the
chat tool rows use on their U+21BB glyph (`.tool-status.status-pending`
Expand Down
17 changes: 16 additions & 1 deletion src/components/WikiSkippedPanel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import { app } from '$lib/state.svelte';
import { navigate } from '$lib/routing.svelte';
import { onWikiChange, emitWikiChange } from '$lib/wiki-events';
import AsciiSpinner from './AsciiSpinner.svelte';
import {
displayTitle,
formatSkipTimestamp,
Expand Down Expand Up @@ -275,7 +276,18 @@
disabled={retrying[row.threadId] || row.retrying}
title="Re-run the wiki agent against this conversation now"
>
{retrying[row.threadId] || row.retrying ? 'Retrying...' : 'Retry'}
{#if retrying[row.threadId] || row.retrying}
<!-- Same in-flight cue as the librarian strip. The
aria-hidden wrapper is load-bearing: the spinner
swaps its glyph ten times a second, and a screen
reader would announce every frame; the button's
"Retrying..." text is the accessible signal. -->
<span class="wiki-skipped-retry-spinner" aria-hidden="true"
><AsciiSpinner /></span
>Retrying...
{:else}
Retry
{/if}
</button>
{#if retryError[row.threadId]}
<span class="wiki-skipped-retry-error" role="status">
Expand Down Expand Up @@ -384,6 +396,9 @@
cursor: progress;
opacity: 0.7;
}
.wiki-skipped-retry-spinner {
margin-right: 0.4rem;
}
.wiki-skipped-retry-error {
color: var(--danger, #b91c1c);
font-size: 0.85rem;
Expand Down
11 changes: 6 additions & 5 deletions src/lib/ui/ascii-spinner.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/**
* Frame data for the text spinner that marks an in-flight row in the
* wiki librarian-run strip (`src/components/AsciiSpinner.svelte`,
* rendered by `src/screens/Wiki.svelte`). The sequence and the wrap
* arithmetic live here so they can be tested without mounting a
* component or running timers.
* Frame data for the text spinner that marks an in-flight wiki-agent
* surface (`src/components/AsciiSpinner.svelte`, rendered by the
* librarian-run strip in `src/screens/Wiki.svelte` and the Skipped
* panel's Retrying button in `WikiSkippedPanel.svelte`). The sequence
* and the wrap arithmetic live here so they can be tested without
* mounting a component or running timers.
*
* The memory strip does NOT use this - it runs `SleepSpinner.svelte`,
* whose frames are CSS keyframes rather than data, because its passes
Expand Down
10 changes: 8 additions & 2 deletions supabase/functions/_shared/venice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,16 +340,22 @@ async function veniceCompleteOnce(opts: VeniceCompleteOptions): Promise<unknown>

if (!res.ok) {
const errBody = await res.text().catch(() => '');
// 600, not the 200 the other endpoints use: completions errors are
// the ones that land verbatim in user-facing surfaces (the Wiki
// Skipped panel stores this message as the per-thread skip reason),
// and Venice's context-length 400 body runs past 200 chars - the
// old cap cut it mid-sentence ("...for a total of at least 1638").
// 600 covers every observed completions error body whole.
if (res.status === 429) {
throw new VeniceError(
`Venice chat/completions 429: ${errBody.slice(0, 200)}`,
`Venice chat/completions 429: ${errBody.slice(0, 600)}`,
'rate_limit',
429,
parseVeniceRetryAfterMs(res.headers)
);
}
throw new VeniceError(
`Venice chat/completions ${res.status}: ${errBody.slice(0, 200)}`,
`Venice chat/completions ${res.status}: ${errBody.slice(0, 600)}`,
'http',
res.status
);
Expand Down
16 changes: 10 additions & 6 deletions supabase/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -9642,11 +9642,12 @@ begin
wiki_claim_expires_at = null,
wiki_failure_count = 0,
wiki_last_skip_at = now(),
-- Truncate at 500 chars to keep the row reasonable when the
-- error body is a large HTTP response. The UI shows enough
-- to identify the failure mode (Venice's classifier message
-- is short); a longer body would just bloat the row store.
wiki_last_skip_reason = nullif(left(coalesce(p_reason, ''), 500), ''),
-- Truncate at 1000 chars to keep the row reasonable when
-- the error body is a large HTTP response. Sized above the
-- edge client's 600-char completions error-body cap plus
-- its message prefix, so the Skipped panel shows the whole
-- captured error rather than clipping it a second time.
wiki_last_skip_reason = nullif(left(coalesce(p_reason, ''), 1000), ''),
-- If the agent gave up with a content-classifier reason,
-- we know the in-agent primary -> fallback retry already
-- ran (the wiki agent always tries the fallback for that
Expand Down Expand Up @@ -10721,7 +10722,10 @@ begin
wiki_record_claim_expires_at = null,
wiki_record_failure_count = 0,
wiki_record_last_skip_at = now(),
wiki_record_last_skip_reason = nullif(left(coalesce(p_reason, ''), 500), '')
-- 1000 mirrors the wiki agent's skip-reason cap: above the
-- edge client's 600-char completions error-body slice plus
-- prefix, so the stored reason is never clipped twice.
wiki_record_last_skip_reason = nullif(left(coalesce(p_reason, ''), 1000), '')
where id = p_thread_id;
return 'skipped';
end if;
Expand Down