Skip to content

Feat/add file quota2 - #233

Closed
Ahmath-Gadji wants to merge 10 commits into
devfrom
feat/add_file_quota2
Closed

Feat/add file quota2#233
Ahmath-Gadji wants to merge 10 commits into
devfrom
feat/add_file_quota2

Conversation

@Ahmath-Gadji

@Ahmath-Gadji Ahmath-Gadji commented Feb 9, 2026

Copy link
Copy Markdown
Collaborator

BREAKING CHANGE: Introduces DEFAULT_FILE_QUOTA environment variable and requires database migration to add file_quota column to users table.

Features:

  • Add file_quota field to users table (nullable integer)
  • Add DEFAULT_FILE_QUOTA env var to set global default quota
  • Add PATCH /users/{user_id}/quota endpoint to update user quotas
  • Add quota enforcement on file upload (indexed files + pending tasks)
  • Admins bypass quota checks; quota<=0 for a user means unlimited; quota > 0 to limit user's quota

API Changes:

  • POST /users/ now accepts optional file_quota parameter
  • GET /users/ and GET /users/{id} return file_quota in response
  • GET /users/info now renders additional fields: indexed_files, pending_files, total_files and file_quota
  • New PATCH /users/{user_id}/quota endpoint for quota management

Tests:

  • test_update_user_quota: verify quota update from 10 to 12
  • test_user_default_quota: verify None quota defaults to 10
  • TestUserQuotaEnforcement class in test_indexer.py:
    • test_unlimited_quota_user_can_exceed_default: user with quota=0 uploads 11 files
    • test_quota_limit_blocks_excess_uploads: user with quota=5 blocked on 6th file

Perform migration: Refer to the doc

```bash title="Apply migrations"
docker compose up -d rdb
docker compose \
run --no-deps --build --rm \
--entrypoint "uv run alembic -c /app/openrag/scripts/migrations/alembic/alembic.ini upgrade head" \
openrag; docker compose down
```

Frontend:

IndexerUI has been updated to show users their current quota usage and enforce quota limit. See this PR. One has to manually update the submodule in order for Indexer UI to point to the newest version.

git submodule update --init --recursive

Summary by CodeRabbit

  • New Features

    • Per-user file quotas with admin-managed quota endpoint; uploads blocked when limits are exceeded.
    • User profile now shows file count, pending uploads, and quota status.
  • Documentation

    • Added File Quotas and DEFAULT_FILE_QUOTA docs explaining semantics and configuration.
  • Database Migration

    • Added user fields for file_quota and file_count.
  • Tests

    • New end-to-end tests covering quota enforcement, count increments/decrements, and quota updates.

Ahmath-Gadji and others added 8 commits January 28, 2026 08:59
UserWarning: Duplicate Operation ID list_models_v1_models_get for function list_models at /app/openrag/routers/openai.py
  warnings.warn(message, stacklevel=1)
Add relationship_id and parent_id fields to support document linking:
- relationship_id: Groups related documents (email threads, folders)
- parent_id: Hierarchical parent reference (parent email, parent folder)

Changes:
- Add SQLAlchemy columns and indexes to File model
- Add Alembic migration for new database columns
- Add PartitionFileManager query methods with recursive CTE for ancestors
- Add VectorDB wrapper methods and Milvus INVERTED indexes
- Add API endpoints: GET /{partition}/relationships/{id} and ancestors
- Add include_related/include_ancestors params to search endpoints
- Add RelationshipAwareRetriever for context-aware retrieval
- Add unit tests (14) and integration tests (11)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
requires database migration to add file_quota column to users table.

Features:
- Add file_quota field to users table (nullable integer)
- Add DEFAULT_FILE_QUOTA env var to set global default quota
- Add PATCH /users/{user_id}/quota endpoint to update user quotas
- Add quota enforcement on file upload (indexed files + pending tasks)
- Admins bypass quota checks; quota<=0 for a user means unlimited; quota > 0 to limit user's quota

API Changes:
- POST /users/ now accepts optional file_quota parameter
- GET /users/ and GET /users/{id} return file_quota in response
- New PATCH /users/{user_id}/quota endpoint for quota management
- GET /users/info now renders additional fields: indexed_files, pending_files, total_files and file_quota

Tests:
- test_update_user_quota: verify quota update from 10 to 12
- test_user_default_quota: verify None quota defaults to 10
- TestUserQuotaEnforcement class in test_indexer.py:
  - test_unlimited_quota_user_can_exceed_default: user with quota=0 uploads 11 files
  - test_quota_limit_blocks_excess_uploads: user with quota=5 blocked on 6th file

Perform migration: Refer to the doc
https://github.com/linagora/openrag/blob/0b5f84cd880e7c75db6f78d12a7681227721c2ad/docs/content/docs/documentation/sql_migration.mdx?plain=1#L47-L53
requires database migration to add file_quota column to users table.

Features:
- Add file_quota field to users table (nullable integer)
- Add DEFAULT_FILE_QUOTA env var to set global default quota
- Add PATCH /users/{user_id}/quota endpoint to update user quotas
- Add quota enforcement on file upload (indexed files + pending tasks)
- Admins bypass quota checks; quota<=0 for a user means unlimited; quota > 0 to limit user's quota

API Changes:
- POST /users/ now accepts optional file_quota parameter
- GET /users/ and GET /users/{id} return file_quota in response
- New PATCH /users/{user_id}/quota endpoint for quota management
- GET /users/info now renders additional fields: file_count, pending_files, total_files and file_quota

Tests:
- test_update_user_quota: verify quota update from 10 to 12
- test_user_default_quota: verify None quota defaults to 10
- TestUserQuotaEnforcement class in test_indexer.py:
  - test_unlimited_quota_user_can_exceed_default: user with quota=0 uploads 11 files
  - test_quota_limit_blocks_excess_uploads: user with quota=5 blocked on 6th file

Perform migration: Refer to the doc
https://github.com/linagora/openrag/blob/0b5f84cd880e7c75db6f78d12a7681227721c2ad/docs/content/docs/documentation/sql_migration.mdx?plain=1#L47-L53
@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a per-user file quota system: DB schema changes (user.file_quota, user.file_count), quota-aware API checks on upload/copy endpoints, task-aware pending counts, vectordb/indexer call-site updates to propagate user context, and new tests and config vars to exercise quota behavior.

Changes

Cohort / File(s) Summary
CI & Environment
\.github/workflows/api_tests.yml, \.github/workflows/api_tests/docker-compose.yaml
Added OPENRAG_ADMIN_TOKEN to CI and AUTH_TOKEN/DEFAULT_FILE_QUOTA to docker-compose for tests/runtime.
Config
\.hydra_config/config.yaml
Bound rdb.default_file_quota to DEFAULT_FILE_QUOTA env var (default -1).
DB Migration
openrag/scripts/migrations/alembic/versions/c224d4befe71_add_file_count_and_file_quota.py
New alembic migration adding file_quota (nullable) and file_count (default 0) to users table.
Vectordb / Models / Utils
openrag/components/indexer/vectordb/utils.py, openrag/components/indexer/vectordb/vectordb.py
User model extended with file_quota and file_count; DEFAULT_FILE_QUOTA applied; APIs updated to accept/return file_quota and file_count; file add/remove/partition-delete operations update user counts; new update_user_quota and get_user_file_count.
Indexer Logic
openrag/components/indexer/indexer.py
Propagates user context to file deletion/add flows; added get_user_pending_task_count RPC to expose pending task counts by user.
API Routes & Utils
openrag/routers/indexer.py, openrag/routers/partition.py, openrag/routers/users.py, openrag/routers/utils.py
Wired check_user_file_quota dependency into upload/copy endpoints; added admin PATCH /users/{id}/quota; get_current_user augmented with indexed/pending/total counts and quota info; check_user_file_quota implements quota logic using DEFAULT_FILE_QUOTA and pending task count.
Docs
docs/content/docs/documentation/data_model.md, docs/content/docs/documentation/env_vars.md
Documented file_quota and file_count and DEFAULT_FILE_QUOTA semantics (≤0 disables quotas, >0 sets default per-user quota).
Tests
tests/api_tests/conftest.py, tests/api_tests/test_indexer.py, tests/api_tests/test_users.py
Added OPENRAG_ADMIN_TOKEN for test auth; expanded tests to cover quota enforcement, file_count increments/decrements, and admin quota updates; test helpers adjusted to pass auth headers and poll task status with headers.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Router as Indexer Router
    participant Validator as Quota Validator
    participant VectorDB as VectorDB
    participant TaskMgr as Task Manager
    participant DB as Database

    Client->>Router: POST /add_file (user token)
    Router->>Validator: check_user_file_quota(user)
    Validator->>VectorDB: get_user_by_token(user_token)
    VectorDB->>DB: Query User (file_quota, file_count)
    DB-->>VectorDB: User data
    Validator->>TaskMgr: get_user_pending_task_count(user_id)
    TaskMgr-->>Validator: pending_count
    Validator->>Validator: total = file_count + pending_count
    alt total >= user_quota
        Validator-->>Router: HTTP 403 Quota Exceeded
        Router-->>Client: 403 Error
    else within quota
        Validator-->>Router: approved
        Router->>VectorDB: add_file_to_partition(file_id, user_id)
        VectorDB->>DB: Insert File, Increment User.file_count
        DB-->>VectorDB: Success
        VectorDB-->>Router: File added / task queued
        Router-->>Client: Task queued response
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • paultranvan

Poem

🐰 I hopped through rows of code and quoth with cheer,

Files now counted, quotas held near,
Admins keep keys, listeners hum with delight,
Pending tasks dance in the soft moonlight,
A carrot-sized celebration — hop on, all is right! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.59% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feat/add file quota2' is vague and generic; it uses a non-descriptive format with a trailing '2' that lacks context about the feature's scope or significance. Clarify the title to describe the main feature more specifically, e.g., 'Add file quota enforcement with per-user limits and admin controls' or 'Implement file upload quotas with default limits'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/add_file_quota2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added the feat Add a new feature label Feb 9, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
openrag/components/indexer/vectordb/vectordb.py (1)

883-890: ⚠️ Potential issue | 🔴 Critical

delete_user calls delete_partition without required user_id argument.

delete_partition in PartitionFileManager now requires a user_id parameter (as seen in the relevant snippet from utils.py line 269), but line 889 calls it without one. This will raise a TypeError at runtime when deleting a user who owns partitions.

Proposed fix
     async def delete_user(self, user_id: int):
         self._check_user_exists(user_id)
         user_partitions = [
             p["partition"] for p in self.partition_file_manager.list_user_partitions(user_id) if p["role"] == "owner"
         ]
         for partition in user_partitions:
-            self.partition_file_manager.delete_partition(partition)
+            self.partition_file_manager.delete_partition(partition, user_id=user_id)
         self.partition_file_manager.delete_user(user_id)
openrag/components/indexer/vectordb/utils.py (2)

270-290: ⚠️ Potential issue | 🟠 Major

delete_partition decrements the caller's file_count by the total files in the partition, ignoring multi-user uploads.

If user A uploaded 3 files and user B uploaded 2 files to the same partition, and user A deletes the partition, user A's file_count is decremented by 5 (all files), not 3 (only A's files). This corrupts the count for user A.

