Skip to content

fix(api): bound ChildChunk content with max_length to cap embedding cost - #40940

Open
Harsh23Kashyap wants to merge 2 commits into
langgenius:mainfrom
Harsh23Kashyap:fix/40825-child-chunk-content-max-length
Open

fix(api): bound ChildChunk content with max_length to cap embedding cost#40940
Harsh23Kashyap wants to merge 2 commits into
langgenius:mainfrom
Harsh23Kashyap:fix/40825-child-chunk-content-max-length

Conversation

@Harsh23Kashyap

Copy link
Copy Markdown
Contributor

Summary

Bound ChildChunkCreatePayload.content and ChildChunkUpdatePayload.content with a max_length=16384 cap to prevent unbounded content from being forwarded to the embedding model and persisted to child_chunks.content (LongText, unbounded).

Sibling of #39825 (TTS text had no max_length). Same pattern, different surface: a service-API caller can no longer submit a multi-MB child chunk and burn tenant-owned embedding budget.

Problem

The two Pydantic models in api/controllers/common/controller_schemas.py accepted arbitrary-length strings. A POST to:

  • POST /v1/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks (service API)
  • POST /console/api/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks (console)

…could carry a content value of any size, and the value was then:

  1. Forwarded to the embedding model via ChildChunkAddToIndexTask / ChildChunkUpdateTask (cost proportional to token count, billed to the tenant owner).
  2. Persisted to child_chunks.content (typed as LongText, unbounded), skewing row size, slowing indexes, and bloating backups.

Fix

Added CHILD_CHUNK_CONTENT_MAX_LENGTH = 16384 as a module-level constant and applied it via max_length=... on both ChildChunkCreatePayload.content and ChildChunkUpdatePayload.content.

The cap sits well above the default child chunk size (~1024 tokens ≈ 4096 chars at ~4 chars/token) and above the existing 10000-char "very long content" test (tests/unit_tests/controllers/service_api/dataset/test_dataset_segment.py:222-226), while still bounding abuse.

# api/controllers/common/controller_schemas.py
CHILD_CHUNK_CONTENT_MAX_LENGTH = 16384


class ChildChunkCreatePayload(BaseModel):
    content: str = Field(
        ...,
        max_length=CHILD_CHUNK_CONTENT_MAX_LENGTH,
        description="Child chunk text content.",
    )


class ChildChunkUpdatePayload(BaseModel):
    content: str = Field(
        ...,
        max_length=CHILD_CHUNK_CONTENT_MAX_LENGTH,
        description="Child chunk text content.",
    )

Tests

Extended the existing TestChildChunkCreatePayload and TestChildChunkUpdatePayload classes in tests/unit_tests/controllers/service_api/dataset/test_dataset_segment.py with one boundary test each per direction:

  • test_payload_accepts_content_at_max_length — content of length CHILD_CHUNK_CONTENT_MAX_LENGTH is accepted.
  • test_payload_rejects_content_above_max_length — content of length CHILD_CHUNK_CONTENT_MAX_LENGTH + 1 raises ValidationError.

The existing 10000-char "very long content" test continues to pass because 10000 < 16384.

Files changed

  • api/controllers/common/controller_schemas.py — 1 new constant, 2 fields updated with max_length.
  • api/tests/unit_tests/controllers/service_api/dataset/test_dataset_segment.py — 4 new test methods (2 per payload class), 1 import.

Verification

  • pytest tests/unit_tests/controllers/service_api/dataset/test_dataset_segment.py::TestChildChunkCreatePayload tests/unit_tests/controllers/service_api/dataset/test_dataset_segment.py::TestChildChunkUpdatePayload --noconftest -v → 11/11 pass (5 + 2 existing, 2 + 2 new).
  • ruff check on both files → all checks passed.
  • ruff format --check on both files → already formatted.
  • pyrefly check on api/controllers/common/controller_schemas.py → 0 diagnostics.

Scope

  • No API contract change for callers who send ≤ 16384 chars (the realistic case for any child chunk).
  • No migration, no schema change, no behaviour change for in-range inputs.
  • The 4× headroom over the default chunk size keeps the cap generous for any real chunk while still bounding abuse.

Out of scope (potential follow-ups)

  • MessageFeedbackPayload.content (api/controllers/common/controller_schemas.py:94) — same pattern (no max_length).
  • ConversationRenamePayload.name (api/controllers/common/controller_schemas.py:13) — same pattern.
  • MetadataUpdatePayload.name (api/controllers/common/controller_schemas.py:212) — same pattern.
  • Parent chunk length limits (ChildChunk lives under a parent Segment).
  • Embedding-side chunking policy in core/rag/index_processor/.

Fixes #40825

ChildChunkCreatePayload.content and ChildChunkUpdatePayload.content
(api/controllers/common/controller_schemas.py) accepted arbitrary-length
strings. A service-API caller could submit a multi-MB content value that
was then forwarded to the embedding model and persisted in
child_chunks.content (LongText, unbounded), with the cost landing on the
tenant owner.

Sibling of langgenius#39825 (TTS text has no max_length). Adds a
CHILD_CHUNK_CONTENT_MAX_LENGTH = 16384 cap (4x the default child chunk
size of ~4096 chars, well above the existing 10000-char "very long
content" test) and two boundary tests per payload: 16384 chars accepted,
16385 raises ValidationError.

Fixes langgenius#40825
@dosubot dosubot Bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Aug 18, 2026
@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Pyrefly Type Coverage

Metric Base PR Delta
Type coverage 59.54% 59.54% -0.00%
Strict coverage 59.12% 59.12% -0.00%
Typed symbols 39,784 39,785 +1
Untyped symbols 27,223 27,226 +3
Modules 3181 3181 0

@autofix-ci
autofix-ci Bot requested a review from crazywoola as a code owner August 18, 2026 13:04
@github-actions github-actions Bot added the web This relates to changes on the web. label Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S This PR changes 10-29 lines, ignoring generated files. web This relates to changes on the web.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(api): add max_length to ChildChunkCreatePayload/UpdatePayload.content — sibling of #39825

1 participant