feat: complete phase 5 -- error hardening, frontend, Docker, demo, docs

Backend:
- ConversationTracker: Protocol + PostgresConversationTracker for lifecycle tracking
- Error handler: ErrorCategory enum, classify_error(), with_retry() exponential backoff
- Wire PostgresAnalyticsRecorder + ConversationTracker into ws_handler
- Rate limiting (10 msg/10s per thread), edge case hardening
- Health endpoint GET /api/health, version 0.5.0
- Demo seed data script + sample OpenAPI spec

Frontend (all new):
- React Router with NavBar (Chat / Replay / Dashboard / Review)
- ReplayListPage + ReplayPage with ReplayTimeline component
- DashboardPage with MetricCard, range selector, zero-state
- ReviewPage for OpenAPI classification review
- ErrorBanner for WebSocket disconnect handling
- API client (api.ts) with typed fetch wrappers

Infrastructure:
- Frontend Dockerfile (multi-stage node -> nginx)
- nginx.conf with SPA routing + API/WS proxy
- docker-compose.yml with frontend service + healthchecks
- .env.example files (root + backend)

Documentation:
- README.md with quick start and architecture
- Agent configuration guide
- OpenAPI import guide
- Deployment guide
- Demo script

48 new tests, 449 total passing, 92.87% coverage
This commit is contained in:
Yaojia Wang
2026-03-31 21:20:06 +02:00
parent 38644594d2
commit 0e78e5b06b
44 changed files with 3397 additions and 169 deletions

View File

@@ -0,0 +1,238 @@
openapi: "3.0.3"
info:
title: "E-Commerce API"
description: "Sample e-commerce API for Smart Support demo."
version: "1.0.0"
servers:
- url: "https://api.example-shop.com/v1"
description: "Production server"
paths:
/orders/{order_id}:
get:
operationId: getOrder
summary: "Get order details"
description: "Retrieve the full details of a specific order."
parameters:
- name: order_id
in: path
required: true
schema:
type: string
responses:
"200":
description: "Order details"
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
/orders/{order_id}/cancel:
post:
operationId: cancelOrder
summary: "Cancel an order"
description: "Cancel an order that has not yet been shipped."
parameters:
- name: order_id
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
reason:
type: string
responses:
"200":
description: "Order cancelled"
"400":
description: "Order cannot be cancelled (already shipped)"
/orders/{order_id}/refund:
post:
operationId: refundOrder
summary: "Request a refund"
description: "Submit a refund request for a completed order."
parameters:
- name: order_id
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
amount:
type: number
description: "Refund amount in USD. Leave null for full refund."
reason:
type: string
responses:
"200":
description: "Refund submitted"
"400":
description: "Invalid refund request"
/customers/{customer_id}:
get:
operationId: getCustomer
summary: "Get customer profile"
description: "Retrieve customer profile and account information."
parameters:
- name: customer_id
in: path
required: true
schema:
type: string
responses:
"200":
description: "Customer profile"
content:
application/json:
schema:
$ref: "#/components/schemas/Customer"
/customers/{customer_id}/orders:
get:
operationId: listCustomerOrders
summary: "List customer orders"
description: "Get a paginated list of orders for a customer."
parameters:
- name: customer_id
in: path
required: true
schema:
type: string
- name: page
in: query
schema:
type: integer
default: 1
- name: per_page
in: query
schema:
type: integer
default: 20
responses:
"200":
description: "List of orders"
/products/{product_id}:
get:
operationId: getProduct
summary: "Get product details"
description: "Retrieve product information including inventory status."
parameters:
- name: product_id
in: path
required: true
schema:
type: string
responses:
"200":
description: "Product details"
/support/tickets:
post:
operationId: createSupportTicket
summary: "Create support ticket"
description: "Open a new support ticket for a customer issue."
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateTicketRequest"
responses:
"201":
description: "Ticket created"
/support/tickets/{ticket_id}:
get:
operationId: getSupportTicket
summary: "Get support ticket"
description: "Retrieve a support ticket and its conversation history."
parameters:
- name: ticket_id
in: path
required: true
schema:
type: string
responses:
"200":
description: "Ticket details"
components:
schemas:
Order:
type: object
properties:
order_id:
type: string
customer_id:
type: string
status:
type: string
enum: [pending, processing, shipped, delivered, cancelled, refunded]
items:
type: array
items:
$ref: "#/components/schemas/OrderItem"
total_usd:
type: number
created_at:
type: string
format: date-time
OrderItem:
type: object
properties:
product_id:
type: string
name:
type: string
quantity:
type: integer
unit_price_usd:
type: number
Customer:
type: object
properties:
customer_id:
type: string
email:
type: string
name:
type: string
tier:
type: string
enum: [standard, premium, vip]
created_at:
type: string
format: date-time
CreateTicketRequest:
type: object
required: [customer_id, subject, description]
properties:
customer_id:
type: string
subject:
type: string
description:
type: string
priority:
type: string
enum: [low, medium, high, urgent]
default: medium