Nori
Nori shortlists public tenders with match reasons and source links.
Solo project · In active development · Python, Docker, local LLM
Procurement recommendation system
Felix Olsson, Stockholm, Sweden. I study enterprise systems and build software. Ten years in retail taught me where systems slow people down.
Looking for: Junior roles in software development, business systems or data automation, in Stockholm.
I start with research and interviews, then build small end-to-end slices using TDD. I use AI during development; I review, test and benchmark the whole flow myself.
Nori shortlists public tenders with match reasons and source links.
Solo project · In active development · Python, Docker, local LLM
Procurement recommendation system
Transcribes Swedish advice meetings, suggests follow-up questions and drafts notes.
Team of four · Prototype · Java, Python, DuckDB, local models
Built with KEFT at Vivicta and Microsoft's Finance & Insurance AI Hackathon, September 2026.
My part: I built the Java backend for transcription, questions, notes and DuckDB storage.
The team: Kaj Grambo, Emily Xiao, Felix Olsson (me) and Tobias Hamrén.
Bumper warns parents about risks in online games without showing chats. GitHub
Team of four · TypeScript, Cloudflare
Built with KEFT in two days at Drivhuset Ideathon, Kista, May 2026.
My part: I built the backend and set up a Cloudflare Tunnel for the live demo.
The team: Kaj Grambo, Emily Xiao, Felix Olsson (me) and Tobias Hamrén.
Värmdö, Stockholm, Sweden
Värmdö, Stockholm, Sweden
Stockholm, Sweden
Bachelor's Programme in Enterprise Systems and Service Design (SAFFK). First year completed (60 credits). Official programme page
Completed courses
Looking for junior roles in software development, business systems or data automation in Stockholm. Email me or find me on LinkedIn.
Nori shortlists public tenders with match reasons and source links.
Procurement recommendation system
I built a Python pipeline that imports tenders, merges duplicates and compares them with a company profile. It produces weekly PDF and web reports with match reasons, source links and missing information.
A tender can mention flooring while buying consultancy. The challenge was distinguishing the work being bought from related words. I made the matcher assess company fit, the purchased service and evidence from the tender text separately.
Fictional example tenders for a Stockholm flooring contractor.
| Tender | Match reason | What to check |
|---|---|---|
| School floor replacement | Floor fitting in Stockholm. | Timetable and required references. |
| Office floor replacement | Carpet and vinyl work in Stockholm. | Required turnover is missing from the profile. |
Coverage is incomplete. Companies still need to check each tender's requirements before deciding to bid.
Remaining integration work includes verifying source-access and reuse permissions.
Python in Docker, with a local Qwen model. Imports run daily; matching and reports run weekly.
Transcribes Swedish advice meetings, suggests follow-up questions and drafts notes.
Built with KEFT at Vivicta and Microsoft's Finance & Insurance AI Hackathon, September 2026.
My part: I built the Java backend for transcription, questions, notes and DuckDB storage.
The team
In one synthetic test meeting, the draft cited the right transcript lines but changed the amount.
| Step | Saved text |
|---|---|
| Transcript | “sexhundratjugo tusen kronor” — 620,000 kr |
| Draft notes | “Kassa och bank vid bokslutet 2025: 660 000 kr” |
Checks against the quoted figures now flag this mismatch. The adviser reviews the draft.
We tested 16 synthetic advice meetings read by computer voices, checking the output against the scripts. No real customer data was used.
Live questions needed to arrive in time, while notes needed both coverage and factual checks. A draft could cite the right lines yet change an amount, so coverage scores alone were not enough.
Runs locally on an RTX 5090 (32 GB), Core Ultra 9 285K and 64 GB RAM.
Measured from the end of each audio chunk to the server result, including the wait between checks (every 0.5 s).
n = transcript lines, k = changed lines per update, T = meeting length.
| Step | Each update | Whole meeting | What we measured |
|---|---|---|---|
| Speech to text | O(1) per audio chunk | O(T) | 0.31 s median, 95% within 0.51 s, over 6,507 audio chunks. |
| Suggesting questions | O(1) | O(n) | About 16 ms median, 95% within 32 ms, over 6,497 results. |
| Ordering updates on the server | O(k log n) | O(n log n) | 12 µs on average to accept and pack one update, with 2,000 lines. |
| Data sent | O(1 + k), or O(n) for the full transcript | O(n) when k stays the same | 1.06 kB when one line changes. 1.00 MB for the full transcript at 2,000 lines. |
| Updating the page | O(n + k log k) | O(n²) when k stays the same | 0.03 ms at 250 lines, 1.33 ms at 9,998 lines. Measured in Node, drawing the page not included. |
| Writing the notes | Once, after the meeting | Reads the transcript once, O(n). The rest depends on the model. | 52.9 s median, 38.8 to 65.3 s over 16 runs. Counted from Stop, loading the model included. |
Whole-meeting costs assume few updates per line and only changes sent. Model timings come from the 16 synthetic meetings; server, data and page timings are separate component tests.
Bumper warns parents about risks in online games without showing chats.
Built with KEFT in two days at Drivhuset Ideathon, Kista, May 2026.
My part: I built the backend and set up a Cloudflare Tunnel for the live demo.
The team
We wanted to warn parents about risky contacts in online games, like someone asking to move to another app or offering free Robux. Parents get an alert and a tip for talking to their child, without seeing the chat.
This project is unavailable.