Skip to content

Commit

Permalink
Merge pull request #61 from sshivaditya2019/issuematch
Browse files Browse the repository at this point in the history
Fix: Allow Issue Matching across org/repos
  • Loading branch information
shiv810 authored Dec 18, 2024
2 parents df10048 + f580905 commit 23a5562
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 16 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ cypress/screenshots
script.ts
.wrangler
test-dashboard.md
auth.users.json
2 changes: 1 addition & 1 deletion dist/index.js

Large diffs are not rendered by default.

34 changes: 32 additions & 2 deletions src/adapters/supabase/helpers/issues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export class Issue extends SuperSupabase {
top_k: 5,
});
if (error) {
this.context.logger.error("Error finding similar issues", {
this.context.logger.error("Unable to find similar issues", {
Error: error,
markdown,
currentId,
Expand All @@ -184,7 +184,7 @@ export class Issue extends SuperSupabase {
}
return data;
} catch (error) {
this.context.logger.error("Error finding similar issues", {
this.context.logger.error("Unable to find similar issues", {
Error: error,
markdown,
currentId,
Expand All @@ -193,4 +193,34 @@ export class Issue extends SuperSupabase {
return null;
}
}

async findSimilarIssuesToMatch({ markdown, currentId, threshold }: FindSimilarIssuesParams): Promise<IssueSimilaritySearchResult[] | null> {
// Create a new issue embedding
try {
const embedding = await this.context.adapters.voyage.embedding.createEmbedding(markdown);
const { data, error } = await this.supabase.rpc("find_similar_issues_to_match", {
current_id: currentId,
query_embedding: embedding,
threshold,
top_k: 5,
});
if (error) {
this.context.logger.error("Error finding similar issues", {
Error: error,
markdown,
threshold,
query_embedding: embedding,
});
return null;
}
return data;
} catch (error) {
this.context.logger.error("Error finding similar issues", {
Error: error,
markdown,
threshold,
});
return null;
}
}
}
10 changes: 7 additions & 3 deletions src/handlers/issue-matching.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export async function issueMatching(context: Context<"issues.opened" | "issues.e
// If alwaysRecommend is enabled, use a lower threshold to ensure we get enough recommendations
const threshold = context.config.alwaysRecommend && context.config.alwaysRecommend > 0 ? 0 : context.config.jobMatchingThreshold;

const similarIssues = await supabase.issue.findSimilarIssues({
const similarIssues = await supabase.issue.findSimilarIssuesToMatch({
markdown: issueContent,
threshold: threshold,
currentId: issue.node_id,
Expand Down Expand Up @@ -91,10 +91,14 @@ export async function issueMatching(context: Context<"issues.opened" | "issues.e
return null;
}
});
const issueList = (await Promise.all(fetchPromises)).filter((issue) => issue !== null);
const issueList = await Promise.allSettled(fetchPromises);

logger.debug("Fetched similar issues", { issueList });
issueList.forEach((issue: IssueGraphqlResponse) => {
issueList.forEach((issuePromise: PromiseSettledResult<IssueGraphqlResponse | null>) => {
if (!issuePromise || issuePromise.status === "rejected") {
return;
}
const issue = issuePromise.value as IssueGraphqlResponse;
// Only use completed issues that have assignees
if (issue.node.closed && issue.node.stateReason === "COMPLETED" && issue.node.assignees.nodes.length > 0) {
const assignees = issue.node.assignees.nodes;
Expand Down
29 changes: 29 additions & 0 deletions supabase/migrations/20241212182636_issue_function.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
CREATE OR REPLACE FUNCTION find_similar_issues_to_match(current_id VARCHAR, query_embedding vector(1024), threshold float8, top_k INT)
RETURNS TABLE(issue_id VARCHAR, issue_plaintext TEXT, similarity float8) AS $$
DECLARE
current_quantized vector(1024);
current_repo TEXT;
current_org TEXT;
BEGIN
-- Ensure the query_embedding is in the correct format
current_quantized := query_embedding;

-- Extract the current issue's repo and org from the payload
SELECT
payload->'repository'->>'name'::text,
payload->'repository'->'owner'->>'login'::text
INTO current_repo, current_org
FROM issues
WHERE id = current_id;

RETURN QUERY
SELECT id AS issue_id,
plaintext AS issue_plaintext,
((0.8 * (1 - cosine_distance(current_quantized, embedding))) + 0.2 * (1 / (1 + l2_distance(current_quantized, embedding)))) as similarity
FROM issues
WHERE id <> current_id
AND ((0.8 * (1 - cosine_distance(current_quantized, embedding))) + 0.2 * (1 / (1 + l2_distance(current_quantized, embedding)))) > threshold
ORDER BY similarity DESC
LIMIT top_k;
END;
$$ LANGUAGE plpgsql;
8 changes: 8 additions & 0 deletions tests/__mocks__/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ export function createMockAdapters(context: Context) {

return [];
}),
findSimilarIssuesToMatch: jest.fn(async (params: { markdown: string; threshold: number; currentId: string }) => {
if (params.currentId === "task_complete") {
return [{ id: "similar3", similarity: 0.98 }];
} else if (params.currentId === "task_complete_always") {
return [{ id: "similar5", similarity: 0.5 }];
}
return [];
}),
createIssue: jest.fn(async (issue: IssueData) => {
issueMap.set(issue.id, issue);
}),
Expand Down
10 changes: 0 additions & 10 deletions tests/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,6 @@ describe("Plugin tests", () => {
);
});

// Mock the findSimilarIssues function to return predefined similar issues
context.adapters.supabase.issue.findSimilarIssues = jest
.fn<typeof context.adapters.supabase.issue.findSimilarIssues>()
.mockResolvedValue([{ id: "similar3", similarity: 0.98 }] as unknown as IssueSimilaritySearchResult[]);

// Mock the graphql function to return predefined issue data
context.octokit.graphql = jest.fn<typeof context.octokit.graphql>().mockResolvedValue({
node: {
Expand Down Expand Up @@ -339,11 +334,6 @@ describe("Plugin tests", () => {
);
});

// Mock findSimilarIssues to return a result with low similarity
context.adapters.supabase.issue.findSimilarIssues = jest
.fn<typeof context.adapters.supabase.issue.findSimilarIssues>()
.mockResolvedValue([{ id: "similar5", similarity: 0.5 }] as unknown as IssueSimilaritySearchResult[]);

// Mock graphql to return issue data with a contributor
context.octokit.graphql = jest.fn<typeof context.octokit.graphql>().mockResolvedValue({
node: {
Expand Down

0 comments on commit 23a5562

Please sign in to comment.