You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#145 — Ask Splitzy: personal spending chatbot (self-hosted LLM)
What to build
The Android/Expo chat screen: a streaming API client for POST /api/chat/stream, a Zustand store for chat state, and a chat screen route using existing UI primitives. Consumes the same contract as #149 (history + streaming), independently buildable in parallel.
Streaming API client — Create Android/src/services/api/chat.api.ts. The existing Android/src/services/api/client.ts uses axios, which does not support incremental response streaming in React Native; implement this call with fetch() directly (attaching Authorization: Bearer <token> via expo-secure-store's tokenStorage.getAccessToken(), matching client.ts's token source) and consume the response body via ReadableStream/async iteration. [HITL]: verify the installed Expo SDK 55 / RN 0.83 runtime supports fetch response streaming (via the whatwg streams polyfill) in a real device/emulator build before relying on it — fall back to a documented workaround if not supported.
Chat store — Create Android/src/stores/chat.store.ts (Zustand, following the pattern in Android/src/stores/dashboard.store.ts): holds the turn history (trimmed per Multi-turn conversation context (stateless history) #148's contract), in-progress streaming message, loading/error state.
Chat screen — Create Android/src/app/chat.tsx (or a tab entry under Android/src/app/(tabs)/ if preferred for discoverability) using existing primitives from Android/src/components/ui/ (Input, Button, LoadingSpinner, GlassCard) and Android/src/components/layout/ScreenWrapper.tsx, styled with NativeWind per this app's design system (Android/CLAUDE.md).
Manual verification — Run npm run android (or iOS) and npx tsc --noEmit; ask a balance question, a category-spend question, a follow-up, and an out-of-scope question; confirm streaming renders and all four states display correctly per this project's existing manual-QA practice.
New dependency additions allowed for this slice: no, unless the [HITL] streaming-support check in step 2 finds fetch streaming unavailable on this RN version — if so, flag back rather than silently adding a polyfill package.
Architecture alignment
Do not modify files outside Android/ in this slice (per Android/CLAUDE.md).
Follow existing services/api/*.api.ts + stores/*.store.ts conventions (see dashboard.api.ts / dashboard.store.ts).
create-git-issue provides routing hints only; it must not assign concrete agent/model names.
run-with-it remains the final runtime routing authority.
User can open the chat screen, ask a question, and see the answer stream in incrementally (or, if streaming proves unsupported on-device, render the complete answer with a clear loading state — documented as a deviation).
A follow-up question is sent with prior turn history and answered in context.
Out-of-scope questions render visibly differently from real data answers.
Parent
#145 — Ask Splitzy: personal spending chatbot (self-hosted LLM)
What to build
The Android/Expo chat screen: a streaming API client for
POST /api/chat/stream, a Zustand store for chat state, and a chat screen route using existing UI primitives. Consumes the same contract as #149 (history + streaming), independently buildable in parallel.Implementation Steps
CHAT_STREAM: "/api/Chat/stream"toAndroid/src/constants/endpoints.ts. AddChatTurn,ChatRequest/ChatMessagetypes toAndroid/src/types/api.types.ts, mirroring the backend DTOs from Chat foundation: streaming balance intent + router skeleton #146/Multi-turn conversation context (stateless history) #148.Android/src/services/api/chat.api.ts. The existingAndroid/src/services/api/client.tsusesaxios, which does not support incremental response streaming in React Native; implement this call withfetch()directly (attachingAuthorization: Bearer <token>viaexpo-secure-store'stokenStorage.getAccessToken(), matchingclient.ts's token source) and consume the response body viaReadableStream/async iteration.[HITL]: verify the installed Expo SDK 55 / RN 0.83 runtime supportsfetchresponse streaming (via the whatwg streams polyfill) in a real device/emulator build before relying on it — fall back to a documented workaround if not supported.Android/src/stores/chat.store.ts(Zustand, following the pattern inAndroid/src/stores/dashboard.store.ts): holds the turn history (trimmed per Multi-turn conversation context (stateless history) #148's contract), in-progress streaming message, loading/error state.Android/src/app/chat.tsx(or a tab entry underAndroid/src/app/(tabs)/if preferred for discoverability) using existing primitives fromAndroid/src/components/ui/(Input,Button,LoadingSpinner,GlassCard) andAndroid/src/components/layout/ScreenWrapper.tsx, styled with NativeWind per this app's design system (Android/CLAUDE.md).npm run android(or iOS) andnpx tsc --noEmit; ask a balance question, a category-spend question, a follow-up, and an out-of-scope question; confirm streaming renders and all four states display correctly per this project's existing manual-QA practice.Agent Routing
Technical Context Snapshot
Current stack in scope
expo-secure-storefor the existing authenticated HTTP client (Android/src/services/api/client.ts).#256af4, background#0a0f18, glass panels — seeAndroid/CLAUDE.md.Dependencies in scope
expo-secure-storetoken helpers, existingcomponents/ui/*primitives, existingstores/*Zustand pattern.[HITL]streaming-support check in step 2 findsfetchstreaming unavailable on this RN version — if so, flag back rather than silently adding a polyfill package.Architecture alignment
Android/in this slice (perAndroid/CLAUDE.md).services/api/*.api.ts+stores/*.store.tsconventions (seedashboard.api.ts/dashboard.store.ts).create-git-issueprovides routing hints only; it must not assign concrete agent/model names.run-with-itremains the final runtime routing authority.Integration touchpoints
POST /api/chat/streamas defined by Chat foundation: streaming balance intent + router skeleton #146/Multi-turn conversation context (stateless history) #148 — no backend changes in this slice.Acceptance criteria
Blocked by