fix: bound empty aitable pagination

This commit is contained in:
Dennis committed 2026-08-17 14:13:38 +08:00
1 parent e14de2b4c2
commit f486105836
3 files changed
+37 -1

No files matched your search

+16 -1
View File
@@ -15,7 +15,10 @@ import (
// drop the envelope fields used by the record projector, turning a successful
// read into an empty collection. Keep every remote request on one service page
// and aggregate only after each page has passed the CLI's shape validation.
const recordQueryServicePageSize = 20
const (
recordQueryServicePageSize = 20
recordQueryMaxConsecutiveEmptyPages = 3
)
type recordQueryWindow struct {
Records []map[string]any
@@ -36,6 +39,7 @@ func queryRecordWindow(rt *shortcut.RuntimeContext, params map[string]any, limit
seen[cursor] = true
}
window := recordQueryWindow{Records: make([]map[string]any, 0, limit)}
consecutiveEmptyPages := 0
for len(window.Records) < limit {
pageSize := minInt(recordQueryServicePageSize, limit-len(window.Records))
@@ -67,6 +71,17 @@ func queryRecordWindow(rt *shortcut.RuntimeContext, params map[string]any, limit
window.NextCursor = ""
return window, nil
}
if len(records) == 0 {
consecutiveEmptyPages++
if consecutiveEmptyPages >= recordQueryMaxConsecutiveEmptyPages {
return recordQueryWindow{}, fmt.Errorf(
"query_records made no progress for %d consecutive pages",
consecutiveEmptyPages,
)
}
} else {
consecutiveEmptyPages = 0
}
next := responseCursor(data)
if next == "" {
return recordQueryWindow{}, fmt.Errorf("query_records page %d reports more data but no next cursor", window.Pages)
@@ -404,6 +404,26 @@ func TestCrossPlatformCoverageRecordQueryFailureAndContinuationBranches(t *testi
}
})
t.Run("changing cursors cannot hide sustained empty pages", func(t *testing.T) {
caller := &upsertByKeyCaller{callFn: func(call int, _, tool string, _ map[string]any) (string, error) {
if tool != "query_records" {
return "", fmt.Errorf("unexpected tool %s", tool)
}
return fmt.Sprintf(
`{"success":true,"data":{"records":[],"hasMore":true,"nextCursor":"empty-%d"}}`,
call,
), nil
}}
helpers.InitDepsForTest(t, caller)
rt := shortcut.RuntimeContextForTest(&cobra.Command{Use: "query"}, RecordQuery)
if _, err := queryRecordWindow(rt, map[string]any{}, 1); err == nil || !strings.Contains(err.Error(), "no progress") {
t.Fatalf("sustained empty pages error = %v", err)
}
if len(caller.calls) != recordQueryMaxConsecutiveEmptyPages {
t.Fatalf("query calls = %d, want %d", len(caller.calls), recordQueryMaxConsecutiveEmptyPages)
}
})
t.Run("invalid shortcut limit", func(t *testing.T) {
caller := &upsertByKeyCaller{}
helpers.InitDepsForTest(t, caller)
@@ -232,6 +232,7 @@ func TestCrossPlatformCoverageMinutesWorkflowCompletion(t *testing.T) {
}
for _, data := range []map[string]any{
{"success": false, "errorMsg": "denied"},
{"success": "yes"},
{"success": true, "result": "bad"},
{"success": true, "result": map[string]any{}},
{"success": true, "result": map[string]any{"resultMap": map[string]any{}}},