fix(web): skip avatar character-traits fetch when custom image exists (LUM-1764)#31407
Conversation
…ssistant/ (LUM-1753.4) These were sitting in `domains/chat/api/assistant.ts` — the runtime identity of the assistant has nothing to do with chat. It's a property of the assistant itself, queried by chat, the identity tab, contacts, intelligence, onboarding, the sync router, and several stream handlers. Misplaced since day one. Now lives at `apps/web/src/assistant/identity.ts`. The `chat` file is renamed only in its header comment — it now solely owns chat-context bootstrapping (`getChatContext`, `fetchAssistantId`). The `domains/chat/api/assistant.ts` filename stays for now to avoid churn; can be renamed in a follow-up if it bothers anyone. Import sites updated: identity-tab, use-assistant-identity-init, chat-page (two lines), chat-route-content, contacts-page, chat-utils. Cross-domain allow-list: 119 → 118 imports. Small numeric drop — the structural win is that `fetchAssistantIdentity` no longer sits in another feature's folder. Verified locally: lint, typecheck, tests all green. https://claude.ai/code/session_01JPGu4yGPTL4JoLaPuqpEW2
… (LUM-1764) Back-port of vellum-ai/vellum-assistant-platform#7468. `useAssistantAvatar()` was unconditionally fetching character traits alongside the components blob and avatar image. When an assistant has a custom avatar image, the traits file is intentionally deleted on the daemon side — so every SSE reconnect (which invalidates `SYNC_TAGS.assistantAvatar`) produced a fresh 404 on the traits endpoint. 308 of them in the platform team's local daemon log. `AvatarRenderer` only reads `traits` when there is no custom image, so the fetch was pure waste in that branch. Switch to: - Fetch components + imageUrl in parallel first. - If imageUrl is non-null, skip the traits fetch entirely. - Otherwise fall through to the original behavior. No new APIs, no behavior change for assistants without a custom image. Adds a unit test mirroring the platform PR's coverage: - skips traits fetch when image resolves - still fetches traits when no image The other half of LUM-1764 — back-porting platform#7494 (remove unused pro-upgrade-machine endpoint) — is blocked. The OSS repo still has an active caller (`compute-upgrade-card.tsx`) that needs to migrate to the unified machine-resize endpoint first. Tracking on the issue. https://claude.ai/code/session_01JPGu4yGPTL4JoLaPuqpEW2
There was a problem hiding this comment.
✦ APPROVE
Value: Eliminates a guaranteed-404 network request on every SSE reconnect for assistants with a custom avatar image — pure noise reduction that cleans up the network panel and removes unnecessary daemon load.
What this does: Two changes in one:
-
The fix (
use-assistant-avatar.ts) — fetchescomponentsandimageUrlin parallel first, then conditionally callsfetchCharacterTraitsonly when no custom image exists. Before: all three ran inPromise.allunconditionally. Since the daemon deletescharacter-traits.jsonwhen a custom image is set, the traits fetch was generating a fresh 404 on everySYNC_TAGS.assistantAvatarinvalidation (i.e., every SSE reconnect).AvatarRendereronly readstraitswhencustomImageUrlis absent, so this was pure waste. -
The refactor (
src/assistant/identity.ts) — liftsAssistantIdentity+fetchAssistantIdentityout ofsrc/domains/chat/api/assistant.tsinto a newsrc/assistant/identity.tsmodule. Identity is a property of the assistant itself, not a chat bootstrapping concern. All six import sites updated; cross-domain allowlist updated correctly (identity-tab.tsxdropschatsince it no longer imports from the chat domain —contacts-page.tsxcorrectly keepschatbecause it still usesuseAssistantContext).
Checks:
- Sequential fetch latency trade-off is intentional and correct — we must know
imageUrlbefore deciding on traits. The extra RTT (components+image → traits, in series) only applies to the no-custom-image path, which is where traits are actually needed anyway. assertHasResponse+!response.okinidentity.tsis slightly redundant (the assert may throw first), but it mirrors the platform pattern exactly and is harmless.- SSR guard for
SDK_BASE_OPTIONSis a no-op in production (SPA, no SSR) but makes the module safely importable in bun test environments. - Test correctly uses
mock.modulebeforeawait import— standard bun isolation pattern. Covers both branches: image present →fetchCharacterTraitsnot called; no image → traits fetched normally.
CI: all 7 checks green ✅ Devin: No Issues Found ✅
Vellum Constitution — Trust-seeking: eliminating predictable 404s on reconnect removes a class of false-alarm errors from network telemetry, keeping the signal-to-noise ratio of diagnostics high.
Closes LUM-1764 (partial — see "blocked" note below).
Summary
Back-port of
vellum-ai/vellum-assistant-platform#7468.useAssistantAvatar()was unconditionally fetching character traits alongside the components blob and avatar image. When an assistant has a custom avatar image, the traits file is intentionally deleted on the daemon side — so every SSE reconnect (which invalidatesSYNC_TAGS.assistantAvatar) produced a fresh 404 on the traits endpoint.AvatarRendereronly readstraitswhen there is no custom image, so the fetch was pure waste in that branch.What changed
src/domains/avatar/use-assistant-avatar.ts— fetchfetchCharacterComponents+fetchAvatarImageUrlin parallel first; conditionally callfetchCharacterTraitsonly when there is no image URL.src/domains/avatar/use-assistant-avatar.test.tsx— new unit test mirroring the platform PR's coverage (skip when image resolves; still fetch when no image).LUM-1764 status
pro-upgrade-machineendpoint)compute-upgrade-card.tsx). Platform's caller had already migrated to the unifiedOrganizationsBillingSubscriptionMachineResizeendpoint in earlier PRs of theirunify-resize-pvc-machineplan; OSS needs the equivalent migration first. Tracking on the Linear issue.Verified locally
bun test src/domains/avatar/use-assistant-avatar.test.tsx— 2/2 pass.bun run lint— 0 errors.bun run typecheck— clean.Test plan
data/avatar/character-traits.json404 on reconnect.https://claude.ai/code/session_01JPGu4yGPTL4JoLaPuqpEW2
Generated by Claude Code