Architecture recap:
ChatProviding is the seam (in AcornCore), streaming-first
(stream(_:) → reply(to:) for free). Implementations:EchoChatProvider— dependency-free default; streams a canned reply so the Assistant tab runs with zero config.ProxyChatProvider— the production path; streams from your backend so the model key never ships. Provider-neutral (your proxy picks OpenAI/Anthropic).OpenAIChatProvider/AnthropicChatProvider(in the optionalAcornCoreAIproduct) — dev-direct, pure URLSession, key ships in the app → prototyping only.
ChatScreen + ChatScreenModel) and the seam expose no vendor types,
so swapping providers never touches the screen.Part A — The chat proxy (one-time, ~15 min)
Skip this if you only need the dev-direct providers for prototyping. For production, this Edge Function holds the key and streams completions back to the app. It speaks theProxyChatProvider contract: accept POST {messages, stream} with the
caller’s JWT, and stream Server-Sent Events where each data: line is
{"delta":"<text>"}, terminated by data: [DONE].
supabase/functions/chat/index.ts
supabase functions deploy chat. Set the key: supabase secrets set OPENAI_API_KEY=sk-….
Anthropic instead? Call
https://api.anthropic.com/v1/messages with headers
x-api-key + anthropic-version: 2023-06-01, lift system out of messages, set
max_tokens, and map the content_block_delta events to {"delta": text}. The app
side is identical — ProxyChatProvider doesn’t care which model is behind the proxy.Part B — App wiring (3 steps)
Add the dependency (only for dev-direct)
ProxyChatProvider, EchoChatProvider, and ChatScreen are in AcornCore — no extra
dependency. For the dev-direct providers, add the AcornCoreAI product to the app
target and import AcornCoreAI in AppConfig.Supply the key (keep it out of source)
Mirror the PostHog/RevenueCat pattern — read from Info.plist:Pre-ship checklist
- Decided the path: Echo (demo) / Proxy (prod) / dev-direct (prototype).
- Production:
chatEdge Function deployed;OPENAI_API_KEY(or Anthropic) in Supabase secrets; rate-limiting in place. -
AppConfig.makeChat()returns the chosen provider; any client key read from Info.plist (never hardcoded). - Dev-direct only:
AcornCoreAIproduct added; key is not shipped to the App Store build. - Live test: send a message → tokens stream into the transcript → stop button cancels mid-stream → an error (e.g. bad key) shows in the error bar.
- Privacy: if chats leave the device, disclose it in App Privacy + your policy.