-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcodebase-query.ts
More file actions
100 lines (86 loc) · 3.21 KB
/
Copy pathcodebase-query.ts
File metadata and controls
100 lines (86 loc) · 3.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { supabaseAdmin } from "../utils/supabase";
import { generateEmbedding, generateText } from "./gemini";
export interface Citation {
file_path: string;
language: string | null;
start_line: number | null;
end_line: number | null;
snippet: string;
similarity: number;
}
export interface AskResult {
answer: string;
citations: Citation[];
}
const SYSTEM_PROMPT = `You are a senior engineer answering questions about a codebase.
CRITICAL — usage vs mention:
When asked whether the repo uses a technology, framework, or tool, distinguish:
- USAGE = code that actually invokes it: imports/requires, manifest entries
(package.json, requirements.txt, go.mod, Gemfile…), config files, function
calls in source files. File extensions matter (.tsx → React/JSX likely; .html/.css alone → static site).
- MENTION = prose, documentation, README/HTML body copy, or comments that
reference the technology by name without using it.
Mentions are NOT evidence of usage. If the only "evidence" of a technology is prose
inside a markdown or HTML file describing past projects, answer that the repo
itself does not use that technology — and explicitly say where the mention came from.
Rules:
- Answer only based on the provided code snippets. If the snippets don't contain
the answer, say so directly — don't speculate or fabricate.
- Always cite by file path and line range, formatted like \`src/foo.ts:12-34\`.
- Be specific and technical. Quote short identifiers in backticks.
- Open with the direct answer in 1-2 sentences, then add detail underneath
if useful.`;
export async function askCodebase(args: {
userId: string;
repoId: string;
question: string;
}): Promise<AskResult> {
const { userId, repoId, question } = args;
if (!question.trim()) throw new Error("question is empty");
const queryEmbedding = await generateEmbedding(question);
const { data: matches, error } = await supabaseAdmin.rpc(
"match_code_chunks",
{
query_embedding: queryEmbedding as unknown as string,
filter_repo_id: repoId,
filter_user_id: userId,
match_count: 8,
}
);
if (error) throw new Error(`vector search failed: ${error.message}`);
type Match = {
file_path: string;
language: string | null;
content: string;
start_line: number | null;
end_line: number | null;
similarity: number;
};
const hits = (matches ?? []) as Match[];
if (hits.length === 0) {
return {
answer:
"I couldn't find any code in this repo that's relevant to that question.",
citations: [],
};
}
const context = hits
.map((h, i) => {
const range = h.start_line && h.end_line
? `${h.file_path}:${h.start_line}-${h.end_line}`
: h.file_path;
return `--- [${i + 1}] ${range} (${h.language ?? "text"})\n${h.content}`;
})
.join("\n\n");
const prompt = `Question: ${question}\n\nRelevant code from the repository:\n\n${context}`;
const answer = await generateText(prompt, SYSTEM_PROMPT);
const citations: Citation[] = hits.map((h) => ({
file_path: h.file_path,
language: h.language,
start_line: h.start_line,
end_line: h.end_line,
snippet: h.content,
similarity: h.similarity,
}));
return { answer, citations };
}