Skip to content

fix(collections): Use correct file batch ID - #1073

Open
vprashrex wants to merge 12 commits into
mainfrom
fix/openai-vector-store-batch-id
Open

fix(collections): Use correct file batch ID#1073
vprashrex wants to merge 12 commits into
mainfrom
fix/openai-vector-store-batch-id

Conversation

@vprashrex

@vprashrex vprashrex commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Issue

Closes #1076

Summary

  • Before: The previous implementation did not use the file batch ID from the create() function when listing failed files.
  • Now: The new implementation utilizes the correct file batch ID for better accuracy in listing failed files.
  • Adjustments were made to the function responsible for listing failed files.
  • Integration of the file batch ID into the previous code structure.

Checklist

Before submitting a pull request, please ensure that you mark these task.

  • Ran fastapi run --reload app/main.py or docker compose up in the repository root and test.
  • If you've fixed a bug or added code that is tested and has test cases.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

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

OpenAI ingestion now separates batch creation from polling and uses the creation batch ID for failed-file lookup. Collection batches retry timeouts, uploads validate content, and XLS/XLSX formats are registered.

Changes

Vector batch ingestion

Layer / File(s) Summary
Separate batch creation and polling
backend/app/crud/rag/open_ai.py, backend/app/services/collections/providers/registry.py
Ingestion captures the ID from create(), polls with it, validates terminal status, retrieves failed files by that ID, and configures OpenAI retries and timeouts.
Batch flow and error-path tests
backend/app/tests/crud/rag/test_open_ai.py, backend/app/tests/services/collections/providers/test_openai_provider.py
Tests distinguish creation and polling IDs, verify arguments and terminal failures, and raise OpenAI exceptions from create().
Shared mocks and ingestion notes
backend/app/tests/utils/*, docs/wiki/modules/knowledge-base.md
Mocks and documentation reflect explicit batch creation, retrieval polling, and timeout behavior.

Document upload validation

Layer / File(s) Summary
Content validation engine
backend/app/services/documents/helpers.py, backend/app/tests/services/documents/test_helpers.py
Adds OLE2 stream discrimination and expanded content validation with client-safe errors and tests.
Upload route and spreadsheet format wiring
backend/app/api/routes/documents.py, backend/app/services/doctransform/registry.py
The upload route invokes validate_upload, and format mappings include .xls and .xlsx.

Collection batch retries

Layer / File(s) Summary
Timeout retry handling
backend/app/services/collections/create_collection.py
Timeouts use bounded Celery retries, preserve vector stores during retries, and deduplicate uploaded document IDs.
Retry behavior coverage
backend/app/tests/services/collections/test_create_collection.py
Tests cover queued retries, scheduling failures, unlimited retry configuration, and exhausted attempts.
Batched execution documentation
docs/architecture/kaapi-knowledge-base-ARCHITECTURE.md
Documentation describes per-batch uploads, retry behavior, cleanup, polling, and vector-store reuse.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant execute_batch_job
  participant OpenAIProvider
  participant OpenAIVectorStoreCrud
  participant OpenAI
  execute_batch_job->>OpenAIProvider: upload files and create batch
  OpenAIProvider->>OpenAIVectorStoreCrud: update(vector_store_id, file_ids)
  OpenAIVectorStoreCrud->>OpenAI: create file batch
  OpenAI-->>OpenAIVectorStoreCrud: batch_id
  OpenAIVectorStoreCrud->>OpenAI: retrieve batch until terminal
  OpenAI-->>OpenAIVectorStoreCrud: status and file counts
Loading

Possibly related PRs

Suggested labels: enhancement, released on @main``

Suggested reviewers: nishika26, prajna1999, kartpop

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes substantial unrelated changes in docs, document validation, and collection retry logic beyond the batch-ID fix. Split unrelated documentation, validation, and retry changes into separate PRs so this one stays focused on the file batch ID fix.
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes use the batch ID from create() when listing failed files, matching issue #1076.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: using the correct file batch ID in collection batch handling.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/openai-vector-store-batch-id

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.

@github-actions github-actions Bot changed the title fix(collections): Use file batch ID from create() when listing failed files fix(collections): Use correct file batch ID Jul 23, 2026
@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

OpenAPI changes   ⚪ No API surface changes

Note

This PR does not modify the API contract.

mainb1e7f8a6 · generated by oasdiff

@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.79381% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...end/app/services/collections/providers/registry.py 50.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@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.

🧹 Nitpick comments (2)
backend/app/tests/crud/rag/test_open_ai.py (2)

72-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the self-evident comment.

assert_not_called() already expresses this expectation; retain comments only when they explain rationale. As per coding guidelines, “Write comments and docstrings to explain why something is done, not what the code already shows.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/tests/crud/rag/test_open_ai.py` at line 72, Remove the
self-evident “list_files should not have been called on the happy path” comment
near the assert_not_called() expectation in the test, leaving the assertion
unchanged.

Source: Coding guidelines


44-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add required type annotations to changed test signatures.

Annotate non-self fixture parameters and return values consistently.

  • backend/app/tests/crud/rag/test_open_ai.py#L44-L44: annotate mock_client: MagicMock.
  • backend/app/tests/crud/rag/test_open_ai.py#L75-L75: annotate injected fixtures and add -> None.
  • backend/app/tests/crud/rag/test_open_ai.py#L106-L108: annotate injected fixtures and add -> None.
  • backend/app/tests/crud/rag/test_open_ai.py#L122-L124: annotate injected fixtures and add -> None.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/tests/crud/rag/test_open_ai.py` at line 44, Add the requested
annotations in backend/app/tests/crud/rag/test_open_ai.py: type _wire_batch’s
mock_client parameter as MagicMock, and annotate all non-self injected fixture
parameters plus return values with -> None in the test functions at lines 75,
106-108, and 122-124. Apply the annotations consistently across all four
affected sites.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@backend/app/tests/crud/rag/test_open_ai.py`:
- Line 72: Remove the self-evident “list_files should not have been called on
the happy path” comment near the assert_not_called() expectation in the test,
leaving the assertion unchanged.
- Line 44: Add the requested annotations in
backend/app/tests/crud/rag/test_open_ai.py: type _wire_batch’s mock_client
parameter as MagicMock, and annotate all non-self injected fixture parameters
plus return values with -> None in the test functions at lines 75, 106-108, and
122-124. Apply the annotations consistently across all four affected sites.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 79f5c90e-9ea2-43f6-8dc9-ff715cb54c65

📥 Commits

Reviewing files that changed from the base of the PR and between 49fd4ad and 6ca053f.

📒 Files selected for processing (6)
  • backend/app/crud/rag/open_ai.py
  • backend/app/tests/crud/rag/test_open_ai.py
  • backend/app/tests/services/collections/providers/test_openai_provider.py
  • backend/app/tests/utils/llm_provider.py
  • backend/app/tests/utils/openai.py
  • docs/wiki/modules/knowledge-base.md

@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.

🧹 Nitpick comments (2)
backend/app/services/documents/helpers.py (2)

1-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Modernize deprecated typing imports.

Callable/Iterable should come from collections.abc, and Tuple is deprecated in favor of builtin tuple (PEP 585). Ruff flags both (UP035).

♻️ Proposed fix
-from typing import Callable, Optional, Tuple, Iterable, Union
+from collections.abc import Callable, Iterable
+from typing import Optional, Union

Note: other Tuple[...] usages later in the file (e.g. _read_edges, validate_document_content... wait, that one already uses lowercase Tuple return) should be updated to tuple[...] for consistency once the import changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/services/documents/helpers.py` around lines 1 - 6, Update the
imports in this module so Callable and Iterable come from collections.abc,
remove Tuple from typing, and replace every Tuple[...] annotation in the file
with the builtin tuple[...] syntax; preserve Optional and Union unless
independently required.

Source: Linters/SAST tools


53-57: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Duplicated magic byte signatures between MISLABELLED_BINARY_SIGNATURES and FORMAT_SPECS; "doc" has no FormatSpec.

b"PK\x03\x04" and b"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1" are inlined in both structures. Extracting them (and the zip-signature triple used for both docx and xlsx) into named constants would remove the duplication.

Separately, "doc" is absent from FORMAT_SPECS even though it shares the OLE2 signature documented for "xls" (comment at line 246 explicitly calls out ".doc"), and registry.py maps .doc"doc". As written, .doc uploads get zero content sniffing while .xls uploads (same binary container format) do.

♻️ Proposed fix
+OLE2_SIGNATURE = b"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1"
+ZIP_SIGNATURES = (b"PK\x03\x04", b"PK\x05\x06", b"PK\x07\x08")
+
 MISLABELLED_BINARY_SIGNATURES: dict[bytes, str] = {
-    b"PK\x03\x04": "an Office/zip file (xlsx, docx)",
-    b"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1": "a legacy Office file (xls, doc)",
+    ZIP_SIGNATURES[0]: "an Office/zip file (xlsx, docx)",
+    OLE2_SIGNATURE: "a legacy Office file (xls, doc)",
     b"%PDF-": "a PDF",
 }
...
     "docx": FormatSpec(
-        signatures=(b"PK\x03\x04", b"PK\x05\x06", b"PK\x07\x08"), checker=_check_docx
+        signatures=ZIP_SIGNATURES, checker=_check_docx
     ),
     "xlsx": FormatSpec(
-        signatures=(b"PK\x03\x04", b"PK\x05\x06", b"PK\x07\x08"), checker=_check_xlsx
+        signatures=ZIP_SIGNATURES, checker=_check_xlsx
     ),
-    "xls": FormatSpec(signatures=(b"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1",)),
+    "xls": FormatSpec(signatures=(OLE2_SIGNATURE,)),
+    "doc": FormatSpec(signatures=(OLE2_SIGNATURE,)),

Also applies to: 237-250

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/services/documents/helpers.py` around lines 53 - 57, Extract the
shared PK/zip and OLE2 byte signatures into named constants, then reuse them in
MISLABELLED_BINARY_SIGNATURES and the relevant FORMAT_SPECS entries, including
the existing zip-signature triple for docx and xlsx. Add a “doc” FORMAT_SPECS
entry matching the “xls” OLE2 signature and appropriate shared sniffing behavior
so .doc uploads are handled like .xls.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@backend/app/services/documents/helpers.py`:
- Around line 1-6: Update the imports in this module so Callable and Iterable
come from collections.abc, remove Tuple from typing, and replace every
Tuple[...] annotation in the file with the builtin tuple[...] syntax; preserve
Optional and Union unless independently required.
- Around line 53-57: Extract the shared PK/zip and OLE2 byte signatures into
named constants, then reuse them in MISLABELLED_BINARY_SIGNATURES and the
relevant FORMAT_SPECS entries, including the existing zip-signature triple for
docx and xlsx. Add a “doc” FORMAT_SPECS entry matching the “xls” OLE2 signature
and appropriate shared sniffing behavior so .doc uploads are handled like .xls.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6e6e3f5f-23a5-4f47-96b3-cfc57b2d0caa

📥 Commits

Reviewing files that changed from the base of the PR and between 6ca053f and 8c02d57.

📒 Files selected for processing (3)
  • backend/app/api/routes/documents.py
  • backend/app/services/doctransform/registry.py
  • backend/app/services/documents/helpers.py

@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: 5

Caution

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

⚠️ Outside diff range comments (1)
backend/app/services/documents/helpers.py (1)

123-128: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject OOXML packages that lack the expected part.

A PPTX or generic OOXML ZIP has [Content_Types].xml but neither word/ nor xl/, so it currently passes validation as both DOCX and XLSX. Require expected_part to be present; only use other_part to improve the mismatch message.

Proposed fix
-    if other_part in sample and expected_part not in sample:
+    if expected_part not in sample:
         raise DocumentValidationError(
             filename,
-            f"parsing error - file is a '{other_part.decode()}' Office package, "
-            "not the format its extension claims",
+            (
+                f"parsing error - file is a '{other_part.decode()}' Office package, "
+                "not the format its extension claims"
+                if other_part in sample
+                else "parsing error - Office package does not match its extension"
+            ),
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/services/documents/helpers.py` around lines 123 - 128, Update the
validation condition around the Office package checks so a package is rejected
whenever expected_part is absent, including cases where neither expected_part
nor other_part exists. Preserve the existing DocumentValidationError and use
other_part only when available to identify the detected package type in the
mismatch message; otherwise report the missing expected part through the
existing validation path.
🧹 Nitpick comments (4)
backend/app/services/collections/create_collection.py (1)

43-43: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use narrow types for retry inputs.

  • backend/app/services/collections/create_collection.py#L43-L43: annotate task_instance with a narrow bound-task protocol/type plus None.
  • backend/app/tests/services/collections/test_create_collection.py#L721-L721: replace BaseException with Timeout | SoftTimeLimitExceeded.
  • backend/app/tests/services/collections/test_create_collection.py#L779-L779: replace BaseException with Reject | TimeoutError.

As per coding guidelines, **/*.py: “Use Python 3.11+ and provide narrow type hints for every function parameter and return value.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/services/collections/create_collection.py` at line 43, Use a
narrow bound-task protocol/type plus None for the task_instance parameter in
_can_retry_batch. In
backend/app/tests/services/collections/test_create_collection.py at lines 721
and 779, replace BaseException annotations with Timeout | SoftTimeLimitExceeded
and Reject | TimeoutError respectively.

Source: Coding guidelines

backend/app/tests/services/collections/test_create_collection.py (1)

720-720: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Mark intentionally unused patched storage mocks.

Ruff reports all four injected mock_get_storage parameters as unused. Rename them to _mock_get_storage (or patch with new= so no argument is injected) to keep the test suite lint-clean.

  • backend/app/tests/services/collections/test_create_collection.py#L720-L720: mark the unused storage mock intentional.
  • backend/app/tests/services/collections/test_create_collection.py#L778-L778: mark the unused storage mock intentional.
  • backend/app/tests/services/collections/test_create_collection.py#L830-L830: mark the unused storage mock intentional.
  • backend/app/tests/services/collections/test_create_collection.py#L881-L881: mark the unused storage mock intentional.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/tests/services/collections/test_create_collection.py` at line
