Commit 64a0085
fix: shrink oversized embedding input instead of dropping the document (#90)
## Problem
`createEmbeddingOrEmpty` logs a failed embedding as
**"transient/non-fatal"** and moves on. For a rate limit that's right.
For an over-length input it isn't: the input is *deterministic*, so the
same document fails on every rebuild, and the index keeps a permanent
hole that looks like a passing run.
Observed on a 567-table MySQL connection during a full reindex:
```
Skipping embedding for RELATIONSHIP document b7efe9d2-… due to
transient/non-fatal error: 400: Invalid 'input': maximum context length is 8192 tokens.
```
## Cause: the character budget, not a missing one
`truncate` already cut input to `app.embedding.max-chars` (default
**30,000**) on the stated assumption of *"roughly 4 chars per token"* —
which would be 7,500 tokens, safely under 8,192.
That ratio holds for prose. It does **not** hold for what this service
actually embeds: schema and relationship documents are dense
identifiers, underscores, punctuation and repeated scaffolding, which
tokenize closer to **2–3 chars per token**. At that density 30,000 chars
is 10,000–15,000 tokens, and the provider rejects the call.
## Fix
No fixed ratio is safe across content, so this stops betting on one.
`LlmErrorCategory.CONTEXT_LENGTH` already documents itself as *"never
retry; the caller may trim"* — until now nothing trimmed.
`embedWithShrink` halves the budget on each CONTEXT_LENGTH rejection
(30,000 → 15,000 → … → 1,875, floor 1,000) and lets the provider decide
when the call fits. No tokenizer dependency, and correct for any content
and any model window.
## Deliberately narrow
- **Only CONTEXT_LENGTH shrinks.** Retries and fail-open are untouched
for every other category — a smaller input answers nothing about a rate
limit or a rejected credential.
- **The inner attempt runs with fail-open off** so the rejection reaches
the shrink loop; fail-open would convert it into an empty vector
indistinguishable from a real one. The operator's fail-open setting is
still honoured once shrinking is exhausted.
- **Batch shrinking only affects members longer than the budget**, so
one oversized text costs the short ones nothing.
- **Bounded at 4 halvings**, so a pathological document cannot loop.
## Test plan
- [x] `shrinksTheInputWhenTheProviderRejectsItAsTooLong` — succeeds
after one halving; asserts the two calls were 30,000 then 15,000 chars.
- [x] `givesUpAfterTheShrinkFloorAndStillHonoursFailOpen` — 5 attempts
then empty, proving the bound.
- [x] `doesNotShrinkForFailuresThatShrinkingCannotFix` — AUTH gets
exactly one call.
## Follow-up (not in this PR)
The `"transient/non-fatal"` wording in
`TrainingService.createEmbeddingOrEmpty` is what made this invisible for
so long — it asserts transience for six call sites without knowing the
category. Worth revisiting separately.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>1 parent 4366ef0 commit 64a0085
2 files changed
Lines changed: 233 additions & 14 deletions
File tree
- backend/src
- main/java/com/dbaagent/service
- test/java/com/dbaagent/service
Lines changed: 114 additions & 14 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
34 | 38 | | |
35 | 39 | | |
36 | 40 | | |
| |||
87 | 91 | | |
88 | 92 | | |
89 | 93 | | |
90 | | - | |
91 | | - | |
92 | | - | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
93 | 111 | | |
94 | 112 | | |
95 | 113 | | |
| |||
98 | 116 | | |
99 | 117 | | |
100 | 118 | | |
101 | | - | |
102 | | - | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
103 | 175 | | |
104 | 176 | | |
105 | 177 | | |
| |||
110 | 182 | | |
111 | 183 | | |
112 | 184 | | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
117 | 202 | | |
118 | 203 | | |
119 | 204 | | |
| |||
166 | 251 | | |
167 | 252 | | |
168 | 253 | | |
169 | | - | |
170 | | - | |
171 | | - | |
172 | | - | |
173 | 254 | | |
174 | 255 | | |
175 | 256 | | |
| |||
180 | 261 | | |
181 | 262 | | |
182 | 263 | | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
183 | 283 | | |
184 | 284 | | |
185 | 285 | | |
186 | 286 | | |
187 | 287 | | |
188 | 288 | | |
189 | 289 | | |
190 | | - | |
| 290 | + | |
191 | 291 | | |
192 | 292 | | |
193 | 293 | | |
| |||
Lines changed: 119 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
476 | 476 | | |
477 | 477 | | |
478 | 478 | | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
479 | 598 | | |
0 commit comments