The preflight pattern probed the LLM with a 1-token ping before each
cold turn (when requested_llm_config_id==0, llm_config_id<0, and the
45s healthy TTL had expired) to detect 429s before fanning out into
planner/classifier/title-gen. To absorb its ~1-5s RTT cost we built the
agent speculatively in parallel; on 429 we discarded the build and
repinned.
Three problems with that design:
1. False security. Provider rate limits are token-bucket. A 1-token
ping consumes ~5 tokens; the real request consumes 10-50K. The
probe can return 200 while the real call still 429s.
2. Pure overhead in the common case. On warm-agent-cache turns the
probe dominates wall time: ~2.5s of TTFT pure tax for ~99% of users
who never see a 429.
3. The in-stream recovery loop (catch of _is_provider_rate_limited
gated by not _first_event_logged) already does the right thing
reactively: mark_runtime_cooldown -> resolve_or_get_pinned_llm_config_id
with exclude_config_ids={previous} -> rebuild agent -> retry the
stream. Preflight was never the only safety net; it was a redundant
probe in front of one.
Changes:
- Delete _preflight_llm, _settle_speculative_agent_build, and the
_PREFLIGHT_TIMEOUT_SEC / _PREFLIGHT_MAX_TOKENS constants.
- Drop the parallel agent_build_task / preflight_task plumbing in
both stream_new_chat and stream_resume_chat; build the agent inline
with await _build_main_agent_for_thread(...).
- Drop the unused is_recently_healthy / mark_healthy imports here
(still exported from auto_model_pin_service since OpenRouter
catalogue refresh and a few tests reference clear_healthy).
- Remove the obsolete preflight + settle-speculative tests from
test_stream_new_chat_contract.py.
Net: -447 LOC. ~2.5s removed from TTFT on every cold preflight-eligible
turn. 429 recovery path is unchanged - same repin/rebuild/retry, just
not paid in advance on the healthy path.
Connector kb_sync_services (gmail, onedrive, google_calendar, jira),
streaming indexers (discord, luma, teams) and the file-processor save
path all called embed_text inside async coroutines, blocking the
background worker's event loop for the duration of the embed. Wrap each
call site in asyncio.to_thread so concurrent indexing tasks stop
serialising on the embed.
_restore_in_place_document and _reinsert_document_from_revision are
async helpers invoked by the synchronous-feeling POST /api/threads/.../revert
route; both ran embed_texts inline, blocking the event loop while the
HTTP client waited.
generate_document_summary and create_document_chunks are async helpers
called from the chat path and from many connector indexers. Both wrapped
embed_text/embed_texts directly inside the coroutine, blocking the event
loop for the full duration of the embedding call.
_create_document and _update_document run on the chat critical path
when the filesystem subagent writes via the user's chat turn. Both
called embed_texts synchronously inside an async coroutine, blocking
the event loop for the duration of the embed.
embed_texts holds a threading.Lock and runs a sync embedding call inside
search_knowledge_base, an async coroutine on the KB priority middleware
critical path. Blocking the event loop here stalls every other coroutine
on the worker (SSE keepalives, concurrent chat requests, background
tasks). Wrap in asyncio.to_thread so the embed runs on the default
executor pool while the loop keeps serving.
LiteLLM normalizes every provider's cache fields onto
usage.prompt_tokens_details (cached_tokens + cache_creation_tokens).
The earlier fallback to usage.cache_read_input_tokens /
usage.cache_creation_input_tokens was wrong: Anthropic-shaped fields
only live there via a trailing setattr loop, and the canonical field
name on the wrapper is cache_creation_tokens (not _input_tokens).
CitationPanelContent uses a single instance across different citations
(RightPanel.tsx:251 renders without a key), so when the user clicks a
new citation while the panel is open, the prior expanded state leaks
into the new citation. The reset effect had an empty dependency array,
so it only fired on mount.
Add chunkId to the effect deps so the expanded state resets each time
the citation changes.
Fixes#1368
Previously, was duplicated in 4 places with 3 subtly different fallback behaviors:
1. inline-citation.tsx: returned on error
2. markdown-text.tsx: returned on error
3. assistant-message.tsx: returned on error
4. citation.tsx: returned on error
Created canonical in that:
- Returns
- Strips prefix from hostname
- Returns on invalid URL (safest contract)
Updated all 4 call sites:
- inline-citation.tsx: (preserves original fallback)
- markdown-text.tsx: (preserves original fallback)
- assistant-message.tsx: (drop-in, both return )
- citation.tsx: (drop-in, both return )
Co-authored-by: guangyang1206 <guangyang1206@users.noreply.github.com>
Fixes#1370
Object.values() produces order-dependent cache keys because the order of values depends on the order of keys in the object. This causes the same logical query to produce different cache keys when the parameter object has keys in different orders.
Added stableEntries() helper that:
1. Filters out undefined values
2. Sorts entries by key name
3. Returns flat array of [key, value] pairs
This ensures cache key identity is stable regardless of parameter object key order.
Co-authored-by: guangyang1206 <guangyang1206@users.noreply.github.com>
Updated the test for the indexing pipeline to verify that both the standard and hybrid chunkers are called via asyncio.to_thread, ensuring non-blocking behavior. This change reflects the routing of non-code documents through the hybrid chunker, maintaining the event loop contract.
This commit modifies various metadata and canonical URLs in the SurfSense application to ensure consistency by using "https://www.surfsense.com" instead of "https://surfsense.com". Changes were made in layout files, blog posts, and SEO components to reflect this update.