To fix this correctly, you'd need to track which user uploaded each file (e.g., a user_id column on the File model) and decrement each user's count by their respective file count in the partition. Alternatively, count files per user via the existing file_count per-user-per-partition before deletion.

🛠️ Sketch of a more correct approach

One option: add uploaded_by to the File model. Then in delete_partition:

# Count files per uploader
from sqlalchemy import func
file_counts = (
    session.query(File.uploaded_by, func.count())
    .filter(File.partition_name == partition)
    .group_by(File.uploaded_by)
    .all()
)
for uid, cnt in file_counts:
    user = session.query(User).filter(User.id == uid).first()
    if user:
        user.file_count = func.greatest(0, User.file_count - cnt)

If adding a column is too invasive now, consider reconciling file_count from the live query (get_user_file_count) after partition deletion instead.


245-268: ⚠️ Potential issue | 🟠 Major

remove_file_from_partition decrements the caller's file_count, not the original uploader's.

The user_id parameter is presumably the user requesting the deletion. If a different user uploaded the file, this user's file_count is incorrectly decremented. This is the same ownership-tracking gap as noted in delete_partition.

🤖 Fix all issues with AI agents
In @.gitmodules:
- Line 7: The .gitmodules entry currently points the submodule branch "branch =
feat/handling_user_file_quota", which risks breakage when feature branches are
deleted; update the .gitmodules configuration for the affected submodule (the
"branch" field) to reference a stable branch (e.g., "dev" or "main") or remove
the branch field and pin the submodule to a specific commit hash instead (or
after merging the feature, set the branch to the merged stable branch) so that
git submodule update remains reproducible.

In `@docs/content/docs/documentation/env_vars.md`:
- Line 420: The table entry for DEFAULT_FILE_QUOTA is incorrect — change its
default value from `0` to `-1` and update the description to match the
config.yaml/data_model.md semantics: show `-1` as the default and state that
`<=0` (or specifically `-1`) disables quotas globally while `>0` sets the
default per-user quota when no explicit quota exists; reference the
DEFAULT_FILE_QUOTA setting and ensure the wording aligns with the value defined
in config.yaml and documented in data_model.md.

In `@openrag/components/indexer/vectordb/utils.py`:
- Around line 231-236: The current read-modify-write pattern user.file_count +=
1 in the indexer functions causes a race; replace the in-memory
increment/decrement with SQL-level atomic updates using a column expression so
the DB executes SET file_count = file_count + 1 (and -1) atomically. Concretely,
in the function where you increment (the block using
session.query(User).filter(User.id == user_id).first()) and in
remove_file_from_partition and delete_partition, switch to a query.update() (or
session.execute with an update clause) targeting User.file_count =
User.file_count + 1 (and User.file_count = User.file_count - 1 for decrements)
filtered by User.id == user_id, then flush/commit as before to persist the
change.

In `@openrag/routers/users.py`:
- Around line 60-93: The response key for indexed files in get_current_user_info
is "file_count" but the docstring/documentation references "indexed_files";
update either the endpoint response to include "indexed_files" (e.g., add
"indexed_files": indexed_count alongside "file_count" or rename the key to
"indexed_files") or change the docstring to reference "file_count" so the docs
and the get_current_user_info response remain consistent; ensure any tests or
consumers referencing file_count/indexed_files are updated accordingly.
- Around line 260-275: The update_user_quota endpoint currently calls
vectordb.update_user_quota.remote and discards its return value, and if the
target user doesn't exist the underlying _check_user_exists exception bubbles up
as a 500; update the update_user_quota function to await
vectordb.update_user_quota.remote into a variable (e.g., updated = await
vectordb.update_user_quota.remote(...)), catch the specific exception raised by
_check_user_exists (or a user-not-found error from the vectordb client) and
return JSONResponse(status_code=404, content={"detail": "User not found"}), and
on success return the updated user dict in the response body (HTTP 200) while
keeping the existing debug log via logger.debug; ensure require_admin and
vectordb dependency usage remains unchanged.

In `@openrag/routers/utils.py`:
- Around line 194-204: Fix the typo "to disabled" → "to disable" in the
docstring and the inline comment near the quota-check logic that references
DEFAULT_FILE_QUOTA and the admin check (the block using user.get("is_admin") and
the conditional "if DEFAULT_FILE_QUOTA <= 0"). Update both the docstring line
describing quota semantics and the inline comment above the DEFAULT_FILE_QUOTA
check to read "to disable quota checking".

In
`@openrag/scripts/migrations/alembic/versions/bd24665e3451_add_file_quota_and_file_count_fields.py`:
- Around line 24-25: The migration adds a NOT NULL users.file_count column
without a DB default which will fail on existing rows; update the op.add_column
call that creates "file_count" (in this migration file where
op.add_column("users", sa.Column("file_count", ...))) to include a
server_default (e.g. server_default=sa.text("0")) so existing rows get 0 at
schema time, keeping nullable=False; after applying the migration you can
optionally remove the server_default in a follow-up migration if you don't want
it permanently.

In `@tests/api_tests/test_indexer.py`:
- Around line 619-620: The finally block in test_file_count_decrements_on_delete
only calls self._cleanup_partition(api_client, partition_name) and omits
cleaning up the test user; add a call to self._cleanup_user(api_client, user_id)
in that finally block (matching other tests) so the created user is
removed—ensure the test uses the same user_id variable created earlier in the
test and keep the partition cleanup call intact (functions referenced:
_cleanup_partition, _cleanup_user, and user_id).
- Around line 486-514: The test test_quota_limit_blocks_excess_uploads relies on
quota enforcement being enabled; ensure the test explicitly verifies or sets
that precondition by checking DEFAULT_FILE_QUOTA > 0 (import DEFAULT_FILE_QUOTA
from openrag.routers.utils) at the start of the test or by using the
partition/user PATCH quota endpoint after creating the user (methods involved:
_create_user_with_quota, _create_partition, _upload_file) to set a positive
file_quota so quota enforcement is guaranteed regardless of environment.

In `@tests/api_tests/test_users.py`:
- Around line 68-84: The test_user_default_quota test is asserting a hardcoded
10 which couples it to environment config; instead import or read the configured
DEFAULT_FILE_QUOTA used by the app (e.g., from the config/settings module or
environment variable) and use that value as the expected value in the assertion.
Update the test to obtain DEFAULT_FILE_QUOTA via the same symbol your app uses
(e.g., DEFAULT_FILE_QUOTA from settings/config) and replace the literal 10 in
the assertion so api_client-based creation and the get_response check compare
against the configured constant.
🧹 Nitpick comments (8)
openrag/routers/partition.py (2)

414-427: Unused request parameter in get_related_files.

The request parameter is declared but never used in the function body, unlike get_file_ancestors where it's also unused. Consider removing it to keep the signature clean.

Proposed fix
 async def get_related_files(
-    request: Request,
     partition: str,
     relationship_id: str,
     vectordb=Depends(get_vectordb),
     partition_viewer=Depends(require_partition_viewer),
 ):

455-475: Unused request parameter in get_file_ancestors.

Same as get_related_filesrequest is injected but never referenced.

docs/content/docs/documentation/linked_files.md (1)

65-65: Add language specifiers to fenced code blocks.

Several code blocks (lines 65, 228, 233, 241, 282) lack language identifiers, flagged by markdownlint (MD040). Use text or plaintext for non-code examples.

Also applies to: 228-246

openrag/routers/utils.py (1)

219-239: TOCTOU window between quota check and task registration.

There's a race condition: two concurrent upload requests for the same user can both pass the quota check before either task is registered in TaskStateManager, allowing the user to exceed their quota by one or more files. Since this is a soft limit and not a financial control, it's likely acceptable — but worth documenting if precision matters.

tests/api_tests/test_users.py (1)

54-57: Brittle assertion on message string content.

Asserting str(user_id) in update_data["message"] and "12" in update_data["message"] couples the test to the exact message format. Consider asserting on structured response fields instead (e.g., checking the updated quota value directly, which you already do on line 63).

tests/api_tests/test_indexer.py (1)

470-480: Redundant status check after assertion.

Line 476 checks if response.status_code in [200, 201, 202], but line 472 already asserts this — so the condition is always True at that point. The same pattern appears in the other tests (lines 504-508). While harmless, it adds noise.

✂️ Simplify by removing the redundant check
                 assert response.status_code in [200, 201, 202], (
                     f"File {i} upload failed with status {response.status_code}: {response.text}"
                 )
                 # Wait for task if needed
-                if response.status_code in [200, 201, 202]:
-                    data = response.json()
-                    if "task_status_url" in data:
-                        task_id = get_task_id(data)
-                        wait_for_task(api_client, task_id, headers={"Authorization": f"Bearer {user_token}"})
+                data = response.json()
+                if "task_status_url" in data:
+                    task_id = get_task_id(data)
+                    wait_for_task(api_client, task_id, headers={"Authorization": f"Bearer {user_token}"})
openrag/components/indexer/vectordb/utils.py (2)

337-343: No-op assignment on line 341.

file_quota = file_quota does nothing. You can remove the elif branch or add a comment if the intent is to document the pass-through case.

✂️ Simplify
             if self.file_quota_per_user > 0:  # Quotas enabled globally
                 if file_quota is None:
                     file_quota = self.file_quota_per_user  # default to default quota
-                elif file_quota > 0:
-                    file_quota = file_quota  # use specified quota
                 else:
-                    pass  # unlimited
+                    pass  # file_quota > 0: use as-is; file_quota <= 0: unlimited

560-580: update_user_quota doesn't guard against user being None.

If the caller doesn't go through the vectordb actor's _check_user_exists gate (or if a future refactor removes that check), line 569 will raise AttributeError. A defensive check would be prudent.

🛡️ Proposed fix
         with self.Session() as s:
             user = s.query(User).filter(User.id == user_id).first()
+            if not user:
+                raise ValueError(f"User {user_id} not found")
             user.file_quota = file_quota

Comment thread .gitmodules
[submodule "extern/indexer-ui"]
path = extern/indexer-ui
url = https://github.com/linagora/openrag-admin-ui.git
branch = feat/handling_user_file_quota

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid pointing submodules to feature branches.

The submodule is now configured to track the feature branch feat/handling_user_file_quota. This creates a maintenance risk because feature branches are typically deleted after merging, which will break git submodule update for anyone cloning or updating the repository later.

Recommended approach:

  1. Merge the indexer-ui feature branch to a stable branch (e.g., dev or main) first
  2. Update this .gitmodules to point to that stable branch, or
  3. Use a specific commit hash instead of a branch name for reproducibility
📌 Example: Using a specific commit instead of a branch

After merging the indexer-ui PR, pin to the merge commit:

 [submodule "extern/indexer-ui"]
 	path = extern/indexer-ui
 	url = https://github.com/linagora/openrag-admin-ui.git
-	branch = feat/handling_user_file_quota
+	branch = dev

Or remove the branch field entirely and use git submodule update --remote with a specific commit when needed.

🤖 Prompt for AI Agents
In @.gitmodules at line 7, The .gitmodules entry currently points the submodule
branch "branch = feat/handling_user_file_quota", which risks breakage when
feature branches are deleted; update the .gitmodules configuration for the
affected submodule (the "branch" field) to reference a stable branch (e.g.,
"dev" or "main") or remove the branch field and pin the submodule to a specific
commit hash instead (or after merging the feature, set the branch to the merged
stable branch) so that git submodule update remains reproducible.

