Skip to content
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
2 changes: 1 addition & 1 deletion examples/slackbot/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ An intelligent Slack bot built with Marvin that provides AI-powered assistance i
uv sync

# Start the bot server
uv run examples/slackbot/start.py
uv run --extra slackbot -m slackbot

# Or with Docker
docker build -f examples/slackbot/Dockerfile.slackbot -t marvin-slackbot .
Expand Down
6 changes: 3 additions & 3 deletions examples/slackbot/Dockerfile.slackbot
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.12-slim
FROM python:3.13-slim

COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

Expand All @@ -11,8 +11,8 @@ RUN apt-get update && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

RUN uv sync --extra slackbot
RUN uv sync --extra slackbot --no-dev

EXPOSE 4200

CMD ["uv", "run", "examples/slackbot/start.py"]
CMD ["uv", "run", "--no-sync", "-m", "slackbot"]
4 changes: 2 additions & 2 deletions examples/slackbot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A Slack chatbot powered by Claude with memories and Prefect-specific knowledge.
├── api.py # FastAPI app and Slack event handlers
├── core.py # Database, agent, and memory management
├── settings.py # Configuration management
└── start.py # Entry point
└── __main__.py # Entry point
```

## Setup
Expand Down Expand Up @@ -70,7 +70,7 @@ ngrok http 4200 # Or your configured port

2. Start the bot in another terminal:
```console
uv run -m examples.slackbot.start
uv run --extra slackbot -m slackbot
```

### Testing
Expand Down
63 changes: 63 additions & 0 deletions examples/slackbot/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
[build-system]
requires = ["hatchling", "uv-dynamic-versioning>=0.7.0"]
build-backend = "hatchling.build"

[tool.hatch.version]
source = "uv-dynamic-versioning"

[tool.uv-dynamic-versioning]
vcs = "git"
style = "pep440"
bump = true

[project]
name = "slackbot"
dynamic = ["version", "dependencies"]
description = "Slackbot for Prefect"
authors = [{ name = "Nathan Nowack", email = "nate@prefect.io" }]
license = "MIT"
readme = "README.md"
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: MIT License",
"Operating System :: Unix",
"Operating System :: POSIX :: Linux",
"Environment :: Console",
"Environment :: MacOS X",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Internet",
]
requires-python = ">=3.9"

[tool.hatch.metadata.hooks.uv-dynamic-versioning]
dependencies = [
"marvin",
"prefect",
"numpy",
"raggy[tpuf]@git+https://github.com/zzstoatzz/raggy.git",
]


[dependency-groups]
dev = []

[tool.hatch.metadata]
allow-direct-references = true


[tool.hatch.build.targets.wheel]
packages = ["src/slackbot"]

[tool.uv.sources]
marvin = { workspace = true }
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@

if __name__ == "__main__":
import uvicorn
from settings import settings

from slackbot.settings import settings

if not (openai_api_key := os.getenv("OPENAI_API_KEY")):
os.environ["OPENAI_API_KEY"] = Secret.load(
settings.openai_api_key_secret_name, _sync=True
).get()

uvicorn.run(
"api:app",
"slackbot.api:app",
host=settings.host,
port=settings.port,
# reload=settings.test_mode,
Expand Down
31 changes: 16 additions & 15 deletions examples/slackbot/api.py → examples/slackbot/src/slackbot/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
from contextlib import asynccontextmanager
from typing import Any

from core import (
Database,
UserContext,
build_user_context,
create_agent,
)
from fastapi import FastAPI, HTTPException, Request
from prefect import flow, get_run_logger, task
from prefect.blocks.notifications import SlackWebhook
Expand All @@ -18,10 +12,17 @@
from prefect.variables import Variable
from pydantic_ai.agent import AgentRunResult
from pydantic_ai.messages import ModelMessage
from settings import settings
from slack import SlackPayload, get_channel_name, post_slack_message
from strings import count_tokens, slice_tokens
from wrap import WatchToolCalls

from slackbot.core import (
Database,
UserContext,
build_user_context,
create_agent,
)
from slackbot.settings import settings
from slackbot.slack import SlackPayload, get_channel_name, post_slack_message
from slackbot.strings import count_tokens, slice_tokens
from slackbot.wrap import WatchToolCalls

BOT_MENTION = r"<@(\w+)>"

Expand Down Expand Up @@ -120,11 +121,11 @@ async def lifespan(app: FastAPI):
@app.post("/chat")
async def chat_endpoint(request: Request) -> dict[str, Any]:
try:
payload = SlackPayload(**await request.json())
payload = SlackPayload.model_validate(await request.json())
except Exception as e:
logger.error(f"Error parsing Slack payload: {e}")
slack_webhook = await SlackWebhook.load("marvin-bot-pager") # type: ignore
await slack_webhook.notify( # type: ignore
slack_webhook = await SlackWebhook.load("marvin-bot-pager")
await slack_webhook.notify(
body=f"Error parsing Slack payload: {e}",
subject="Slackbot Error",
)
Expand Down Expand Up @@ -155,8 +156,8 @@ async def chat_endpoint(request: Request) -> dict[str, Any]:
channel_name = await get_channel_name(payload.event.channel)
if channel_name.startswith("D"):
logger.warning(f"Attempted DM in channel: {channel_name}")
slack_webhook = await SlackWebhook.load("marvin-bot-pager") # type: ignore
await slack_webhook.notify( # type: ignore
slack_webhook = await SlackWebhook.load("marvin-bot-pager")
await slack_webhook.notify(
body=f"Attempted DM: {channel_name}",
subject="Slackbot DM Warning",
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from contextlib import asynccontextmanager
from dataclasses import dataclass
from pathlib import Path
from typing import AsyncIterator, TypedDict, cast
from typing import AsyncIterator, TypedDict

from prefect import get_run_logger, task
from prefect.blocks.system import Secret
Expand All @@ -17,11 +17,12 @@
from pydantic_ai.settings import ModelSettings
from raggy.documents import Document
from raggy.vectorstores.tpuf import TurboPuffer, query_namespace
from research_agent import research_prefect_topic
from search import read_github_issues
from settings import settings
from turbopuffer.error import NotFoundError

from slackbot.research_agent import research_prefect_topic
from slackbot.search import read_github_issues
from slackbot.settings import settings

GITHUB_API_TOKEN = Secret.load(settings.github_token_secret_name, _sync=True).get() # type: ignore

logger = get_logger(__name__)
Expand Down Expand Up @@ -155,12 +156,11 @@ def create_agent(
logger = get_run_logger()
logger.info("Creating new agent")
ai_model = model or AnthropicModel(
model_name=Variable.get(
"marvin_bot_model", default=settings.model_name, _sync=True
),
provider="anthropic",
api_key=Secret.load(settings.claude_key_secret_name, _sync=True).get(), # type: ignore
model=cast(
str,
Variable.get("marvin_bot_model", default=settings.model_name, _sync=True), # type: ignore
),
)
agent = Agent[UserContext, str](
model=ai_model,
Expand Down
File renamed without changes.
Empty file.
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"""Research agent for improved information gathering."""

from modules import display_signature
from prefect import task
from prefect.cache_policies import INPUTS
from pydantic import BaseModel, Field
from pydantic_ai import Agent
from pydantic_ai.models import Model
from search import (

from slackbot.modules import display_signature
from slackbot.search import (
explore_module_offerings,
get_latest_prefect_release_notes,
review_common_3x_gotchas,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@

import httpx
import turbopuffer as tpuf
from modules import ModuleTreeExplorer
from prefect import task
from prefect.blocks.system import Secret
from pydantic import BaseModel, Field, field_validator
from raggy.vectorstores.tpuf import multi_query_tpuf
from settings import settings
from strings import slice_tokens

import marvin
from marvin.utilities.logging import get_logger
from slackbot.modules import ModuleTreeExplorer
from slackbot.settings import settings
from slackbot.strings import slice_tokens


def verify_import_statements(import_statements: list[str]) -> str:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

import httpx
from pydantic import BaseModel, ValidationInfo, field_validator, model_validator
from settings import settings

from slackbot.settings import settings


class EventBlockElement(BaseModel):
Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion justfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ run-pre-commits: check-uv
uv run pre-commit run --all-files

run-slackbot: check-uv
uv run --extra slackbot examples/slackbot/start.py
uv run --extra slackbot -m slackbot
13 changes: 7 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,7 @@ dev = [
[project.optional-dependencies]
audio = ["pyaudio>=0.2.14"]

slackbot = [
"pydantic-ai",
"prefect",
"numpy",
"raggy[tpuf]@git+https://github.com/zzstoatzz/raggy.git",
]
slackbot = ["slackbot"]

mcp = ["fastmcp"]

Expand Down Expand Up @@ -101,6 +96,12 @@ extend-select = ["I"]
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401", "I001", "RUF013"]

[tool.uv.sources]
slackbot = { workspace = true }

[tool.uv.workspace]
members = ["examples/slackbot"]

[build-system]
requires = ["hatchling>=1.21.0", "hatch-vcs>=0.4.0"]
build-backend = "hatchling.build"
Loading