Skip to content

Commit 0b89781

Browse files
committed
reduce context usage for get_pull_request_review_comments
1 parent 16ff74a commit 0b89781

File tree

3 files changed

+118
-57
lines changed

3 files changed

+118
-57
lines changed

pkg/github/minimal_types.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,41 @@ func convertToMinimalCommit(commit *github.RepositoryCommit, includeDiffs bool)
600600
return minimalCommit
601601
}
602602

603+
// MinimalPageInfo contains pagination cursor information.
604+
type MinimalPageInfo struct {
605+
HasNextPage bool `json:"has_next_page"`
606+
HasPreviousPage bool `json:"has_previous_page"`
607+
StartCursor string `json:"start_cursor,omitempty"`
608+
EndCursor string `json:"end_cursor,omitempty"`
609+
}
610+
611+
// MinimalReviewComment is the trimmed output type for PR review comment objects.
612+
type MinimalReviewComment struct {
613+
Body string `json:"body,omitempty"`
614+
Path string `json:"path"`
615+
Line *int `json:"line,omitempty"`
616+
Author string `json:"author,omitempty"`
617+
CreatedAt string `json:"created_at,omitempty"`
618+
UpdatedAt string `json:"updated_at,omitempty"`
619+
HTMLURL string `json:"html_url"`
620+
}
621+
622+
// MinimalReviewThread is the trimmed output type for PR review thread objects.
623+
type MinimalReviewThread struct {
624+
IsResolved bool `json:"is_resolved"`
625+
IsOutdated bool `json:"is_outdated"`
626+
IsCollapsed bool `json:"is_collapsed"`
627+
Comments []MinimalReviewComment `json:"comments"`
628+
TotalCount int `json:"total_count"`
629+
}
630+
631+
// MinimalReviewThreadsResponse is the trimmed output for a paginated list of PR review threads.
632+
type MinimalReviewThreadsResponse struct {
633+
ReviewThreads []MinimalReviewThread `json:"review_threads"`
634+
TotalCount int `json:"total_count"`
635+
PageInfo MinimalPageInfo `json:"page_info"`
636+
}
637+
603638
// convertToMinimalBranch converts a GitHub API Branch to MinimalBranch
604639
func convertToMinimalBranch(branch *github.Branch) MinimalBranch {
605640
return MinimalBranch{
@@ -608,3 +643,61 @@ func convertToMinimalBranch(branch *github.Branch) MinimalBranch {
608643
Protected: branch.GetProtected(),
609644
}
610645
}
646+
647+
func convertToMinimalReviewThreadsResponse(query reviewThreadsQuery) MinimalReviewThreadsResponse {
648+
threads := query.Repository.PullRequest.ReviewThreads
649+
650+
minimalThreads := make([]MinimalReviewThread, 0, len(threads.Nodes))
651+
for _, thread := range threads.Nodes {
652+
minimalThreads = append(minimalThreads, convertToMinimalReviewThread(thread))
653+
}
654+
655+
return MinimalReviewThreadsResponse{
656+
ReviewThreads: minimalThreads,
657+
TotalCount: int(threads.TotalCount),
658+
PageInfo: MinimalPageInfo{
659+
HasNextPage: bool(threads.PageInfo.HasNextPage),
660+
HasPreviousPage: bool(threads.PageInfo.HasPreviousPage),
661+
StartCursor: string(threads.PageInfo.StartCursor),
662+
EndCursor: string(threads.PageInfo.EndCursor),
663+
},
664+
}
665+
}
666+
667+
func convertToMinimalReviewThread(thread reviewThreadNode) MinimalReviewThread {
668+
comments := make([]MinimalReviewComment, 0, len(thread.Comments.Nodes))
669+
for _, c := range thread.Comments.Nodes {
670+
comments = append(comments, convertToMinimalReviewComment(c))
671+
}
672+
673+
return MinimalReviewThread{
674+
IsResolved: bool(thread.IsResolved),
675+
IsOutdated: bool(thread.IsOutdated),
676+
IsCollapsed: bool(thread.IsCollapsed),
677+
Comments: comments,
678+
TotalCount: int(thread.Comments.TotalCount),
679+
}
680+
}
681+
682+
func convertToMinimalReviewComment(c reviewCommentNode) MinimalReviewComment {
683+
m := MinimalReviewComment{
684+
Body: string(c.Body),
685+
Path: string(c.Path),
686+
Author: string(c.Author.Login),
687+
HTMLURL: c.URL.String(),
688+
}
689+
690+
if c.Line != nil {
691+
line := int(*c.Line)
692+
m.Line = &line
693+
}
694+
695+
if !c.CreatedAt.IsZero() {
696+
m.CreatedAt = c.CreatedAt.Format(time.RFC3339)
697+
}
698+
if !c.UpdatedAt.IsZero() {
699+
m.UpdatedAt = c.UpdatedAt.Format(time.RFC3339)
700+
}
701+
702+
return m
703+
}

pkg/github/pullrequests.go

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -409,24 +409,7 @@ func GetPullRequestReviewComments(ctx context.Context, gqlClient *githubv4.Clien
409409
}
410410
}
411411

412-
// Build response with review threads and pagination info
413-
response := map[string]any{
414-
"reviewThreads": query.Repository.PullRequest.ReviewThreads.Nodes,
415-
"pageInfo": map[string]any{
416-
"hasNextPage": query.Repository.PullRequest.ReviewThreads.PageInfo.HasNextPage,
417-
"hasPreviousPage": query.Repository.PullRequest.ReviewThreads.PageInfo.HasPreviousPage,
418-
"startCursor": string(query.Repository.PullRequest.ReviewThreads.PageInfo.StartCursor),
419-
"endCursor": string(query.Repository.PullRequest.ReviewThreads.PageInfo.EndCursor),
420-
},
421-
"totalCount": int(query.Repository.PullRequest.ReviewThreads.TotalCount),
422-
}
423-
424-
r, err := json.Marshal(response)
425-
if err != nil {
426-
return nil, fmt.Errorf("failed to marshal response: %w", err)
427-
}
428-
429-
return utils.NewToolResultText(string(r)), nil
412+
return MarshalledTextResult(convertToMinimalReviewThreadsResponse(query)), nil
430413
}
431414

