### Summary
The fix is one line per helper — assign the `order_by(...)` result back
so the
ordering is actually applied to the query.
Co-authored-by: rsnetworkinginc <rsnetworkinginc@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
### Summary
The fix turns a negative timeout into a finite 60 second wait inside
`MysqlDatabaseLock`, which both servers accept. Callers keep expressing
"block until available", and the equivalent PostgreSQL and GaussDB work
on the same `-1` semantics (#16346, #18506) is untouched. Non-negative
timeouts keep their current behaviour.
---------
Co-authored-by: Krim <git@krim.dev>
### Summary
Multi-type memory prompts were assembled from sets, so their instruction
and output sections could change order across Python processes with
different hash seeds. Because the generated default prompt is persisted
and later compared as a string, a restart could make an untouched
default look custom and prevent it from being regenerated when memory
types change.
### Summary
Adds You.com as a built-in Web Search provider for RAGFlow Chat,
alongside Tavily and Querit, using the provider-neutral dispatch #17813
put in place. No changes to existing Tavily or Querit behaviour.
You.com runs its own web index and returns several extracted passages
per result rather than a single meta description, so retrieved chunks
arrive with usable context.
---------
Co-authored-by: Brian Sparker <brainsparker@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Summary
Fixes#18025. Both merge paths deduplicated IDs by scanning a plain list
(`item not in list`) inside a loop while appending — O(n²) per merge.
Replaced with a set-backed `seen` check alongside the existing ordered
list: same order, same dedup result, O(n).
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
An image whose OCR text is shorter than the CV LLM threshold produces zero chunks when the tenant has no image2text model configured. The extracted text is discarded.
## Summary
- Pass `vector_similarity_weight` from Python and Go retrieval requests
into Infinity's weighted fusion expression.
- Keep fusion weights ordered as text first and vector second, with the
existing default vector weight of `0.3`.
---------
Co-authored-by: chenglinpeng <1042527908@qq.com>
### Summary
This PR adds [Serply](https://serply.io) as a third web search provider
for chat assistants, alongside the existing Tavily and Querit options.
### Summary
Closes#18414.
`rag/res/term.freq` is not shipped, and both term-weight implementations
therefore assigned the same `300` fallback frequency to every lowercase
Latin token. With no tokenizer frequency, NER, or POS signal, function
words and content words received identical lexical boosts.
This PR adds the same bounded out-of-vocabulary prior to Python and Go:
- Use it only when the explicit DF dictionary or tokenizer has no
frequency.
- Count Latin, Greek, and Cyrillic letters, including uppercase and
accented forms.
- Keep the existing frequency of `300` for words up to three letters,
halve it every two additional letters, and clamp it at `10`.
- Reject digits, underscores, and logographic terms so Chinese and other
existing fine-grained-tokenizer paths are unchanged.
- Treat an absent optional `term.freq` as the supported fallback path
without a startup warning, while still logging inaccessible or malformed
dictionaries.
A corpus-derived table was intentionally not added: that would require
provenance/licensing decisions, language detection, and handling
cross-language homographs. The bounded prior is deterministic,
dependency-free, and fixes the equal-weight degradation for
whitespace-delimited alphabetic languages without claiming
corpus-specific precision.
Python and Go consume one shared fixture covering ASCII, uppercase,
accented Latin, Greek, Cyrillic, separators, invalid mixed tokens, and a
CJK non-match. Both sides also verify the issue's ordering (`was <
largest < supplier < equipment`) and that an explicit dictionary entry
still takes precedence.
Co-authored-by: Loong <184861530+yzl0ng@users.noreply.github.com>
## Summary
- require a `valid_at` timestamp in the semantic-memory output schema
- tell the extraction model to use conversation time when a fact has no
date of its own
- add a regression test for the assembled semantic prompt
This addresses the deterministic prompt inconsistency reported in
#18415. The invalid-timestamp fallback is intentionally left unchanged
because selecting its replacement policy requires a separate design
decision.
## Summary
- allow ISO 8601 normalization callers to provide an explicit fallback
while preserving the existing default behavior
- use the extraction conversation time when `valid_at` is missing or
invalid
- clear an invalid optional `invalid_at` instead of writing an
unparseable value
- include the rejected timestamp value in the error log
This addresses the timestamp write-through portion of #18415. It is
intentionally separate from #18462, which fixes the semantic output
prompt.
Co-authored-by: Loong <184861530+yzl0ng@users.noreply.github.com>
### Summary
- Forward each memory's stored system_prompt and user_prompt to
extract_by_llm.
- Cover both immediate save and queued extraction paths with focused
regression tests.
- Preserve the existing default-prompt fallback when stored prompts are
empty.
Fixes#18413.
### Summary
RAGFlow's "Create empty document" flow accepts names without a file
extension. The `POST /datasets/<dataset_id>/documents?type=empty` route
calls `_upload_empty_document()`, where `Path(name).suffix.lstrip(".")`
returns `""`.
In GaussDB's A/ORA compatibility mode, that empty string is persisted as
SQL `NULL`. Because `document.suffix` was defined as `NOT NULL`, the
insert failed with a constraint violation.
### Summary
Providers whose static catalogue is empty discover their models by
calling the base URL the user typed. `verify_api_key` wrapped that call
in a bare `except Exception: pass` and then returned a flat `No models
found for provider 'X'`, so an unreachable host, a closed port, a wrong
scheme and a bad TLS setup all produced the same sentence, with the
actual error discarded and not even logged.
## Summary
- treat empty Markdown binaries as in-memory content instead of local
file paths
- add a regression test ensuring empty content does not access the
filesystem
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Summary
- Disable DeepSeek V4 thinking (chain-of-thought) by default.
- litellm 1.82.x drops `thinking: disabled`; carry the toggle through
`extra_body.thinking.type` and strip `reasoning_effort` to avoid the
400.
- Use local timezone for agent `sys.date` instead of UTC.
Reference: https://api-docs.deepseek.com/guides/thinking_mode
---------
Co-authored-by: Claude <claude@example.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude <noreply@anthropic.com>
This PR fixes two issues with MinerU PDF parser where table and image chunks were not properly classified or associated:
1. **Table chunks** were incorrectly classified as `text` instead of `table`
2. **Image chunks** were missing image resource association and classified as `text`
### Summary
GaussDB DocEngine could return no chunks for conversational queries even
when relevant content was available. `Dealer.search()` supplies
`minimum_should_match` (30%, then 10% on retry), but the GaussDB adapter
discarded it and built a single `plainto_tsquery` from every token. This
effectively required all conversational filler terms to match.
Follow-up to #17526 ("Refactor: merge dataset scope graph"), which introduced two code paths that touch Infinity columns the deployed schema does not declare. This PR makes the runtime robust against the old schema while also adding the new column to the new schema so freshly created tables are correct.
This PR fixes **#18193** — the Python `rag/app/book.py` naive-branch `split("@")` bug that destroys PDF coordinate (`@@`) tags, so chunks lose their clickable page highlight.
### Summary
Closes#17885.
MinerU figure enrichment now passes the resolved dataset language to
`vision_llm_figure_describe_prompt`. Missing and empty language values
use `English`, matching the other figure-description paths.
This change is limited to MinerU. PR #18021 already fixed the Mistral
path.
### Summary
Add Querit Contents as a built-in page content tool for RAGFlow Agents
and Canvas workflows.
Querit Contents crawls one or more known URLs and returns their page
content and optional metadata. It complements the existing Querit Search
tool: Search discovers relevant pages, while Contents retrieves pages
already selected by an Agent or workflow.
This integration supports two usage modes:
- A standalone `QueritContents` node in Canvas workflows.
- An embedded content tool available to RAGFlow Agents.
### Summary
`format_document_soup` tracks "am I inside a table" and "am I inside a
link" with sticky flags that are meant to be reset by `elif e.name ==
"/table"` and `elif e.name == "/a"`. BeautifulSoup's `.descendants` only
yields opening tags — a `Tag` named `/table` or `/a` never exists — so
both branches are dead code and neither flag is ever cleared.
Everything after the first `<table>` on a page is therefore formatted as
if it were still table content: paragraphs lose their newline, list
items lose their `- ` marker, headings lose their break, and the text is
glued onto the last table cell. Under
`HTML_BASED_CONNECTOR_TRANSFORM_LINKS_STRATEGY=markdown` the same bug
leaks a link's `href` into everything that follows it, including whole
subsequent paragraphs. The Confluence connector
(`confluence_connector.py:948`) goes through this path.
Real output for a Confluence-shaped page (heading, intro, spec table,
then the body) via the public `parse_html_page_basic`:
**Before**
```
prod us-east-1 Rollback procedure If the canary fails, run the rollback script immediately. Drain the load balancer Revert the deployment Escalate to the on-call rota if the rollback stalls. Do not skip the post-mortem.
```
**After**
```
prod us-east-1
Rollback procedure
If the canary fails, run the rollback script immediately.
- Drain the load balancer
- Revert the deployment
Escalate to [the on-call rota](http://oncall.example.com) if the rollback stalls.
Do not skip the post-mortem.
```
Every heading, paragraph and list marker after the table is lost, and
the whole body is indexed as one run-on line hanging off a table cell.
### Fix
Derive both scopes from each element's **ancestors** instead of from
flags that nothing can clear, and drop the two dead branches plus the
two that become redundant.
The scopes are resolved in one up-front pass into `id`-keyed maps
(`table_scope`, `href_scope`) and looked up in O(1) per element. Probing
per element with `find_parent` instead is O(depth) each, which measured
12–13× slower on table-heavy pages and up to 103× on deeply nested
markup; the map version costs a depth-independent 1.13–1.35× over
`main`. Numbers and method are in the round-2 comment below.
This also changes one adjacent behaviour worth calling out explicitly: a
link **inside** a table cell now renders as markdown, where before it
rendered as plain text. That previous behaviour was not by design — it
only held when no link preceded the table. With a link before the table,
`main` stamps the stale href onto every cell:
```
main: '[pre](http://STALE.com)\n\t[cellA](http://STALE.com)\t[cellB](http://STALE.com)'
branch: '[pre](http://STALE.com)\n\tcellA\tcellB'
```
Those cells are not links. Both symptoms are the same sticky-state bug,
so they are fixed together rather than left half-done.
### Testing
`test/unit_test/data_source/test_html_utils.py` is new —
`format_document_soup` had no test coverage. 11 tests: 8 fail on `main`
and pass on this branch, 3 are controls that pass on both (the table
itself still separates rows and cells, anchor text is still linkified,
the default `strip` strategy still strips).
Representative failures on `main`:
```
assert '\nAfter' in 'Before\n\tA\tB After'
assert '\n- item1' in 'Before\n\tA\tB item1 item2'
assert 'see [link](http://x.com) [ after](http://x.com)' == 'see [link](http://x.com) after'
assert '[next paragraph]' not in '[link](http://x.com)\n[next paragraph](http://x.com)'
```
Reverting each clause of the fix independently keeps the anchors honest:
reverting only the table clause fails exactly the 4 table tests and
leaves the link tests green; reverting only the link clause fails
exactly the 3 link tests and leaves the table tests green.
(`test_link_inside_a_table_cell_is_linkified` needs both clauses broken
to fail, so it appears in neither single-clause revert — it is covered
by the 8-fail run against `main`.)
Full `test/unit_test/data_source/` suite: **3 failed, 199 passed**, and
the failure set is byte-identical to clean `main` (**3 failed, 188
passed**) — the 3 are `TestSSRFValidation::*`, which resolve
`api.example.com` against real DNS and are unrelated to this change.
`ruff check` and `ruff format --check` are clean on both touched files.
---
This PR was drafted with AI assistance (Claude). I reviewed the change,
independently reproduced both symptoms against `main`, and take
responsibility for it.
### Summary
Fixes#18107.
`editdistance==0.8.1` (the only recent release on PyPI) has no cp313
wheels for any platform. Since this project requires exactly Python
3.13, `uv`/`pip`/`poetry` fall back to building it from source (Cython),
which fails on Windows for anyone without a working C build toolchain —
that's the PEP 517 build error in the issue.
Swapped `editdistance` for `rapidfuzz`, which ships full cp313 wheels
(win32/win_amd64/win_arm64 included) and has no build-from-source step
on any of our target platforms. The only call site was
`EntityResolution.is_similarity` in `rag/graphrag/entity_resolution.py`,
using `editdistance.eval(a, b)` to get the unweighted Levenshtein
distance between two entity names.
`rapidfuzz.distance.Levenshtein.distance(a, b)` computes the same thing
(verified identical output on several string pairs) and is used as a
direct replacement.
### Summary
Refs #17885.
Mistral figure enrichment now receives the dataset language through the
production parsing path. `by_mistral_ocr` forwards `lang` to
`MistralParser.parse_pdf`; the parser stores the normalized language and
passes it to the figure-description prompt. Empty or missing values
still fall back to English.
### Summary
Brings both halves of the Tenki sandbox provider onto current SDKs and
removes `project_id`, which Tenki deleted from its API.
**Go:** `github.com/LuxorLabs/tenki-sdk-go/sandbox` `v0.5.2` → `v0.7.0`
(current latest).
**Python:** the provider's SDK was renamed on PyPI — `tenki-sandbox` is
frozen at 0.4.0 and everything from 0.5 ships as
[`tenki`](https://pypi.org/project/tenki/). The docs told operators to
`pip install tenki-sandbox`, which installs a stale SDK that no longer
matches this provider's expectations.
**`project_id` is gone.** Tenki removed project scoping from the sandbox
API in 0.5.x: `Client.create()` no longer accepts `project_id`, so the
current code path would raise `TypeError` against a current SDK. It was
also marked `required: True` in the config schema, so the Admin >
Sandbox Settings form asked for a value that no longer exists.
### What problem does this PR solve?
Incremental Wiki compilation could lose provenance for claim-light
entities, produce unstable page groups across embedding models, route
entities to unrelated pages, and assign topics without sufficient
page-level context. Document removals and page membership changes could
also leave stale Wiki state.
This PR:
- preserves source document and chunk provenance throughout entity
matching, reduction, page generation, and deletion;
- uses embeddings to retrieve candidates and the LLM to make final page
grouping and incremental routing decisions;
- batches embedding and LLM operations with bounded concurrency and
deterministic fallbacks;
- selects source-scoped topic candidates with embeddings before the page
LLM chooses the final topic;
- rebuilds Wiki state when the compilation mode or embedding model
changes;
- normalizes Wiki array fields returned by the API and retains entities
without relations in graph responses.
### Type of change
- [x] Bug Fix (non-breaking change which fixes an issue)