720, Rename each unused injected mock_get_storage parameter to _mock_get_storage
in backend/app/tests/services/collections/test_create_collection.py at lines
720-720, 778-778, 830-830, and 881-881, preserving the existing patched-storage
behavior while marking the mocks intentional and keeping Ruff lint-clean.

Source: Linters/SAST tools

backend/app/services/documents/helpers.py (1)

231-238: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the shared ZIP signatures.

Both specs duplicate the same signatures. Define a ZIP_SIGNATURES constant and reuse it. As per coding guidelines, “Do not use magic values; extract repeated literals into constants, enums, or settings.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/services/documents/helpers.py` around lines 231 - 238, Define a
shared ZIP_SIGNATURES constant containing the repeated ZIP signature tuple, then
update the docx and xlsx FormatSpec definitions to reference it instead of
duplicating the literal signatures. Keep needs_tail and each checker unchanged.

Source: Coding guidelines

backend/app/services/collections/providers/registry.py (1)

8-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep provider configuration out of the CRUD module.

backend/app/services/collections/providers/registry.py now imports shared client settings from backend/app/crud/rag/open_ai.py, creating a service-to-CRUD dependency. Move these constants to a neutral settings/provider-configuration module and import them from both locations.

As per coding guidelines, use the repository's layered architecture: models represent database entities, CRUD handles database operations, API routes expose REST endpoints, services contain business logic, and Celery tasks handle asynchronous work.

Also applies to: 67-71

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/services/collections/providers/registry.py` at line 8, Move
OPENAI_MAX_RETRIES and OPENAI_TIMEOUT_SECONDS out of app.crud.rag.open_ai into a
neutral settings or provider-configuration module. Update both
providers.registry and the CRUD OpenAI implementation to import these constants
from the new module, removing the service-to-CRUD dependency while preserving
their existing values and behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/app/crud/rag/open_ai.py`:
- Around line 140-141: Update _poll_file_batch() so only a batch with status
"completed" is returned as successful; explicitly handle terminal statuses such
as "failed" and "cancelled" by raising or otherwise failing the upload,
including cancelled counts in the failure details where applicable. Do not let
non-completed terminal batches reach the successful-log path.
- Around line 136-140: Update _poll_file_batch so each _retrieve_file_batch call
cannot run past the remaining BATCH_POLL_TIMEOUT_SECONDS deadline. Compute the
remaining time before retrieval and pass it through the client/request timeout
while disabling or limiting retries for this polling operation; preserve the
existing status handling and stop polling when the deadline is exhausted.

In `@backend/app/services/documents/helpers.py`:
- Line 240: Update the XLS FormatSpec near OLE2_SIGNATURE to use an XLS-specific
checker that inspects the OLE compound document for a Workbook or Book stream
before accepting it. Keep generic OLE2 validation for other formats, but reject
legacy .doc and unrelated OLE containers in the XLS path.

In `@docs/architecture/kaapi-knowledge-base-ARCHITECTURE.md`:
- Around line 292-302: Update the sequence diagram to match the revised Phase
1/Phase 2 lifecycle: show setup creating and recording the vector store before
persisting metadata, enqueue batch 1 with the resolved vector_store_id, and
remove any setup step that uploads all documents. Show each batch performing its
bounded document upload and vector-store attach operation.

In `@docs/wiki/modules/knowledge-base.md`:
- Line 39: Update the knowledge-base documentation sentence to use standard
Python terminology: replace “contextvar” with “ContextVar” or “context
variable.” Preserve the existing explanation about caller coupling and polling
deadlines.

---

Outside diff comments:
In `@backend/app/services/documents/helpers.py`:
- Around line 123-128: Update the validation condition around the Office package
checks so a package is rejected whenever expected_part is absent, including
cases where neither expected_part nor other_part exists. Preserve the existing
DocumentValidationError and use other_part only when available to identify the
detected package type in the mismatch message; otherwise report the missing
expected part through the existing validation path.

---

Nitpick comments:
In `@backend/app/services/collections/create_collection.py`:
- Line 43: Use a narrow bound-task protocol/type plus None for the task_instance
parameter in _can_retry_batch. In
backend/app/tests/services/collections/test_create_collection.py at lines 721
and 779, replace BaseException annotations with Timeout | SoftTimeLimitExceeded
and Reject | TimeoutError respectively.

In `@backend/app/services/collections/providers/registry.py`:
- Line 8: Move OPENAI_MAX_RETRIES and OPENAI_TIMEOUT_SECONDS out of
app.crud.rag.open_ai into a neutral settings or provider-configuration module.
Update both providers.registry and the CRUD OpenAI implementation to import
these constants from the new module, removing the service-to-CRUD dependency
while preserving their existing values and behavior.

In `@backend/app/services/documents/helpers.py`:
- Around line 231-238: Define a shared ZIP_SIGNATURES constant containing the
repeated ZIP signature tuple, then update the docx and xlsx FormatSpec
definitions to reference it instead of duplicating the literal signatures. Keep
needs_tail and each checker unchanged.

In `@backend/app/tests/services/collections/test_create_collection.py`:
- Line 720: Rename each unused injected mock_get_storage parameter to
_mock_get_storage in
backend/app/tests/services/collections/test_create_collection.py at lines
720-720, 778-778, 830-830, and 881-881, preserving the existing patched-storage
behavior while marking the mocks intentional and keeping Ruff lint-clean.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 08d37652-b4a3-4011-9097-4498923146b3

📥 Commits

Reviewing files that changed from the base of the PR and between 8c02d57 and d391ad2.

📒 Files selected for processing (9)
  • backend/app/crud/rag/open_ai.py
  • backend/app/services/collections/create_collection.py
  • backend/app/services/collections/providers/registry.py
  • backend/app/services/documents/helpers.py
  • backend/app/tests/crud/rag/test_open_ai.py
  • backend/app/tests/services/collections/providers/test_openai_provider.py
  • backend/app/tests/services/collections/test_create_collection.py
  • docs/architecture/kaapi-knowledge-base-ARCHITECTURE.md
  • docs/wiki/modules/knowledge-base.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/app/tests/services/collections/providers/test_openai_provider.py
  • backend/app/tests/crud/rag/test_open_ai.py

Comment thread backend/app/crud/rag/open_ai.py Outdated
Comment on lines +136 to +140
deadline = time.monotonic() + BATCH_POLL_TIMEOUT_SECONDS

while True:
batch = self._retrieve_file_batch(batch_id, vector_store_id)
if batch.status != "in_progress":

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## locate file"
fd -a 'open_ai\.py$' . || true

echo "## inspect relevant file outline/section"
if [ -f backend/app/crud/rag/open_ai.py ]; then
  wc -l backend/app/crud/rag/open_ai.py
  ast-grep outline backend/app/crud/rag/open_ai.py || true
  echo "## lines 1-240"
  sed -n '1,240p' backend/app/crud/rag/open_ai.py | nl -ba
  echo "## timeout/retry references"
  rg -n "timeout|retries|deadline|BATCH_POLL_TIMEOUT_SECONDS|_retrieve_file_batch|AsyncOpenAI|OpenAI" backend/app/crud/rag/open_ai.py || true
fi

echo "## dependency/version manifests"
for f in requirements.txt requirements-dev.txt pyproject.toml setup.cfg setup.py uv.lock poetry.lock Pipfile.lock; do
  [ -f "$f" ] && { echo "--- $f"; rg -n "openai|httpx" "$f" || true; }
done

echo "## other timeout/retry usages"
rg -n "AsyncOpenAI|OpenAI\(|timeout\s*=|retries\s*=" backend || true

echo "## git diff stat and relevant diff"
git diff --stat || true
git diff -- backend/app/crud/rag/open_ai.py || true

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 1362


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## lines 1-240"
sed -n '1,240p' backend/app/crud/rag/open_ai.py

echo "## timeout/retry references"
rg -n "timeout|retries|deadline|BATCH_POLL_TIMEOUT_SECONDS|_retrieve_file_batch|AsyncOpenAI|OpenAI" backend/app/crud/rag/open_ai.py || true

echo "## dependency/version manifests"
for f in requirements.txt requirements-dev.txt pyproject.toml setup.cfg setup.py uv.lock poetry.lock Pipfile.lock; do
  [ -f "$f" ] && { echo "--- $f"; rg -n "openai|httpx" "$f" || true; }
done

echo "## other timeout/retry usages"
rg -n "AsyncOpenAI|OpenAI\(|timeout\s*=|retries\s*=" backend || true

echo "## focused tests for poll/retrieval client"
rg -n "OpenAIVectorStoreCrud|_poll_file_batch|_retrieve_file_batch|OPENAI_MAX_RETRIES|OPENAI_TIMEOUT_SECONDS|vector_store_file_batches|retrieve" backend/app/tests backend/app/crud backend/app/services || true

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 35059


🌐 Web query:

OpenAI Python SDK configuration 90 second timeout max_retries=2 default retry behavior

💡 Result:

To configure the OpenAI Python SDK with a 90-second timeout and 2 retries, you can set these parameters when initializing the OpenAI client [1][2]. Configuration Example: from openai import OpenAI import httpx # Configure timeout and retries client = OpenAI( timeout=90.0, # Set total timeout to 90 seconds [1][3] max_retries=2, # Set maximum number of retries [1][3]) Default Behavior and Details: - Timeout: The default timeout is 10 minutes (600 seconds) [4]. Setting timeout=90.0 overrides this global default [1][2]. You can also pass an httpx.Timeout object for more granular control (e.g., specifying connect, read, or write timeouts) [1][5]. - Retries: The default number of retries is already 2 [1][6]. These retries occur automatically for specific error types, including connection errors, 408 Request Timeout, 409 Conflict, 429 Rate Limit, and >=500 Internal Server Errors [1][3][6]. - Per-Request Overrides: If you need to apply different settings to a specific call rather than the entire client, you can use the with_options method [1][7]: response = client.with_options(timeout=90.0, max_retries=2).chat.completions.create(...) Note: Requests that time out are subject to the same retry logic and will be retried up to the configured max_retries limit [1][2][5].

Citations:


Make the batch-poll deadline enforceable.

_retrieve_file_batch() uses the client’s 90s timeout and default retries, but _poll_file_batch() only checks the 240s deadline after _retrieve_file_batch() returns. If the SDK retries timed-out retrievals, polling can exceed the advertised wall-clock budget. Bound each retrieve/retry by the remaining deadline, or disable/limit retries for polling specifically.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/crud/rag/open_ai.py` around lines 136 - 140, Update
_poll_file_batch so each _retrieve_file_batch call cannot run past the remaining
BATCH_POLL_TIMEOUT_SECONDS deadline. Compute the remaining time before retrieval
and pass it through the client/request timeout while disabling or limiting
retries for this polling operation; preserve the existing status handling and
stop polling when the deadline is exhausted.

