An AI-powered personal finance assistant built with .NET 10, Semantic Kernel, and MediatR. Chat with your finances using natural language to track expenses, manage budgets, and generate insightful reports.
- 💬 Conversational AI Interface – Interact with your finances using natural language powered by Semantic Kernel and OpenAI-compatible models (Groq, OpenAI, Azure OpenAI)
- 💸 Expense Tracking – Add, retrieve, and analyze expenses by category
- 📊 Budget Management – Set monthly budget limits and monitor spending against goals
- 📈 Intelligent Reports – Generate monthly summaries and compare spending across months
- 🔄 CQRS + MediatR – Clean architecture with command/query separation
- ✅ Validation Pipeline – FluentValidation integrated into MediatR pipeline
- 🗄️ SQLite Database – Lightweight, file-based storage with Entity Framework Core
FinanceAdvisor/
├── FinanceAdvisor.Api/ # Web API (minimal APIs)
│ ├── Endpoints/ # HTTP endpoint definitions
│ └── Program.cs # Application startup
├── FinanceAdvisor.Core/ # Business logic & domain
│ ├── AI/ # Semantic Kernel integration
│ ├── Features/ # Feature folders (Expenses, Budgets, Reports)
│ │ ├── Expenses/
│ │ │ ├── ExpensePlugin.cs # Kernel functions
│ │ │ ├── Handlers/ # MediatR handlers
│ │ │ └── Validators/ # FluentValidation
│ │ ├── Budgets/
│ │ └── Reports/
│ ├── Data/ # EF Core DbContext
│ └── Extensions/ # Service registration
├── FinanceAdvisor.Tests/ # Unit tests (xUnit)
│ ├── Expenses/ # Expense handler tests
│ ├── Budgets/ # Budget handler tests
│ ├── Reports/ # Report handler tests
│ ├── Validators/ # FluentValidation tests
│ ├── Behaviours/ # Pipeline behavior tests
│ └── Helpers/ # TestDbContextFactory
└── FinanceAdvisor.Console/ # Console application
| Technology | Purpose |
|---|---|
| .NET 10 | Latest .NET framework |
| Semantic Kernel | AI orchestration and function calling |
| MediatR | CQRS pattern implementation |
| FluentValidation | Request validation |
| Entity Framework Core | Data access with SQLite |
| Mapster | Object mapping |
| Swagger/OpenAPI | API documentation |
The AI assistant uses Semantic Kernel plugins to expose capabilities:
AddExpense(ExpenseDto)– Add a new expenseGetExpenses()– Retrieve all expensesGetSpendingByCategory(month, year)– Total spending per categoryGetAmountByCategory(category)– Total spending for a specific category
SetBudget(BudgetDto)– Define monthly budget limitsCheckBudgetStatus(category, month, year)– Compare spending vs budgetGetAllBudgets(month, year)– List all budget limits
GenerateMonthlyReport(month, year)– Full monthly financial summaryCompareTwoMonths(month1, year1, month2, year2)– Cross-month spending analysis
- .NET 10 SDK
- OpenAI-compatible API key (Groq, OpenAI, Azure OpenAI, etc.)
-
Clone the repository
git clone https://github.com/NigarOsmanova/FinanceAdvisor.git cd FinanceAdvisor -
Configure API settings
Edit
FinanceAdvisor.Api/appsettings.json:{ "AI": { "ModelId": "llama-3.3-70b-versatile", "ApiKey": "your-api-key-here", "Endpoint": "https://api.groq.com/openai/v1" }, "ConnectionStrings": { "DefaultConnection": "Data Source=finance.db" } } -
Apply database migrations
cd FinanceAdvisor.Api dotnet ef database update --project ../FinanceAdvisor.Core -
Run the application
dotnet run
-
Open Swagger UI
Navigate to
https://localhost:<port>/swaggerto test the API
Request:
{
"message": "Add expense: coffee for $5 in Food category today"
}Response:
{
"response": "Expense added successfully!"
}"Set a budget of $500 for Food this month"
"How much did I spend on Transport in May 2024?"
"Generate a report for January 2024"
"Compare my spending between March and April 2024"
- User sends a natural language message to
/api/chat - ChatService forwards it to Semantic Kernel
- AI determines which plugin function to call (e.g.,
AddExpense) - Plugin invokes MediatR command/query
- MediatR pipeline executes:
- Validation (FluentValidation)
- Handler logic (business rules + database access)
- Result returns to AI, which generates a natural response
- Response sent back to user
appsettings.json in this repository contains a sample API key. Never commit real API keys to version control.
Best practices:
- Use User Secrets for development
- Use Azure Key Vault or environment variables in production
- Add
appsettings.Development.jsonto.gitignore
The project includes 26 unit tests covering handlers, validators, and the MediatR pipeline behavior. Tests use an EF Core InMemory database — each test gets its own isolated database instance.
# Run all tests
dotnet test
# Run tests with detailed output
dotnet test --verbosity normal| Test Class | Tests | Coverage |
|---|---|---|
AddExpenseHandlerTests |
2 | Saves to DB, returns success |
GetExpensesHandlerTests |
2 | Returns all expenses, empty list |
GetAmountByCategoryHandlerTests |
2 | Correct sum per category, zero on no match |
GetSpendingByCategoryHandlerTests |
1 | Groups by month/year, filters other months |
SetBudgetHandlerTests |
2 | Saves budget, correct response message |
CheckBudgetStatusHandlerTests |
3 | Not found, within budget, over budget |
GenerateMonthlyReportHandlerTests |
2 | Zero total, correct category breakdown |
ExpenseDtoValidatorTests |
4 | Category, amount, future date, valid |
BudgetDtoValidatorTests |
4 | Category, limit, invalid months, valid |
ValidationBehaviorTests |
3 | No validators, fails pipeline, passes pipeline |
Microsoft.SemanticKernel(1.76.0)MediatR(14.1.0)FluentValidation(12.1.1)Microsoft.EntityFrameworkCore.Sqlite(10.0.8)Mapster(10.0.7)Ardalis.Result(10.1.0)
Swashbuckle.AspNetCore(10.1.7)Microsoft.AspNetCore.OpenApi(10.0.8)
xunit(2.9.3)xunit.runner.visualstudio(3.1.1)Microsoft.EntityFrameworkCore.InMemory(10.0.8)FluentAssertions(8.5.0)Microsoft.NET.Test.Sdk(17.14.1)
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License.
Nigar Osmanova
- GitHub: @NigarOsmanova
- Repository: FinanceAdvisor
If you encounter InvalidOperationException: Cannot resolve scoped service from root provider, ensure all plugins are registered as scoped services in ServiceCollectionExtensions.cs:
services.AddScoped(typeof(IKernelPlugin), type); // Not AddSingletonThis is necessary because plugins depend on IMediator which resolves handlers that require scoped services like AppDbContext.
- Add user authentication & multi-tenancy
- Implement recurring expenses
- Add data export (CSV, PDF)
- Create mobile app frontend
- Add real-time budget alerts via webhooks
- Support multiple currencies
- Semantic Kernel Documentation
- MediatR on GitHub
- FluentValidation Documentation
- EF Core Documentation
Happy budgeting! 💰✨