Commit Graph
342 Commits
Author SHA1 Message Date
Sbaaoui Idriss 9e932547e5 fix(storage):preserve errors during backend retries (#19578) 2026-09-14 14:39:30 +08:00
Anupam Mediratta 3a9b84495c fix: fix security issue in ob_conn_base.py (#19524)
Fix critical SQL injection in OceanBase connector — column names (dict keys) were interpolated unescaped in filter expressions.
2026-09-14 11:49:38 +08:00
huyua9 b8f0fc496f fix: handle indirect PDF annotation arrays (#18894)
Dereference indirect /Annots arrays before PDF hyperlink extraction.
2026-09-14 10:40:49 +08:00
Sbaaoui Idriss 2dbce82990 fix(storage): retry transient S3 failures (#19526) 2026-09-11 15:50:41 +08:00
Wenchao An cf18c94acd fix(storage): accept tenant arguments in encrypted Azure calls (#19457) 2026-09-09 23:24:15 +08:00
Wenchao An 30a29603fb fix(storage): overwrite existing Azure SAS objects (#19458) 2026-09-09 23:23:49 +08:00
Wenchao AnandSbaaoui Idriss 9d6f76d217 fix(storage): honor client region in Python S3 bucket creation (#19291)
Fix Python S3 bucket creation in non-us-east-1 regions — now sends LocationConstraint via CreateBucketConfiguration. 

---------

Co-authored-by: Sbaaoui Idriss <112825897+6ba3i@users.noreply.github.com>
2026-09-07 17:38:11 +08:00
Huang-404-Q 028d7fa45a fix(storage): make bucket deletion work on the AWS_S3 backend (#19162)
Fix #19100 — S3 deletion now works for knowledge bases/users. Renamed rm_bucket → remove_bucket, rewrote with boto3 batch delete, single-bucket mode uses prefix-scoped deletion (keeps physical bucket).
2026-09-03 15:12:38 +08:00
SYED ALI ABBAS RAHIL d33368cd30 fix(utils): round minimum_should_match fraction instead of truncating (#19031)
Fix #19027 — fractional minimum_should_match now uses round() not int(). 0.29 → "29%" (was "28%").
2026-09-02 13:38:23 +08:00
MarMar Labs 020f1d3b16 fix(serenedb): keep the available_int=0 chunk filter (#18928)
Fix SereneDB available_int=0 filter dropped as falsy — now handles it before skip, matching other backends. ?available=false now returns only disabled chunks.
2026-08-31 18:15:20 +08:00
Wang Qi 7e1a1d2b41 Revert "Refactor: update similarity threshold tooltip from 0.2 to 20" (#19034)
Reverts infiniflow/ragflow#19033
2026-08-31 10:13:49 +08:00
Wang Qi e41b1790bd Refactor: update similarity threshold tooltip from 0.2 to 20 (#19033) 2026-08-31 10:13:08 +08:00
kic635andJin Hai 88e80fc5d1 fix: support SeekDB vector score extraction (#18874)
Fix #18873 — add missing get_scores() to OBConnection for SeekDB score extraction. Vector/hybrid retrieval and Chat RAG now work. 

Co-authored-by: Jin Hai <haijin.chn@gmail.com>
2026-08-27 21:03:59 +08:00
Andrew ChenandClaude Opus 4.8 77ed0aed43 fix(rag/utils): ensure_v1 must recognize v1beta/v1alpha-style version suffixes (#16916)
Fix ensure_v1() mangling Gemini's v1beta/openai endpoint by appending /v1. Now recognizes any segment starting with v{digit} as already versioned.

Signed-off-by: chuenchen309 <48723787+chuenchen309@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 19:35:27 +08:00
wangyunlai 1b5cb3caa0 Fix oversized important keywords in OceanBase and SeekDB (#18872)
Fix OceanBase write failures when important_kwd exceeds VARCHAR(256) limit. Truncates to 256 UTF-8 bytes (preserving multi-byte), recomputes tokens, and applies to inserts/updates/appends.
2026-08-27 19:32:10 +08:00
05c67ca854 Fix OceanBase chunk order field persistence (#16997)
## Summary

Add chunk_order_int to OceanBase chunk schema and migration. Defaults to 0 on NULL or missing field. Tests added.

Fixes #16967

---------

Co-authored-by: zq <zhouquan1511@163.com>
Co-authored-by: Jin Hai <haijin.chn@gmail.com>
2026-08-27 14:23:37 +08:00
Aari 775ffb7836 fix(opensearch): scope multi-document update to the target knowledge base (#17048)
Fix OpenSearch update missing kb_id scope. Unlike Elasticsearch, OSConnection.update dropped the knowledgebaseId argument, causing updates (like clearing pagerank) to apply to all datasets in a tenant. Now adds kb_id to the query condition.
2026-08-26 15:59:42 +08:00
Wang Qi 92c69db79e Refactor: expose knn top_k and knn num_candidaties (#18737)
Follow on #18300
2026-08-25 17:16:36 +08:00
Paul Yao 647cddc3cc fix(admin): display AWS S3 status correctly (#17300)
Related to #17294
2026-08-25 12:24:24 +08:00
Brian SparkerandBrian Sparker 9c98f18600 fix(rag): keep search queries and page text out of connector logs (#18582)
### Summary

@JinHai-CN — the cleanup you invited in #18478. This is the same finding
CodeRabbit raised against the You.com connector there, applied to the
three that shipped before it.

Co-authored-by: Brian Sparker <brainsparker@users.noreply.github.com>
2026-08-25 12:20:42 +08:00
Andras BARTHA 7c455fba18 fix(opensearch): stop zeroing vector scores by mapping similarity to knn boost (#18662)
### Summary

Fix OpenSearch retrieval returning `vector_similarity = 0.000` for every
chunk when hybrid search is enabled.

On the OpenSearch backend, retrieval uses a second KNN-only search
(`Dealer._knn_scores()`) to recover per-chunk cosine scores for
reranking. That pass intentionally sends `MatchDenseExpr(...,
{"similarity": 0.0})` to mean “no minimum similarity cutoff.”

However, `OSConnection.search()` was incorrectly mapping `similarity` to
the KNN clause `boost` field:

```python
knn_query[vector_column_name]["boost"] = similarity
```

With `similarity=0.0`, this produced `boost=0.0`, which zeroed out KNN
`_score` values. `get_scores()` then returned `0.0` for every hit, so
`vector_similarity` was always zero and hybrid ranking ignored the
vector component — with no exception raised.

This is separate from the `get_scores()` `AttributeError` crash
addressed in #14970 / #15390; here retrieval succeeds but vector scores
are silently lost.
2026-08-24 11:59:44 +08:00
09a9b629ee feat(chat): add You.com web search provider (#18478)
### 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>
2026-08-20 12:12:18 +08:00
Serply 371d83c2d8 feat(chat): add Serply web search provider (#18475)
### Summary

This PR adds [Serply](https://serply.io) as a third web search provider
for chat assistants, alongside the existing Tavily and Querit options.
2026-08-19 18:59:01 +08:00
Sevenzuo f1e6b22c4b fix: honor minimum_should_match in GaussDB search (#18456)
### 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.
2026-08-18 21:05:49 +08:00
ump45nose 15aad78518 fix: isolate extract html request headers (#18322) 2026-08-18 16:49:17 +08:00
zws202606 d02723ca76 feat: Gaussdb adaptation (#17703) 2026-08-18 09:53:11 +08:00
S c6ba54bc72 fix: stop three recurring errors on doc delete (#17685) (#17686)
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.
2026-08-16 09:25:51 +08:00
qinling0210 d49af7f218 Generate navigation, navigation search (#18096)
### Summary
2 API

POST /api/v1/datasets/{dataset_id}/navigation

GET
/api/v1/datasets/{dataset_id}/navigation/search?q={query}&mode={mode}&top_k={topk}


2 cli

uv run --no-sync python3 admin/client/ragflow_cli.py -h 127.0.0.1 -p
9380 -t user

ragflow> GENERATE NAVIGATION OF DATASET 'frames tree';

ragflow> NAVIGATION SEARCH 'Christie introduced blockchain-based digital
passports' IN DATASET 'frames tree' MODE 'all' topk 20;

(mode: chunk, nav_cluster, nav_doc, navigation_tree, all)
2026-08-11 17:48:24 +08:00
Wang Qi 23b20a098a Fix ragflow server hung after parsing a big file (#17936) 2026-08-06 17:01:53 +08:00
buua436 8379165c12 fix: record generated wiki page versions (#17931) 2026-08-06 16:49:50 +08:00
EthanZhang bdcd8aadde feat(chat): add Querit web search provider (#17813) 2026-08-05 09:54:46 +08:00
deadtricksterandClaude Opus 4.8 197b142cef feat(serenedb): add SereneDB doc-store engine (Go + Python connectors) (#17375)
## What

Adds [**SereneDB**](https://serenedb.com) as a selectable doc-store
engine on **both** RAGFlow paths:
- the **Go** `DocEngine` (`internal/engine/serenedb`), alongside
Elasticsearch and Infinity;
- the **Python** `DocStoreConnection` (`rag/utils/serenedb_conn.py`) +
`DOC_ENGINE=serenedb` registration.

SereneDB is a PostgreSQL-wire engine (DuckDB execution) whose single
inverted index carries **both** a scored text column (`@@`, BM25) and an
IVF vector column (`<#>`, inner product), so hybrid search is one SQL
statement. The Go engine connects with `database/sql` + `lib/pq`
(already a dependency, no new module); the Python connector uses
psycopg2 (already a dependency).

## Storage model

One table per tenant with `kb_id` as a filter column - the
**Elasticsearch / OceanBase** model, not Infinity's per-dataset tables.
This keeps BM25 statistics (IDF, avgdl) computed over the whole tenant
corpus (global IDF). Both connectors use this identical layout, so they
are storage- and retrieval-compatible: `hybrid` proxy routing and
Python↔Go switching are safe. On the Python side the connector is wired
as OceanBase's plain-SQL sibling (chunk_data JSON metadata, inline chunk
vectors, verbatim ES field names); the ES tokenizer path is unchanged.
Metadata stays one table per tenant (`ragflow_doc_meta_<tenant>`).

The query shapes mirror the Python connector, including the five
empirically-found landmines: the scored dictionary needs `frequency +
norm` (else `BM25()` silently returns 0.0), the `@@` query is the
tokenized query, the scored lexical branch matches one column, vectors
use an L2-normalized shadow column with `ip`/`sq8`, and the similarity
threshold goes directly in the ANN scan's `WHERE`. **Minimum engine
version: SereneDB 26.07.4.**

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 14:16:39 +08:00
buua436 1d141aff18 fix: optimize dataflow indexing and logs (#17737) 2026-08-03 19:15:44 +08:00
buua436 3e7cfbe052 refa: simplify RAPTOR tree clustering and configuration (#17614) 2026-08-03 17:46:50 +08:00
Sbaaoui Idriss e9637c9f94 fix: list model function for nvidia on python/go not returning current models (#17501)
### Summary

fix the list model logic for nvidia models
2026-07-29 13:15:34 +08:00
qinling0210 ad2102c1a8 fix(infinity): parent_kwd incorrectly split as keyword list causing orphaned clusters (#17475)
…rphaned clusters

### Summary

**Problem**
When deleting a document from the knowledge base, the parent cluster
could never be located and was therefore never cleaned up, leaving
orphaned clusters in Infinity indefinitely.

**Root Cause**
InfinityConnection.field_keyword() treats all _kwd-suffixed columns as
keyword lists and applies .split("###") to them in get_fields().
parent_kwd was missing from the exclusion list. This caused a parent
cluster name like "Toronto Transit & Authority Control 5e8d5eb6" to
become ['Toronto Transit & Authority Control 5e8d5eb6'] (a
single-element list). When that list was fed into _nav_cluster_id(), it
produced a different hash than the actual cluster's hash (computed from
the plain string), so every deletion-time cluster lookup silently
failed.

**Fix**
Added "parent_kwd" to the exclusion list in field_keyword(), alongside
docnm_kwd, important_kwd, question_kwd, etc.
2026-07-28 15:26:40 +08:00
Kevin Hu f9e47ae0b9 Feat: More APIs for compiler list and deletion of compilation result. (#17425)
### Summary

 More APIs for comliler list and deletion of compilation result.
2026-07-27 20:11:18 +08:00
buua436 2ba7ccecaf fix: stabilize knowledge compilation navigation updates (#17345) 2026-07-24 17:48:27 +08:00
Yingfeng 08e5d9c6be Fix infinity for compilation (#17313) 2026-07-23 21:43:56 +08:00
buua436 0c5732108a fix: support Infinity knowledge compilation (#17288)
### What problem does this PR solve?

Fix Infinity compatibility issues in knowledge compilation.

This change:

- Stores compilation source ID lists as JSON arrays in Infinity.
- Parses JSON array fields when reading compiled documents.
- Uses `json_contains` for filtering JSON array fields.
- Adds the missing `name` column to the Infinity mapping.
- Updates dataset navigation KNN search to use the unified
`MatchDenseExpr` interface.
- Handles unavailable embeddings without querying an invalid `q_0_vec`
field.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2026-07-23 20:48:39 +08:00
Kevin Hu 3e4c6dfc0a Feat: refine the tree navigation during compilations (#17140) 2026-07-23 18:39:16 +08:00
Andrew ChenandClaude Opus 4.8 99a77a574e fix(metadata): discover ES fields after sparse table chunks (#16949)
### What problem does this PR solve?

When a table dataset's `field_map` is missing or stale,
`aggregate_table_doc_metadata` falls back to probing chunk dictionaries
for each column's Elasticsearch field key. It currently performs that
probe only once, against the first dictionary chunk, and caches `(None,
"none")` if the field is absent there.

Sparse table rows commonly omit empty columns. If the first row has no
`notes` field but a later row contains `notes_raw`, the cached miss
causes every later row to be skipped and the document-level `notes`
metadata is silently lost. The result depends only on row order:

```python
chunks = [{}, {"notes_raw": "Handle with care"}]
aggregate_table_doc_metadata(chunks, task)           # before: {}
aggregate_table_doc_metadata(list(reversed(chunks)), task)
# before: {"notes": ["Handle with care"]}
```

This was also identified in CodeRabbit's review of the merged
table-metadata implementation in #15780, but remained unfixed after that
PR merged:
https://github.com/infiniflow/ragflow/pull/15780#pullrequestreview-4448490676

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)

### Fix

When the initial lookup found no key for a column, retry the existing
`_resolve_es_chunk_field_key` against the current chunk. Cache the first
successful resolution so subsequent rows retain the existing fast path.
Field-map-backed columns and columns found in the first chunk are
unchanged.

### Testing

- Added `test_aggregate_auto_mode_probes_later_sparse_chunks` with an
empty first row and a populated second row.
- Confirmed red→green: before the fix the assertion received `{}`; after
the fix it receives `{"notes": ["Handle with care"]}`.
- Full existing `test_table_metadata_aggregation.py`: **15 passed**.
- `ruff check` and `ruff format --check`: clean.
- `compileall` for both changed files: clean.

The local test environment did not contain the repository's full service
dependency set and had a corrupt pre-existing NLTK `wordnet.zip`. The
test module does not use those services or corpora, so the run stubbed
only `common.settings` engine flags, `json_repair`, and the global
conftest's NLTK resource lookup; the production module and aggregation
tests themselves ran unchanged.

### Duplicate-work check

Checked all currently open PRs (including changed file paths) and found
none touching `rag/utils/table_es_metadata.py` or its aggregation test.
The earlier #15780 review is historical context, not active competing
work.

### Disclosure

AI-assisted (Codex): the candidate came from an AI-assisted review
queue. I independently reproduced the order-dependent data loss against
the real module, checked the historical review and all open PR file
paths, and ran the regression plus full existing test file before
submitting.

Signed-off-by: chuenchen309 <48723787+chuenchen309@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 13:41:46 +08:00
Kevin HuandYingfeng Zhang 454dea686e Feat: agentic search framework (#16859)
### Summary

Agentic search

<img width="1149" height="1575" alt="image"
src="https://github.com/user-attachments/assets/bce9a3e7-0517-4fb2-80a2-5d2a81a4da78"
/>

---------

Co-authored-by: Yingfeng Zhang <yingfeng.zhang@gmail.com>
2026-07-15 23:46:23 +08:00
Lynn 0ba1d37a10 Feat: optional url v1 (#16774) 2026-07-09 15:53:06 +08:00
Wang Qi b82169fba1 Fix: ValueError: Operation on closed image (#16697) 2026-07-07 17:00:40 +08:00
Wang Qi 6a4b9be426 Refactor: reformat all code for lefthook using ruff and gofmt (#16585) 2026-07-03 12:53:39 +08:00
Kevin Hu 62f94cd59b Feat: Add knowledge compilation workflows (#16515)
## Summary
- Add knowledge compilation template APIs, services, and builtin
template seed data
- Add advanced knowledge compile structure/artifact/RAPTOR workflow
support
- Update parsing, dataset/document APIs, and supporting services for
compilation workflows
2026-07-02 23:22:07 +08:00
Wang Qi f7e39a09dc Fix graphrag generate error - AttributeError: 'RedisDB' object has no attribute 'mget' (#16573) 2026-07-02 19:06:16 +08:00
Zhichang Yu 195bfffb5e fix(security): address 93 CodeQL code-scanning alerts across 61 files (#16407)
## Summary

Resolves all 93 open alerts at
https://github.com/infiniflow/ragflow/security/code-scanning by rule:

| Rule | Count | Treatment |
|------|-------|-----------|
| py/clear-text-logging-sensitive-data | 23 | Real fix — log scrubbing |
| go/path-injection | 15 | Real fix where possible, suppression with
rationale |
| go/request-forgery | 8 | Suppression with rationale
(operator-controlled URLs) |
| go/clear-text-logging | 10 | Real fix — log scrubbing |
| go/unsafe-quoting | 5 | Real fix — escape or refactor |
| go/sql-injection | 3 | Real fix — orderby whitelist + CodeQL comment |
| go/uncontrolled-allocation-size | 2 | Real fix — cap to 1024 |
| go/incorrect-integer-conversion | 3 | Real fix — ParseInt + range
check |
| go/insecure-hostkeycallback | 1 | Real fix — known_hosts file |
| go/disabled-certificate-check | 2 | Suppression with rationale |
| go/command-injection | 1 | Suppression (sanitized via shq()) |
| go/email-injection | 1 | Suppression with rationale |
| go/cookie-httponly-not-set | 1 | Suppression (SPA bootstrap) |
| js/stack-trace-exposure | 1 | Real fix — generic client message |
| js/prototype-pollution-utility | 1 | Real fix — reject
__proto__/constructor/prototype |
| py/weak-sensitive-data-hashing | 1 | Real fix — MD5 → SHA-256 |
| py/incomplete-url-substring-sanitization | 3 | Real fix —
urlparse(hostname) |
| py/paramiko-missing-host-key-validation | 1 | Real fix —
load_system_host_keys + RejectPolicy |
| cpp/integer-multiplication-cast-to-long | 2 | Real fix — cast to
size_t |

## Real fixes (with measurable security improvement)

**SSH host key verification (Go + Python)**  
Replace `InsecureIgnoreHostKey()` / `paramiko.AutoAddPolicy()` with
proper host key verification against a known_hosts file (configurable
via `SSH_KNOWN_HOSTS` env / `known_hosts` config field; fail-closed when
unset). Loads `~/.ssh/known_hosts` first via `load_system_host_keys()`
so existing setups keep working.

**SQL injection in `user_canvas`**  
Add `userCanvasOrderableColumns` whitelist + `userCanvasOrderClause`
helper. Both `GetList()` and `ListByTenantIDs()` now route the
user-supplied `orderby` query param through the helper, defaulting to
`create_time` on miss.

**SQL injection in `pipeline_operation_log`**  
Existing whitelist documented via CodeQL comment.

**Real SQL injection in `infinity/chunk.go:931`**  
Escape `'` → `''` on user-controlled `questionText` before splicing into
`filter_fulltext(...)` SQL filter.

**Real SQL injection in `elasticsearch/sql.go:75`**  
Defense-in-depth escape on tokenizer output before splicing into
`MATCH(...)`.

**Python code injection in `result_protocol.go`**  
Replace raw JSON literal embedding into Python/JS expressions with
base64 + `json.loads` / `JSON.parse(Buffer.from(...,
'base64').toString('utf8'))`. Eliminates both the unsafe-quoting sink
and the brittleness of mixing JSON true/false/null with Python syntax.

**URL substring check bypass in `embedding_model.py`**  
Replace `if "dashscope-intl.aliyuncs.com" in u` with
`urlparse(u).hostname == "dashscope-intl.aliyuncs.com"` so a base_url
like `https://attacker.example/?u=dashscope-intl.aliyuncs.com` cannot
bypass the routing.

**Prototype pollution in `setNestedValue` (TS)**  
Reject `__proto__`/`constructor`/`prototype` keys before any assignment.

**Integer overflow**  
- scrypt params via `ParseInt` + non-positive check
(`internal/common/password.go`)
- `topN` and `n` caps to 1024 (retrieval_service.go, dataset.go)
- `nalloc*statesize` cast to `size_t` (cpp/re2/onepass.cc)

**Cookie httponly**  
Set explicitly with rationale: this is the OAuth bootstrap cookie
intentionally read by the SPA.

**Stack trace exposure**  
Replace `error.message` in HTTP 500 response with generic `"internal
error"`; full error still logged server-side via `console.error`.

**Weak hashing**  
MD5 → SHA-256 for deterministic `conv_id` derivation
(`conversation_service.py`).

**Log scrubbing**  
Remove or redact user-controlled / sensitive content from clear-text
logs across 8 ingestion parsers, `llm_service.py` ×11,
`tenant_llm_service.py` ×7, `misc_utils.py` ×4, `redis_conn.py` ×10,
`conftest.py` ×4, `init_data.py`, `dataset_api_service.py`,
`generator.py`, `mysql_migration.py`, `cli.go`, `user_command.go`,
`pdf_parser.go`. Most patterns converted to parameterized logging
(`logging.info("...: %d", n)`) or static messages.

## CodeQL suppressions (each with rationale)

For alerts where the data flow is genuinely safe but CodeQL can't see
the context — operator-controlled URLs, sanitized inputs, etc. — I added
`// codeql[go/<rule>] <rationale>` annotations rather than dismissing
them, so future readers can audit the rationale inline:

- `internal/agent/component/invoke.go:135` — Invoke is a generic canvas
HTTP client
- `internal/service/langfuse.go` ×2 — host is per-tenant operator config
- `internal/service/file.go:1184` — already SSRF-guarded by
`assertURLSafe`
- `internal/utility/mcp_client.go` ×3 — already `AssertURLSafe` +
IP-pinned
- `internal/entity/models/bedrock.go` — sigv4-signed request, URL can't
be tampered
- `internal/service/deep_researcher.go:269` — `callback` is SSE display
string, not SQL
- `internal/engine/infinity/chunk.go:346` — UUIDs can't contain `'` (RFC
4122)
- `internal/cli/common_command.go` ×2 — CLI trusts operator-configured
URL
- `internal/utility/smtp.go:194` — msg is server-built, not user form
input
- `internal/entity/models/*` ×14 (path-injection) — audio file paths are
caller-supplied

## Test plan

- ✅ All 13 modified Go packages build cleanly
- ✅ 663 tests pass across `internal/agent/sandbox`, `internal/common`,
`internal/agent/component`, `internal/engine/infinity`, `internal/dao`
- ✅ All 11 modified Python files parse via `ast.parse`
- ✅ TypeScript `tsc --noEmit` clean on the modified
`use-provider-fields.tsx`
- ✅ `node --check` clean on the modified JS file

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-29 09:45:16 +08:00
Öndery 8081a77c7c Fix missing move and copy methods in Python RAGFlowS3 storage implementation (#16350) 2026-06-26 15:51:24 +08:00