Software engineer · AI/ML

I build AI products and the systems behind them.

I'm a software developer on the AI/ML team at SOTI, building natural-language app generation into the company's low-code platform. On my own time I write inference engines, distributed training and interpretability tools from scratch, and I test every one against the reference implementation.

Attention pattern of GPT-2 layer 5 head 5 on a random token sequence repeated twice; in the second copy each token attends to the token that followed it the first time. random tokens the same tokens again keys → queries ↓
Fig. 0GPT-2 layer 5, head 5, reading a random sequence twice. The bright off-diagonal is an induction head: each token in the repeat attends to whatever followed it the first time. Measured with NeuronLens.
4.8e-5largest logit gap between llama-wgsl and Hugging Face transformers on SmolLM2-135M
6.1×faster decoding from FlashServe's KV cache than recomputing the sequence
227Mmessages per second through LatencyArb's lock-free C++ queue
80%less time to build an app with natural-language generation in SOTI Snap
01

Featured projects

WebGPU · TypeScript · Postgres

llama-wgsl

A language model running on your GPU, inside the browser.

llama-wgsl downloads SmolLM2 from Hugging Face, streams the weights straight into GPU memory and runs every layer with compute shaders I wrote by hand in WGSL. No server, no WebAssembly, no ML framework: about 1,500 lines of plain JavaScript.

  • Hand-written kernels. Fused QKV and SwiGLU matrix-vector kernels, grouped-query attention, RoPE with a KV cache, and a GPU argmax for greedy decoding.
  • bf16 without shader-f16. Two weights per 32-bit word, widened in registers, so every WebGPU device can run it at half the memory of f32.
  • Checked against transformers. Logits agree within 4.8e-5 and a 32-token greedy reply is identical, token for token.
The llama-wgsl chat interface: a model picker, a chat panel, live tokens-per-second stats and a list of the kernels that run for each token.
Fig. 1The demo picks a SmolLM2 checkpoint, caches it in the browser and chats with it, showing tokens per second, KV-cache use and the adapter it runs on.
Per-token pipeline: embedding gather, then for each of 30 layers rmsnorm, fused QKV matvec, RoPE and KV-cache write, attention, output projection with residual, rmsnorm, fused gate/up matvec with SiLU, down projection with residual; then final norm, LM head and sampling. 243 dispatches in one compute pass. embed 1 row rmsnorm QKV fused RoPE + KV write attention GQA O proj + residual rmsnorm gate·up + SiLU down + residual × 30 layers · 8 dispatches each LM head tied embed sample argmax / top-p one compute pass per token: 243 dispatches for SmolLM2-135M
Fig. 2What one token costs. Every dispatch for a token goes into a single compute pass; prompt tokens are queued back to back with no round trip to the CPU.
Two bf16 weights packed in one 32-bit word. The high half becomes an f32 by masking off the low 16 bits; the low half by shifting it up 16 bits. bits 31 … 16: weight 2j+1 bits 15 … 0: weight 2j sign exponent (8) mantissa (7) bitcast<f32>(w & 0xffff0000u) → weight 2j+1 bitcast<f32>(w << 16u) → weight 2j
Fig. 3bf16 is the top half of an f32, so a mask or a shift turns each half of a packed word back into a float.

Also inside

streamA safetensors parser feeds each tensor to the GPU as soon as its bytes arrive; the JavaScript heap never holds the checkpoint.
cacheChat turns reuse the KV cache for the shared prefix, so a follow-up only computes its new tokens.
53tests: kernels against float64 references, parity runs on tiny random checkpoints, the tokenizer against Hugging Face.
Yardline's quote editor: a customer's materials list matched to catalog items, priced with tier and volume discounts, with totals, HST, board feet and an activity timeline.
Fig. 4A pasted list becomes priced lines. Stock is checked per yard, a shortfall shows in red, and every change lands in the activity log.

Yardline

Quoting and ordering for building-supply yards, full stack.

A contractor texts a messy materials list; the rep pastes it in, gets a priced quote in seconds, sends the customer a link, and turns the accepted quote into an order that reserves stock. React and TypeScript on a Hono API and Postgres. The live demo runs the same API and a real Postgres inside your browser.

  • Reads lists the way people write them. “2x6-16' x 24”, “(12) 2x10x12 DF”, “5/8 type x drywall 4x8 - 60 sheets” are matched to the catalog with a confidence level and priced per thousand board feet, with tier and volume discounts and a margin floor.
  • Safe under concurrency. Version checks stop lost updates. Orders reserve stock under row locks with an idempotency key, tested with transactions racing for the last boards on Postgres.
  • Live everywhere. Events publish after commit: Server-Sent Events and LISTEN/NOTIFY on the server, a SharedWorker across tabs in the demo.
02

More I've built

every number below comes from the repo's own tests and benchmarks

Python · PyTorch · FastAPI

FlashServe

An inference engine for Hugging Face Llama checkpoints with an OpenAI-compatible streaming API: FlashAttention-style tiled attention, a KV cache, speculative decoding and a paged KV cache.