Source: MCP tools

Comment thread backend/app/crud/rag/open_ai.py
Comment thread backend/app/services/documents/helpers.py Outdated
Comment thread docs/architecture/kaapi-knowledge-base-ARCHITECTURE.md
Comment thread docs/wiki/modules/knowledge-base.md 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: 3

🧹 Nitpick comments (2)
backend/app/tests/services/documents/test_helpers.py (2)

265-279: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Annotate the test-method parameters.

Line 265 and Line 278 leave both self and monkeypatch untyped; the same applies to the other newly added test methods. Use self: Self and monkeypatch: pytest.MonkeyPatch throughout the new test classes.

As per coding guidelines, “Use Python 3.11+ and provide narrow type hints for every function parameter and return value.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/tests/services/documents/test_helpers.py` around lines 265 - 279,
Annotate every newly added test method in the test classes with narrow parameter
types: use self: Self and monkeypatch: pytest.MonkeyPatch, while preserving the
existing return annotations. Apply this consistently to
test_valid_csv_returns_format_and_transformer,
test_invalid_content_raises_http_400_with_client_message, and the other new test
methods.

Source: Coding guidelines


205-219: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the large-payload size from the configured parse threshold.

300000 is duplicated and only exercises the closure-only branch while it remains above JSON_FULL_PARSE_MAX_BYTES. Import that constant and build both fixtures from it so these tests continue to cover the intended branch if the limit changes.

Proposed fix
 from app.services.documents.helpers import (
+    JSON_FULL_PARSE_MAX_BYTES,
     SNIFF_HEAD_BYTES,
     SNIFF_TAIL_BYTES,
@@
-        content = b"[" + b" " * 300000 + b"]"
+        content = b"[" + b" " * JSON_FULL_PARSE_MAX_BYTES + b"]"
@@
-        content = b"[" + b" " * 300000
+        content = b"[" + b" " * JSON_FULL_PARSE_MAX_BYTES

As per coding guidelines, “Do not use magic values; extract repeated literals into constants, enums, or settings.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/tests/services/documents/test_helpers.py` around lines 205 - 219,
Update test_large_json_closed_correctly_passes and
test_large_json_not_closed_raises to import and use JSON_FULL_PARSE_MAX_BYTES
when constructing their payloads, adding enough bytes to remain above the
configured full-parse threshold. Remove the duplicated 300000 literal while
preserving the existing closed and unclosed tail behaviors.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/app/tests/crud/rag/test_open_ai.py`:
- Line 305: Update the new test methods
test_raises_interrupted_error_past_deadline and the method at the additional
referenced location with narrow type annotations for self if required, crud, and
mock_client, and add -> None return annotations. Follow the existing test typing
conventions and do not alter test behavior.
- Around line 332-336: Update get_mock_openai_client_with_vector_store() to
configure file_batches.retrieve.return_value to the same mock_file_batch used
for the batch contract, then replace the poll.return_value assertion in the test
with an assertion that file_batches.retrieve.return_value is batch.
- Around line 307-309: Update the monotonic side-effect sequence in the timeout
test to derive its post-deadline value from the production
BATCH_POLL_TIMEOUT_SECONDS constant instead of hard-coding 300, while preserving
the existing timing sequence and timeout behavior.

---

Nitpick comments:
In `@backend/app/tests/services/documents/test_helpers.py`:
- Around line 265-279: Annotate every newly added test method in the test
classes with narrow parameter types: use self: Self and monkeypatch:
pytest.MonkeyPatch, while preserving the existing return annotations. Apply this
consistently to test_valid_csv_returns_format_and_transformer,
test_invalid_content_raises_http_400_with_client_message, and the other new test
methods.
- Around line 205-219: Update test_large_json_closed_correctly_passes and
test_large_json_not_closed_raises to import and use JSON_FULL_PARSE_MAX_BYTES
when constructing their payloads, adding enough bytes to remain above the
configured full-parse threshold. Remove the duplicated 300000 literal while
preserving the existing closed and unclosed tail behaviors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8f4d2555-2352-4e72-88c6-112d1cc14252

📥 Commits

Reviewing files that changed from the base of the PR and between d391ad2 and a86653c.

📒 Files selected for processing (2)
  • backend/app/tests/crud/rag/test_open_ai.py
  • backend/app/tests/services/documents/test_helpers.py

Comment thread backend/app/tests/crud/rag/test_open_ai.py Outdated
Comment on lines +307 to +309
# monotonic: deadline setup, first below-deadline check, its sleep arg,
# then a value past the deadline that trips the timeout.
mock_time.monotonic.side_effect = [0, 0, 0, 300]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Tie the timeout test to the configured deadline.

Line 309 hard-codes 300; if BATCH_POLL_TIMEOUT_SECONDS changes, this sequence may no longer reach the deadline. Use the production timeout constant instead.

As per coding guidelines, do not use magic values; extract them into constants or settings.

Proposed fix
-            mock_time.monotonic.side_effect = [0, 0, 0, 300]
+            mock_time.monotonic.side_effect = [
+                0,
+                0,
+                0,
+                BATCH_POLL_TIMEOUT_SECONDS,
+            ]
📝 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
# monotonic: deadline setup, first below-deadline check, its sleep arg,
# then a value past the deadline that trips the timeout.
mock_time.monotonic.side_effect = [0, 0, 0, 300]
# monotonic: deadline setup, first below-deadline check, its sleep arg,
# then a value past the deadline that trips the timeout.
mock_time.monotonic.side_effect = [
0,
0,
0,
BATCH_POLL_TIMEOUT_SECONDS,
]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/tests/crud/rag/test_open_ai.py` around lines 307 - 309, Update
the monotonic side-effect sequence in the timeout test to derive its
post-deadline value from the production BATCH_POLL_TIMEOUT_SECONDS constant
instead of hard-coding 300, while preserving the existing timing sequence and
timeout behavior.

