Skip to content

Commit cd69644

Browse files
authored
example: slack bot (#401)
1 parent 6fd3adf commit cd69644

File tree

7 files changed

+2636
-0
lines changed

7 files changed

+2636
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SLACK_SIGNING_SECRET="..."
2+
SLACK_BOT_TOKEN="..."
3+
OPENAI_API_KEY="..."
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Codegen Slack Bot
2+
3+
<p align="center">
4+
<a href="https://docs.codegen.com">
5+
<img src="https://i.imgur.com/6RF9W0z.jpeg" />
6+
</a>
7+
</p>
8+
9+
<h2 align="center">
10+
A Slack bot for answering questions about Codegen's implementation
11+
</h2>
12+
13+
<div align="center">
14+
15+
[![Documentation](https://img.shields.io/badge/Docs-docs.codegen.com-purple?style=flat-square)](https://docs.codegen.com)
16+
[![License](https://img.shields.io/badge/Code%20License-Apache%202.0-gray?&color=gray)](https://github.com/codegen-sh/codegen-sdk/tree/develop?tab=Apache-2.0-1-ov-file)
17+
18+
</div>
19+
20+
This example demonstrates how to build a Slack chatbot that can answer questions about Codegen's implementation using VectorIndex for RAG. The bot:
21+
22+
1. Maintains an up-to-date index of Codegen's source code
23+
1. Uses semantic search to find relevant code snippets
24+
1. Generates detailed answers about Codegen's internals using GPT-4
25+
26+
## Quick Start
27+
28+
1. Install dependencies:
29+
30+
```bash
31+
pip install modal-client codegen slack-bolt openai
32+
```
33+
34+
2. Create a Slack app and get tokens:
35+
36+
- Create a new Slack app at https://api.slack.com/apps
37+
- Add bot token scopes:
38+
- `app_mentions:read`
39+
- `chat:write`
40+
- `reactions:write`
41+
- Install the app to your workspace
42+
43+
1. Set up environment:
44+
45+
```bash
46+
# Copy template and fill in your tokens
47+
cp .env.template .env
48+
```
49+
50+
4. Start the bot:
51+
52+
```bash
53+
modal serve api.py
54+
```
55+
56+
## Usage
57+
58+
Just mention the bot in any channel and ask your question about Codegen:
59+
60+
```
61+
@your-bot-name How does the VectorIndex work?
62+
@your-bot-name What's the implementation of semantic search?
63+
@your-bot-name How does Codegen handle file operations?
64+
```
65+
66+
The bot will:
67+
68+
1. Find the most relevant Codegen source code
69+
1. Generate a detailed explanation
70+
1. Show you links to the actual implementation
71+
72+
## Response Format
73+
74+
The bot responds with:
75+
76+
1. A detailed answer about Codegen's implementation
77+
1. Links to relevant source files on GitHub
78+
1. Error messages if something goes wrong
79+
80+
Example response:
81+
82+
```
83+
*Answer:*
84+
The VectorIndex class uses OpenAI's embeddings to create searchable vectors
85+
for all files in a codebase. It handles chunking large files and maintains
86+
a persistent index for faster subsequent queries.
87+
88+
*Relevant Files:*
89+
• src/codegen/extensions/vector_index.py
90+
• src/codegen/extensions/tools/semantic_search.py
91+
```
92+
93+
## Environment Variables
94+
95+
Required environment variables (in `.env`):
96+
97+
- `SLACK_BOT_TOKEN`: Slack Bot User OAuth Token
98+
- `SLACK_SIGNING_SECRET`: Slack Signing Secret
99+
- `OPENAI_API_KEY`: OpenAI API key
100+
101+
## Development
102+
103+
The bot is built using:
104+
105+
- Modal for serverless deployment
106+
- Codegen for codebase analysis
107+
- Slack Bolt for the Slack integration
108+
- OpenAI for embeddings and Q&A
109+
110+
To deploy changes:
111+
112+
```bash
113+
modal deploy api.py
114+
```
115+
116+
## Learn More
117+
118+
- [Codegen Documentation](https://docs.codegen.com)
119+
- [Slack Bolt Python](https://slack.dev/bolt-python/concepts)
120+
- [Modal Documentation](https://modal.com/docs)
121+
- [VectorIndex Tutorial](https://docs.codegen.com/building-with-codegen/semantic-code-search)
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
"""Slack chatbot for answering questions about FastAPI using Codegen's VectorIndex."""
2+
3+
import os
4+
from typing import Any
5+
6+
import modal
7+
from codegen import Codebase
8+
from codegen.extensions import VectorIndex
9+
from codegen.sdk.enums import ProgrammingLanguage
10+
from fastapi import FastAPI, Request
11+
from openai import OpenAI
12+
from slack_bolt import App
13+
from slack_bolt.adapter.fastapi import SlackRequestHandler
14+
15+
########################################################
16+
# Core RAG logic
17+
########################################################
18+
19+
20+
def format_response(answer: str, context: list[tuple[str, int]]) -> str:
21+
"""Format the response for Slack with file links."""
22+
response = f"*Answer:*\n{answer}\n\n*Relevant Files:*\n"
23+
for filename, score in context:
24+
github_link = f"https://github.com/codegen-sh/codegen-sdk/blob/develop/{filename}"
25+
response += f"• <{github_link}|{filename}>\n"
26+
return response
27+
28+
29+
def answer_question(query: str) -> tuple[str, list[tuple[str, int]]]:
30+
"""Use RAG to answer a question about FastAPI."""
31+
# Initialize codebase. Smart about caching.
32+
codebase = Codebase.from_repo("codegen-sh/codegen-sdk", programming_language=ProgrammingLanguage.PYTHON, tmp_dir="/root")
33+
34+
# Initialize vector index
35+
index = VectorIndex(codebase)
36+
37+
# Try to load existing index or create new one
38+
index_path = "/root/E.pkl"
39+
try:
40+
index.load(index_path)
41+
except FileNotFoundError:
42+
# Create new index if none exists
43+
index.create()
44+
index.save(index_path)
45+
46+
# Find relevant files
47+
results = index.similarity_search(query, k=5)
48+
49+
# Collect context from relevant files
50+
context = ""
51+
for filepath, score in results:
52+
if "#chunk" in filepath:
53+
filepath = filepath.split("#chunk")[0]
54+
file = codebase.get_file(filepath)
55+
context += f"File: {file.filepath}\n```\n{file.content}\n```\n\n"
56+
57+
# Create prompt for OpenAI
58+
prompt = f"""You are an expert on FastAPI. Given the following code context and question, provide a clear and accurate answer.
59+
Focus on the specific code shown in the context and FastAPI's implementation details.
60+
61+
Note that your response will be rendered in Slack, so make sure to use Slack markdown. Keep it short + sweet, like 2 paragraphs + some code blocks max.
62+
63+
Question: {query}
64+
65+
Relevant code:
66+
{context}
67+
68+
Answer:"""
69+
70+
client = OpenAI()
71+
response = client.chat.completions.create(
72+
model="gpt-4o",
73+
messages=[
74+
{"role": "system", "content": "You are a code expert. Answer questions about the given repo based on RAG'd results."},
75+
{"role": "user", "content": prompt},
76+
],
77+
temperature=0,
78+
)
79+
80+
return response.choices[0].message.content, results
81+
82+
83+
########################################################
84+
# Modal + Slack Setup
85+
########################################################
86+
87+
# Create image with dependencies
88+
image = (
89+
modal.Image.debian_slim(python_version="3.13")
90+
.apt_install("git")
91+
.pip_install(
92+
"slack-bolt>=1.18.0",
93+
"codegen>=0.6.1",
94+
"openai>=1.1.0",
95+
)
96+
)
97+
98+
# Create Modal app
99+
app = modal.App("codegen-slack-demo")
100+
101+
102+
@app.function(
103+
image=image,
104+
secrets=[modal.Secret.from_dotenv()],
105+
timeout=3600,
106+
)
107+
@modal.asgi_app()
108+
def fastapi_app():
109+
"""Create FastAPI app with Slack handlers."""
110+
# Initialize Slack app with secrets from environment
111+
slack_app = App(
112+
token=os.environ["SLACK_BOT_TOKEN"],
113+
signing_secret=os.environ["SLACK_SIGNING_SECRET"],
114+
)
115+
116+
# Create FastAPI app
117+
web_app = FastAPI()
118+
handler = SlackRequestHandler(slack_app)
119+
120+
# Store responded messages to avoid duplicates
121+
responded = {}
122+
123+
@slack_app.event("app_mention")
124+
def handle_mention(event: dict[str, Any], say: Any) -> None:
125+
"""Handle mentions of the bot in channels."""
126+
print("#####[ Received Event ]#####")
127+
print(event)
128+
129+
# Skip if we've already answered this question
130+
# Seems like Slack likes to double-send events while debugging (?)
131+
if event["ts"] in responded:
132+
return
133+
responded[event["ts"]] = True
134+
135+
# Get message text without the bot mention
136+
query = event["text"].split(">", 1)[1].strip()
137+
if not query:
138+
say("Please ask a question about FastAPI!")
139+
return
140+
141+
try:
142+
# Add typing indicator emoji
143+
slack_app.client.reactions_add(
144+
channel=event["channel"],
145+
timestamp=event["ts"],
146+
name="writing_hand",
147+
)
148+
149+
# Get answer using RAG
150+
answer, context = answer_question(query)
151+
152+
# Format and send response in thread
153+
response = format_response(answer, context)
154+
say(text=response, thread_ts=event["ts"])
155+
156+
except Exception as e:
157+
# Send error message in thread
158+
say(text=f"Error: {str(e)}", thread_ts=event["ts"])
159+
160+
@web_app.post("/")
161+
async def endpoint(request: Request):
162+
"""Handle Slack events and verify requests."""
163+
return await handler.handle(request)
164+
165+
@web_app.post("/slack/verify")
166+
async def verify(request: Request):
167+
"""Handle Slack URL verification challenge."""
168+
data = await request.json()
169+
if data["type"] == "url_verification":
170+
return {"challenge": data["challenge"]}
171+
return await handler.handle(request)
172+
173+
return web_app
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[project]
2+
name = "fastapi-slack-bot"
3+
version = "0.1.0"
4+
description = "Slack chatbot for answering questions about FastAPI's implementation using Codegen"
5+
requires-python = ">=3.13"
6+
dependencies = [
7+
"modal>=0.73.25",
8+
"slack-bolt>=1.18.0",
9+
"codegen>=0.5.30",
10+
"openai>=1.1.0",
11+
]

0 commit comments

Comments
 (0)