Phase 1 feature: /api/suggest end-to-end (GPT-4o-mini → Claude Haiku fallback)
The first real product feature — a backend English-coach suggestion endpoint with provider fallback, plus a SwiftUI client to drive it. Verified up to the API-key boundary.
AI 버전
The first feature that actually does the job: take what the other person said, return 2-3 natural English responses. This is the Phase 1 core ("영어 답변 제안") wired end to end.
Backend — POST /api/suggest (commit 6bd6ef7)
backend/src/index.ts now exposes a second route. The orchestration lives in src/api/llm.ts — the file CLAUDE.md had pointed at as an "Important File" but which didn't exist until now (doc/code drift resolved).
- Dual provider with fallback: OpenAI
gpt-4o-mini(primary) → Anthropicclaude-haiku-4-5(fallback). Only providers with a configured key are attempted, in order; the first success wins. - Structured output, three layers of defense: both providers are asked for a strict JSON schema (
response_format/output_config), the system prompt also spells out the exact JSON shape, andsrc/parse.tstolerantly extracts + validates (normalizing unknown tones, dropping empty entries). Any one layer alone is enough. - Prompt (
src/prompt.ts): the english-coach system prompt (fromprompts/english-coach.mdv0.1), markedcache_control: ephemeral. Note: Haiku 4.5's minimum cacheable prefix is 4096 tokens and the current prompt is far shorter, so caching is a no-op today — it activates automatically once the prompt grows (personalization, glossary). - Keys stay server-side: Bun auto-loads
.env; clients never see a key.
Contract:
POST /api/suggest { "text": "...", "context"?: "..." }
→ { "suggestions": [{ "text": "...", "tone": "professional|casual|safe" }], "provider", "model", "latencyMs" }iOS — suggestion UI (commit 98cc004)
ContentView.swift went from a health-poller HUD to the real thing: a text field → async POST /api/suggest → tone-colored suggestion cards in the glass-HUD style. Swift 6 strict concurrency (complete), async/await throughout.
One required change: the app talks to http://localhost:3001 (cleartext), which iOS App Transport Security blocks by default. project.yml now generates an Info.plist with NSAppTransportSecurity.NSAllowsLocalNetworking = true — the minimal, App-Store-safe exception for loopback.
What's verified — and what isn't
Verified:
- Backend typechecks under strict TS;
bun testis 7/7 (the parser). /healthand/api/suggestare live-wired: a request flows through validation → provider selection → API call → error mapping. With no valid credentials it fast-fails in ~0.22s with a structured 502.- iOS:
xcodebuild→ BUILD SUCCEEDED on the simulator SDK.
Not yet verified (honest): the provider success path. Live testing surfaced that OPENAI_API_KEY is unset and the ANTHROPIC_API_KEY in .env returns 401 invalid x-api-key. So no real suggestion has come back yet — that's gated on valid keys, which are mine to add. The three-layer parsing defense is what makes shipping the code ahead of that comfortable.
Commits: backend 6bd6ef7a39feca75678def489192a9a93d62767e, iOS 98cc004da089ddba9a74d1025b80f3b312554fb5.
리뷰 필요
내 시각이 아직 안 들어간 entry.