Source: Coding guidelines

Comment thread backend/app/tests/crud/rag/test_open_ai.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: 1

Caution

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

⚠️ Outside diff range comments (1)
backend/app/crud/rag/open_ai.py (1)

98-98: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Annotate self on the touched CRUD methods.

create, _create_file_batch, _retrieve_file_batch, and _poll_file_batch annotate parameters and returns but leave self untyped. Also applies to read, update, and delete, which should be typed consistently to match the required every-parameter typing policy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/crud/rag/open_ai.py` at line 98, Update the CRUD methods create,
_create_file_batch, _retrieve_file_batch, _poll_file_batch, read, update, and
delete to add the required type annotation for self, while preserving their
existing parameter and return annotations.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/app/crud/rag/open_ai.py`:
- Line 136: Centralize the OpenAI file-batch status values used by the CRUD flow
instead of hard-coding "in_progress" and "completed" in the status handling
around batch.status. Define reusable constants or an enum in the appropriate
OpenAI module, update this path and dependent tests/callers to reference them,
and preserve the existing protocol values.

---

Outside diff comments:
In `@backend/app/crud/rag/open_ai.py`:
- Line 98: Update the CRUD methods create, _create_file_batch,
_retrieve_file_batch, _poll_file_batch, read, update, and delete to add the
required type annotation for self, while preserving their existing parameter and
return annotations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8cc6c27b-e9d4-49de-892f-74ddf9bdb4a2

