Conversation
WalkthroughThis PR establishes a complete Facebook Messenger AI Bot project, introducing configuration files, database migrations, Pydantic data models, FastAPI endpoints, CLI setup workflow, and service layer components for agent orchestration, Copilot SDK integration, Facebook Graph API communication, and website content extraction. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI as setup_cli.py
participant Scraper
participant CopilotSvc as copilot_service
participant RefDoc as reference_doc.py
participant DB as repository.py
participant Config as config.py
User->>CLI: Run setup command
CLI->>Config: get_settings()
CLI->>Scraper: scrape_website(url)
Scraper-->>CLI: text_chunks[]
CLI->>CopilotSvc: synthesize_reference(url, chunks)
CopilotSvc-->>CLI: markdown_content
CLI->>RefDoc: build_reference_doc(copilot, url, chunks)
RefDoc->>CopilotSvc: synthesize_reference(...)
RefDoc-->>CLI: (content, hash)
CLI->>DB: create_reference_document(content, url, hash)
DB-->>CLI: doc_id
User->>CLI: Provide tone & Facebook config
CLI->>DB: create_bot_configuration(page_id, website_url, doc_id, tone, tokens)
DB-->>CLI: BotConfiguration
CLI-->>User: Print webhook URL & next steps
sequenceDiagram
participant FB as Facebook
participant Webhook as webhook.py
participant API as main.py
participant Agent as agent_service.py
participant Copilot as copilot_service
participant FBService as facebook_service
participant DB as repository.py
FB->>Webhook: POST /webhook (message payload)
Webhook->>API: Parse & extract message/sender
API->>DB: get_bot_configuration_by_page_id(page_id)
DB-->>API: bot_config + reference_doc
API->>Agent: respond(context, user_message)
Agent->>Copilot: chat(system_prompt, messages)
Copilot-->>Agent: response_text
Agent-->>API: AgentResponse{message, confidence, escalation}
API->>DB: save_message_history(...)
API->>FBService: send_message(token, recipient_id, text)
FBService->>FB: POST /graph.facebook.com/v18.0/me/messages
FB-->>FBService: 200 OK
FBService-->>API: success
API-->>Webhook: 200 OK
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
| """Fallback to OpenAI when Copilot is unavailable.""" | ||
| # TODO: Implement OpenAI API call | ||
| # This requires openai package and API key from settings | ||
| raise NotImplementedError("OpenAI fallback not yet implemented") |
There was a problem hiding this comment.
Bug: The _fallback_to_openai function raises a NotImplementedError. This will cause a crash whenever the Copilot service is unavailable, disabled, or returns an error.
Severity: HIGH
Suggested Fix
Implement the OpenAI API call within the _fallback_to_openai function. This involves using the openai package, retrieving the openai_api_key from settings, and making the appropriate chat completion request to the OpenAI API, returning the content of the response.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: src/services/copilot_service.py#L118
Potential issue: The `_fallback_to_openai` method in `copilot_service.py` is intended to
provide a recovery mechanism when the Copilot service is unavailable. However, it
currently raises a `NotImplementedError`. This fallback is invoked when
`copilot_enabled` is `False`, when `is_available()` returns `False`, or when the Copilot
API call fails. In any of these scenarios, which are anticipated production conditions,
the application will crash, rendering the service non-functional. The feature is
documented and the configuration supports it, but the implementation is incomplete.
Did we get this right? 👍 / 👎 to inform future reviews.
There was a problem hiding this comment.
Pull request overview
Initial project scaffolding for a FastAPI-based Facebook Messenger bot that scrapes a website, synthesizes a reference document via a Copilot SDK wrapper (with planned fallback), and stores configuration/history in Supabase.
Changes:
- Added FastAPI app + webhook/health endpoints and deployment config (Railway).
- Implemented initial service layer (scraper, Copilot wrapper, reference doc builder, agent response service, Facebook send API helper).
- Added Supabase client/repository plus initial DB migration and setup CLI, along with architecture/guardrails docs.
Reviewed changes
Copilot reviewed 32 out of 33 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| src/services/scraper.py | Adds async website fetch + text extraction and chunking. |
| src/services/reference_doc.py | Builds synthesized markdown reference doc + content hash. |
| src/services/facebook_service.py | Adds async helper to send messages via Facebook Graph API. |
| src/services/copilot_service.py | Introduces Copilot SDK wrapper with availability check + planned fallback. |
| src/services/agent_service.py | Adds agent service building prompts and producing AgentResponse. |
| src/services/init.py | Declares services package. |
| src/models/messenger.py | Adds Pydantic models for webhook payload/message shapes. |
| src/models/config_models.py | Adds configuration models for website/tone/Facebook/bot config. |
| src/models/agent_models.py | Adds agent context/response Pydantic models. |
| src/models/init.py | Declares models package. |
| src/main.py | Creates FastAPI app, lifespan init, CORS, and router registration. |
| src/db/repository.py | Adds Supabase repository functions for configs/docs/history. |
| src/db/client.py | Adds Supabase client initialization. |
| src/db/init.py | Declares db package. |
| src/config.py | Adds Pydantic settings for env-based configuration. |
| src/cli/setup_cli.py | Adds Typer CLI for interactive setup + persistence. |
| src/cli/init.py | Declares CLI package. |
| src/api/webhook.py | Adds webhook verification and placeholder POST handler. |
| src/api/setup.py | Adds placeholder setup API module. |
| src/api/health.py | Adds /health endpoint. |
| src/api/init.py | Declares API package. |
| src/init.py | Declares src package. |
| railway.toml | Railway build/deploy configuration. |
| pyproject.toml | Project metadata and dependencies. |
| migrations/001_initial.sql | Initial DB schema for bot configs, reference docs, and message history. |
| main.py | Adds a root entrypoint stub. |
| PROJECT_STRUCTURE.md | Documents folder structure and file responsibilities. |
| GUARDRAILS.md | Documents safety/guardrails policies and operational guidelines. |
| ARCHITECTURE.md | Documents system architecture, data flow, and fallback logic. |
| AGENTS.md | Conventions and contributor/developer guidance for the project. |
| .python-version | Pins Python version. |
| .gitignore | Adds Python/venv/env/test/cache ignores. |
| .env.example | Adds environment variable template and explanations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| settings = get_settings() | ||
| supabase = get_supabase_client() | ||
| copilot = CopilotService( |
There was a problem hiding this comment.
supabase = get_supabase_client() is assigned but never used in this CLI command (the repository functions re-create their own client). Remove the unused variable, or refactor the repository to accept an injected client so the CLI can reuse this instance.
| async def scrape_website(url: str, max_pages: int = 5) -> List[str]: | ||
| """ | ||
| Scrape website and return text chunks. | ||
|
|
||
| Args: | ||
| url: Root URL to scrape | ||
| max_pages: Maximum number of pages to scrape | ||
|
|
||
| Returns: | ||
| List of text chunks (500-800 words each) | ||
| """ |
There was a problem hiding this comment.
max_pages parameter is unused and the implementation only fetches the root url once. Either implement multi-page crawling (respecting max_pages) or remove the parameter/update the docstring to avoid misleading callers.
| if not self.enabled or not await self.is_available(): | ||
| # Fallback to OpenAI or other LLM | ||
| logger.warning("Copilot SDK not available, falling back to OpenAI") | ||
| return await self._fallback_to_openai(system_prompt, messages) |
There was a problem hiding this comment.
chat() calls await self.is_available() for every request, and is_available() creates a new HTTP client and performs a network call each time. This adds an extra round-trip per message. Consider caching the availability result for a short TTL, or performing a single startup check and then handling runtime failures via exception handling/retries.
| app.add_middleware( | ||
| CORSMiddleware, | ||
| allow_origins=["*"], # Configure appropriately for production | ||
| allow_credentials=True, |
There was a problem hiding this comment.
CORS middleware is configured with allow_origins=["*"] while also setting allow_credentials=True. Starlette/FASTAPI CORS middleware forbids wildcard origins when credentials are allowed and will error/behave incorrectly. Use an explicit origin allowlist (recommended), or set allow_credentials=False if wildcard origins are required.
| allow_credentials=True, | |
| allow_credentials=False, |
| settings = get_settings() | ||
| supabase = get_supabase_client() | ||
| copilot = CopilotService( | ||
| base_url=settings.copilot_cli_host, | ||
| enabled=settings.copilot_enabled | ||
| ) | ||
|
|
There was a problem hiding this comment.
The CLI calls get_settings() before prompting for Facebook credentials, but Settings requires facebook_page_access_token and facebook_verify_token (non-optional). This means the CLI will fail with a validation error unless those env vars are already set—making the prompts redundant. Consider splitting settings (CLI vs server), or making the Facebook fields optional for CLI execution and validating only when needed.
| create table reference_documents ( | ||
| id uuid primary key default gen_random_uuid(), | ||
| bot_id uuid not null references bot_configurations(id) on delete cascade, | ||
| content text not null, | ||
| source_url text not null, | ||
| content_hash text not null, | ||
| created_at timestamptz default now() | ||
| ); |
There was a problem hiding this comment.
This table requires bot_id uuid not null, but the repository/CLI flow creates a reference document before a bot exists. To support that flow, bot_id must be nullable (or the flow must be inverted to create the bot first and then create the reference document with bot_id).
| if settings.copilot_enabled: | ||
| is_available = await copilot.is_available() | ||
| if not is_available: | ||
| print("Warning: Copilot SDK not available, will use OpenAI fallback") | ||
|
|
There was a problem hiding this comment.
Avoid using print() for runtime warnings; it bypasses log configuration and is discouraged by the project’s own guidance (see AGENTS.md:312). Use the module logger (or logging.getLogger(__name__)) so warnings are captured consistently in production.
| # Step 6: Create bot configuration | ||
| typer.echo("\nCreating bot configuration...") | ||
| try: | ||
| bot_config = create_bot_configuration( |
There was a problem hiding this comment.
Variable bot_config is not used.
| bot_config = create_bot_configuration( | |
| create_bot_configuration( |
| """GitHub Copilot SDK wrapper service.""" | ||
|
|
||
| import logging | ||
| from typing import Any |
There was a problem hiding this comment.
Import of 'Any' is not used.
| from typing import Any |
|
|
||
| import asyncio | ||
| import typer | ||
| from typing_extensions import Annotated |
There was a problem hiding this comment.
Import of 'Annotated' is not used.
| from typing_extensions import Annotated |

TL;DR
Initial project setup for a Facebook Messenger AI bot that answers questions based on scraped website content using GitHub Copilot SDK.
What changed?
This PR establishes the foundational structure for the Facebook Messenger AI bot project:
How to test?
.env.exampleto.envand fill in your credentialsuv syncuv run python -m src.cli.setup_cli setupuv run uvicorn src.main:app --reloadcurl "http://localhost:8000/webhook?hub.mode=subscribe&hub.verify_token=your_token&hub.challenge=challenge"Why make this change?
This project creates a production-ready Facebook Messenger bot that can answer questions about a website using AI. The bot scrapes website content, synthesizes it into a reference document using GitHub Copilot SDK, and uses this knowledge to respond to user messages. This implementation provides a scalable foundation with proper error handling, fallback mechanisms, and a clean architecture.
Summary by CodeRabbit
Release Notes
New Features
Documentation
Chores
✏️ Tip: You can customize this high-level summary in your review settings.