|
| 1 | +import { describe, it, expect } from "vitest" |
| 2 | +import { |
| 3 | + compressToolResult, |
| 4 | + shouldCompressToolResult, |
| 5 | + getCompressionLimitsForContextWindow, |
| 6 | + DEFAULT_TOOL_RESULT_CHARACTER_LIMIT, |
| 7 | + DEFAULT_TOOL_RESULT_LINE_LIMIT, |
| 8 | +} from "../compressToolResult" |
| 9 | + |
| 10 | +describe("compressToolResult", () => { |
| 11 | + describe("compressToolResult function", () => { |
| 12 | + it("should return original result when within limits", () => { |
| 13 | + const shortResult = "This is a short result" |
| 14 | + const compressed = compressToolResult(shortResult) |
| 15 | + expect(compressed).toBe(shortResult) |
| 16 | + }) |
| 17 | + |
| 18 | + it("should return empty string for empty input", () => { |
| 19 | + expect(compressToolResult("")).toBe("") |
| 20 | + expect(compressToolResult(null as any)).toBe(null) |
| 21 | + expect(compressToolResult(undefined as any)).toBe(undefined) |
| 22 | + }) |
| 23 | + |
| 24 | + it("should compress result when exceeding character limit", () => { |
| 25 | + const longResult = "A".repeat(60000) // Exceeds default 50000 char limit |
| 26 | + const compressed = compressToolResult(longResult) |
| 27 | + |
| 28 | + expect(compressed).not.toBe(longResult) |
| 29 | + expect(compressed.length).toBeLessThan(longResult.length) |
| 30 | + expect(compressed).toContain("[Tool result compressed:") |
| 31 | + expect(compressed).toContain("characters omitted") |
| 32 | + }) |
| 33 | + |
| 34 | + it("should compress result when exceeding line limit", () => { |
| 35 | + const manyLines = Array(1500).fill("line").join("\n") // Exceeds default 1000 line limit |
| 36 | + const compressed = compressToolResult(manyLines) |
| 37 | + |
| 38 | + expect(compressed).not.toBe(manyLines) |
| 39 | + expect(compressed.split("\n").length).toBeLessThan(manyLines.split("\n").length) |
| 40 | + expect(compressed).toContain("[Tool result compressed:") |
| 41 | + expect(compressed).toContain("lines omitted") |
| 42 | + }) |
| 43 | + |
| 44 | + it("should use custom limits when provided", () => { |
| 45 | + const result = "A".repeat(200) |
| 46 | + const compressed = compressToolResult(result, 100, 10) // Custom limits |
| 47 | + |
| 48 | + expect(compressed).not.toBe(result) |
| 49 | + expect(compressed).toContain("[Tool result compressed:") |
| 50 | + }) |
| 51 | + |
| 52 | + it("should preserve structure with compression note at beginning", () => { |
| 53 | + const longResult = "A".repeat(60000) |
| 54 | + const compressed = compressToolResult(longResult) |
| 55 | + |
| 56 | + expect(compressed.startsWith("[Tool result compressed:")).toBe(true) |
| 57 | + expect(compressed).toContain("Original 60000 characters") |
| 58 | + }) |
| 59 | + |
| 60 | + it("should handle mixed character and line limits", () => { |
| 61 | + // Create content that exceeds both limits |
| 62 | + const longLines = Array(1500).fill("A".repeat(100)).join("\n") |
| 63 | + const compressed = compressToolResult(longLines) |
| 64 | + |
| 65 | + expect(compressed).not.toBe(longLines) |
| 66 | + expect(compressed).toContain("[Tool result compressed:") |
| 67 | + }) |
| 68 | + }) |
| 69 | + |
| 70 | + describe("shouldCompressToolResult function", () => { |
| 71 | + it("should return false for short results", () => { |
| 72 | + const shortResult = "Short result" |
| 73 | + expect(shouldCompressToolResult(shortResult)).toBe(false) |
| 74 | + }) |
| 75 | + |
| 76 | + it("should return true for results exceeding character limit", () => { |
| 77 | + const longResult = "A".repeat(60000) |
| 78 | + expect(shouldCompressToolResult(longResult)).toBe(true) |
| 79 | + }) |
| 80 | + |
| 81 | + it("should return true for results exceeding line limit", () => { |
| 82 | + const manyLines = Array(1500).fill("line").join("\n") |
| 83 | + expect(shouldCompressToolResult(manyLines)).toBe(true) |
| 84 | + }) |
| 85 | + |
| 86 | + it("should return false for empty results", () => { |
| 87 | + expect(shouldCompressToolResult("")).toBe(false) |
| 88 | + expect(shouldCompressToolResult(null as any)).toBe(false) |
| 89 | + expect(shouldCompressToolResult(undefined as any)).toBe(false) |
| 90 | + }) |
| 91 | + |
| 92 | + it("should respect custom limits", () => { |
| 93 | + const result = "A".repeat(200) |
| 94 | + expect(shouldCompressToolResult(result, 100, 10)).toBe(true) |
| 95 | + expect(shouldCompressToolResult(result, 300, 10)).toBe(false) |
| 96 | + }) |
| 97 | + }) |
| 98 | + |
| 99 | + describe("getCompressionLimitsForContextWindow function", () => { |
| 100 | + it("should return appropriate limits for small context windows", () => { |
| 101 | + const limits = getCompressionLimitsForContextWindow(8000) // Small context window |
| 102 | + |
| 103 | + expect(limits.characterLimit).toBeGreaterThanOrEqual(DEFAULT_TOOL_RESULT_CHARACTER_LIMIT) |
| 104 | + expect(limits.lineLimit).toBeGreaterThanOrEqual(DEFAULT_TOOL_RESULT_LINE_LIMIT) |
| 105 | + }) |
| 106 | + |
| 107 | + it("should return larger limits for large context windows", () => { |
| 108 | + const smallLimits = getCompressionLimitsForContextWindow(8000) |
| 109 | + const largeLimits = getCompressionLimitsForContextWindow(200000) // Large context window |
| 110 | + |
| 111 | + expect(largeLimits.characterLimit).toBeGreaterThanOrEqual(smallLimits.characterLimit) |
| 112 | + expect(largeLimits.lineLimit).toBeGreaterThanOrEqual(smallLimits.lineLimit) |
| 113 | + }) |
| 114 | + |
| 115 | + it("should never return limits below defaults", () => { |
| 116 | + const limits = getCompressionLimitsForContextWindow(1000) // Very small context window |
| 117 | + |
| 118 | + expect(limits.characterLimit).toBeGreaterThanOrEqual(DEFAULT_TOOL_RESULT_CHARACTER_LIMIT) |
| 119 | + expect(limits.lineLimit).toBeGreaterThanOrEqual(DEFAULT_TOOL_RESULT_LINE_LIMIT) |
| 120 | + }) |
| 121 | + |
| 122 | + it("should scale limits proportionally with context window", () => { |
| 123 | + const limits1 = getCompressionLimitsForContextWindow(50000) |
| 124 | + const limits2 = getCompressionLimitsForContextWindow(100000) |
| 125 | + |
| 126 | + // Larger context window should allow larger tool results |
| 127 | + expect(limits2.characterLimit).toBeGreaterThanOrEqual(limits1.characterLimit) |
| 128 | + }) |
| 129 | + |
| 130 | + it("should cap limits at reasonable maximums", () => { |
| 131 | + const limits = getCompressionLimitsForContextWindow(1000000) // Extremely large context window |
| 132 | + |
| 133 | + // Should not exceed 2x the default limits |
| 134 | + expect(limits.characterLimit).toBeLessThanOrEqual(DEFAULT_TOOL_RESULT_CHARACTER_LIMIT * 2) |
| 135 | + }) |
| 136 | + }) |
| 137 | + |
| 138 | + describe("integration with truncateOutput", () => { |
| 139 | + it("should preserve beginning and end of content", () => { |
| 140 | + const longResult = "START" + "A".repeat(60000) + "END" |
| 141 | + const compressed = compressToolResult(longResult) |
| 142 | + |
| 143 | + // Should contain compression note plus truncated content |
| 144 | + expect(compressed).toContain("[Tool result compressed:") |
| 145 | + // The truncated content should preserve structure from truncateOutput |
| 146 | + expect(compressed).toContain("START") |
| 147 | + expect(compressed).toContain("END") |
| 148 | + }) |
| 149 | + |
| 150 | + it("should handle line-based truncation", () => { |
| 151 | + const lines = Array(1500) |
| 152 | + .fill(0) |
| 153 | + .map((_, i) => `Line ${i + 1}`) |
| 154 | + const longResult = lines.join("\n") |
| 155 | + const compressed = compressToolResult(longResult) |
| 156 | + |
| 157 | + expect(compressed).toContain("[Tool result compressed:") |
| 158 | + expect(compressed).toContain("Line 1") // Should preserve beginning |
| 159 | + expect(compressed).toContain("lines omitted") // Should indicate truncation |
| 160 | + }) |
| 161 | + }) |
| 162 | +}) |
0 commit comments