432415
func GetPullRequestReviews(ctx context.Context, client *github.Client, deps ToolDependencies, owner, repo string, pullNumber int) (*mcp.CallToolResult, error) {

pkg/github/pullrequests_test.go

Lines changed: 24 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,45 +1619,35 @@ func Test_GetPullRequestComments(t *testing.T) {
16191619
},
16201620
expectError: false,
16211621
validateResult: func(t *testing.T, textContent string) {
1622-
var result map[string]any
1622+
var result MinimalReviewThreadsResponse
16231623
err := json.Unmarshal([]byte(textContent), &result)
16241624
require.NoError(t, err)
16251625

1626-
// Validate response structure
1627-
assert.Contains(t, result, "reviewThreads")
1628-
assert.Contains(t, result, "pageInfo")
1629-
assert.Contains(t, result, "totalCount")
1630-
16311626
// Validate review threads
1632-
threads := result["reviewThreads"].([]any)
1633-
assert.Len(t, threads, 1)
1627+
assert.Len(t, result.ReviewThreads, 1)
16341628

1635-
thread := threads[0].(map[string]any)
1636-
assert.Equal(t, "RT_kwDOA0xdyM4AX1Yz", thread["ID"])
1637-
assert.Equal(t, false, thread["IsResolved"])
1638-
assert.Equal(t, false, thread["IsOutdated"])
1639-
assert.Equal(t, false, thread["IsCollapsed"])
1629+
thread := result.ReviewThreads[0]
1630+
assert.Equal(t, false, thread.IsResolved)
1631+
assert.Equal(t, false, thread.IsOutdated)
1632+
assert.Equal(t, false, thread.IsCollapsed)
16401633

16411634
// Validate comments within thread
1642-
comments := thread["Comments"].(map[string]any)
1643-
commentNodes := comments["Nodes"].([]any)
1644-
assert.Len(t, commentNodes, 2)
1635+
assert.Len(t, thread.Comments, 2)
16451636

16461637
// Validate first comment
1647-
comment1 := commentNodes[0].(map[string]any)
1648-
assert.Equal(t, "PRRC_kwDOA0xdyM4AX1Y0", comment1["ID"])
1649-
assert.Equal(t, "This looks good", comment1["Body"])
1650-
assert.Equal(t, "file1.go", comment1["Path"])
1638+
comment1 := thread.Comments[0]
1639+
assert.Equal(t, "This looks good", comment1.Body)
1640+
assert.Equal(t, "file1.go", comment1.Path)
1641+
assert.Equal(t, "reviewer1", comment1.Author)
16511642

16521643
// Validate pagination info
1653-
pageInfo := result["pageInfo"].(map[string]any)
1654-
assert.Equal(t, false, pageInfo["hasNextPage"])
1655-
assert.Equal(t, false, pageInfo["hasPreviousPage"])
1656-
assert.Equal(t, "cursor1", pageInfo["startCursor"])
1657-
assert.Equal(t, "cursor2", pageInfo["endCursor"])
1644+
assert.Equal(t, false, result.PageInfo.HasNextPage)
1645+
assert.Equal(t, false, result.PageInfo.HasPreviousPage)
1646+
assert.Equal(t, "cursor1", result.PageInfo.StartCursor)
1647+
assert.Equal(t, "cursor2", result.PageInfo.EndCursor)
16581648

16591649
// Validate total count
1660-
assert.Equal(t, float64(1), result["totalCount"])
1650+
assert.Equal(t, 1, result.TotalCount)
16611651
},
16621652
},
16631653
{
@@ -1761,27 +1751,22 @@ func Test_GetPullRequestComments(t *testing.T) {
17611751
expectError: false,
17621752
lockdownEnabled: true,
17631753
validateResult: func(t *testing.T, textContent string) {
1764-
var result map[string]any
1754+
var result MinimalReviewThreadsResponse
17651755
err := json.Unmarshal([]byte(textContent), &result)
17661756
require.NoError(t, err)
17671757

17681758
// Validate that only maintainer comment is returned
1769-
threads := result["reviewThreads"].([]any)
1770-
assert.Len(t, threads, 1)
1759+
assert.Len(t, result.ReviewThreads, 1)
17711760

1772-
thread := threads[0].(map[string]any)
1773-
comments := thread["Comments"].(map[string]any)
1761+
thread := result.ReviewThreads[0]
17741762

17751763
// Should only have 1 comment (maintainer) after filtering
1776-
assert.Equal(t, float64(1), comments["TotalCount"])
1777-
1778-
commentNodes := comments["Nodes"].([]any)
1779-
assert.Len(t, commentNodes, 1)
1764+
assert.Equal(t, 1, thread.TotalCount)
1765+
assert.Len(t, thread.Comments, 1)
17801766

1781-
comment := commentNodes[0].(map[string]any)
1782-
author := comment["Author"].(map[string]any)
1783-
assert.Equal(t, "maintainer", author["Login"])
1784-
assert.Equal(t, "Maintainer review comment", comment["Body"])
1767+
comment := thread.Comments[0]
1768+
assert.Equal(t, "maintainer", comment.Author)
1769+
assert.Equal(t, "Maintainer review comment", comment.Body)
17851770
},
17861771
},
17871772
}

0 commit comments

Comments
 (0)