A FastAPI + vanilla JS chat app fronting an Anthropic Claude agent for order status, returns, and policy questions. Architecture: - agent.py: system prompt, runtime reminder injection, output validation, agentic tool-use loop with prompt caching on the system prompt block - tools.py: four tools (lookup_order, check_return_eligibility, initiate_return, lookup_policy) with per-session SessionGuardState enforcing protocol ordering on the tool side - mock_data.py: orders, return policy, and FAQ entries used as the single source of truth by both the prompt and the tools - server.py: FastAPI app exposing /api/chat, /health, and the static UI - static/: vanilla HTML/CSS/JS chat UI, no build step - tests/: 30 tests covering tool-side enforcement, the privacy boundary, output validation, and the agent loop with a mocked Anthropic client - deploy/: systemd unit and nginx site config for production
110 lines
4.1 KiB
Python
110 lines
4.1 KiB
Python
"""In-memory data fixtures for orders, returns, and FAQ policies.
|
|
|
|
`ORDERS` and `RETURN_POLICY` are read by both the system prompt (so the prompt
|
|
quotes policy verbatim instead of paraphrasing) and the tool handlers (so the
|
|
two never drift apart). `RETURNS` is mutated by `initiate_return` at runtime.
|
|
"""
|
|
|
|
from datetime import date, timedelta
|
|
|
|
# A frozen "today" so the four-order fixture stays deterministic across runs.
|
|
TODAY = date(2026, 4, 14)
|
|
|
|
|
|
def _days_ago(n: int) -> str:
|
|
return (TODAY - timedelta(days=n)).isoformat()
|
|
|
|
|
|
RETURN_POLICY: dict = {
|
|
"window_days": 30,
|
|
"condition_requirements": "Items must be unread, undamaged, and in their original packaging.",
|
|
"refund_method": "Refunds are issued to the original payment method.",
|
|
"refund_timeline_days": 7,
|
|
"non_returnable_categories": ["ebooks", "audiobooks", "gift cards", "personalized items"],
|
|
}
|
|
|
|
|
|
# Four orders covering the interesting scenarios. Sarah Chen has two orders so
|
|
# the agent must disambiguate when she says "my order".
|
|
ORDERS: dict = {
|
|
"BK-10042": {
|
|
"order_id": "BK-10042",
|
|
"customer_name": "Sarah Chen",
|
|
"email": "sarah.chen@example.com",
|
|
"status": "delivered",
|
|
"order_date": _days_ago(20),
|
|
"delivered_date": _days_ago(15),
|
|
"tracking_number": "1Z999AA10123456784",
|
|
"items": [
|
|
{"title": "The Goldfinch", "author": "Donna Tartt", "price": 16.99, "category": "fiction"},
|
|
{"title": "Sapiens", "author": "Yuval Noah Harari", "price": 19.99, "category": "nonfiction"},
|
|
],
|
|
"total": 36.98,
|
|
},
|
|
"BK-10089": {
|
|
"order_id": "BK-10089",
|
|
"customer_name": "James Murphy",
|
|
"email": "james.murphy@example.com",
|
|
"status": "shipped",
|
|
"order_date": _days_ago(4),
|
|
"delivered_date": None,
|
|
"tracking_number": "1Z999AA10987654321",
|
|
"items": [
|
|
{"title": "Project Hail Mary", "author": "Andy Weir", "price": 18.99, "category": "fiction"},
|
|
],
|
|
"total": 18.99,
|
|
},
|
|
"BK-10103": {
|
|
"order_id": "BK-10103",
|
|
"customer_name": "Sarah Chen",
|
|
"email": "sarah.chen@example.com",
|
|
"status": "processing",
|
|
"order_date": _days_ago(1),
|
|
"delivered_date": None,
|
|
"tracking_number": None,
|
|
"items": [
|
|
{"title": "Tomorrow, and Tomorrow, and Tomorrow", "author": "Gabrielle Zevin", "price": 17.99, "category": "fiction"},
|
|
],
|
|
"total": 17.99,
|
|
},
|
|
"BK-9871": {
|
|
"order_id": "BK-9871",
|
|
"customer_name": "Maria Gonzalez",
|
|
"email": "maria.gonzalez@example.com",
|
|
"status": "delivered",
|
|
"order_date": _days_ago(60),
|
|
"delivered_date": _days_ago(55),
|
|
"tracking_number": "1Z999AA10555555555",
|
|
"items": [
|
|
{"title": "The Midnight Library", "author": "Matt Haig", "price": 15.99, "category": "fiction"},
|
|
],
|
|
"total": 15.99,
|
|
},
|
|
}
|
|
|
|
|
|
# Verbatim FAQ entries returned by `lookup_policy`. The agent quotes these
|
|
# without paraphrasing.
|
|
POLICIES: dict[str, str] = {
|
|
"shipping": (
|
|
"Standard shipping is free on orders over $25 and takes 3-5 business days. "
|
|
"Expedited shipping (1-2 business days) is $9.99. We ship to all 50 US states. "
|
|
"International shipping is not currently available."
|
|
),
|
|
"password_reset": (
|
|
"To reset your password, go to bookly.com/account/login and click \"Forgot password.\" "
|
|
"Enter the email on your account and we will send you a reset link. "
|
|
"The link expires after 24 hours. If you do not receive the email, check your spam folder."
|
|
),
|
|
"returns_overview": (
|
|
"You can return most items within 30 days of delivery for a full refund to your original "
|
|
"payment method. Items must be unread, undamaged, and in their original packaging. "
|
|
"Ebooks, audiobooks, gift cards, and personalized items are not returnable. "
|
|
"Refunds typically post within 7 business days of us receiving the return."
|
|
),
|
|
}
|
|
|
|
|
|
# Mutated at runtime by `initiate_return`. Keyed by return_id.
|
|
RETURNS: dict[str, dict] = {}
|