Comment thread docs/content/docs/documentation/env_vars.md Outdated
Comment thread openrag/components/indexer/vectordb/utils.py
Comment thread openrag/routers/users.py
Comment thread openrag/routers/users.py
Comment thread openrag/routers/utils.py Outdated
Comment thread tests/api_tests/test_indexer.py
Comment thread tests/api_tests/test_indexer.py Outdated
Comment on lines +68 to +84
def test_user_default_quota(self, api_client):
"""Test that user created with None quota gets default value (10)."""
# Create a user without specifying quota (None)
create_response = api_client.post(
"/users/",
data={"display_name": "default_quota_user"},
)
assert create_response.status_code == 201
user_data = create_response.json()
user_id = user_data["id"]

try:
# Verify the user has the default quota (10)
get_response = api_client.get(f"/users/{user_id}")
assert get_response.status_code == 200
get_data = get_response.json()
assert get_data["file_quota"] == 10, "User should have default quota of 10"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded default quota value couples test to environment config.

The expected default quota of 10 (line 84) must match the DEFAULT_FILE_QUOTA configured in the test environment. If the config changes, this test silently becomes incorrect. Consider reading the expected default from the same config source or documenting the dependency on the test environment configuration.

🤖 Prompt for AI Agents
In `@tests/api_tests/test_users.py` around lines 68 - 84, The
test_user_default_quota test is asserting a hardcoded 10 which couples it to
environment config; instead import or read the configured DEFAULT_FILE_QUOTA
used by the app (e.g., from the config/settings module or environment variable)
and use that value as the expected value in the assertion. Update the test to
obtain DEFAULT_FILE_QUOTA via the same symbol your app uses (e.g.,
DEFAULT_FILE_QUOTA from settings/config) and replace the literal 10 in the
assertion so api_client-based creation and the get_response check compare
against the configured constant.

@Ahmath-Gadji
Ahmath-Gadji force-pushed the feat/add_file_quota2 branch 2 times, most recently from 13515d2 to 8b24f46 Compare February 9, 2026 13:29

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
openrag/components/indexer/indexer.py (1)

296-312: ⚠️ Potential issue | 🟡 Minor

Wrong type hint: partition: int should be partition: str.

In set_details, the partition parameter is typed as int but it holds a partition name string (e.g., used in metadata dict on line 309).

Proposed fix
     async def set_details(
         self,
         task_id: str,
         *,
         file_id: str,
-        partition: int,
+        partition: str,
         metadata: dict,
         user_id: int,
     ):
openrag/components/indexer/vectordb/vectordb.py (2)

53-62: 🛠️ Refactor suggestion | 🟠 Major

Abstract base class signatures are out of sync with the implementation.

BaseVectorDB.delete_partition (line 53) and BaseVectorDB.delete_file (line 61) don't include user_id, but MilvusDB now requires it. This breaks the Liskov Substitution Principle and will confuse any future implementers of BaseVectorDB.

♻️ Update ABC signatures
     `@abstractmethod`
-    async def delete_partition(self, partition: str):
+    async def delete_partition(self, partition: str, user_id: int):
         pass

     ...

     `@abstractmethod`
-    async def delete_file(self, file_id: str, partition: str):
+    async def delete_file(self, file_id: str, partition: str, user_id: int):
         pass

898-905: ⚠️ Potential issue | 🔴 Critical

Bug: delete_partition called without required user_id argument.

Line 904 calls self.partition_file_manager.delete_partition(partition), but the updated signature in utils.py line 300 now requires user_id: int as a second positional argument. This will raise a TypeError at runtime when deleting a user who owns partitions.

🐛 Proposed fix
     async def delete_user(self, user_id: int):
         self._check_user_exists(user_id)
         user_partitions = [
             p["partition"] for p in self.partition_file_manager.list_user_partitions(user_id) if p["role"] == "owner"
         ]
         for partition in user_partitions:
-            self.partition_file_manager.delete_partition(partition)
+            self.partition_file_manager.delete_partition(partition, user_id=user_id)
         self.partition_file_manager.delete_user(user_id)
