$ uv run tools/flow.py budget budget $1.0 / used $0.006822 · remaining $0.993178 · over False · per user 20/min · question 500 chars $ uv run tools/flow.py test cases/lab03.json -v POST http://localhost:5678/webhook/my-cost · 3 case(s) × 1 ✔ before: POST http://localhost:8800/usage-clear · HTTP 200 · 0.0s · {"cleared": 19} → request POST http://localhost:8800/usage-clear {} ← response HTTP 200 { "cleared": 19 } ✔ before: POST http://localhost:8800/budget · HTTP 200 · 0.0s · {"daily_usd": 0.001, "user_limit_per_min": 20, "max_question_chars": 500, "used_usd": 0, "remaining_usd": 0.001, "over": → request POST http://localhost:8800/budget { "daily_usd": 0.001 } ← response HTTP 200 { "daily_usd": 0.001, "user_limit_per_min": 20, "max_question_chars": 500, "used_usd": 0, "remaining_usd": 0.001, "over": false } ✔ before: POST http://localhost:8800/usage · HTTP 200 · 0.0s · {"stored": 1, "total": 1} → request POST http://localhost:8800/usage { "request_id": "seed", "user": "seed", "step": "answer", "model": "gpt-4.1-mini", "cost_usd": 0.002 } ← response HTTP 200 { "stored": 1, "total": 1 } ✘ 한도를 넘으면 · FAQ 원문 · 1.1s · 자동 답변 · 1 call · $0.000154 · route = 자동 답변 (expected 한도 초과); cost_usd = 0.000154 (expected 0); llm_calls = 1 (expected 0) → request POST /webhook/my-cost { "question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?", "user": "이준호" } ← response HTTP 200 { "route": "자동 답변", "answer": "VPN 접속 오류는 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속하시기 바랍니다. 현재 접수된 관련 티켓이 없어 추가 지원이 필요하면 담당자가 도와드리겠습니다.", "sources": [ "FAQ-003" ], "llm_calls": 1, "usage": { "prompt_tokens": 165, "completion_tokens": 55, "by_step": { "answer": { "model": "gpt-4.1-mini", "prompt_tokens": 165, "completion_tokens": 55, "cost_usd": 0.000154 } } }, "cost_usd": 0.000154, "trimmed": false, "question_chars": 24, "recorded": true } ✘ 한도를 넘으면 · 접수 원문 · 1.2s · 자동 답변 · 1 call · $0.000115 · route = 자동 답변 (expected 한도 초과); cost_usd = 0.0001152 (expected 0) → request POST /webhook/my-cost { "question": "T-1002 처리 상태 알려 주세요", "user": "박서연" } ← response HTTP 200 { "route": "자동 답변", "answer": "T-1002 티켓은 현재 처리 중이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다.", "sources": [], "llm_calls": 1, "usage": { "prompt_tokens": 152, "completion_tokens": 34, "by_step": { "answer": { "model": "gpt-4.1-mini", "prompt_tokens": 152, "completion_tokens": 34, "cost_usd": 0.0001152 } } }, "cost_usd": 0.0001152, "trimmed": false, "question_chars": 19, "recorded": true } ✔ before: POST http://localhost:8800/budget/reset · HTTP 200 · 0.0s · {"daily_usd": 1.0, "user_limit_per_min": 20, "max_question_chars": 500, "used_usd": 0.0022692, "remaining_usd": 0.997730 → request POST http://localhost:8800/budget/reset {} ← response HTTP 200 { "daily_usd": 1.0, "user_limit_per_min": 20, "max_question_chars": 500, "used_usd": 0.0022692, "remaining_usd": 0.9977308, "over": false } ✔ before: POST http://localhost:8800/usage-clear · HTTP 200 · 0.0s · {"cleared": 3} → request POST http://localhost:8800/usage-clear {} ← response HTTP 200 { "cleared": 3 } ✔ 한도 안이면 평소대로 · 1.4s · 자동 답변 · 1 call · $0.000141 → request POST /webhook/my-cost { "question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?", "user": "이준호" } ← response HTTP 200 { "route": "자동 답변", "answer": "VPN 접속 오류는 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속하시기 바랍니다. 추가 도움이 필요하시면 담당 직원이 도와드리겠습니다.", "sources": [ "FAQ-003" ], "llm_calls": 1, "usage": { "prompt_tokens": 165, "completion_tokens": 47, "by_step": { "answer": { "model": "gpt-4.1-mini", "prompt_tokens": 165, "completion_tokens": 47, "cost_usd": 0.0001412 } } }, "cost_usd": 0.0001412, "trimmed": false, "question_chars": 24, "recorded": true } Passed 1/3 report: runs/lab03-20261028-210707.json ✔ after: POST http://localhost:8800/budget/reset · HTTP 200 · 0.0s · {"daily_usd": 1.0, "user_limit_per_min": 20, "max_question_chars": 500, "used_usd": 0.0001412, "remaining_usd": 0.999858 → request POST http://localhost:8800/budget/reset {} ← response HTTP 200 { "daily_usd": 1.0, "user_limit_per_min": 20, "max_question_chars": 500, "used_usd": 0.0001412, "remaining_usd": 0.9998588, "over": false } ✔ after: POST http://localhost:8800/usage-clear · HTTP 200 · 0.0s · {"cleared": 1} → request POST http://localhost:8800/usage-clear {} ← response HTTP 200 { "cleared": 1 }