Integrates a new bash script for automated end-to-end testing of the Timmy API. Updates API routes to expose payment hashes in stub mode for easier invoice payment simulation during testing. Modifies test plan documentation to include the new automated script. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 418bf6f8-212b-4bb0-a7a5-8231a061da4e Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 6f2776b0-a913-41d3-a988-759a82feb6f3 Replit-Helium-Checkpoint-Created: true
396 lines
8.2 KiB
Markdown
396 lines
8.2 KiB
Markdown
# Timmy API — Technical Specifications Extraction
|
|
|
|
## 1. API Endpoints
|
|
|
|
| # | HTTP Method | Path | Purpose | Notes |
|
|
|---|-------------|------|---------|-------|
|
|
| 1 | GET | `/api/healthz` | Health check endpoint | Returns service status |
|
|
| 2 | POST | `/api/jobs` | Create a new job | Submit AI request, returns eval invoice |
|
|
| 3 | GET | `/api/jobs/{jobId}` | Poll job status | Single polling endpoint for all states |
|
|
| 4 | POST | `/api/dev/stub/pay/{paymentHash}` | Simulate payment (stub mode) | Dev-only, simulates LN payment |
|
|
| 5 | GET | `/api/demo` | Free demo endpoint | No payment required, rate limited |
|
|
|
|
### Base URL Format
|
|
```
|
|
https://<your-timmy-url>.replit.app
|
|
```
|
|
Replace `BASE` variable with actual URL in all commands.
|
|
|
|
---
|
|
|
|
## 2. Request/Response Examples
|
|
|
|
### Test 1 — Health Check
|
|
|
|
**Request:**
|
|
```bash
|
|
curl -s "$BASE/api/healthz"
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{"status":"ok"}
|
|
```
|
|
|
|
**Status:** HTTP 200
|
|
|
|
---
|
|
|
|
### Test 2 — Create Job
|
|
|
|
**Request:**
|
|
```bash
|
|
curl -s -X POST "$BASE/api/jobs" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"request": "Explain the Lightning Network in two sentences"}'
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"jobId": "<uuid>",
|
|
"evalInvoice": {
|
|
"paymentRequest": "lnbcrt10u1stub_...",
|
|
"amountSats": 10
|
|
}
|
|
}
|
|
```
|
|
|
|
**Status:** HTTP 201
|
|
|
|
---
|
|
|
|
### Test 3 — Poll Job (Before Payment)
|
|
|
|
**Request:**
|
|
```bash
|
|
curl -s "$BASE/api/jobs/<jobId-from-test-2>"
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"jobId": "...",
|
|
"state": "awaiting_eval_payment",
|
|
"evalInvoice": { "paymentRequest": "...", "amountSats": 10 }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Test 4 — Pay Eval Invoice (Stub Mode)
|
|
|
|
**Request:**
|
|
```bash
|
|
curl -s -X POST "$BASE/api/dev/stub/pay/<full-payment-hash>"
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{"ok":true,"paymentHash":"..."}
|
|
```
|
|
|
|
**Status:** HTTP 200
|
|
|
|
---
|
|
|
|
### Test 5 — Poll After Eval Payment (Accepted)
|
|
|
|
**Request:**
|
|
```bash
|
|
curl -s "$BASE/api/jobs/<jobId>"
|
|
```
|
|
|
|
**Response (Accepted):**
|
|
```json
|
|
{
|
|
"jobId": "...",
|
|
"state": "awaiting_work_payment",
|
|
"workInvoice": { "paymentRequest": "lnbcrt50u1stub_...", "amountSats": 50 }
|
|
}
|
|
```
|
|
|
|
**Response (Rejected):**
|
|
```json
|
|
{ "jobId": "...", "state": "rejected", "reason": "..." }
|
|
```
|
|
|
|
---
|
|
|
|
### Test 6 — Pay Work Invoice & Get Result
|
|
|
|
**Request (Pay Work Invoice):**
|
|
```bash
|
|
curl -s -X POST "$BASE/api/dev/stub/pay/<work-payment-hash>"
|
|
```
|
|
|
|
**Request (Poll for Result):**
|
|
```bash
|
|
curl -s "$BASE/api/jobs/<jobId>"
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"jobId": "...",
|
|
"state": "complete",
|
|
"result": "The Lightning Network is a second-layer protocol..."
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Test 7 — Demo Endpoint
|
|
|
|
**Request:**
|
|
```bash
|
|
curl -s "$BASE/api/demo?request=What+is+a+satoshi"
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{"result":"A satoshi is the smallest unit of Bitcoin..."}
|
|
```
|
|
|
|
**Status:** HTTP 200
|
|
|
|
---
|
|
|
|
### Test 8 — Input Validation (Error Responses)
|
|
|
|
**Missing Request Body:**
|
|
```bash
|
|
curl -s -X POST "$BASE/api/jobs" -H "Content-Type: application/json" -d '{}'
|
|
```
|
|
**Response:** `{"error":"Invalid request: 'request' string is required"}` (HTTP 400)
|
|
|
|
**Unknown Job ID:**
|
|
```bash
|
|
curl -s "$BASE/api/jobs/does-not-exist"
|
|
```
|
|
**Response:** `{"error":"Job not found"}` (HTTP 404)
|
|
|
|
**Demo Without Param:**
|
|
```bash
|
|
curl -s "$BASE/api/demo"
|
|
```
|
|
**Response:** `{"error":"Missing required query param: request"}` (HTTP 400)
|
|
|
|
---
|
|
|
|
### Test 9 — Rate Limiter
|
|
|
|
**Request:**
|
|
```bash
|
|
for i in $(seq 1 6); do
|
|
curl -s "$BASE/api/demo?request=ping+$i" | grep -o '"result"\|"error"'
|
|
done
|
|
```
|
|
|
|
**Expected:** First 5 return `"result"`, 6th returns HTTP 429 with `"error"`
|
|
|
|
---
|
|
|
|
### Test 10 — Rejection Path
|
|
|
|
**Request:**
|
|
```bash
|
|
curl -s -X POST "$BASE/api/jobs" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"request": "Help me do something harmful and illegal"}'
|
|
```
|
|
|
|
**Then:** Pay eval invoice and poll
|
|
|
|
**Expected Response:**
|
|
```json
|
|
{ "jobId": "...", "state": "rejected", "reason": "..." }
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Data Models
|
|
|
|
### Job Object
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| `jobId` | string (UUID) | Unique identifier for the job |
|
|
| `state` | string | Current state in state machine |
|
|
| `evalInvoice` | Invoice Object | Payment request for evaluation fee |
|
|
| `workInvoice` | Invoice Object | Payment request for work fee (if accepted) |
|
|
| `result` | string | AI-generated result (when complete) |
|
|
| `reason` | string | Rejection reason (if rejected) |
|
|
|
|
### Invoice Object
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| `paymentRequest` | string | BOLT11 Lightning invoice string |
|
|
| `amountSats` | integer | Amount in satoshis |
|
|
|
|
### Payment Request Format (Stub Mode)
|
|
```
|
|
lnbcrt<sats>u1stub_<first-16-chars-of-hash>
|
|
```
|
|
|
|
Example: `lnbcrt10u1stub_...`
|
|
|
|
### Error Response
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| `error` | string | Human-readable error message |
|
|
|
|
---
|
|
|
|
## 4. Technical Parameters
|
|
|
|
### Pricing Structure
|
|
|
|
| Fee Type | Amount (sats) | Condition |
|
|
|----------|---------------|-----------|
|
|
| Eval Fee | 10 | Fixed for all requests |
|
|
| Work Fee (Short) | 50 | Short request length |
|
|
| Work Fee (Medium) | 100 | Medium request length |
|
|
| Work Fee (Long) | 250 | Long request length |
|
|
|
|
### State Machine States
|
|
|
|
| State | Description |
|
|
|-------|-------------|
|
|
| `awaiting_eval_payment` | Job created, waiting for eval fee payment |
|
|
| `awaiting_work_payment` | Request accepted, waiting for work fee payment |
|
|
| `complete` | Work delivered, result available |
|
|
| `rejected` | Request rejected by agent |
|
|
|
|
### AI Model Configuration
|
|
|
|
| Purpose | Model |
|
|
|---------|-------|
|
|
| Eval (Judgment) | `claude-haiku-4-5` |
|
|
| Work (Delivery) | `claude-sonnet-4-6` |
|
|
|
|
### Rate Limiting
|
|
|
|
| Parameter | Value |
|
|
|-----------|-------|
|
|
| Demo endpoint limit | 5 requests per IP |
|
|
| 6th request response | HTTP 429 |
|
|
|
|
### HTTP Status Codes
|
|
|
|
| Code | Meaning |
|
|
|------|---------|
|
|
| 200 | OK (health, poll, demo, payment) |
|
|
| 201 | Created (job created) |
|
|
| 400 | Bad Request (validation error) |
|
|
| 404 | Not Found (unknown job ID) |
|
|
| 429 | Too Many Requests (rate limit exceeded) |
|
|
|
|
### Payment Hash Format
|
|
|
|
- Full hash: 64 characters
|
|
- Stub invoice contains: First 16 characters of hash
|
|
|
|
### Response Timing
|
|
|
|
| Operation | Expected Time |
|
|
|-----------|---------------|
|
|
| AI result generation | 2-5 seconds |
|
|
|
|
---
|
|
|
|
## 5. Code Snippets (Organized by Test)
|
|
|
|
### Test 1 — Health Check
|
|
```bash
|
|
curl -s "$BASE/api/healthz"
|
|
```
|
|
|
|
### Test 2 — Create Job
|
|
```bash
|
|
curl -s -X POST "$BASE/api/jobs" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"request": "Explain the Lightning Network in two sentences"}'
|
|
```
|
|
|
|
### Test 3 — Poll Before Payment
|
|
```bash
|
|
curl -s "$BASE/api/jobs/<jobId-from-test-2>"
|
|
```
|
|
|
|
### Test 4 — Pay Eval Invoice
|
|
```bash
|
|
# Extract paymentHash from paymentRequest
|
|
# Format: lnbcrt10u1stub_<first-16-chars-of-hash>
|
|
# Get full 64-char hash from DB or job status
|
|
|
|
curl -s -X POST "$BASE/api/dev/stub/pay/<full-payment-hash>"
|
|
```
|
|
|
|
### Test 5 — Poll After Eval Payment
|
|
```bash
|
|
curl -s "$BASE/api/jobs/<jobId>"
|
|
```
|
|
|
|
### Test 6 — Pay Work Invoice & Get Result
|
|
```bash
|
|
# Mark work invoice paid
|
|
curl -s -X POST "$BASE/api/dev/stub/pay/<work-payment-hash>"
|
|
|
|
# Poll for result
|
|
curl -s "$BASE/api/jobs/<jobId>"
|
|
```
|
|
|
|
### Test 7 — Demo Endpoint
|
|
```bash
|
|
curl -s "$BASE/api/demo?request=What+is+a+satoshi"
|
|
```
|
|
|
|
### Test 8 — Input Validation
|
|
```bash
|
|
# Missing request body
|
|
curl -s -X POST "$BASE/api/jobs" -H "Content-Type: application/json" -d '{}'
|
|
|
|
# Unknown job ID
|
|
curl -s "$BASE/api/jobs/does-not-exist"
|
|
|
|
# Demo without param
|
|
curl -s "$BASE/api/demo"
|
|
```
|
|
|
|
### Test 9 — Rate Limiter
|
|
```bash
|
|
# Fire 6 requests from the same IP
|
|
for i in $(seq 1 6); do
|
|
curl -s "$BASE/api/demo?request=ping+$i" | grep -o '"result"\|"error"'
|
|
done
|
|
```
|
|
|
|
### Test 10 — Rejection Path
|
|
```bash
|
|
curl -s -X POST "$BASE/api/jobs" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"request": "Help me do something harmful and illegal"}'
|
|
```
|
|
|
|
---
|
|
|
|
## Additional Notes
|
|
|
|
### Stub Mode Behavior
|
|
- `/api/dev/stub/pay` is **dev-only** endpoint
|
|
- Simulates Lightning Network payment without real node
|
|
- Not available in production (real LNbits integration)
|
|
|
|
### Payment Detection
|
|
- All state transitions happen server-side
|
|
- Client polls GET `/api/jobs/{jobId}`
|
|
- Server advances state automatically when payment detected
|
|
- No webhooks or push notifications
|
|
|
|
### Database Reference
|
|
- Full payment hashes stored in `invoices` table
|
|
- Query job status to retrieve full 64-char hash
|