Skip to content

Commit f1adaf9

Browse files
mwbrookszimeg
andauthored
refactor: restructure repo as monorepo with Pydantic AI and OpenAI Agents SDK (#3)
* refactor: move all files into pydantic-ai subdirectory * feat: add OpenAI Agents SDK sample app * build: update ruff workflow for monorepo with matrix strategy * feat(openai): update to 'Casey - OpenAI SDK' * docs: add monorepo CLAUDE.md and update per-project docs * docs: add monorepo README * feat(openai): sync eyes reaction and feedback blocks with pydantic-ai * docs: remove agent style row from README table * Update .github/workflows/ruff.yml Co-authored-by: Eden Zimbelman <zim@o526.net> * chore: add .gitignore --------- Co-authored-by: Eden Zimbelman <zim@o526.net>
1 parent ce420b3 commit f1adaf9

80 files changed

Lines changed: 1800 additions & 261 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/CLAUDE.md

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,17 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
44

55
## What This Is
66

7-
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.
7+
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:
8+
9+
- `pydantic-ai/` — Built with **Pydantic AI**
10+
- `openai-agents-sdk/` — Built with **OpenAI Agents SDK**
11+
12+
All tool data (knowledge base, tickets, password resets, system status, permissions) is hardcoded for demo purposes.
813

914
## Commands
1015

16+
All commands must be run from within the respective project directory (`pydantic-ai/` or `openai-agents-sdk/`).
17+
1118
```sh
1219
# Run the app (requires .env with OPENAI_API_KEY; Slack tokens optional with CLI)
1320
slack run # via Slack CLI
@@ -21,28 +28,46 @@ ruff format --check .
2128
pytest
2229
```
2330

24-
## Architecture
31+
## Monorepo Structure
32+
33+
```
34+
.github/ # Shared CI workflows and dependabot config
35+
pydantic-ai/ # Pydantic AI implementation
36+
openai-agents-sdk/ # OpenAI Agents SDK implementation
37+
```
38+
39+
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.
40+
41+
## Architecture (shared across both implementations)
2542

2643
Three-layer design: **app.py****listeners/****agent/**
2744

2845
**Entry point (`app.py`)** initializes Bolt with Socket Mode and calls `register_listeners(app)`.
2946

3047
**Listeners** are organized by Slack platform feature:
3148
- `listeners/events/``app_home_opened`, `app_mentioned`, `message_im`
32-
- `listeners/actions/``category_buttons` (regex `^category_`), `feedback_good`, `feedback_bad`
49+
- `listeners/actions/``category_buttons` (regex `^category_`), feedback handlers
3350
- `listeners/views/``issue_submission` modal handler
3451

3552
Each sub-package has a `register(app)` function called from `listeners/__init__.py`.
3653

37-
**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/`.
54+
**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.
3855

39-
**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()`.
56+
**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.
4057

41-
**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/`.
58+
**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.
4259

43-
## Key Patterns
60+
## Key Differences Between Implementations
4461

45-
- 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()`.
46-
- Emoji/reaction logic is in the handlers, not the agent. Resolution detection checks `result.output` against a hardcoded phrase list.
47-
- 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()`.
48-
- The `message_im` handler filters out bot messages (`event.bot_id`) and subtypes to avoid self-reply loops.
62+
| Aspect | Pydantic AI | OpenAI Agents SDK |
63+
|--------|-------------|-------------------|
64+
| Agent file | `agent/casey.py` | `agent/support_agent.py` |
65+
| Agent definition | `Agent(deps_type=CaseyDeps)` | `Agent[CaseyDeps](model="gpt-4o-mini")` |
66+
| Model config | Passed at runtime via `run_sync(model=DEFAULT_MODEL)` | Set directly on agent constructor |
67+
| Tool definition | Plain async functions | `@function_tool` decorated functions |
68+
| Tool context param | `RunContext[CaseyDeps]` | `RunContextWrapper[CaseyDeps]` |
69+
| Execution | `casey_agent.run_sync(text, model=..., deps=..., message_history=...)` | `Runner.run_sync(casey_agent, input=..., context=...)` |
70+
| Result output | `result.output` | `result.final_output` |
71+
| Result messages | `result.all_messages()` | `result.to_input_list()` |
72+
| History type | `list[ModelMessage]` (framework-native) | `list` (generic, manually constructed) |
73+
| Feedback blocks | Native `FeedbackButtonsElement` | Native `FeedbackButtonsElement` |

.github/dependabot.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
version: 2
22
updates:
33
- package-ecosystem: "pip"
4-
directory: "/"
4+
directories:
5+
- "/"
6+
- "/openai-agents-sdk"
7+
- "/pydantic-ai"
58
schedule:
6-
interval: "monthly"
9+
interval: "weekly"
710
labels:
811
- "pip"
912
- "dependencies"
1013
- package-ecosystem: "github-actions"
1114
directory: "/"
1215
schedule:
13-
interval: "monthly"
16+
interval: "weekly"

.github/workflows/ruff.yml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,24 @@ on:
66
pull_request:
77

88
jobs:
9-
build:
9+
lint:
1010
runs-on: ubuntu-latest
1111
timeout-minutes: 5
1212
strategy:
1313
matrix:
14-
python-version: ["3.13"]
14+
directory:
15+
- openai-agents-sdk
16+
- pydantic-ai
17+
defaults:
18+
run:
19+
working-directory: ${{ matrix.directory }}
1520

1621
steps:
1722
- uses: actions/checkout@v6
18-
- name: Set up Python ${{ matrix.python-version }}
23+
- name: Set up Python
1924
uses: actions/setup-python@v6
2025
with:
21-
python-version: ${{ matrix.python-version }}
26+
python-version: "3.13"
2227
- name: Install dependencies
2328
run: |
2429
pip install -U pip

.gitignore

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,2 @@
1-
# general things to ignore
2-
build/
3-
dist/
4-
docs/_sources/
5-
docs/.doctrees
6-
.eggs/
7-
*.egg-info/
8-
*.egg
9-
*.py[cod]
10-
__pycache__/
11-
*.so
12-
*~
13-
14-
# virtualenv
15-
env*/
16-
venv/
17-
.venv*
18-
.env*
19-
!.env.sample
20-
21-
# codecov / coverage
22-
.coverage
23-
cov_*
24-
coverage.xml
25-
26-
# due to using tox and pytest
27-
.tox
28-
.cache
29-
.pytest_cache/
30-
.python-version
31-
pip
32-
.mypy_cache/
33-
34-
# misc
35-
tmp.txt
36-
.DS_Store
37-
logs/
38-
*.db
39-
.pytype/
40-
.idea/
41-
42-
# claude
1+
# Claude
432
.claude/*.local.json

.slack/config.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)