📥 Commits

Reviewing files that changed from the base of the PR and between a86653c and 50ce4c9.

📒 Files selected for processing (7)
  • backend/app/crud/rag/open_ai.py
  • backend/app/services/documents/helpers.py
  • backend/app/tests/crud/rag/test_open_ai.py
  • backend/app/tests/services/documents/test_helpers.py
  • backend/app/tests/utils/openai.py
  • docs/architecture/kaapi-knowledge-base-ARCHITECTURE.md
  • docs/wiki/modules/knowledge-base.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/architecture/kaapi-knowledge-base-ARCHITECTURE.md
  • docs/wiki/modules/knowledge-base.md

"""Poll until indexing finishes; the Celery soft time limit is the deadline."""
while True:
batch = self._retrieve_file_batch(batch_id, vector_store_id)
if batch.status != "in_progress":

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and inspect the target file and relevant literals/usages without running repo code.
if [ -f backend/app/crud/rag/open_ai.py ]; then
  echo "== target file size =="
  wc -l backend/app/crud/rag/open_ai.py
  echo
  echo "== relevant file sections =="
  sed -n '120,145p;270,290p' backend/app/crud/rag/open_ai.py
  echo
  echo "== batch.status/status value literals in target =="
  python3 - <<'PY'
import re
from pathlib import Path
p=Path('backend/app/crud/rag/open_ai.py')
text=p.read_text()
for i,line in enumerate(text.splitlines(),1):
    if '.status' in line or re.search(r'"(?:in_progress|completed|failed|pending|success|cancelled)"', line):
        print(f"{i}: {line}")
PY
else
  echo "backend/app/crud/rag/open_ai.py not found"
  git ls-files | rg 'open_ai\.py$|rag' || true
fi

echo
echo "== tests/files mentioning open_ai batch status literals =="
git ls-files | rg 'test|spec|py$' | xargs -r grep -E 'batch\.status|in_progress|completed|success' | head -n 200 || true

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 22815


Centralize OpenAI file-batch status values.

"in_progress" and "completed" are hard-coded directly in the CRUD path. Extract batch-status constants or an enum here and use the same values in any tests/callers that depend on this protocol state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/crud/rag/open_ai.py` at line 136, Centralize the OpenAI
file-batch status values used by the CRUD flow instead of hard-coding
"in_progress" and "completed" in the status handling around batch.status. Define
reusable constants or an enum in the appropriate OpenAI module, update this path
and dependent tests/callers to reference them, and preserve the existing
protocol values.

Source: Coding guidelines

@vprashrex vprashrex linked an issue Jul 31, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Collection: Correct batch ID usage Knowledge Base: Detail failures in API

1 participant