🤖 Fix all issues with AI agents
In `@docs/content/docs/documentation/linked_files.md`:
- Line 65: Several fenced code blocks in the markdown lack language specifiers
(triple-backtick blocks), causing markdownlint MD040 failures; edit each
offending fenced code block and add the appropriate language identifier—use
`text` for plain-text examples and `txt` for diagram-like blocks—specifically
update the triple-backtick blocks mentioned in the comment so they read ```text
or ```txt as appropriate.
- Line 32: Fix the minor grammar in the sentence that currently reads "The
`File` model includes now two relationship fields:" by changing it to "The
`File` model now includes two relationship fields:" so the placement of "now" is
correct; update the sentence where the `File` model is described in the docs
(the exact line containing "The `File` model includes now two relationship
fields:").

In `@openrag/components/indexer/indexer.py`:
- Around line 148-157: delete_file assumes user is a dict and calls
user.get("id"), but update_file_metadata can pass None; either make user
required in update_file_metadata or make delete_file accept None: change
delete_file signature to user: dict | None = None (or keep current signature)
and guard before accessing user (e.g., compute user_id = user.get("id") if user
else None) and pass that to vectordb.delete_file.remote; also update type hints
and any callers (notably update_file_metadata) to match the new optional user
handling so no AttributeError occurs.

In `@openrag/components/indexer/vectordb/utils.py`:
- Around line 645-693: The recursive CTE in get_file_ancestors can loop on
cyclic parent_id chains; fix by adding a depth guard/limit to the CTE (e.g.,
introduce a MAX_DEPTH parameter and increment depth in the recursive step, then
constrain recursion with "WHERE a.depth < :max_depth" or equivalent) and surface
max_depth as a parameter to get_file_ancestors so callers can control it; ensure
the query binds the new max_depth parameter when executing the text SQL and
still returns results ordered from root to the file.
- Around line 575-595: update_user_quota currently assumes the user exists and
then accesses/returns fields after committing which can cause AttributeError or
DetachedInstanceError; update the method to first check if user is None after
the query (e.g., if user is None: raise ValueError or a suitable NotFound error)
to guard against missing users, and ensure the returned fields are safe to
access by either reading needed attributes before calling s.commit() or calling
s.refresh(user) after s.commit() (use the existing Session variable s and the
user object) before building and returning the dict.

In `@openrag/components/indexer/vectordb/vectordb.py`:
- Around line 330-342: The calls to index_params.add_index for dynamic document
fields use field_name but Milvus expects dynamic-field indexing to pass
json_path and a mandatory json_cast_type; update the two add_index calls (the
ones adding "relationship_id_idx" and "parent_id_idx") to replace
field_name="relationship_id"/"parent_id" with
json_path="$.relationship_id"/"$.parent_id" (or the correct JSON path used by
your stored documents) and add json_cast_type set to the proper Milvus type
(e.g., "VARCHAR" if these are strings or "DOUBLE"/"BOOL" as appropriate); keep
index_type="INVERTED" and index_name the same.

In `@openrag/routers/utils.py`:
- Around line 304-310: The code mixes safe .get() access
(llm_param.get("base_url"), llm_param.get("model"), llm_param.get("api_key"))
with direct indexing llm_param["base_url"] / llm_param["model"] when creating
the logger; change the logger.bind call to use the already-read variables
(base_url and model) or use llm_param.get(...) consistently (with sensible
defaults) so no KeyError can be raised—update the logger.bind invocation
(symbol: logger.bind and variables llm_param, base_url, model, log) to reference
base_url and model (or llm_param.get with defaults) instead of direct dict
indexing.

In
`@openrag/scripts/migrations/alembic/versions/344b49ce4f69_add_file_quota_field.py`:
- Around line 1-32: This migration adds the same file_quota column already added
in bd24665e3451_add_file_quota_and_file_count_fields.py, causing
duplicate/branching migrations; fix by consolidating to a single migration
chain: remove the op.add_column("users", sa.Column("file_quota", ...)) and
corresponding op.drop_column("users", "file_quota") from this file
(344b49ce4f69_add_file_quota_field.py) so only bd24665e3451... owns the column,
or alternatively merge the unique changes into one file and update the
revision/down_revision values accordingly; locate the op.add_column and
op.drop_column calls in this module to perform the change and ensure the
revision and down_revision identifiers remain consistent.

In `@openrag/tests/test_relationships_integration.py`:
- Around line 66-72: Replace the use of set(...) on a generator with a set
comprehension to satisfy Ruff C401: in
test_index_folder_files_share_relationship_id compute relationship_ids with a
comprehension like {doc.metadata.get("relationship_id") for doc in
folder_documents} instead of set(doc.metadata.get("relationship_id") for doc in
folder_documents); apply the same change to the other similar test that builds
relationship_ids in the file.
- Around line 12-14: Reorder and group the imports into standard-library,
third-party, and local groups with a blank line between groups; specifically
move "from unittest.mock import AsyncMock, MagicMock, patch" above "import
pytest" and keep "from langchain_core.documents.base import Document" in the
third-party group below a blank line, so the import order becomes:
standard-library (unittest.mock...), third-party (pytest, langchain_core...),
ensuring imports reference AsyncMock, MagicMock, patch, pytest, and Document as
shown.

In `@tests/api_tests/test_search.py`:
- Around line 90-93: Remove the Git conflict markers (<<<<<<<, =======, >>>>>>>)
in this test file and keep the intended new test declaration for
test_search_with_include_related; specifically locate the conflict around the
test_search_with_include_related function and delete the conflict marker lines
so the function signature and body remain valid and the module parses correctly.
- Around line 93-143: The test uses missing fixtures indexed_folder_partition,
exact_match_query, and folder_files which causes collection failures; add
fixture implementations in tests/api_tests/conftest.py that (1)
indexed_folder_partition: create and return a test partition id with an indexed
folder/thread relationship, (2) folder_files: create and index three files
(e.g., "file1.txt","file2.txt","file3.txt") under the same relationship_id and
return a mapping like {filename: (content, relationship_id)} so the test can
assert expected IDs, and (3) exact_match_query: return a query string that will
match a known chunk from file1.txt; ensure the fixtures handle indexing and
cleanup and remove any leftover merge conflict markers related to this test
addition.
🧹 Nitpick comments (7)
openrag/routers/users.py (2)

1-9: Module-level Ray actor initialization.

task_state_manager = get_task_state_manager() (line 9) runs at import time. This works because ray.init() is called in api.py before routers are imported, but it creates an implicit ordering dependency. If this module is ever imported in a context where Ray hasn't been initialized (e.g., tests, scripts), it will fail.


123-139: file_quota parameter ordering may confuse API consumers.

file_quota (line 128) is declared after vectordb (a DI dependency, line 127) in the function signature. While FastAPI handles this correctly, placing all user-facing form parameters together improves readability.

♻️ Suggested reorder
 async def create_user(
     display_name: str | None = Form(None),
     external_user_id: str | None = Form(None),
     is_admin: bool = Form(False),
+    file_quota: int | None = Form(None),
     vectordb=Depends(get_vectordb),
-    file_quota: int | None = Form(None),
     admin_user=Depends(require_admin),
 ):
openrag/routers/partition.py (1)

455-475: Unused request parameter.

The request: Request parameter is injected but never referenced in the function body. Same applies to get_related_files above. Consider removing them to keep the signature clean, unless they're needed for future use.

Proposed fix
 async def get_file_ancestors(
-    request: Request,
     partition: str,
     file_id: str,
     vectordb=Depends(get_vectordb),
     partition_viewer=Depends(require_partition_viewer),
 ):
openrag/components/retriever.py (1)

206-247: Consider parallelizing expansion fetches for lower latency.

Related and ancestor chunks are fetched sequentially in two loops. When there are multiple unique relationship_ids or file_ids from the initial results, this could add noticeable latency. Using asyncio.gather (with return_exceptions=True) would allow concurrent fetches.

openrag/components/test_relationships.py (1)

41-49: Test to_dict() structure diverges from production File.to_dict().

The test helper nests metadata under "file_metadata" key, while the production File.to_dict() in openrag/components/indexer/vectordb/utils.py (lines 69–78) spreads metadata into the top-level dict with **metadata. This means test assertions like file_dict["file_metadata"]["filename"] (line 457) wouldn't catch regressions where production flattens these fields differently.

Not blocking since these tests focus on relationship fields, but be aware of this when extending coverage.

openrag/routers/search.py (1)

17-99: Sequential remote calls in loops could slow expansion significantly.

Lines 57-75 and 78-97 each await a Ray remote call per unique relationship/file. With many distinct relationship IDs or file IDs in the result set, this becomes a waterfall of serial RPCs. Consider parallelizing with asyncio.gather.

♻️ Proposed refactor — parallelize remote calls
     # Fetch related chunks by relationship_id
     if include_related:
+        tasks = []
         for partition, rel_id in relationship_ids:
             if partition and rel_id:
-                try:
-                    related_chunks = await vectordb.get_related_chunks.remote(
+                tasks.append(vectordb.get_related_chunks.remote(
                         partition=partition,
                         relationship_id=rel_id,
                         limit=related_limit,
-                    )
-                    for chunk in related_chunks:
-                        chunk_id = chunk.metadata.get("_id")
-                        if chunk_id and chunk_id not in seen_ids:
-                            seen_ids.add(chunk_id)
-                            expanded_results.append(chunk)
-                except Exception as e:
-                    logger.warning(
-                        "Failed to fetch related chunks",
-                        relationship_id=rel_id,
-                        error=str(e),
-                    )
+                ))
+        related_results = await asyncio.gather(*tasks, return_exceptions=True)
+        for result in related_results:
+            if isinstance(result, Exception):
+                logger.warning("Failed to fetch related chunks", error=str(result))
+                continue
+            for chunk in result:
+                chunk_id = chunk.metadata.get("_id")
+                if chunk_id and chunk_id not in seen_ids:
+                    seen_ids.add(chunk_id)
+                    expanded_results.append(chunk)

Apply the same pattern to the ancestor chunk fetching loop.

openrag/components/indexer/vectordb/utils.py (1)

33-35: Module-level load_config() call — will fail at import time if config is unavailable.

config = load_config() and DEFAULT_FILE_QUOTA = config.rdb.get(...) execute at module import time. If the config subsystem isn't initialized (e.g., in isolated unit tests), importing this module will raise an exception. This may explain why the relationship integration tests use pytest.skip as a workaround.

Consider lazy-loading the config value or providing a fallback, similar to how other modules handle this pattern.

Comment thread docs/content/docs/documentation/linked_files.md Outdated
Comment thread docs/content/docs/documentation/linked_files.md Outdated
Comment thread openrag/components/indexer/vectordb/utils.py
Comment thread openrag/components/indexer/vectordb/utils.py Outdated
Comment thread openrag/components/indexer/vectordb/vectordb.py Outdated
Comment thread openrag/scripts/migrations/alembic/versions/344b49ce4f69_add_file_quota_field.py Outdated
Comment on lines +12 to +14
import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from langchain_core.documents.base import Document

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix import ordering to unblock the pipeline.

The linting pipeline is failing due to unsorted imports (Ruff I001). pytest should come after unittest.mock in standard import ordering, and langchain_core is a third-party import that should be in a separate group.

🔧 Proposed fix
-import pytest
 from unittest.mock import AsyncMock, MagicMock, patch
+
+import pytest
 from langchain_core.documents.base import Document
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from langchain_core.documents.base import Document
from unittest.mock import AsyncMock, MagicMock, patch
from langchain_core.documents.base import Document
import pytest
🧰 Tools
🪛 GitHub Actions: Linting

[error] 12-12: Ruff check failed: Import block is un-sorted or un-formatted.

🪛 GitHub Check: lint (3.12)

[failure] 12-14: Ruff (I001)
openrag/tests/test_relationships_integration.py:12:1: I001 Import block is un-sorted or un-formatted

🤖 Prompt for AI Agents
In `@openrag/tests/test_relationships_integration.py` around lines 12 - 14,
Reorder and group the imports into standard-library, third-party, and local
groups with a blank line between groups; specifically move "from unittest.mock
import AsyncMock, MagicMock, patch" above "import pytest" and keep "from
langchain_core.documents.base import Document" in the third-party group below a
blank line, so the import order becomes: standard-library (unittest.mock...),
third-party (pytest, langchain_core...), ensuring imports reference AsyncMock,
MagicMock, patch, pytest, and Document as shown.

Comment thread openrag/tests/test_relationships_integration.py Outdated
Comment thread tests/api_tests/test_search.py Outdated
Comment thread tests/api_tests/test_search.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
openrag/components/indexer/vectordb/vectordb.py (1)

898-905: ⚠️ Potential issue | 🔴 Critical

delete_user calls delete_partition without the required user_id argument — will raise TypeError at runtime.

delete_partition in PartitionFileManager (utils.py line 301) now requires user_id: int as a positional parameter (no default value). But line 904 calls it without user_id, which will crash when an admin tries to delete a user who owns partitions.

Proposed fix
     async def delete_user(self, user_id: int):
         self._check_user_exists(user_id)
         user_partitions = [
             p["partition"] for p in self.partition_file_manager.list_user_partitions(user_id) if p["role"] == "owner"
         ]
         for partition in user_partitions:
-            self.partition_file_manager.delete_partition(partition)
+            self.partition_file_manager.delete_partition(partition, user_id=user_id)
         self.partition_file_manager.delete_user(user_id)
🤖 Fix all issues with AI agents
In `@docs/content/docs/documentation/data_model.md`:
- Around line 103-111: The docs claim DEFAULT_FILE_QUOTA defaults to -1 but the
code sets DEFAULT_FILE_QUOTA via config.rdb.get("default_file_quota", 0) in
openrag/routers/utils.py and openrag/components/indexer/vectordb/utils.py;
update either the docs or the code so they match: change the fallback in both
modules to use -1 (i.e., set DEFAULT_FILE_QUOTA =
config.rdb.get("default_file_quota", -1)) or change the documentation text to
state the code-default is 0; ensure you only modify the DEFAULT_FILE_QUOTA
default value in the two referenced modules (DEFAULT_FILE_QUOTA) or the
documentation paragraph to keep behavior and docs consistent.

In `@openrag/components/indexer/vectordb/utils.py`:
- Around line 285-290: The decrement of User.file_count is vulnerable to race
conditions because it reads the Python-cached value then writes a SQL-level
value; replace the current check-and-assign with an atomic SQL expression using
func.greatest to floor at zero (consistent with delete_partition). For example,
perform an update via session.query(User).filter(User.id ==
user_id).update({User.file_count: func.greatest(User.file_count - 1, 0)}) (and
ensure func is imported from sqlalchemy) so the decrement is done server-side
and cannot go negative under concurrency.
- Around line 682-694: Import the json module and parse row.file_metadata (which
can be a JSON string when using session.execute/text) before unpacking it with
**; inside the list comprehension that builds the returned dict (the code around
session.execute(...) and the dict with keys
"file_id","partition","parent_id","relationship_id","depth"), detect if
row.file_metadata is a str and call json.loads(row.file_metadata) (or use {}
when null/empty) and then merge that parsed dict into the returned dict to avoid
the TypeError when unpacking a raw JSON string.

In `@openrag/components/indexer/vectordb/vectordb.py`:
- Around line 917-919: The return type annotation for update_user_quota is
incorrect: update_user_quota currently declares -> bool but delegates to
partition_file_manager.update_user_quota which returns a dict of user fields;
update the signature of update_user_quota to return a dict (e.g., -> dict or ->
dict[str, Any] and add Any import if using typing) and adjust any callers or
tests expecting a bool accordingly; reference the update_user_quota function in
vectordb and the partition_file_manager.update_user_quota implementation when
making the change.
🧹 Nitpick comments (1)
openrag/routers/users.py (1)

60-93: Quota resolution logic is duplicated between here and check_user_file_quota.

The quota precedence logic (admin → kill-switch → per-user → default fallback) in get_current_user_info (lines 66–75) is a near-copy of the same logic in check_user_file_quota (utils.py lines 202–216). Consider extracting a shared helper like resolve_user_quota(user) -> int | float to avoid the two implementations drifting apart.

Comment thread docs/content/docs/documentation/data_model.md
Comment on lines +285 to +290

# Decrement file_count for the user
user = session.query(User).filter(User.id == user_id).first()
if user and user.file_count > 0:
user.file_count = User.file_count - 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

file_count decrement is not fully guarded against going negative under concurrency.

Line 288 checks user.file_count > 0 using the Python-cached value, but the actual decrement on line 289 is SQL-level. Under concurrent deletes, two sessions could both read file_count == 1, both pass the check, and both decrement — resulting in file_count == -1. Use func.greatest for consistency with delete_partition (line 313).

Proposed fix
                     # Decrement file_count for the user
                     user = session.query(User).filter(User.id == user_id).first()
-                    if user and user.file_count > 0:
-                        user.file_count = User.file_count - 1
+                    if user:
+                        user.file_count = func.greatest(0, User.file_count - 1)
🤖 Prompt for AI Agents
In `@openrag/components/indexer/vectordb/utils.py` around lines 285 - 290, The
decrement of User.file_count is vulnerable to race conditions because it reads
the Python-cached value then writes a SQL-level value; replace the current
check-and-assign with an atomic SQL expression using func.greatest to floor at
zero (consistent with delete_partition). For example, perform an update via
session.query(User).filter(User.id == user_id).update({User.file_count:
func.greatest(User.file_count - 1, 0)}) (and ensure func is imported from
sqlalchemy) so the decrement is done server-side and cannot go negative under
concurrency.

Comment on lines +682 to +694
result = session.execute(query, {"file_id": file_id, "partition": partition})

return [
{
"file_id": row.file_id,
"partition": row.partition_name,
"parent_id": row.parent_id,
"relationship_id": row.relationship_id,
"depth": row.depth,
**(row.file_metadata or {}),
}
for row in result
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, check if json is imported in utils.py
grep -n "^import json" openrag/components/indexer/vectordb/utils.py

Repository: linagora/openrag

Length of output: 42


🏁 Script executed:

# Get the full context around lines 682-694 to see the actual code
sed -n '675,700p' openrag/components/indexer/vectordb/utils.py

Repository: linagora/openrag

Length of output: 1029


🏁 Script executed:

# Check the imports at the top of utils.py
head -50 openrag/components/indexer/vectordb/utils.py

Repository: linagora/openrag

Length of output: 1189


🏁 Script executed:

# Look at test_relationships.py line 131 to verify the claim
sed -n '125,140p' openrag/components/indexer/vectordb/tests/test_relationships.py

Repository: linagora/openrag

Length of output: 166


🏁 Script executed:

# Search for test_relationships.py
fd test_relationships.py

Repository: linagora/openrag

Length of output: 100


🏁 Script executed:

# Check if json is imported anywhere in utils.py (full file)
grep -n "json" openrag/components/indexer/vectordb/utils.py

Repository: linagora/openrag

Length of output: 42


🏁 Script executed:

# Check the query definition to see how file_metadata is constructed
sed -n '660,694p' openrag/components/indexer/vectordb/utils.py

Repository: linagora/openrag

Length of output: 1516


🏁 Script executed:

# Check test_relationships.py around line 131
sed -n '125,145p' openrag/components/test_relationships.py

Repository: linagora/openrag

Length of output: 860


🏁 Script executed:

# Also check the File model to see how file_metadata column is defined
grep -n "file_metadata" openrag/components/indexer/vectordb/utils.py | head -20

Repository: linagora/openrag

Length of output: 546


Add json import and parse file_metadata before unpacking.

When using session.execute(text(...)), SQLAlchemy doesn't infer column types, so file_metadata (a JSON column) is returned as a raw string by the database driver rather than a parsed dict. Attempting to unpack it with ** on line 691 will raise TypeError. The test file (test_relationships.py, line 131) uses the same pattern with json.loads() to handle this correctly.

Proposed fix
+import json
+
 ...
             return [
                 {
                     "file_id": row.file_id,
                     "partition": row.partition_name,
                     "parent_id": row.parent_id,
                     "relationship_id": row.relationship_id,
                     "depth": row.depth,
-                    **(row.file_metadata or {}),
+                    **(json.loads(row.file_metadata) if row.file_metadata else {}),
                 }
                 for row in result
             ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
result = session.execute(query, {"file_id": file_id, "partition": partition})
return [
{
"file_id": row.file_id,
"partition": row.partition_name,
"parent_id": row.parent_id,
"relationship_id": row.relationship_id,
"depth": row.depth,
**(row.file_metadata or {}),
}
for row in result
]
import json
result = session.execute(query, {"file_id": file_id, "partition": partition})
return [
{
"file_id": row.file_id,
"partition": row.partition_name,
"parent_id": row.parent_id,
"relationship_id": row.relationship_id,
"depth": row.depth,
**(json.loads(row.file_metadata) if row.file_metadata else {}),
}
for row in result
]
🤖 Prompt for AI Agents
In `@openrag/components/indexer/vectordb/utils.py` around lines 682 - 694, Import
the json module and parse row.file_metadata (which can be a JSON string when
using session.execute/text) before unpacking it with **; inside the list
comprehension that builds the returned dict (the code around
session.execute(...) and the dict with keys
"file_id","partition","parent_id","relationship_id","depth"), detect if
row.file_metadata is a str and call json.loads(row.file_metadata) (or use {}
when null/empty) and then merge that parsed dict into the returned dict to avoid
the TypeError when unpacking a raw JSON string.

Comment thread openrag/components/indexer/vectordb/vectordb.py Outdated
@Ahmath-Gadji
Ahmath-Gadji force-pushed the feat/add_file_quota2 branch 2 times, most recently from f32a983 to e4a777e Compare February 9, 2026 14:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
openrag/components/indexer/indexer.py (1)

164-189: ⚠️ Potential issue | 🟡 Minor

update_file_metadata still declares user: dict | None = None but delete_file requires user: dict.

Line 170 allows user to be None, but Line 183 passes it directly to self.delete_file(...) which requires a non-optional dict (Line 149). While current API callers always supply a user, the type mismatch could cause an AttributeError if this method is ever called without a user.

Proposed fix — tighten the type hint
     async def update_file_metadata(
         self,
         file_id: str,
         metadata: dict,
         partition: str,
-        user: dict | None = None,
+        user: dict,
     ):
openrag/components/indexer/vectordb/utils.py (1)

243-250: ⚠️ Potential issue | 🟠 Major

PartitionMembership created with potentially None user_id.

user_id is typed as int | None = None (line 219), but when called from vectordb.py:387 with user.get("id"), it can be None if the "id" key is missing. If the partition doesn't exist yet, line 249 creates a PartitionMembership(user_id=None). The user_id column on PartitionMembership has a NOT NULL constraint (line 133), so this will raise an IntegrityError at commit time. Either validate user_id early or guard this block.

Proposed guard
                 if not partition_obj:
                     partition_obj = Partition(partition=partition)
                     session.add(partition_obj)
                     log.info("Created new partition")
 
-                    membership = PartitionMembership(partition_name=partition, user_id=user_id, role="owner")
-                    session.add(membership)
+                    if user_id is not None:
+                        membership = PartitionMembership(partition_name=partition, user_id=user_id, role="owner")
+                        session.add(membership)
+                    else:
+                        log.warning("No user_id provided; partition created without owner membership")
🤖 Fix all issues with AI agents
In `@openrag/components/indexer/indexer.py`:
- Around line 148-157: The call to vectordb.delete_file in indexer.delete_file
passes a user_id keyword (user.get("id")) but vectordb.delete_file only accepts
(self, file_id: str, partition: str), causing a TypeError; fix by either
removing the user_id kwarg from the call in indexer.delete_file (the call site
in delete_file) or update the vectordb.delete_file method signature to accept
(file_id, partition, user_id=None) and handle or log the user_id there—pick the
approach consistent with access control expectations and update the
corresponding function (indexer.delete_file or Vectordb.delete_file)
accordingly.

In `@openrag/components/indexer/vectordb/utils.py`:
- Around line 71-80: In to_dict, avoid metadata silently overwriting explicit
columns by either spreading metadata first or filtering out reserved keys:
change the dict construction in to_dict so metadata is applied before the
explicit keys (e.g., d = {**(self.file_metadata or {}), "partition":
self.partition_name, "file_id": self.file_id, "relationship_id":
self.relationship_id, "parent_id": self.parent_id}) or filter self.file_metadata
to remove reserved keys ("partition","file_id","relationship_id","parent_id")
before merging; update the to_dict method accordingly.

In `@openrag/routers/utils.py`:
- Around line 184-187: The check_user_file_quota function currently injects an
unused dependency vectordb via Depends(get_vectordb); remove the vectordb
parameter from the function signature (leave user=Depends(current_user) intact)
so the get_vectordb dependency is not resolved unnecessarily, and then remove
any now-unused import or reference to get_vectordb if it becomes unused
elsewhere.
🧹 Nitpick comments (1)
openrag/routers/users.py (1)

9-9: Consider making task_state_manager a FastAPI dependency instead of a module-level singleton.

get_vectordb is injected via Depends() (lazy), but task_state_manager is eagerly resolved at import time. If Ray isn't initialized when this module loads (e.g., during unit tests or import-time errors), this will fail. Wrapping it in a dependency (or a lru_cache-backed getter used via Depends) would make it consistent with get_vectordb and easier to mock in tests.

Comment thread openrag/components/indexer/indexer.py
Comment on lines 71 to 80
def to_dict(self):
metadata = self.file_metadata or {}
d = {"partition": self.partition_name, "file_id": self.file_id, **metadata}
d = {
"partition": self.partition_name,
"file_id": self.file_id,
"relationship_id": self.relationship_id,
"parent_id": self.parent_id,
**metadata,
}
return d

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential key collision: file_metadata could overwrite relationship_id or parent_id.

The **metadata spread on line 78 is applied after the explicit keys on lines 76-77. If file_metadata contains a "relationship_id" or "parent_id" key, it will silently overwrite the column values. Consider placing **metadata first or filtering out reserved keys.

Proposed fix — spread metadata first so column values take precedence
     def to_dict(self):
         metadata = self.file_metadata or {}
         d = {
-            "partition": self.partition_name,
-            "file_id": self.file_id,
-            "relationship_id": self.relationship_id,
-            "parent_id": self.parent_id,
             **metadata,
+            "partition": self.partition_name,
+            "file_id": self.file_id,
+            "relationship_id": self.relationship_id,
+            "parent_id": self.parent_id,
         }
         return d
🤖 Prompt for AI Agents
In `@openrag/components/indexer/vectordb/utils.py` around lines 71 - 80, In
to_dict, avoid metadata silently overwriting explicit columns by either
spreading metadata first or filtering out reserved keys: change the dict
construction in to_dict so metadata is applied before the explicit keys (e.g., d
= {**(self.file_metadata or {}), "partition": self.partition_name, "file_id":
self.file_id, "relationship_id": self.relationship_id, "parent_id":
self.parent_id}) or filter self.file_metadata to remove reserved keys
("partition","file_id","relationship_id","parent_id") before merging; update the
to_dict method accordingly.

Comment thread openrag/routers/utils.py
Comment on lines +184 to +187
async def check_user_file_quota(
user=Depends(current_user),
vectordb=Depends(get_vectordb),
):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n openrag/routers/utils.py | sed -n '184,241p'

Repository: linagora/openrag

Length of output: 2114


Remove unused vectordb parameter.

The vectordb parameter on line 186 is injected via Depends(get_vectordb) but never used in the function body. This unnecessarily forces the dependency to be resolved on every call.

Proposed fix
 async def check_user_file_quota(
     user=Depends(current_user),
-    vectordb=Depends(get_vectordb),
 ):
🤖 Prompt for AI Agents
In `@openrag/routers/utils.py` around lines 184 - 187, The check_user_file_quota
function currently injects an unused dependency vectordb via
Depends(get_vectordb); remove the vectordb parameter from the function signature
(leave user=Depends(current_user) intact) so the get_vectordb dependency is not
resolved unnecessarily, and then remove any now-unused import or reference to
get_vectordb if it becomes unused elsewhere.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
openrag/components/indexer/vectordb/vectordb.py (1)

878-885: ⚠️ Potential issue | 🔴 Critical

delete_partition call missing required user_id argument — will raise TypeError at runtime.

PartitionFileManager.delete_partition(partition, user_id) requires user_id as a positional argument (see openrag/components/indexer/vectordb/utils.py lines 281–301), but the call on line 884 omits it. When deleting a user who owns partitions, this will crash.

🐛 Proposed fix
     async def delete_user(self, user_id: int):
         self._check_user_exists(user_id)
         user_partitions = [
             p["partition"] for p in self.partition_file_manager.list_user_partitions(user_id) if p["role"] == "owner"
         ]
         for partition in user_partitions:
-            self.partition_file_manager.delete_partition(partition)
+            self.partition_file_manager.delete_partition(partition, user_id=user_id)
         self.partition_file_manager.delete_user(user_id)
openrag/components/indexer/indexer.py (1)

164-189: ⚠️ Potential issue | 🟡 Minor

update_file_metadata passes user to delete_file, but user can be None.

user is typed dict | None = None at line 170, but delete_file at line 149 expects user: dict (non-optional). If user is None, user.get("id") inside delete_file will raise AttributeError.

Consider either making user required here or adding a guard.

🤖 Fix all issues with AI agents
In
`@openrag/scripts/migrations/alembic/versions/c224d4befe71_add_file_count_and_file_quota.py`:
- Around line 24-25: Add a data backfill step in the migration that, after
adding the file_count column (added via op.add_column with server_default="0"),
runs an UPDATE to set users.file_count from actual file counts (joining files,
partitions, partition_memberships and filtering pm.role IN ('owner','editor') or
whatever ownership logic your app uses) so existing users reflect true usage;
then remove the server_default on file_count (alter the column to drop the
default) so future inserts behave normally. Also mention check_user_file_quota
and the usage pattern user.get("file_count", 0) so reviewers know this backfill
is to keep quota enforcement consistent.

In `@tests/api_tests/test_indexer.py`:
- Around line 458-518: The test test_quota_limit_blocks_excess_uploads can fail
if quota enforcement is globally disabled (DEFAULT_FILE_QUOTA <= 0); make the
test self-contained by asserting or ensuring quota enforcement is enabled before
exercising limits: either add a guard at the top of
test_quota_limit_blocks_excess_uploads that checks the server's effective
DEFAULT_FILE_QUOTA (or an endpoint/flag) and skips/fails the test if quotas are
disabled, or explicitly set the user's quota via the PATCH /users/{id}/quota (or
use the helper _create_user_with_quota and then call the PATCH) to a positive
value and verify the server acknowledged it before uploading files; reference
the test name test_quota_limit_blocks_excess_uploads and the PATCH
/users/{id}/quota (or _create_user_with_quota) to locate where to add the guard
or quota-enforcing call.
🧹 Nitpick comments (3)
openrag/components/indexer/vectordb/vectordb.py (1)

52-62: Abstract base class signatures are out of sync with the MilvusDB overrides.

BaseVectorDB.delete_partition (line 53) and BaseVectorDB.delete_file (line 61) still lack the user_id parameter that the MilvusDB implementations now require. This breaks the Liskov substitution principle and will confuse anyone coding against the abstract interface.

♻️ Proposed fix
     `@abstractmethod`
-    async def delete_partition(self, partition: str):
+    async def delete_partition(self, partition: str, user_id: int):
         pass

     `@abstractmethod`
     def list_partition_files(self, partition: str, limit: int | None = None):
         pass

     `@abstractmethod`
-    async def delete_file(self, file_id: str, partition: str):
+    async def delete_file(self, file_id: str, partition: str, user_id: int):
         pass
docs/content/docs/documentation/data_model.md (1)

22-23: New columns documented correctly; minor formatting nit.

The file_quota and file_count column documentation matches the migration schema. However, line 23 is missing a space before the | separator after the column name (compare with other rows).

-| `file_count`| Integer (default=0) | Number of uploaded files.|
+| `file_count`   | Integer (default=0) | Number of uploaded files. |
openrag/routers/users.py (1)

60-93: Quota resolution logic is duplicated between here and check_user_file_quota.

The quota determination logic (admin → kill-switch → per-user → default fallback) on lines 66–75 is nearly identical to lines 200–215 of openrag/routers/utils.py. If the semantics evolve (e.g., new quota tiers), both must be updated in lockstep.

Consider extracting a shared helper like resolve_user_quota(user, default_file_quota) -> int | float that both sites call.

Comment on lines +24 to +25
op.add_column("users", sa.Column("file_quota", sa.Integer(), nullable=True))
op.add_column("users", sa.Column("file_count", sa.Integer(), nullable=False, server_default="0"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

file_count will be 0 for all existing users after migration — no backfill for existing files.

Existing users who already have indexed files will have file_count = 0 after this migration. Since the quota enforcement relies on file_count to track usage (indexed_count = user.get("file_count", 0) in check_user_file_quota), these users will appear to have no files and could upload beyond their actual usage until the count is reconciled.

Consider adding a data migration step that backfills file_count from the actual file counts, e.g.:

UPDATE users SET file_count = (
    SELECT COUNT(*) FROM files f
    JOIN partitions p ON f.partition_name = p.partition
    JOIN partition_memberships pm ON pm.partition_name = p.partition
    WHERE pm.user_id = users.id AND pm.role IN ('owner', 'editor')
);

The exact query depends on how file ownership is determined in your system. Without this, the quota system will be inaccurate for pre-existing users.

🤖 Prompt for AI Agents
In
`@openrag/scripts/migrations/alembic/versions/c224d4befe71_add_file_count_and_file_quota.py`
around lines 24 - 25, Add a data backfill step in the migration that, after
adding the file_count column (added via op.add_column with server_default="0"),
runs an UPDATE to set users.file_count from actual file counts (joining files,
partitions, partition_memberships and filtering pm.role IN ('owner','editor') or
whatever ownership logic your app uses) so existing users reflect true usage;
then remove the server_default on file_count (alter the column to drop the
default) so future inserts behave normally. Also mention check_user_file_quota
and the usage pattern user.get("file_count", 0) so reviewers know this backfill
is to keep quota enforcement consistent.

Comment thread tests/api_tests/test_indexer.py
@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/linagora/openrag/issues/comments/3871632238","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- This is an auto-generated comment: review paused by coderabbit.ai -->\n\n> [!NOTE]\n> ## Reviews paused\n> \n> It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the `reviews.auto_review.auto_pause_after_reviewed_commits` setting.\n> \n> Use the following commands to manage reviews:\n> - `@coderabbitai resume` to resume automatic reviews.\n> - `@coderabbitai review` to trigger a single review.\n> \n> Use the checkboxes below for quick actions:\n> - [ ] <!-- {\"checkboxId\": \"7f6cc2e2-2e4e-497a-8c31-c9e4573e93d1\"} --> ▶️ Resume reviews\n> - [ ] <!-- {\"checkboxId\": \"e9bb8d72-00e8-4f67-9cb2-caf3b22574fe\"} --> 🔍 Trigger review\n\n<!-- end of auto-generated comment: review paused by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n<details>\n<summary>📝 Walkthrough</summary>\n\n## Walkthrough\n\nAdds a per-user file quota system: DB schema changes (user.file_quota, user.file_count), quota-aware API checks on upload/copy endpoints, task-aware pending counts, vectordb/indexer call-site updates to propagate user context, and new tests and config vars to exercise quota behavior.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**CI & Environment** <br> `\\.github/workflows/api_tests.yml`, `\\.github/workflows/api_tests/docker-compose.yaml`|Added `OPENRAG_ADMIN_TOKEN` to CI and `AUTH_TOKEN`/`DEFAULT_FILE_QUOTA` to docker-compose for tests/runtime.|\n|**Config** <br> `\\.hydra_config/config.yaml`|Bound `rdb.default_file_quota` to `DEFAULT_FILE_QUOTA` env var (default -1).|\n|**DB Migration** <br> `openrag/scripts/migrations/alembic/versions/c224d4befe71_add_file_count_and_file_quota.py`|New alembic migration adding `file_quota` (nullable) and `file_count` (default 0) to `users` table.|\n|**Vectordb / Models / Utils** <br> `openrag/components/indexer/vectordb/utils.py`, `openrag/components/indexer/vectordb/vectordb.py`|User model extended with `file_quota` and `file_count`; DEFAULT_FILE_QUOTA applied; APIs updated to accept/return `file_quota` and `file_count`; file add/remove/partition-delete operations update user counts; new `update_user_quota` and `get_user_file_count`.|\n|**Indexer Logic** <br> `openrag/components/indexer/indexer.py`|Propagates user context to file deletion/add flows; added `get_user_pending_task_count` RPC to expose pending task counts by user.|\n|**API Routes & Utils** <br> `openrag/routers/indexer.py`, `openrag/routers/partition.py`, `openrag/routers/users.py`, `openrag/routers/utils.py`|Wired `check_user_file_quota` dependency into upload/copy endpoints; added admin PATCH `/users/{id}/quota`; `get_current_user` augmented with indexed/pending/total counts and quota info; `check_user_file_quota` implements quota logic using DEFAULT_FILE_QUOTA and pending task count.|\n|**Docs** <br> `docs/content/docs/documentation/data_model.md`, `docs/content/docs/documentation/env_vars.md`|Documented `file_quota` and `file_count` and `DEFAULT_FILE_QUOTA` semantics (≤0 disables quotas, >0 sets default per-user quota).|\n|**Tests** <br> `tests/api_tests/conftest.py`, `tests/api_tests/test_indexer.py`, `tests/api_tests/test_users.py`|Added `OPENRAG_ADMIN_TOKEN` for test auth; expanded tests to cover quota enforcement, file_count increments/decrements, and admin quota updates; test helpers adjusted to pass auth headers and poll task status with headers.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant Router as Indexer Router\n    participant Validator as Quota Validator\n    participant VectorDB as VectorDB\n    participant TaskMgr as Task Manager\n    participant DB as Database\n\n    Client->>Router: POST /add_file (user token)\n    Router->>Validator: check_user_file_quota(user)\n    Validator->>VectorDB: get_user_by_token(user_token)\n    VectorDB->>DB: Query User (file_quota, file_count)\n    DB-->>VectorDB: User data\n    Validator->>TaskMgr: get_user_pending_task_count(user_id)\n    TaskMgr-->>Validator: pending_count\n    Validator->>Validator: total = file_count + pending_count\n    alt total >= user_quota\n        Validator-->>Router: HTTP 403 Quota Exceeded\n        Router-->>Client: 403 Error\n    else within quota\n        Validator-->>Router: approved\n        Router->>VectorDB: add_file_to_partition(file_id, user_id)\n        VectorDB->>DB: Insert File, Increment User.file_count\n        DB-->>VectorDB: Success\n        VectorDB-->>Router: File added / task queued\n        Router-->>Client: Task queued response\n    end\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~60 minutes\n\n## Suggested reviewers\n\n- paultranvan\n\n## Poem\n\n> 🐰 I hopped through rows of code and quoth with cheer,  \n> > Files now counted, quotas held near,  \n> > Admins keep keys, listeners hum with delight,  \n> > Pending tasks dance in the soft moonlight,  \n> > A carrot-sized celebration — hop on, all is right! 🥕\n\n</details>\n\n<!-- walkthrough_end -->\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 1 | ❌ 2</summary>\n\n<details>\n<summary>❌ Failed checks (1 warning, 1 inconclusive)</summary>\n\n|     Check name     | Status         | Explanation                                                                                                                                                               | Resolution                                                                                                                                                                                          |\n| :----------------: | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning     | Docstring coverage is 70.59% which is insufficient. The required threshold is 80.00%.                                                                                     | Write docstrings for the functions missing them to satisfy the coverage threshold.                                                                                                                  |\n|     Title check    | ❓ Inconclusive | The title 'Feat/add file quota2' is vague and generic; it uses a non-descriptive format with a trailing '2' that lacks context about the feature's scope or significance. | Clarify the title to describe the main feature more specifically, e.g., 'Add file quota enforcement with per-user limits and admin controls' or 'Implement file upload quotas with default limits'. |\n\n</details>\n<details>\n<summary>✅ Passed checks (1 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                 |\n| :---------------: | :------- | :---------------------------------------------------------- |\n| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing touches</summary>\n\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"07f1e7d6-8a8e-4e23-9900-8731c2c87f58\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Post copyable unit tests in a comment\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Commit unit tests in branch `feat/add_file_quota2`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=linagora/openrag&utm_content=233)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/linagora/openrag/issues/comments/3871632238","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:28:21 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"8061:13E1C5:E5D26E:3DD0310:698A0B24","x-ratelimit-limit":"10750","x-ratelimit-remaining":"10638","x-ratelimit-reset":"1770655968","x-ratelimit-resource":"core","x-ratelimit-used":"112","x-xss-protection":"0"},"data":""}}

3 similar comments
@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/linagora/openrag/issues/comments/3871632238","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- This is an auto-generated comment: review paused by coderabbit.ai -->\n\n> [!NOTE]\n> ## Reviews paused\n> \n> It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the `reviews.auto_review.auto_pause_after_reviewed_commits` setting.\n> \n> Use the following commands to manage reviews:\n> - `@coderabbitai resume` to resume automatic reviews.\n> - `@coderabbitai review` to trigger a single review.\n> \n> Use the checkboxes below for quick actions:\n> - [ ] <!-- {\"checkboxId\": \"7f6cc2e2-2e4e-497a-8c31-c9e4573e93d1\"} --> ▶️ Resume reviews\n> - [ ] <!-- {\"checkboxId\": \"e9bb8d72-00e8-4f67-9cb2-caf3b22574fe\"} --> 🔍 Trigger review\n\n<!-- end of auto-generated comment: review paused by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n<details>\n<summary>📝 Walkthrough</summary>\n\n## Walkthrough\n\nAdds a per-user file quota system: DB schema changes (user.file_quota, user.file_count), quota-aware API checks on upload/copy endpoints, task-aware pending counts, vectordb/indexer call-site updates to propagate user context, and new tests and config vars to exercise quota behavior.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**CI & Environment** <br> `\\.github/workflows/api_tests.yml`, `\\.github/workflows/api_tests/docker-compose.yaml`|Added `OPENRAG_ADMIN_TOKEN` to CI and `AUTH_TOKEN`/`DEFAULT_FILE_QUOTA` to docker-compose for tests/runtime.|\n|**Config** <br> `\\.hydra_config/config.yaml`|Bound `rdb.default_file_quota` to `DEFAULT_FILE_QUOTA` env var (default -1).|\n|**DB Migration** <br> `openrag/scripts/migrations/alembic/versions/c224d4befe71_add_file_count_and_file_quota.py`|New alembic migration adding `file_quota` (nullable) and `file_count` (default 0) to `users` table.|\n|**Vectordb / Models / Utils** <br> `openrag/components/indexer/vectordb/utils.py`, `openrag/components/indexer/vectordb/vectordb.py`|User model extended with `file_quota` and `file_count`; DEFAULT_FILE_QUOTA applied; APIs updated to accept/return `file_quota` and `file_count`; file add/remove/partition-delete operations update user counts; new `update_user_quota` and `get_user_file_count`.|\n|**Indexer Logic** <br> `openrag/components/indexer/indexer.py`|Propagates user context to file deletion/add flows; added `get_user_pending_task_count` RPC to expose pending task counts by user.|\n|**API Routes & Utils** <br> `openrag/routers/indexer.py`, `openrag/routers/partition.py`, `openrag/routers/users.py`, `openrag/routers/utils.py`|Wired `check_user_file_quota` dependency into upload/copy endpoints; added admin PATCH `/users/{id}/quota`; `get_current_user` augmented with indexed/pending/total counts and quota info; `check_user_file_quota` implements quota logic using DEFAULT_FILE_QUOTA and pending task count.|\n|**Docs** <br> `docs/content/docs/documentation/data_model.md`, `docs/content/docs/documentation/env_vars.md`|Documented `file_quota` and `file_count` and `DEFAULT_FILE_QUOTA` semantics (≤0 disables quotas, >0 sets default per-user quota).|\n|**Tests** <br> `tests/api_tests/conftest.py`, `tests/api_tests/test_indexer.py`, `tests/api_tests/test_users.py`|Added `OPENRAG_ADMIN_TOKEN` for test auth; expanded tests to cover quota enforcement, file_count increments/decrements, and admin quota updates; test helpers adjusted to pass auth headers and poll task status with headers.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant Router as Indexer Router\n    participant Validator as Quota Validator\n    participant VectorDB as VectorDB\n    participant TaskMgr as Task Manager\n    participant DB as Database\n\n    Client->>Router: POST /add_file (user token)\n    Router->>Validator: check_user_file_quota(user)\n    Validator->>VectorDB: get_user_by_token(user_token)\n    VectorDB->>DB: Query User (file_quota, file_count)\n    DB-->>VectorDB: User data\n    Validator->>TaskMgr: get_user_pending_task_count(user_id)\n    TaskMgr-->>Validator: pending_count\n    Validator->>Validator: total = file_count + pending_count\n    alt total >= user_quota\n        Validator-->>Router: HTTP 403 Quota Exceeded\n        Router-->>Client: 403 Error\n    else within quota\n        Validator-->>Router: approved\n        Router->>VectorDB: add_file_to_partition(file_id, user_id)\n        VectorDB->>DB: Insert File, Increment User.file_count\n        DB-->>VectorDB: Success\n        VectorDB-->>Router: File added / task queued\n        Router-->>Client: Task queued response\n    end\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~60 minutes\n\n## Suggested reviewers\n\n- paultranvan\n\n## Poem\n\n> 🐰 I hopped through rows of code and quoth with cheer,  \n> > Files now counted, quotas held near,  \n> > Admins keep keys, listeners hum with delight,  \n> > Pending tasks dance in the soft moonlight,  \n> > A carrot-sized celebration — hop on, all is right! 🥕\n\n</details>\n\n<!-- walkthrough_end -->\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 1 | ❌ 2</summary>\n\n<details>\n<summary>❌ Failed checks (1 warning, 1 inconclusive)</summary>\n\n|     Check name     | Status         | Explanation                                                                                                                                                               | Resolution                                                                                                                                                                                          |\n| :----------------: | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning     | Docstring coverage is 70.59% which is insufficient. The required threshold is 80.00%.                                                                                     | Write docstrings for the functions missing them to satisfy the coverage threshold.                                                                                                                  |\n|     Title check    | ❓ Inconclusive | The title 'Feat/add file quota2' is vague and generic; it uses a non-descriptive format with a trailing '2' that lacks context about the feature's scope or significance. | Clarify the title to describe the main feature more specifically, e.g., 'Add file quota enforcement with per-user limits and admin controls' or 'Implement file upload quotas with default limits'. |\n\n</details>\n<details>\n<summary>✅ Passed checks (1 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                 |\n| :---------------: | :------- | :---------------------------------------------------------- |\n| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing touches</summary>\n\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"07f1e7d6-8a8e-4e23-9900-8731c2c87f58\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Post copyable unit tests in a comment\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Commit unit tests in branch `feat/add_file_quota2`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=linagora/openrag&utm_content=233)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/linagora/openrag/issues/comments/3871632238","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:28:21 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"8061:13E1C5:E5D26E:3DD0310:698A0B24","x-ratelimit-limit":"10750","x-ratelimit-remaining":"10638","x-ratelimit-reset":"1770655968","x-ratelimit-resource":"core","x-ratelimit-used":"112","x-xss-protection":"0"},"data":""}}

@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/linagora/openrag/issues/comments/3871632238","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- This is an auto-generated comment: review paused by coderabbit.ai -->\n\n> [!NOTE]\n> ## Reviews paused\n> \n> It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the `reviews.auto_review.auto_pause_after_reviewed_commits` setting.\n> \n> Use the following commands to manage reviews:\n> - `@coderabbitai resume` to resume automatic reviews.\n> - `@coderabbitai review` to trigger a single review.\n> \n> Use the checkboxes below for quick actions:\n> - [ ] <!-- {\"checkboxId\": \"7f6cc2e2-2e4e-497a-8c31-c9e4573e93d1\"} --> ▶️ Resume reviews\n> - [ ] <!-- {\"checkboxId\": \"e9bb8d72-00e8-4f67-9cb2-caf3b22574fe\"} --> 🔍 Trigger review\n\n<!-- end of auto-generated comment: review paused by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n<details>\n<summary>📝 Walkthrough</summary>\n\n## Walkthrough\n\nAdds a per-user file quota system: DB schema changes (user.file_quota, user.file_count), quota-aware API checks on upload/copy endpoints, task-aware pending counts, vectordb/indexer call-site updates to propagate user context, and new tests and config vars to exercise quota behavior.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**CI & Environment** <br> `\\.github/workflows/api_tests.yml`, `\\.github/workflows/api_tests/docker-compose.yaml`|Added `OPENRAG_ADMIN_TOKEN` to CI and `AUTH_TOKEN`/`DEFAULT_FILE_QUOTA` to docker-compose for tests/runtime.|\n|**Config** <br> `\\.hydra_config/config.yaml`|Bound `rdb.default_file_quota` to `DEFAULT_FILE_QUOTA` env var (default -1).|\n|**DB Migration** <br> `openrag/scripts/migrations/alembic/versions/c224d4befe71_add_file_count_and_file_quota.py`|New alembic migration adding `file_quota` (nullable) and `file_count` (default 0) to `users` table.|\n|**Vectordb / Models / Utils** <br> `openrag/components/indexer/vectordb/utils.py`, `openrag/components/indexer/vectordb/vectordb.py`|User model extended with `file_quota` and `file_count`; DEFAULT_FILE_QUOTA applied; APIs updated to accept/return `file_quota` and `file_count`; file add/remove/partition-delete operations update user counts; new `update_user_quota` and `get_user_file_count`.|\n|**Indexer Logic** <br> `openrag/components/indexer/indexer.py`|Propagates user context to file deletion/add flows; added `get_user_pending_task_count` RPC to expose pending task counts by user.|\n|**API Routes & Utils** <br> `openrag/routers/indexer.py`, `openrag/routers/partition.py`, `openrag/routers/users.py`, `openrag/routers/utils.py`|Wired `check_user_file_quota` dependency into upload/copy endpoints; added admin PATCH `/users/{id}/quota`; `get_current_user` augmented with indexed/pending/total counts and quota info; `check_user_file_quota` implements quota logic using DEFAULT_FILE_QUOTA and pending task count.|\n|**Docs** <br> `docs/content/docs/documentation/data_model.md`, `docs/content/docs/documentation/env_vars.md`|Documented `file_quota` and `file_count` and `DEFAULT_FILE_QUOTA` semantics (≤0 disables quotas, >0 sets default per-user quota).|\n|**Tests** <br> `tests/api_tests/conftest.py`, `tests/api_tests/test_indexer.py`, `tests/api_tests/test_users.py`|Added `OPENRAG_ADMIN_TOKEN` for test auth; expanded tests to cover quota enforcement, file_count increments/decrements, and admin quota updates; test helpers adjusted to pass auth headers and poll task status with headers.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant Router as Indexer Router\n    participant Validator as Quota Validator\n    participant VectorDB as VectorDB\n    participant TaskMgr as Task Manager\n    participant DB as Database\n\n    Client->>Router: POST /add_file (user token)\n    Router->>Validator: check_user_file_quota(user)\n    Validator->>VectorDB: get_user_by_token(user_token)\n    VectorDB->>DB: Query User (file_quota, file_count)\n    DB-->>VectorDB: User data\n    Validator->>TaskMgr: get_user_pending_task_count(user_id)\n    TaskMgr-->>Validator: pending_count\n    Validator->>Validator: total = file_count + pending_count\n    alt total >= user_quota\n        Validator-->>Router: HTTP 403 Quota Exceeded\n        Router-->>Client: 403 Error\n    else within quota\n        Validator-->>Router: approved\n        Router->>VectorDB: add_file_to_partition(file_id, user_id)\n        VectorDB->>DB: Insert File, Increment User.file_count\n        DB-->>VectorDB: Success\n        VectorDB-->>Router: File added / task queued\n        Router-->>Client: Task queued response\n    end\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~60 minutes\n\n## Suggested reviewers\n\n- paultranvan\n\n## Poem\n\n> 🐰 I hopped through rows of code and quoth with cheer,  \n> > Files now counted, quotas held near,  \n> > Admins keep keys, listeners hum with delight,  \n> > Pending tasks dance in the soft moonlight,  \n> > A carrot-sized celebration — hop on, all is right! 🥕\n\n</details>\n\n<!-- walkthrough_end -->\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 1 | ❌ 2</summary>\n\n<details>\n<summary>❌ Failed checks (1 warning, 1 inconclusive)</summary>\n\n|     Check name     | Status         | Explanation                                                                                                                                                               | Resolution                                                                                                                                                                                          |\n| :----------------: | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning     | Docstring coverage is 70.59% which is insufficient. The required threshold is 80.00%.                                                                                     | Write docstrings for the functions missing them to satisfy the coverage threshold.                                                                                                                  |\n|     Title check    | ❓ Inconclusive | The title 'Feat/add file quota2' is vague and generic; it uses a non-descriptive format with a trailing '2' that lacks context about the feature's scope or significance. | Clarify the title to describe the main feature more specifically, e.g., 'Add file quota enforcement with per-user limits and admin controls' or 'Implement file upload quotas with default limits'. |\n\n</details>\n<details>\n<summary>✅ Passed checks (1 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                 |\n| :---------------: | :------- | :---------------------------------------------------------- |\n| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing touches</summary>\n\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"07f1e7d6-8a8e-4e23-9900-8731c2c87f58\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Post copyable unit tests in a comment\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Commit unit tests in branch `feat/add_file_quota2`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=linagora/openrag&utm_content=233)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/linagora/openrag/issues/comments/3871632238","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:28:21 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"8061:13E1C5:E5D26E:3DD0310:698A0B24","x-ratelimit-limit":"10750","x-ratelimit-remaining":"10638","x-ratelimit-reset":"1770655968","x-ratelimit-resource":"core","x-ratelimit-used":"112","x-xss-protection":"0"},"data":""}}

@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/linagora/openrag/issues/comments/3871632238","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- This is an auto-generated comment: review paused by coderabbit.ai -->\n\n> [!NOTE]\n> ## Reviews paused\n> \n> It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the `reviews.auto_review.auto_pause_after_reviewed_commits` setting.\n> \n> Use the following commands to manage reviews:\n> - `@coderabbitai resume` to resume automatic reviews.\n> - `@coderabbitai review` to trigger a single review.\n> \n> Use the checkboxes below for quick actions:\n> - [ ] <!-- {\"checkboxId\": \"7f6cc2e2-2e4e-497a-8c31-c9e4573e93d1\"} --> ▶️ Resume reviews\n> - [ ] <!-- {\"checkboxId\": \"e9bb8d72-00e8-4f67-9cb2-caf3b22574fe\"} --> 🔍 Trigger review\n\n<!-- end of auto-generated comment: review paused by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n<details>\n<summary>📝 Walkthrough</summary>\n\n## Walkthrough\n\nAdds a per-user file quota system: DB schema changes (user.file_quota, user.file_count), quota-aware API checks on upload/copy endpoints, task-aware pending counts, vectordb/indexer call-site updates to propagate user context, and new tests and config vars to exercise quota behavior.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**CI & Environment** <br> `\\.github/workflows/api_tests.yml`, `\\.github/workflows/api_tests/docker-compose.yaml`|Added `OPENRAG_ADMIN_TOKEN` to CI and `AUTH_TOKEN`/`DEFAULT_FILE_QUOTA` to docker-compose for tests/runtime.|\n|**Config** <br> `\\.hydra_config/config.yaml`|Bound `rdb.default_file_quota` to `DEFAULT_FILE_QUOTA` env var (default -1).|\n|**DB Migration** <br> `openrag/scripts/migrations/alembic/versions/c224d4befe71_add_file_count_and_file_quota.py`|New alembic migration adding `file_quota` (nullable) and `file_count` (default 0) to `users` table.|\n|**Vectordb / Models / Utils** <br> `openrag/components/indexer/vectordb/utils.py`, `openrag/components/indexer/vectordb/vectordb.py`|User model extended with `file_quota` and `file_count`; DEFAULT_FILE_QUOTA applied; APIs updated to accept/return `file_quota` and `file_count`; file add/remove/partition-delete operations update user counts; new `update_user_quota` and `get_user_file_count`.|\n|**Indexer Logic** <br> `openrag/components/indexer/indexer.py`|Propagates user context to file deletion/add flows; added `get_user_pending_task_count` RPC to expose pending task counts by user.|\n|**API Routes & Utils** <br> `openrag/routers/indexer.py`, `openrag/routers/partition.py`, `openrag/routers/users.py`, `openrag/routers/utils.py`|Wired `check_user_file_quota` dependency into upload/copy endpoints; added admin PATCH `/users/{id}/quota`; `get_current_user` augmented with indexed/pending/total counts and quota info; `check_user_file_quota` implements quota logic using DEFAULT_FILE_QUOTA and pending task count.|\n|**Docs** <br> `docs/content/docs/documentation/data_model.md`, `docs/content/docs/documentation/env_vars.md`|Documented `file_quota` and `file_count` and `DEFAULT_FILE_QUOTA` semantics (≤0 disables quotas, >0 sets default per-user quota).|\n|**Tests** <br> `tests/api_tests/conftest.py`, `tests/api_tests/test_indexer.py`, `tests/api_tests/test_users.py`|Added `OPENRAG_ADMIN_TOKEN` for test auth; expanded tests to cover quota enforcement, file_count increments/decrements, and admin quota updates; test helpers adjusted to pass auth headers and poll task status with headers.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    participant Client\n    participant Router as Indexer Router\n    participant Validator as Quota Validator\n    participant VectorDB as VectorDB\n    participant TaskMgr as Task Manager\n    participant DB as Database\n\n    Client->>Router: POST /add_file (user token)\n    Router->>Validator: check_user_file_quota(user)\n    Validator->>VectorDB: get_user_by_token(user_token)\n    VectorDB->>DB: Query User (file_quota, file_count)\n    DB-->>VectorDB: User data\n    Validator->>TaskMgr: get_user_pending_task_count(user_id)\n    TaskMgr-->>Validator: pending_count\n    Validator->>Validator: total = file_count + pending_count\n    alt total >= user_quota\n        Validator-->>Router: HTTP 403 Quota Exceeded\n        Router-->>Client: 403 Error\n    else within quota\n        Validator-->>Router: approved\n        Router->>VectorDB: add_file_to_partition(file_id, user_id)\n        VectorDB->>DB: Insert File, Increment User.file_count\n        DB-->>VectorDB: Success\n        VectorDB-->>Router: File added / task queued\n        Router-->>Client: Task queued response\n    end\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~60 minutes\n\n## Suggested reviewers\n\n- paultranvan\n\n## Poem\n\n> 🐰 I hopped through rows of code and quoth with cheer,  \n> > Files now counted, quotas held near,  \n> > Admins keep keys, listeners hum with delight,  \n> > Pending tasks dance in the soft moonlight,  \n> > A carrot-sized celebration — hop on, all is right! 🥕\n\n</details>\n\n<!-- walkthrough_end -->\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 1 | ❌ 2</summary>\n\n<details>\n<summary>❌ Failed checks (1 warning, 1 inconclusive)</summary>\n\n|     Check name     | Status         | Explanation                                                                                                                                                               | Resolution                                                                                                                                                                                          |\n| :----------------: | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning     | Docstring coverage is 70.59% which is insufficient. The required threshold is 80.00%.                                                                                     | Write docstrings for the functions missing them to satisfy the coverage threshold.                                                                                                                  |\n|     Title check    | ❓ Inconclusive | The title 'Feat/add file quota2' is vague and generic; it uses a non-descriptive format with a trailing '2' that lacks context about the feature's scope or significance. | Clarify the title to describe the main feature more specifically, e.g., 'Add file quota enforcement with per-user limits and admin controls' or 'Implement file upload quotas with default limits'. |\n\n</details>\n<details>\n<summary>✅ Passed checks (1 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                 |\n| :---------------: | :------- | :---------------------------------------------------------- |\n| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing touches</summary>\n\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"07f1e7d6-8a8e-4e23-9900-8731c2c87f58\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Post copyable unit tests in a comment\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Commit unit tests in branch `feat/add_file_quota2`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=linagora/openrag&utm_content=233)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/linagora/openrag/issues/comments/3871632238","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:28:21 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"8061:13E1C5:E5D26E:3DD0310:698A0B24","x-ratelimit-limit":"10750","x-ratelimit-remaining":"10638","x-ratelimit-reset":"1770655968","x-ratelimit-resource":"core","x-ratelimit-used":"112","x-xss-protection":"0"},"data":""}}

requires database migration to add file_quota column to users table.

Features:
- Add file_quota field to users table (nullable integer)
- Add DEFAULT_FILE_QUOTA env var to set global default quota
- Add PATCH /users/{user_id}/quota endpoint to update user quotas
- Add quota enforcement on file upload (indexed files + pending tasks)
- Admins bypass quota checks; quota<=0 for a user means unlimited; quota > 0 to limit user's quota

API Changes:
- POST /users/ now accepts optional file_quota parameter
- GET /users/ and GET /users/{id} return file_quota in response
- New PATCH /users/{user_id}/quota endpoint for quota management
- GET /users/info now renders additional fields: file_count, pending_files, total_files and file_quota

Tests:
- test_update_user_quota: verify quota update from 10 to 12
- test_user_default_quota: verify None quota defaults to 10
- TestUserQuotaEnforcement class in test_indexer.py:
  - test_unlimited_quota_user_can_exceed_default: user with quota=0 uploads 11 files
  - test_quota_limit_blocks_excess_uploads: user with quota=5 blocked on 6th file

Perform migration: Refer to the doc
https://github.com/linagora/openrag/blob/0b5f84cd880e7c75db6f78d12a7681227721c2ad/docs/content/docs/documentation/sql_migration.mdx?plain=1#L47-L53
@Ahmath-Gadji
Ahmath-Gadji force-pushed the feat/add_file_quota2 branch 3 times, most recently from d75b5fe to b46a7af Compare February 9, 2026 17:25
@Ahmath-Gadji
Ahmath-Gadji deleted the feat/add_file_quota2 branch February 12, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat Add a new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants