Skip to content
← All projects
Case study

KizilelmaAI

CSS / JavaScriptOpen repository ↗

Problem

I worked in the finance track of the BTK Academy Hackathon 2026. The concrete problem I took on: someone trying to manage a budget under high inflation struggles to sort their own spending into meaningful categories, and then to get from that list to an actionable plan. Keeping a spreadsheet solves the data-entry problem but not the interpretation problem. My goal was a backend that takes free-text expense lines, categorises them, and can then produce a budget plan grounded in Turkish economic conditions.

Approach

A FastAPI backend exposes four endpoints: /chat (multi-turn conversation), /harcama-analiz (agentic categorisation plus report), /butce-olustur (budget plan) and /health. The model is gemini-2.5-flash, accessed through the google-genai SDK. Rather than leaving categorisation to free text, I declared it as a function declaration: the kategorize_harcama tool must pick a category from a closed enum (groceries, transport, rent, bills, entertainment, health, clothing, education, savings, other), pick a risk level from three, and return a short recommendation. The analysis flow runs in two passes: the first call produces tool calls, the code collects them and counts the category distribution, and the second call feeds that distribution back into the prompt to write the markdown report. The frontend is React 18 + Vite; conversation history arrives from the client and build_contents converts it into Gemini's user/model role format, so the server keeps no session state.

Decisions and trade-offs

  • I did categorisation through Gemini function calling rather than free text (types.FunctionDeclaration with an enum-constrained types.Schema), because I wanted categories drawn from a fixed set and required fields guaranteed by the schema; the alternative was asking the model for JSON, which lets category names drift between runs and forces you to hand-catch parse failures.
  • I split expense analysis into two calls: a tool-call pass first, then a report pass where the collected category distribution is fed back into the prompt. Asking for structured category output and a long markdown report in a single turn weakened both; the alternative was one call, and I traded one extra request of latency for a cleaner separation.
  • I set temperature per endpoint: 0.7 for chat, 0.3 for expense analysis, 0.4 for budgeting. Chat wants fluency while categorisation and reporting want consistency; the alternative of one global temperature made either the chat wooden or the analysis loose.
  • I collected the system prompt into a single constant and wrote explicit limits into it (give no definitive investment advice, stay within general information, politely decline non-finance questions). In a finance context the model drifts into an advisory register on its own; the alternative was scattering those limits across each endpoint's prompt, which made the boundaries inconsistent between endpoints.
  • When no API key is present I leave the client as None and let endpoints return 500, because failing loudly beats quietly emitting a fake or empty answer; the alternative was returning a canned default, which during a demo could be mistaken for a real response.

Outcome

NO MEASUREMENT — I can't claim a number here, because the repo has no tests, no evaluation set and no recorded runs. What concretely exists: a working four-endpoint FastAPI service (chat, expense analysis, budget creation, health), one enum-constrained tool declaration, the two-pass analysis flow, per-endpoint temperature settings, and a React 18 + Vite frontend. The /harcama-analiz response returns the analysis text alongside the total spend, the category distribution and how many agent calls were made (agent_calls) — so the agentic behaviour is reported back to the client in a measurable form. The repo's CLAUDE.md records the competition calendar: submission 2026-05-19, preliminary jury 2026-05-20 to 05-30, jury presentation 2026-06-05. There is NO competition result, score or placement in the repo, and this page claims none. An honest note on technical debt: frontend/src still carries unused leftovers from Vite's vanilla-TypeScript template (main.ts, counter.ts, style.css, assets/typescript.svg) — index.html only loads /src/main.jsx, so those are dead files.

Stack

Python 3.11FastAPIuvicorngoogle-genai SDKgemini-2.5-flashpydanticReact 18Vite 5

Sources

Every technical claim on this page was derived from the sources below.

  • https://github.com/msgxr/KizilelmaAI
  • https://github.com/msgxr/KizilelmaAI/blob/main/README.md
  • https://github.com/msgxr/KizilelmaAI/blob/main/src/api.py
  • https://github.com/msgxr/KizilelmaAI/blob/main/src/main.py
  • https://github.com/msgxr/KizilelmaAI/blob/main/requirements.txt
  • https://github.com/msgxr/KizilelmaAI/blob/main/frontend/package.json
  • https://github.com/msgxr/KizilelmaAI/blob/main/frontend/index.html
  • https://github.com/msgxr/KizilelmaAI/blob/main/CLAUDE.md

What I could not verify

Points I could not confirm while writing. I keep them visible rather than asserting them silently.

  • lib/projects.ts stack'i ['JavaScript','Google Gemini','CSS','HTML'] diyor — bu GERÇEK YIĞINI YANLIŞ GÖSTERİYOR. Depoda Python 3.11 + FastAPI + uvicorn + google-genai arka ucu ve React 18 + Vite ön yüzü var; 'HTML' ve 'CSS' etiketleri asıl işi gizliyor. GitHub'ın dili 'CSS' göstermesi App.css'in (8.5 KB) büyüklüğünden kaynaklanıyor, projenin doğasından değil. Kart dili de 'CSS / JavaScript' diyor; 'Python / JavaScript' olmalı.
  • DOGRULANAMADI: Yarışma sonucu, puanı veya derecesi. Depoda yalnızca takvim ve jüri kriter başlıkları var (CLAUDE.md), hiçbir sonuç kaydı yok. Vaka sayfası derece/başarı iddia ETMEMELİ.
  • DOGRULANAMADI: CLAUDE.md zorunlu teslim olarak "1 dakikalık video" ve opsiyonel "canlı link" listeliyor; depoda ne video linki ne dağıtım yapılandırması var. Canlı demo YOK (GitHub Pages API 404) ve ön yüz localhost:5173 / arka uç localhost:8000 varsayıyor.
  • DOGRULANAMADI: CLAUDE.md agentic katman için "LangGraph veya google-genai ile tool calling" diyor; gerçekleşen yol google-genai tool calling. LangGraph depoda hiç kullanılmıyor — plan belgesindeki alternatif, uygulanmış mimari değil.
  • Kodda doğrulanmış güvenlik notu (vaka sayfasında övgüye çevrilmemeli): CORS allow_origins=["*"] ile tamamen açık. Hackathon demosu için anlaşılır, üretim duruşu olarak sunulamaz.
  • DOGRULANAMADI: README "Türkiye'de milyonlarca kişi bütçe yönetiminde zorlanmakta" diyor; bu bir kaynak gösterilmeyen pazar iddiası, ölçüm değil. Problem metninde bunu sayısal iddiaya çevirmedim.