-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: restructure repo as monorepo with Pydantic AI and OpenAI Agents SDK #3
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
Changes from 8 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
7d4937e
refactor: move all files into pydantic-ai subdirectory
mwbrooks 169de43
feat: add OpenAI Agents SDK sample app
mwbrooks d7fd85a
build: update ruff workflow for monorepo with matrix strategy
mwbrooks 5a989cf
feat(openai): update to 'Casey - OpenAI SDK'
mwbrooks ac0f00a
docs: add monorepo CLAUDE.md and update per-project docs
mwbrooks cec90c6
docs: add monorepo README
mwbrooks 0f92639
feat(openai): sync eyes reaction and feedback blocks with pydantic-ai
mwbrooks 6080d54
docs: remove agent style row from README table
mwbrooks f8126ce
Update .github/workflows/ruff.yml
mwbrooks 0bff937
chore: add .gitignore
mwbrooks File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,10 +4,17 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co | |
|
|
||
| ## What This Is | ||
|
|
||
| Casey is an AI-powered IT helpdesk agent for Slack, built with Bolt for Python and Pydantic AI. It uses simulated tools (knowledge base, ticket creation, password reset, system status, permissions lookup) to demonstrate an agentic IT support workflow. All tool data is hardcoded for demo purposes. | ||
| A monorepo containing two parallel implementations of **Casey**, an AI-powered IT helpdesk agent for Slack built with Bolt for Python. Both implementations are functionally identical from the Slack user's perspective but use different AI agent frameworks: | ||
|
|
||
| - `pydantic-ai/` — Built with **Pydantic AI** | ||
| - `openai-agents-sdk/` — Built with **OpenAI Agents SDK** | ||
|
|
||
| All tool data (knowledge base, tickets, password resets, system status, permissions) is hardcoded for demo purposes. | ||
|
|
||
| ## Commands | ||
|
|
||
| All commands must be run from within the respective project directory (`pydantic-ai/` or `openai-agents-sdk/`). | ||
|
|
||
| ```sh | ||
| # Run the app (requires .env with OPENAI_API_KEY; Slack tokens optional with CLI) | ||
| slack run # via Slack CLI | ||
|
|
@@ -21,28 +28,46 @@ ruff format --check . | |
| pytest | ||
| ``` | ||
|
|
||
| ## Architecture | ||
| ## Monorepo Structure | ||
|
|
||
| ``` | ||
| .github/ # Shared CI workflows and dependabot config | ||
| pydantic-ai/ # Pydantic AI implementation | ||
| openai-agents-sdk/ # OpenAI Agents SDK implementation | ||
| ``` | ||
|
|
||
| CI runs ruff lint/format checks against both directories via a matrix strategy in `.github/workflows/ruff.yml`. Dependabot monitors `requirements.txt` in both directories independently. | ||
|
|
||
| ## Architecture (shared across both implementations) | ||
|
|
||
| Three-layer design: **app.py** → **listeners/** → **agent/** | ||
|
|
||
| **Entry point (`app.py`)** initializes Bolt with Socket Mode and calls `register_listeners(app)`. | ||
|
|
||
| **Listeners** are organized by Slack platform feature: | ||
| - `listeners/events/` — `app_home_opened`, `app_mentioned`, `message_im` | ||
| - `listeners/actions/` — `category_buttons` (regex `^category_`), `feedback_good`, `feedback_bad` | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ⭐ praise: Feedback good! |
||
| - `listeners/actions/` — `category_buttons` (regex `^category_`), feedback handlers | ||
| - `listeners/views/` — `issue_submission` modal handler | ||
|
|
||
| Each sub-package has a `register(app)` function called from `listeners/__init__.py`. | ||
|
|
||
| **Agent (`agent/casey.py`)** is a Pydantic AI `Agent` with `deps_type=CaseyDeps`. The model is **not** set on the agent (to avoid import-time OpenAI client creation); instead `DEFAULT_MODEL` (`openai:gpt-4o-mini`) is passed at each `run_sync()` call site. Tools are passed via the `tools=[]` constructor parameter (not decorators) so each tool lives in its own file under `agent/tools/`. | ||
| **CaseyDeps** (`agent/deps.py`) is a dataclass carrying `client`, `user_id`, `channel_id`, `thread_ts`. Constructed in each listener handler and passed to the agent at runtime. | ||
|
|
||
| **CaseyDeps** (`agent/deps.py`) is a dataclass carrying `client`, `user_id`, `channel_id`, `thread_ts`. It's constructed in each listener handler and passed as `deps=` to `run_sync()`. | ||
| **Conversation history** (`conversation/store.py`) is a thread-safe in-memory dict keyed by `(channel_id, thread_ts)` with TTL-based cleanup. This enables multi-turn context. | ||
|
|
||
| **Conversation history** (`conversation/store.py`) is an in-memory dict keyed by `(channel_id, thread_ts)` storing `list[ModelMessage]` from Pydantic AI. This is what enables multi-turn context. The singleton `conversation_store` is imported from `conversation/`. | ||
| **Handler flow** (DM, mention, modal submit): add `:eyes:` reaction → get history from store → run agent → post response in thread with feedback blocks → store updated messages. | ||
|
|
||
| ## Key Patterns | ||
| ## Key Differences Between Implementations | ||
|
|
||
| - All three message handlers (DM, mention, modal submit) follow the same flow: add :eyes: reaction → get history from store → `casey_agent.run_sync(text, model=DEFAULT_MODEL, deps=deps, message_history=history)` → post `result.output` in thread with feedback blocks → store `result.all_messages()`. | ||
| - Emoji/reaction logic is in the handlers, not the agent. Resolution detection checks `result.output` against a hardcoded phrase list. | ||
| - View builders (`app_home_builder.py`, `modal_builder.py`, `feedback_block.py`) return raw dicts or Block Kit objects, not views themselves. The handlers call `client.views_publish()` or `client.views_open()`. | ||
| - The `message_im` handler filters out bot messages (`event.bot_id`) and subtypes to avoid self-reply loops. | ||
| | Aspect | Pydantic AI | OpenAI Agents SDK | | ||
| |--------|-------------|-------------------| | ||
| | Agent file | `agent/casey.py` | `agent/support_agent.py` | | ||
| | Agent definition | `Agent(deps_type=CaseyDeps)` | `Agent[CaseyDeps](model="gpt-4o-mini")` | | ||
| | Model config | Passed at runtime via `run_sync(model=DEFAULT_MODEL)` | Set directly on agent constructor | | ||
| | Tool definition | Plain async functions | `@function_tool` decorated functions | | ||
| | Tool context param | `RunContext[CaseyDeps]` | `RunContextWrapper[CaseyDeps]` | | ||
| | Execution | `casey_agent.run_sync(text, model=..., deps=..., message_history=...)` | `Runner.run_sync(casey_agent, input=..., context=...)` | | ||
| | Result output | `result.output` | `result.final_output` | | ||
| | Result messages | `result.all_messages()` | `result.to_input_list()` | | ||
| | History type | `list[ModelMessage]` (framework-native) | `list` (generic, manually constructed) | | ||
| | Feedback blocks | Native `FeedbackButtonsElement` | Native `FeedbackButtonsElement` | | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,16 @@ | ||
| version: 2 | ||
| updates: | ||
| - package-ecosystem: "pip" | ||
| directory: "/" | ||
| directories: | ||
| - "/" | ||
| - "/openai-agents-sdk" | ||
| - "/pydantic-ai" | ||
| schedule: | ||
| interval: "monthly" | ||
| interval: "weekly" | ||
| labels: | ||
| - "pip" | ||
| - "dependencies" | ||
| - package-ecosystem: "github-actions" | ||
| directory: "/" | ||
| schedule: | ||
| interval: "monthly" | ||
| interval: "weekly" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,208 +1,28 @@ | ||
| # Casey: IT Helpdesk Agent (Bolt for Python and Pydantic) | ||
| # Casey: IT Helpdesk Agent | ||
|
|
||
| Meet Casey — an AI-powered IT helpdesk agent that lives in Slack. Casey can troubleshoot common issues, search knowledge base articles, reset passwords, check system status, and create support tickets, all without leaving the conversation. | ||
|
|
||
| Built with [Bolt for Python](https://docs.slack.dev/tools/bolt-python/) and [Pydantic AI](https://ai.pydantic.dev/) using models from [OpenAI](https://openai.com). | ||
| Built with [Bolt for Python](https://docs.slack.dev/tools/bolt-python/). | ||
|
|
||
| ## App Overview | ||
| ## Choose Your Framework | ||
|
|
||
| Casey gives your team instant IT support through three entry points: | ||
|
|
||
| * **App Home** — Users open Casey's Home tab and choose from common issue categories (Password Reset, Access Request, Software Help, Network Issues, Something Else). A modal collects details, then Casey starts a DM thread with a resolution. | ||
| * **Direct Messages** — Users message Casey directly to describe any IT issue. Casey responds in-thread, maintaining context across follow-ups. | ||
| * **Channel @mentions** — Users mention `@Casey` in any channel to get help without leaving the conversation. | ||
|
|
||
| Casey uses five simulated tools to assist users: | ||
|
|
||
| * **Knowledge Base Search** — Finds relevant articles for common topics like VPN, email, Wi-Fi, printers, and more. | ||
| * **Support Ticket Creation** — Creates a tracked ticket when issues need human follow-up. | ||
| * **Password Reset** — Triggers a password reset and confirms the action. | ||
| * **System Status Check** — Reports the operational status of company systems. | ||
| * **User Permissions Lookup** — Shows access levels and group memberships. | ||
|
|
||
| > **Note:** All tools return simulated data for demonstration purposes. In a production app, these would connect to your actual IT systems. | ||
|
|
||
| ## Setup | ||
|
|
||
| Before getting started, make sure you have a development workspace where you have permissions to install apps. | ||
|
|
||
| ### Developer Program | ||
|
|
||
| Join the [Slack Developer Program](https://api.slack.com/developer-program) for exclusive access to sandbox environments for building and testing your apps, tooling, and resources created to help you build and grow. | ||
|
|
||
| ### Create the Slack app | ||
|
|
||
| <details><summary><strong>Using Slack CLI</strong></summary> | ||
|
|
||
| Install the latest version of the Slack CLI for your operating system: | ||
|
|
||
| - [Slack CLI for macOS & Linux](https://docs.slack.dev/tools/slack-cli/guides/installing-the-slack-cli-for-mac-and-linux/) | ||
| - [Slack CLI for Windows](https://docs.slack.dev/tools/slack-cli/guides/installing-the-slack-cli-for-windows/) | ||
|
|
||
| You'll also need to log in if this is your first time using the Slack CLI. | ||
|
|
||
| ```sh | ||
| slack login | ||
| ``` | ||
|
|
||
| #### Initializing the project | ||
|
|
||
| ```sh | ||
| slack create my-casey-agent --template slack-samples/bolt-python-support-agent | ||
| cd my-casey-agent | ||
| ``` | ||
|
|
||
| </details> | ||
|
|
||
| <details><summary><strong>Using App Settings</strong></summary> | ||
|
|
||
| #### Create Your Slack App | ||
|
|
||
| 1. Open [https://api.slack.com/apps/new](https://api.slack.com/apps/new) and choose "From an app manifest" | ||
| 2. Choose the workspace you want to install the application to | ||
| 3. Copy the contents of [manifest.json](./manifest.json) into the text box that says `*Paste your manifest code here*` (within the JSON tab) and click _Next_ | ||
| 4. Review the configuration and click _Create_ | ||
| 5. Click _Install to Workspace_ and _Allow_ on the screen that follows. You'll then be redirected to the App Configuration dashboard. | ||
|
|
||
| #### Environment Variables | ||
|
|
||
| Before you can run the app, you'll need to store some environment variables. | ||
|
|
||
| 1. Rename `.env.sample` to `.env`. | ||
| 2. Open your apps setting page from [this list](https://api.slack.com/apps), click _OAuth & Permissions_ in the left hand menu, then copy the _Bot User OAuth Token_ into your `.env` file under `SLACK_BOT_TOKEN`. | ||
|
|
||
| ```sh | ||
| SLACK_BOT_TOKEN=YOUR_SLACK_BOT_TOKEN | ||
| ``` | ||
|
|
||
| 3. Click _Basic Information_ from the left hand menu and follow the steps in the _App-Level Tokens_ section to create an app-level token with the `connections:write` scope. Copy that token into your `.env` as `SLACK_APP_TOKEN`. | ||
|
|
||
| ```sh | ||
| SLACK_APP_TOKEN=YOUR_SLACK_APP_TOKEN | ||
| ``` | ||
|
|
||
| #### Initializing the project | ||
|
|
||
| ```sh | ||
| git clone https://github.com/slack-samples/bolt-python-support-agent.git my-casey-agent | ||
| cd my-casey-agent | ||
| ``` | ||
|
|
||
| </details> | ||
|
|
||
| ### Setup your python virtual environment | ||
|
|
||
| ```sh | ||
| python3 -m venv .venv | ||
| source .venv/bin/activate # for Windows OS, .\.venv\Scripts\Activate instead should work | ||
| ``` | ||
|
|
||
| #### Install dependencies | ||
|
|
||
| ```sh | ||
| pip install -r requirements.txt | ||
| # or pip install -e . | ||
| ``` | ||
|
|
||
| ## Providers | ||
| This repo contains the same app built with two different AI agent frameworks. Pick the one that fits your stack: | ||
|
|
||
| ### OpenAI Setup | ||
| | | [Pydantic AI](./pydantic-ai/) | [OpenAI Agents SDK](./openai-agents-sdk/) | | ||
| |---|---|---| | ||
| | **Framework** | [pydantic-ai](https://ai.pydantic.dev/) | [openai-agents](https://openai.github.io/openai-agents-python/) | | ||
| | **Get started** | [View README](./pydantic-ai/README.md) | [View README](./openai-agents-sdk/README.md) | | ||
|
|
||
| This app uses OpenAI's `gpt-4o-mini` model through Pydantic AI. | ||
| Both implementations share the same Slack listener layer, the same five simulated IT tools, and the same user experience. The only difference is how the agent is defined and executed under the hood. | ||
|
|
||
| 1. Create an API key from your [OpenAI dashboard](https://platform.openai.com/api-keys). | ||
| 1. Rename `.env.sample` to `.env`. | ||
| 3. Save the OpenAI API key to `.env`: | ||
| ## What Casey Can Do | ||
|
|
||
| ```sh | ||
| OPENAI_API_KEY=YOUR_OPENAI_API_KEY | ||
| ``` | ||
|
|
||
| ## Development | ||
|
|
||
| ### Starting the app | ||
|
|
||
| <details><summary><strong>Using the Slack CLI</strong></summary> | ||
|
|
||
| #### Slack CLI | ||
|
|
||
| ```sh | ||
| slack run | ||
| ``` | ||
| </details> | ||
|
|
||
| <details><summary><strong>Using the Terminal</strong></summary> | ||
|
|
||
| #### Terminal | ||
|
|
||
| ```sh | ||
| python3 app.py | ||
| ``` | ||
|
|
||
| </details> | ||
|
|
||
| ### Using the App | ||
|
|
||
| Once Casey is running, there are three ways to interact: | ||
|
|
||
| **App Home** — Open Casey in Slack and click the _Home_ tab. You'll see five category buttons. Click one to open a modal, describe your issue, and submit. Casey will start a DM thread with you containing a diagnosis and next steps. | ||
|
|
||
| **Direct Messages** — Open a DM with Casey and describe your issue. Casey will react with :eyes: while processing, then reply in a thread. Send follow-up messages in the same thread and Casey will maintain the full conversation context. | ||
|
|
||
| **Channel @mentions** — In any channel where Casey has been added, type `@Casey` followed by your issue. Casey responds in a thread so the channel stays clean. | ||
|
|
||
| Casey will add a :white_check_mark: reaction when it believes an issue has been resolved, and occasionally adds a contextual emoji reaction to keep things friendly. | ||
|
|
||
| ### Linting | ||
|
|
||
| ```sh | ||
| # Run ruff check from root directory for linting | ||
| ruff check | ||
|
|
||
| # Run ruff format from root directory for code formatting | ||
| ruff format | ||
| ``` | ||
|
|
||
| ## Project Structure | ||
|
|
||
| ### `manifest.json` | ||
|
|
||
| `manifest.json` is a configuration for Slack apps. With a manifest, you can create an app with a pre-defined configuration, or adjust the configuration of an existing app. | ||
|
|
||
| ### `app.py` | ||
|
|
||
| `app.py` is the entry point for the application and is the file you'll run to start the server. This project aims to keep this file as thin as possible, primarily using it as a way to route inbound requests. | ||
|
|
||
| ### `/listeners` | ||
|
|
||
| Every incoming request is routed to a "listener". This directory groups each listener based on the Slack Platform feature used. | ||
|
|
||
| **`/listeners/events`** — Handles incoming events: | ||
|
|
||
| - `app_home_opened.py` — Publishes the App Home view with category buttons. | ||
| - `app_mentioned.py` — Responds to `@Casey` mentions in channels. | ||
| - `message_im.py` — Responds to direct messages from users. | ||
|
|
||
| **`/listeners/actions`** — Handles interactive components: | ||
|
|
||
| - `category_buttons.py` — Opens the issue submission modal when a category button is clicked. | ||
| - `feedback.py` — Handles thumbs up/down feedback on Casey's responses. | ||
|
|
||
| **`/listeners/views`** — Handles view submissions and builds Block Kit views: | ||
|
|
||
| - `issue_modal.py` — Processes modal submissions, starts a DM thread, and runs the agent. | ||
| - `app_home_builder.py` — Constructs the App Home Block Kit view. | ||
| - `modal_builder.py` — Constructs the issue submission modal. | ||
| - `feedback_block.py` — Creates the feedback button block attached to responses. | ||
|
|
||
| ### `/agent` | ||
|
|
||
| The `casey.py` file defines the Pydantic AI Agent with a system prompt, personality, and tool configuration. | ||
|
|
||
| The `deps.py` file defines the `CaseyDeps` dataclass passed to the agent at runtime, providing access to the Slack client and conversation context. | ||
| Casey gives your team instant IT support through three entry points: | ||
|
|
||
| The `tools` directory contains five IT helpdesk tools that the agent can call during a conversation. | ||
| * **App Home** — Choose from common issue categories. A modal collects details, then Casey starts a DM thread with a resolution. | ||
| * **Direct Messages** — Describe any IT issue and Casey responds in-thread, maintaining context across follow-ups. | ||
| * **Channel @mentions** — Mention `@Casey` in any channel to get help without leaving the conversation. | ||
|
|
||
| ### `/conversation` | ||
| Behind the scenes, Casey has access to five simulated tools: knowledge base search, support ticket creation, password reset, system status checks, and user permissions lookup. | ||
|
|
||
| The `store.py` file implements a thread-safe in-memory conversation history store, keyed by channel and thread. This enables multi-turn conversations where Casey remembers previous context within a thread. | ||
| > **Note:** All tools return simulated data for demonstration purposes. In a production app, these would connect to your actual IT systems. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # CLAUDE.md | ||
|
|
||
| This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. | ||
|
|
||
| See the root `../.claude/CLAUDE.md` for monorepo-wide architecture, commands, and a comparison of the two implementations. | ||
|
|
||
| ## OpenAI Agents SDK Specifics | ||
|
|
||
| **Agent (`agent/support_agent.py`)** is an `Agent[CaseyDeps]` with the model set directly on the agent (`model="gpt-4o-mini"`). Tools are `@function_tool`-decorated async functions, each in its own file under `agent/tools/`. Execution uses `Runner.run_sync(casey_agent, input=..., context=deps)`. | ||
|
|
||
| **Conversation history** is stored as a generic `list` and must be manually combined with new user input before passing to `Runner.run_sync()`: `input_items = history + [{"role": "user", "content": text}]`. After execution, `result.to_input_list()` is stored back. | ||
|
|
||
| **Feedback blocks** use the native `FeedbackButtonsElement` from `slack_sdk.models.blocks`. A single `feedback` action ID is registered. | ||
|
|
||
| The `message_im` handler adds the `:eyes:` reaction on every message (including thread replies). |
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "manifest": { | ||
| "source": "remote" | ||
| }, | ||
| "project_id": "f4a2c8e1-7b3d-4e9a-b5f6-2d8c1a0e3f7b" | ||
| } |
File renamed without changes.
File renamed without changes.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👾