(text: string)
| 725 | } |
| 726 | |
| 727 | function hasKnownLoopSignature(text: string): boolean { |
| 728 | const matchCount = DEGRADED_LOOP_PATTERNS.reduce( |
| 729 | (count, pattern) => (pattern.test(text) ? count + 1 : count), |
| 730 | 0, |
| 731 | ); |
| 732 | if (matchCount >= 2) return true; |
| 733 | |
| 734 | // Generic repetitive loop fallback for short repeated lines. |
| 735 | const lines = text |
| 736 | .split(/\r?\n/) |
| 737 | .map((line) => line.trim()) |
| 738 | .filter(Boolean); |
| 739 | if (lines.length < 8) return false; |
| 740 | |
| 741 | const counts = new Map<string, number>(); |
| 742 | for (const line of lines) { |
| 743 | counts.set(line, (counts.get(line) ?? 0) + 1); |
| 744 | } |
| 745 | |
| 746 | const maxRepeat = Math.max(...counts.values()); |
| 747 | const uniqueRatio = counts.size / lines.length; |
| 748 | return maxRepeat >= 3 && uniqueRatio <= 0.45; |
| 749 | } |
| 750 | |
| 751 | /** |
| 752 | * Detect degraded 200-response payloads that should trigger model fallback. |
no test coverage detected