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
Extend the chat router and query layer from #146 with three more fixed intents: category spend totals, overall spend totals, and transaction counts — each distinguishing "my share of the split" from "what I paid". Adds the grocery/groceries → ExpenseCategory.Food synonym mapping the router uses to resolve informal category words.
Implementation Steps
Router prompt update — Extend the router prompt (backend/splitzy-dotnet/Services/Chat/Prompts/) and RouterResult intent enum from Chat foundation: streaming balance intent + router skeleton #146 with CategoryTotal, OverallTotal, TransactionCount, each carrying params { Metric: share|paid, Category?, From, To }. Add a category synonym map to the prompt (e.g. "grocery"/"groceries" → Food) covering the existing ExpenseCategory enum values in backend/splitzy-dotnet/Models/Expense.cs. [HITL]: confirm the grocery→Food mapping with product — flag it as a comment in the prompt template so it's easy to change later.
Category total query — Add Task<decimal> CategoryTotal(int userId, SpendMetric metric, ExpenseCategory category, DateTime from, DateTime to) to IChatQueryService/ChatQueryService (backend/splitzy-dotnet/Services/Chat/). share sums ExpenseSplit.OwedAmount where UserId = userId joined to Expense.Category; paid sums Expense.Amount where PaidByUserId = userId and Category matches. Both filter Expense.CreatedAt within [from, to].
Overall total + transaction count queries — Add Task<decimal> OverallTotal(int userId, SpendMetric metric, DateTime from, DateTime to) (same share/paid distinction, no category filter) and Task<int> TransactionCount(int userId, ExpenseCategory? category, DateTime from, DateTime to) to the same service.
Tests — Extend backend/spllitzy-dotnet-tests/ChatControllerTests.cs (or a new ChatQueryServiceTests.cs following the same NUnit + TestHelper.CreateTestContext() pattern): category total correctness for both share and paid metrics against seeded fixture data, overall total correctness, transaction count correctness, and a router test proving "how much did I spend on groceries" resolves to CategoryTotal with Category = Food.
Agent Routing
agent_routing:
complexity_hint: mediumrequired_capability: balancedparallel_safe: falsecost_preference: balancedspeed_preference: balancedownership_scope:
- backend/splitzy-dotnet/Services/Chat/
- backend/spllitzy-dotnet-tests/verification:
- dotnet test backend/spllitzy-dotnet-tests
- Manual Swagger call: "how much did I spend on groceries this month"and "how much have I paid overall this year"
Parent
#145 — Ask Splitzy: personal spending chatbot (self-hosted LLM)
What to build
Extend the chat router and query layer from #146 with three more fixed intents: category spend totals, overall spend totals, and transaction counts — each distinguishing "my share of the split" from "what I paid". Adds the grocery/groceries →
ExpenseCategory.Foodsynonym mapping the router uses to resolve informal category words.Implementation Steps
backend/splitzy-dotnet/Services/Chat/Prompts/) andRouterResultintent enum from Chat foundation: streaming balance intent + router skeleton #146 withCategoryTotal,OverallTotal,TransactionCount, each carrying params{ Metric: share|paid, Category?, From, To }. Add a category synonym map to the prompt (e.g. "grocery"/"groceries" →Food) covering the existingExpenseCategoryenum values inbackend/splitzy-dotnet/Models/Expense.cs.[HITL]: confirm the grocery→Foodmapping with product — flag it as a comment in the prompt template so it's easy to change later.Task<decimal> CategoryTotal(int userId, SpendMetric metric, ExpenseCategory category, DateTime from, DateTime to)toIChatQueryService/ChatQueryService(backend/splitzy-dotnet/Services/Chat/).sharesumsExpenseSplit.OwedAmountwhereUserId = userIdjoined toExpense.Category;paidsumsExpense.AmountwherePaidByUserId = userIdandCategorymatches. Both filterExpense.CreatedAtwithin[from, to].Task<decimal> OverallTotal(int userId, SpendMetric metric, DateTime from, DateTime to)(same share/paid distinction, no category filter) andTask<int> TransactionCount(int userId, ExpenseCategory? category, DateTime from, DateTime to)to the same service.ChatOrchestrator.AskAsync(from Chat foundation: streaming balance intent + router skeleton #146) to dispatch the three new intents to the correspondingChatQueryServicemethods before synthesis.backend/spllitzy-dotnet-tests/ChatControllerTests.cs(or a newChatQueryServiceTests.csfollowing the same NUnit +TestHelper.CreateTestContext()pattern): category total correctness for bothshareandpaidmetrics against seeded fixture data, overall total correctness, transaction count correctness, and a router test proving "how much did I spend on groceries" resolves toCategoryTotalwithCategory = Food.Agent Routing
Technical Context Snapshot
Current stack in scope
Services/Chat/module created there — no new infrastructure.ExpenseCategoryenum (backend/splitzy-dotnet/Models/Expense.cs),ExpenseSplit.OwedAmount,Expense.Amount/Expense.PaidByUserId/Expense.CreatedAt.Dependencies in scope
Architecture alignment
IChatQueryService/ChatQueryServiceand the router/orchestrator from Chat foundation: streaming balance intent + router skeleton #146 rather than introducing parallel structures.userIdcontinues to be resolved server-side only, never taken from router/LLM output.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/streamcontract from Chat foundation: streaming balance intent + router skeleton #146.Expense/ExpenseSplit.Acceptance criteria
Food-category share total for the authenticated user.Blocked by