Skip to content

Commit 445667a

Browse files
Fix mypy error: Rename api.py to rag_api.py in modal_repo_rag to avoid duplicate module name
1 parent d0f13ae commit 445667a

2 files changed

Lines changed: 127 additions & 1 deletion

File tree

codegen-examples/examples/modal_repo_rag/deploy.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ fi
2929
echo -e "${BLUE}Deploying modal_repo_rag to Modal...${NC}"
3030

3131
# Deploy the app
32-
if modal deploy api.py; then
32+
if modal deploy rag_api.py; then
3333
echo -e "${GREEN}Deployment successful!${NC}"
3434
echo -e "Your API is now available at: https://codegen-rag-qa--answer-code-question.modal.run"
3535
exit 0
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
"""Modal API endpoint for RAG-based code Q&A using Codegen's VectorIndex."""
2+
3+
import modal
4+
from codegen import Codebase
5+
from codegen.extensions import VectorIndex
6+
from pydantic import BaseModel
7+
8+
# Create image with dependencies
9+
image = (
10+
modal.Image.debian_slim(python_version="3.13")
11+
.apt_install("git")
12+
.pip_install(
13+
"fastapi[standard]",
14+
"codegen==0.52.19",
15+
"openai>=1.1.0",
16+
)
17+
)
18+
19+
# Create Modal app
20+
app = modal.App("codegen-rag-qa")
21+
22+
# Create stub for persistent volume to store vector indices
23+
stub = modal.Stub("codegen-rag-qa")
24+
volume = modal.Volume.from_name("codegen-indices")
25+
26+
27+
class QARequest(BaseModel):
28+
"""Request model for code Q&A."""
29+
30+
repo_name: str
31+
query: str
32+
33+
34+
class QAResponse(BaseModel):
35+
"""Response model for code Q&A."""
36+
37+
answer: str = ""
38+
context: list[dict[str, str]] = [] # List of {filepath, snippet} used for answer
39+
status: str = "success"
40+
error: str = ""
41+
42+
43+
@stub.function(
44+
image=image,
45+
volumes={"/root/.codegen/indices": volume},
46+
timeout=600,
47+
)
48+
@modal.web_endpoint(method="POST")
49+
async def answer_code_question(request: QARequest) -> QAResponse:
50+
"""Answer questions about code using RAG with Codegen's VectorIndex.
51+
52+
Args:
53+
request: QARequest containing repository name and query
54+
55+
Returns:
56+
QAResponse containing answer and context snippets
57+
"""
58+
try:
59+
# Validate input
60+
if "/" not in request.repo_name:
61+
return QAResponse(status="error", error="Repository name must be in format 'owner/repo'")
62+
63+
# Initialize codebase
64+
codebase = Codebase.from_repo(request.repo_name)
65+
66+
# Initialize vector index
67+
index = VectorIndex(codebase)
68+
69+
# Try to load existing index or create new one
70+
try:
71+
index.load(f"/root/.codegen/indices/{request.repo_name.replace('/', '_')}.pkl")
72+
except FileNotFoundError:
73+
# Create new index if none exists
74+
index.create()
75+
index.save(f"/root/.codegen/indices/{request.repo_name.replace('/', '_')}.pkl")
76+
77+
# Find relevant files
78+
results = index.similarity_search(request.query, k=3)
79+
80+
# Collect context from relevant files
81+
context = []
82+
for filepath, score in results:
83+
try:
84+
file = codebase.get_file(filepath)
85+
if file:
86+
context.append(
87+
{
88+
"filepath": filepath,
89+
"snippet": file.content[:1000], # First 1000 chars as preview
90+
"score": f"{score:.3f}",
91+
}
92+
)
93+
except Exception as e:
94+
print(f"Error reading file {filepath}: {e}")
95+
96+
# Format context for prompt
97+
context_str = "\n\n".join([f"File: {c['filepath']}\nScore: {c['score']}\n```\n{c['snippet']}\n```" for c in context])
98+
99+
# Create prompt for OpenAI
100+
prompt = f"""Given the following code context and question, provide a clear and accurate answer.
101+
Focus on the specific code shown in the context.
102+
103+
Question: {request.query}
104+
105+
Relevant code context:
106+
{context_str}
107+
108+
Answer:"""
109+
110+
# Get answer from OpenAI
111+
from openai import OpenAI
112+
113+
client = OpenAI()
114+
response = client.chat.completions.create(
115+
model="gpt-4-turbo-preview",
116+
messages=[
117+
{"role": "system", "content": "You are a helpful code assistant. Answer questions about code accurately and concisely based on the provided context."},
118+
{"role": "user", "content": prompt},
119+
],
120+
temperature=0,
121+
)
122+
123+
return QAResponse(answer=response.choices[0].message.content, context=[{"filepath": c["filepath"], "snippet": c["snippet"]} for c in context])
124+
125+
except Exception as e:
126+
return QAResponse(status="error", error=str(e))

0 commit comments

Comments
 (0)