Skip to content

Commit 44b4694

Browse files
committed
* map: improved map behavior;
1 parent 75cdbf5 commit 44b4694

5 files changed

Lines changed: 478 additions & 38 deletions

File tree

TODO.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@
22
- [ ] если выбрал режим verbose, то каждая команда сообщает о том, что она делает аккуратно в одной строке в stderr, но не через log.
33
Добавить параметр debug, где будут выводиться все логи в stderr
44
- [ ] Добавить конфигурирование таймаута для llm-клиента
5-
- [ ] разбивать по токенам, а не по байтам
65
- [ ] локализация (подумать о том, что LANG может быть "кривым")

internal/app/map/chunking.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,17 @@ type textChunk struct {
2121
const (
2222
approxMessageOverheadTokens = 12
2323
approxMapRequestReserve = 64
24-
approxBudgetNumerator = 3
25-
approxBudgetDenominator = 5
24+
mapBudgetNumerator = 3
25+
mapBudgetDenominator = 5
26+
reduceBudgetNumerator = 4
27+
reduceBudgetDenominator = 5
2628
)
2729

2830
func SplitByApproxTokens(ctx context.Context, r io.Reader, question string, maxTokens int) ([]textChunk, error) {
2931
if maxTokens < 1 {
3032
return nil, fmt.Errorf("max tokens must be >= 1")
3133
}
32-
safeMaxTokens := effectiveApproxTokenBudget(maxTokens)
34+
safeMaxTokens := effectiveMapApproxTokenBudget(maxTokens)
3335
if safeMaxTokens < 1 {
3436
return nil, fmt.Errorf("max tokens must be >= 1")
3537
}
@@ -169,12 +171,20 @@ func estimateReduceRequestTokens(question, facts string) int {
169171
return estimateReduceRequestOverheadTokens(question) + approxTokenCount(facts)
170172
}
171173

172-
func effectiveApproxTokenBudget(maxTokens int) int {
174+
func effectiveMapApproxTokenBudget(maxTokens int) int {
175+
return effectiveApproxTokenBudget(maxTokens, mapBudgetNumerator, mapBudgetDenominator)
176+
}
177+
178+
func effectiveReduceApproxTokenBudget(maxTokens int) int {
179+
return effectiveApproxTokenBudget(maxTokens, reduceBudgetNumerator, reduceBudgetDenominator)
180+
}
181+
182+
func effectiveApproxTokenBudget(maxTokens, numerator, denominator int) int {
173183
if maxTokens <= 0 {
174184
return 0
175185
}
176186

177-
budget := (maxTokens * approxBudgetNumerator) / approxBudgetDenominator
187+
budget := (maxTokens * numerator) / denominator
178188
if budget < 1 {
179189
return 1
180190
}

internal/app/map/chunking_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ func TestEffectiveApproxTokenBudget(t *testing.T) {
3737
}
3838

3939
for _, tt := range tests {
40-
if got := effectiveApproxTokenBudget(tt.input); got != tt.want {
41-
t.Fatalf("effectiveApproxTokenBudget(%d) = %d, want %d", tt.input, got, tt.want)
40+
if got := effectiveMapApproxTokenBudget(tt.input); got != tt.want {
41+
t.Fatalf("effectiveMapApproxTokenBudget(%d) = %d, want %d", tt.input, got, tt.want)
4242
}
4343
}
4444
}
@@ -62,7 +62,7 @@ func TestSplitByApproxTokens(t *testing.T) {
6262
name: "multiple ascii lines fit into one chunk",
6363
question: "Что в файле?",
6464
input: "abc\ndef",
65-
maxTokens: (estimateMapRequestTokens("Что в файле?", "abc\ndef") * approxBudgetDenominator / approxBudgetNumerator) + 1,
65+
maxTokens: (estimateMapRequestTokens("Что в файле?", "abc\ndef") * mapBudgetDenominator / mapBudgetNumerator) + 1,
6666
expectedChunks: []string{
6767
"abc\ndef",
6868
},
@@ -71,7 +71,7 @@ func TestSplitByApproxTokens(t *testing.T) {
7171
name: "new line makes next chunk overflow",
7272
question: "Что в файле?",
7373
input: "abcdef\nghi",
74-
maxTokens: (estimateMapRequestTokens("Что в файле?", "abcdef") * approxBudgetDenominator / approxBudgetNumerator) + 1,
74+
maxTokens: (estimateMapRequestTokens("Что в файле?", "abcdef") * mapBudgetDenominator / mapBudgetNumerator) + 1,
7575
expectedChunks: []string{
7676
"abcdef",
7777
"ghi",
@@ -81,7 +81,7 @@ func TestSplitByApproxTokens(t *testing.T) {
8181
name: "unicode respects runes not bytes",
8282
question: "Что в файле?",
8383
input: "привет\nмир",
84-
maxTokens: (estimateMapRequestTokens("Что в файле?", "привет") * approxBudgetDenominator / approxBudgetNumerator) + 1,
84+
maxTokens: (estimateMapRequestTokens("Что в файле?", "привет") * mapBudgetDenominator / mapBudgetNumerator) + 1,
8585
expectedChunks: []string{
8686
"привет",
8787
"мир",
@@ -91,7 +91,7 @@ func TestSplitByApproxTokens(t *testing.T) {
9191
name: "oversized line splits by rune boundaries",
9292
question: "Что в файле?",
9393
input: "abcdefghij",
94-
maxTokens: (estimateMapRequestTokens("Что в файле?", "abcdefghi") * approxBudgetDenominator / approxBudgetNumerator) + 1,
94+
maxTokens: (estimateMapRequestTokens("Что в файле?", "abcdefghi") * mapBudgetDenominator / mapBudgetNumerator) + 1,
9595
expectedChunks: []string{
9696
"abcdefghi",
9797
"j",
@@ -101,7 +101,7 @@ func TestSplitByApproxTokens(t *testing.T) {
101101
name: "exact boundary",
102102
question: "Что в файле?",
103103
input: "abcdef",
104-
maxTokens: (estimateMapRequestTokens("Что в файле?", "abcdef") * approxBudgetDenominator / approxBudgetNumerator) + 1,
104+
maxTokens: (estimateMapRequestTokens("Что в файле?", "abcdef") * mapBudgetDenominator / mapBudgetNumerator) + 1,
105105
expectedChunks: []string{
106106
"abcdef",
107107
},
@@ -139,7 +139,7 @@ func TestSplitByApproxTokens_LargeInput(t *testing.T) {
139139
builder.WriteString("line\n")
140140
}
141141

142-
chunks, err := SplitByApproxTokens(context.Background(), strings.NewReader(builder.String()), "Что в файле?", (estimateMapRequestTokens("Что в файле?", "line\nline\nline")*approxBudgetDenominator/approxBudgetNumerator)+1)
142+
chunks, err := SplitByApproxTokens(context.Background(), strings.NewReader(builder.String()), "Что в файле?", (estimateMapRequestTokens("Что в файле?", "line\nline\nline")*mapBudgetDenominator/mapBudgetNumerator)+1)
143143
if err != nil {
144144
t.Fatalf("SplitByApproxTokens() error = %v", err)
145145
}
@@ -159,7 +159,7 @@ func TestSplitByApproxTokens_RespectsContextCancellation(t *testing.T) {
159159
ctx, cancel := context.WithCancel(context.Background())
160160
cancel()
161161

162-
_, err := SplitByApproxTokens(ctx, strings.NewReader("abc"), "Что в файле?", (estimateMapRequestTokens("Что в файле?", "abc")*approxBudgetDenominator/approxBudgetNumerator)+1)
162+
_, err := SplitByApproxTokens(ctx, strings.NewReader("abc"), "Что в файле?", (estimateMapRequestTokens("Что в файле?", "abc")*mapBudgetDenominator/mapBudgetNumerator)+1)
163163
if err == nil {
164164
t.Fatal("expected context cancellation error")
165165
}

0 commit comments

Comments
 (0)