Skip to content

example: slack bot #401

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions codegen-examples/examples/slack_chatbot/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SLACK_SIGNING_SECRET="..."
SLACK_BOT_TOKEN="..."
OPENAI_API_KEY="..."
121 changes: 121 additions & 0 deletions codegen-examples/examples/slack_chatbot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Codegen Slack Bot

<p align="center">
<a href="https://docs.codegen.com">
<img src="https://i.imgur.com/6RF9W0z.jpeg" />
</a>
</p>

<h2 align="center">
A Slack bot for answering questions about Codegen's implementation
</h2>

<div align="center">

[![Documentation](https://img.shields.io/badge/Docs-docs.codegen.com-purple?style=flat-square)](https://docs.codegen.com)
[![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)

</div>

This example demonstrates how to build a Slack chatbot that can answer questions about Codegen's implementation using VectorIndex for RAG. The bot:

1. Maintains an up-to-date index of Codegen's source code
1. Uses semantic search to find relevant code snippets
1. Generates detailed answers about Codegen's internals using GPT-4

## Quick Start

1. Install dependencies:

```bash
pip install modal-client codegen slack-bolt openai
```

2. Create a Slack app and get tokens:

- Create a new Slack app at https://api.slack.com/apps
- Add bot token scopes:
- `app_mentions:read`
- `chat:write`
- `reactions:write`
- Install the app to your workspace

1. Set up environment:

```bash
# Copy template and fill in your tokens
cp .env.template .env
```

4. Start the bot:

```bash
modal serve api.py
```

## Usage

Just mention the bot in any channel and ask your question about Codegen:

```
@your-bot-name How does the VectorIndex work?
@your-bot-name What's the implementation of semantic search?
@your-bot-name How does Codegen handle file operations?
```

The bot will:

1. Find the most relevant Codegen source code
1. Generate a detailed explanation
1. Show you links to the actual implementation

## Response Format

The bot responds with:

1. A detailed answer about Codegen's implementation
1. Links to relevant source files on GitHub
1. Error messages if something goes wrong

Example response:

```
*Answer:*
The VectorIndex class uses OpenAI's embeddings to create searchable vectors
for all files in a codebase. It handles chunking large files and maintains
a persistent index for faster subsequent queries.

*Relevant Files:*
• src/codegen/extensions/vector_index.py
• src/codegen/extensions/tools/semantic_search.py
```

## Environment Variables

Required environment variables (in `.env`):

- `SLACK_BOT_TOKEN`: Slack Bot User OAuth Token
- `SLACK_SIGNING_SECRET`: Slack Signing Secret
- `OPENAI_API_KEY`: OpenAI API key

## Development

The bot is built using:

- Modal for serverless deployment
- Codegen for codebase analysis
- Slack Bolt for the Slack integration
- OpenAI for embeddings and Q&A

To deploy changes:

```bash
modal deploy api.py
```

## Learn More

- [Codegen Documentation](https://docs.codegen.com)
- [Slack Bolt Python](https://slack.dev/bolt-python/concepts)
- [Modal Documentation](https://modal.com/docs)
- [VectorIndex Tutorial](https://docs.codegen.com/building-with-codegen/semantic-code-search)
173 changes: 173 additions & 0 deletions codegen-examples/examples/slack_chatbot/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
"""Slack chatbot for answering questions about FastAPI using Codegen's VectorIndex."""

import os
from typing import Any

import modal
from codegen import Codebase
from codegen.extensions import VectorIndex
from codegen.sdk.enums import ProgrammingLanguage
from fastapi import FastAPI, Request
from openai import OpenAI
from slack_bolt import App
from slack_bolt.adapter.fastapi import SlackRequestHandler

########################################################
# Core RAG logic
########################################################


def format_response(answer: str, context: list[tuple[str, int]]) -> str:
"""Format the response for Slack with file links."""
response = f"*Answer:*\n{answer}\n\n*Relevant Files:*\n"
for filename, score in context:
github_link = f"https://github.com/codegen-sh/codegen-sdk/blob/develop/{filename}"
response += f"• <{github_link}|{filename}>\n"
return response


def answer_question(query: str) -> tuple[str, list[tuple[str, int]]]:
"""Use RAG to answer a question about FastAPI."""
# Initialize codebase. Smart about caching.
codebase = Codebase.from_repo("codegen-sh/codegen-sdk", programming_language=ProgrammingLanguage.PYTHON, tmp_dir="/root")

# Initialize vector index
index = VectorIndex(codebase)

# Try to load existing index or create new one
index_path = "/root/E.pkl"
try:
index.load(index_path)
except FileNotFoundError:
# Create new index if none exists
index.create()
index.save(index_path)

# Find relevant files
results = index.similarity_search(query, k=5)

# Collect context from relevant files
context = ""
for filepath, score in results:
if "#chunk" in filepath:
filepath = filepath.split("#chunk")[0]
file = codebase.get_file(filepath)
context += f"File: {file.filepath}\n```\n{file.content}\n```\n\n"

# Create prompt for OpenAI
prompt = f"""You are an expert on FastAPI. Given the following code context and question, provide a clear and accurate answer.
Focus on the specific code shown in the context and FastAPI's implementation details.

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.

Question: {query}

Relevant code:
{context}

Answer:"""

client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "You are a code expert. Answer questions about the given repo based on RAG'd results."},
{"role": "user", "content": prompt},
],
temperature=0,
)

return response.choices[0].message.content, results

Check failure on line 80 in codegen-examples/examples/slack_chatbot/api.py

View workflow job for this annotation

GitHub Actions / mypy

error: Incompatible return value type (got "tuple[str | None, list[tuple[str, float]]]", expected "tuple[str, list[tuple[str, int]]]") [return-value]


########################################################
# Modal + Slack Setup
########################################################

# Create image with dependencies
image = (
modal.Image.debian_slim(python_version="3.13")
.apt_install("git")
.pip_install(
"slack-bolt>=1.18.0",
"codegen>=0.6.1",
"openai>=1.1.0",
)
)

# Create Modal app
app = modal.App("codegen-slack-demo")


@app.function(
image=image,
secrets=[modal.Secret.from_dotenv()],
timeout=3600,
)
@modal.asgi_app()
def fastapi_app():
"""Create FastAPI app with Slack handlers."""
# Initialize Slack app with secrets from environment
slack_app = App(
token=os.environ["SLACK_BOT_TOKEN"],
signing_secret=os.environ["SLACK_SIGNING_SECRET"],
)

# Create FastAPI app
web_app = FastAPI()
handler = SlackRequestHandler(slack_app)

# Store responded messages to avoid duplicates
responded = {}

@slack_app.event("app_mention")
def handle_mention(event: dict[str, Any], say: Any) -> None:
"""Handle mentions of the bot in channels."""
print("#####[ Received Event ]#####")
print(event)

# Skip if we've already answered this question
# Seems like Slack likes to double-send events while debugging (?)
if event["ts"] in responded:
return
responded[event["ts"]] = True

# Get message text without the bot mention
query = event["text"].split(">", 1)[1].strip()
if not query:
say("Please ask a question about FastAPI!")
return

try:
# Add typing indicator emoji
slack_app.client.reactions_add(
channel=event["channel"],
timestamp=event["ts"],
name="writing_hand",
)

# Get answer using RAG
answer, context = answer_question(query)

# Format and send response in thread
response = format_response(answer, context)
say(text=response, thread_ts=event["ts"])

except Exception as e:
# Send error message in thread
say(text=f"Error: {str(e)}", thread_ts=event["ts"])

@web_app.post("/")
async def endpoint(request: Request):
"""Handle Slack events and verify requests."""
return await handler.handle(request)

@web_app.post("/slack/verify")
async def verify(request: Request):
"""Handle Slack URL verification challenge."""
data = await request.json()
if data["type"] == "url_verification":
return {"challenge": data["challenge"]}
return await handler.handle(request)

return web_app
11 changes: 11 additions & 0 deletions codegen-examples/examples/slack_chatbot/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[project]
name = "fastapi-slack-bot"
version = "0.1.0"
description = "Slack chatbot for answering questions about FastAPI's implementation using Codegen"
requires-python = ">=3.13"
dependencies = [
"modal>=0.73.25",
"slack-bolt>=1.18.0",
"codegen>=0.5.30",
"openai>=1.1.0",
]
Loading
Loading