KV cache: 17.7 tok/s; Full recompute: 2.9 tok/s KV cache 17.7 tok/s Full recompute 2.9 tok/s SmolLM2-135M, 64-token prompt, 128 new tokens, 2 vCPUs
Decode throughput with and without the KV cache.
  • logits within 2.5e-5 of transformers
  • greedy output identical
  • speculative decoding
Source

Python · PyTorch · torch.distributed

TinyTrain

Data, tensor (Megatron-style) and pipeline (GPipe) parallelism for GPT training, written on point-to-point ops with a hand-written ring all-reduce. Multi-process tests prove each one matches single-process training.

Training loss for 300 steps: single process, data parallel, tensor parallel and pipeline parallel runs lie on top of each other. 2.5 3.0 3.5 4.0 0 100 200 300 steps single process data parallel tensor parallel pipeline parallel
Same batches, same initial weights: four strategies, one curve.
  • ring all-reduce
  • 42 tests
  • loss scaling
Source

Python · PyTorch · interpretability

NeuronLens

Activation patching, causal tracing and logit attribution on GPT-2. Its tests reproduce three published results from scratch: induction heads, the indirect-object circuit and causal tracing of a fact.

Logit difference recovered by patching each GPT-2 head on the indirect-object task. Heads 9.9, 9.6 and 10.0 are strongly positive (name movers); 10.7 and 11.10 are negative. 0 1 2 3 4 5 6 7 8 9 10 11 0 1 2 3 4 5 6 7 8 9 10 11 head layer 9.9 name mover 9.6 name mover 10.0 name mover 10.7 negative 11.10 negative
Patching each head into a corrupted prompt: the name movers light up.
  • induction heads 5.5 · 6.9 · 7.10
  • P(" Apple") 0.83 → 0.002 under noise
Source

C++17 · Python

LatencyArb

A header-only C++17 core (lock-free SPSC queue, memory pool, integer-tick order book, execution engine) plus a Python research layer with cointegration tests, a Kalman-filter hedge ratio and a look-ahead-free backtester.

p50 round trip: 63 ns; p99: 72 ns; p99.9: 94 ns p50 round trip 63 ns p99 72 ns p99.9 94 ns ping-pong between two threads through the SPSC queue
Round trip through the queue, measured on a 2-vCPU VM.
  • 227M msgs/s
  • clean under TSan and ASan
Source

Python · asyncio · LLM agents

AgentForge

A planner splits a task into steps with dependencies, executors run the ready steps concurrently with typed tools, and a verifier sends failures back with feedback. It speaks any OpenAI-compatible API, hosted or local.

  • OpenAI-style tool calling
  • sandboxed tools
  • 41 tests
Source
Planner splits the task into steps, executors run ready steps concurrently with tools, the verifier checks the result and sends failures back with feedback. task Planner steps + deps Executor tools Executor tools Executor tools Verifier check result retry with feedback
03

Experience

May 2026 – present
SOTI
Software Developer, AI/ML
  • Lead natural-language app generation in SOTI Snap, the company's enterprise low-code platform: plain-English descriptions become production-ready iOS and Android apps, cutting build time 80%.
  • Architect LLM pipelines that combine RAG, prompt engineering and agentic workflows to turn requirements into UI, data models and backend logic, in 100+ languages.
  • Work 1:1 with the CEO and executives on the AI roadmap; shipped a working prototype in my first month.
Jan 2024 – May 2026
Tegra Lumber
Software Engineer, Full-Stack
  • As the sole engineer, built the company's operations platform end to end in React/TypeScript, Node and PostgreSQL; inventory and yard management cut cycle-count time 45% and stockouts 28%.
  • Built a pricing and quoting service that cut quote turnaround from 6 hours to 5 minutes, and a customer order portal that moved 14% of counter orders online in six weeks.
  • Set up CI/CD with GitHub Actions and Docker (deploys from 15 to 2 minutes) and raised test coverage to 82% across 14 releases.
Jun – Aug 2025
Canada Border Services Agency
Business Support Clerk
  • Supported the Partners in Protection program (3,500+ members); built tracking tools for its deliverables that raised task completion efficiency 30%.
04

About

I like understanding systems all the way down, so I build them myself and then prove they work.

Every project on this page ships with tests against an independent reference, and the numbers here come from those runs. At work I bring the same habit to product: AI features that real users depend on, backed by automated tests, benchmarks and evaluation of the model's output.

Education

York University

B.Eng. in Software Engineering · GPA 3.7/4.0
Dean's List · Honour Roll · Entrance Scholarship

Tools I use

Languages
Python, TypeScript, JavaScript, C#, C++, SQL, Java, WGSL
AI / ML
PyTorch, Hugging Face, LLM inference, RAG, agents, evals, LangGraph, WebGPU
Backend
FastAPI, Node.js, .NET, PostgreSQL, Redis, Docker, Kubernetes, AWS, GCP
Frontend
React, Angular, Jest, Playwright, Pytest