13 KiB
💥 Breaking Changes
llm.modelno longer auto-fillsgemma3:12b. Pre-1.7 installs silently downloaded a multi-GB Ollama binary on first use. The field is now empty by default — pick a model in Settings → LLM, or research will fail loudly with a clearValueError. Existing users with a savedllm.modelsetting are unaffected; fresh installs see an inline warning in Settings (aria-live="polite") and a clear error on research start. Affects all built-in providers (openai,anthropic,ollama,lmstudio,llamacpp,google,openrouter,ionos,xai). (#3670)llamacppprovider migrated from in-process to HTTP. No longer loads.gguffiles in-process viallama-cpp-python— connects tollama-server's OpenAI-compatible HTTP endpoint instead. Removed settings:llm.llamacpp_model_path,llm.llamacpp_n_batch,llm.llamacpp_n_gpu_layers,llm.llamacpp_f16_kv. New settings:llm.llamacpp.url(defaulthttp://localhost:8080/v1),llm.llamacpp.api_key(optional, for auth proxies). Migration: runllama-server -m <your-model.gguf>and the default URL picks it up; alternatively use theopenai_endpointprovider pointed at the same URL. (#3670)
🔒 Security
-
Fix LM Studio model detection (#3800) — the settings dropdown was silently dropping models for every auto-discovered provider except Ollama / OpenAI / Anthropic / OpenAI-Compatible-Endpoint, including LM Studio, Llama.cpp, Google Gemini, xAI, OpenRouter, and IONOS. While investigating, also fixed a separate credential-leak class where the route's auto-discovery loop sent every other provider's configured API key in the
Authorizationheader to the local LM Studio / llama-server endpoint on every settings-page load. (#3800) -
Fix SSRF parser-differential bypass (GHSA-g23j-2vwm-5c25). URLs containing backslash, whitespace, or ASCII control bytes are now rejected upfront by the SSRF validator and notification-URL validator; hostname extraction switched from
urllib.parse.urlparsetourllib3.util.parse_urlso the validator and the HTTP client agree on destination by construction. Credit: @Fushuling, @RacerZ-fighting. -
Hardened SSRF defenses against AWS ECS task metadata (
169.254.170.2,169.254.170.23), Tencent Cloud (169.254.0.23), and AlibabaCloud (100.100.100.200) metadata endpoints — these are now always blocked alongside the existing AWS IMDS / Azure / OCI / DigitalOcean entry (169.254.169.254). Redacted credentials, path, and query from URL-rejection logs (operators with grep/regex tooling on the rejection log lines will see authority-onlyscheme://host:portinstead of full URLs going forward). -
SSRF defense-in-depth: block IPv6 transition prefixes that can wrap private IPv4 destinations on hosts with kernel sit0/NAT64 routes.
2002::/16(6to4, RFC 3056 — deprecated by RFC 7526)64:ff9b::/96(NAT64 well-known, RFC 6052)64:ff9b:1::/48(NAT64 local-use, RFC 8215 — same SSRF threat class as the WKP; missing it earned a HackerOne bounty against ssrf_filter)2001::/32(Teredo, RFC 4380)100::/64(IPv6 discard, RFC 6666)::/96(IPv4-Compatible IPv6, RFC 4291 §2.5.5.1 — DEPRECATED 2006; same SSRF threat class as the transition prefixes)
The metadata-IP block is hardened against IPv6-wrapped IMDS access: when an IPv6 destination falls in a NAT64 prefix, the embedded IPv4 is extracted and matched against
ALWAYS_BLOCKED_METADATA_IPS, so[64:ff9b::a9fe:a9fe]cannot reach 169.254.169.254 even on a NAT64 host.Operators on IPv6-only deployments using DNS64+NAT64 (where outbound IPv4 traffic is synthesized through
64:ff9b::/96) can opt back in via the env-only settingsecurity.allow_nat64(LDR_SECURITY_ALLOW_NAT64=true). The opt-in is scoped strictly to the two NAT64 prefixes — 6to4, Teredo, and discard remain blocked unconditionally, and the IMDS embedded-IPv4 carve-out still applies.
✨ New Features
- LM Studio optional API key. New
llm.lmstudio.api_keysetting for LM Studio instances with authentication enabled. (#3670) - Release notes now use towncrier news fragments. Each PR drops one tiny
changelog.d/<id>.<category>.mdfile (categories:breaking,security,feature,bugfix,removal,misc). At release prep, the maintainer runspdm run towncrier build --version <X.Y.Z> --yes, which renders the fragments intodocs/release_notes/<X.Y.Z>.mdand removes them. Replaces the previous shared-file workflow, which scaled poorly at LDR's PR throughput. Seechangelog.d/README.mdfor the convention. (#3773) - New citation format: source-tagged with global numbering. Reports can now render citations as
[arxiv-1],[openai.com-2],[arxiv-3]— the source tag identifies what kind of source each citation is (short URLClassifier tag for known academic sources, cleaned domain for generic web URLs, or the collection name for local RAG/library hits, e.g.[my-papers-4]), while the number stays the original global counter so labels never collide and inline citations match the bibliography order. Opt in viareport.citation_format → "Source-tagged with global numbering"; the existing default remainsNumbers with hyperlinks [1]. (#4012) - Context-overflow warnings (front-page banner, result-page banner, completion toast) now show a clickable action that takes you straight to the relevant diagnostics — historical warnings link to the context-overflow metrics page, while per-research warnings link to that research's overflow section on its details page.
- Context-overflow warnings now include structured fields (
research_id,model,provider) and detect both input-only and total-context (input+output) overflow. Four distinct tags ([provider-confirmed],[total-context],[estimated],[estimated-total-context]) make different overflow causes distinguishable in logs and alerts. Hosted providers (OpenAI, Anthropic, OpenRouter) get estimation-based detection so their truncation events are no longer invisible. The high-context VRAM warning now links directly to the context-overflow metrics page. - OpenAI-compatible runtime errors (LM Studio / vLLM / llama.cpp server / OpenRouter / custom endpoint) now surface a message that names the provider, configured base URL, and model. The existing
Error type: <code>token convention is extended with sevenopenai_*codes (openai_connection_refused,openai_timeout,openai_auth,openai_permission_denied,openai_model_not_found,openai_bad_request,openai_unknown), andErrorReportermaps each to the rightErrorCategory. The original exception text is preserved in aDetails:suffix; userinfo embedded in a base URL (https://u:key@host/v1) is stripped before display. - The
/metrics/context-overflowpage is reorganised around per-research truncation diagnosis: redundant aggregate sections (Token Usage Overview, Token Usage Over Time, Model Usage Breakdown, Context Limit Distribution doughnut, Token Usage by Phase) are dropped in favour of empty-state messages and a recoloured and re-shaped scatter chart that buckets points by context-utilisation ratio (green circle / amber triangle / red diamond — shape gives a redundant signal for colourblind users) rather than per-limit dashed reference lines. Tooltips now explicitly label requests with unknown context limits or unknown token counts. The main/metricsdashboard gains a small Context Overflow summary panel linking to the full diagnostics page; on aggregation failure the panel shows—rather than a misleading0%. - Upload rate limits are now configurable via
LDR_SECURITY_RATE_LIMIT_UPLOAD_USERandLDR_SECURITY_RATE_LIMIT_UPLOAD_IP. The default cap is raised from10 per minute;100 per hourto60 per minute;1000 per hour, unblocking bulk RAG library uploads.
🐛 Bug Fixes
- Fix progress page log panel sizing so it uses CSS layout instead of JavaScript height calculations. (#2606)
- Progress page current-task text now preserves line breaks and wraps long generated task messages instead of collapsing them into a hard-to-read wall of text. (#2609)
- Live research log entries now keep the same newest-first visual order as loaded log history instead of appearing at the bottom of the log panel. (#2610)
- Journal-quality dashboard ("Your Research") no longer shows orphan papers from deleted research sessions. Deleting a research session previously left behind
Paperrows whosePaperAppearancehad cascade-deleted, so the dashboard kept counting them. The aggregate top-200 query and the predatory-blocked count now exclude papers that have no remaining appearances. Per-research views were already correct. (#3544) - Fix the model dropdown in Settings to populate for every auto-discovered LLM provider, not just Ollama / OpenAI / Anthropic / OpenAI-Compatible-Endpoint. LM Studio, Llama.cpp, Google Gemini, xAI, OpenRouter, and IONOS were silently dropped by the frontend (
processModelData) even when the backend returned their models correctly. (#3800) - Fix file descriptor exhaustion (#3816) caused by
ChatOllama._async_clientnot being released alongside_client._close_base_llm()now closes both the sync and async httpx clients owned byChatOllama, addressing the eventpoll-FD growth seen in long-running deployments. (#3816) - Fix the research details "View Journals" button so it opens the scoped journal-quality dashboard instead of a missing page. (#3825)
- Fix Download Manager failure after 20–30 PDFs with
UNIQUE constraint failed: documents.document_hashand the cascading PendingRollbackError that prevented further downloads. (#3827) - Fix login/registration over
http://localhost:5001on Docker Desktop. The session cookie'sSecureflag is now driven purely by the request protocol (HTTPS) rather than by source-IP heuristics, so HTTP requests from any IP — including Docker Desktop's NAT gateway — now keep their cookies and CSRF tokens. (#3849) - Fix Ollama embedding indexing aborting with
"input length exceeds the context length"(HTTP 500) by exposing a configurableembeddings.ollama.num_ctxsetting. The new field appears on the embedding settings page when the Ollama provider is selected and ships with a default of 8192 tokens, which covers chunk sizes up to ~5000 characters fornomic-embed-text,mxbai-embed-large,bge-m3, andqwen3-embedding. (#3870) - LangGraph agent strategy error messages now include the exception type, and the
'str' object has no attribute 'model_dump'failure (issue #3897) is translated into a rich on-result-page recommendation listing the three possible causes — proxy/shim, serving stack, or model — with concrete remediation steps for each. (#3926) - The Context Overflow summary panel on
/metricsnow honors the dashboard's research-mode filter (Quick / Detailed / All) the same way the rest of the panels do.
📝 Other Changes
- Added
LDR_DISABLE_RATE_LIMITINGas the canonical name for disabling HTTP rate limiting, matching theLDR_env-var convention. The legacyDISABLE_RATE_LIMITINGstill works but emits a deprecation warning. Note: this is distinct fromLDR_RATE_LIMITING_ENABLED, which controls the adaptive search-engine rate limiter — a different subsystem (#3905). - Migrated CI workflows to the canonical
LDR_DISABLE_RATE_LIMITINGenv var (added in #3936) and closed a test-isolation gap intest_enabled_by_defaultthat did not clear the canonical var. - Removed unused chart-rendering JavaScript on the context-overflow analytics page (token-usage-over-time stacked bar, model-token-stats table, context-limits-distribution doughnut, and the configured-limits list). These functions stopped being called when the page was reorganised to focus on truncation diagnosis (#3792); deleting them now drops ~200 lines of dead code without any user-visible change.
- The prerelease Docker workflow now also re-points the floating
prereleasetag at each new RC manifest. Testers can pinimage: localdeepresearch/local-deep-research:prereleasein their compose file and rundocker compose pull && docker compose up -dto fetch the latest RC without editing the tag each cycle. Versioned tags (prerelease-vX.Y.Z-<sha>) are still published for reproducible testing.