feat: Add CLAUDE.md for project guidance and update project files

- Created CLAUDE.md to provide development commands, architecture overview, and environment variables for the Nuxt 3 portfolio website.
- Refactored project pages to remove unused color mappings and improve project filtering logic.
- Updated content.config.ts to enforce stricter project type definitions and added short descriptions for projects.
- Deleted outdated project files and added new projects related to hackathons and academic research.
- Enhanced existing project descriptions with short summaries for better clarity.
This commit is contained in:
2026-02-16 19:48:31 +01:00
parent 51c60a2790
commit 5a4a4f380f
21 changed files with 493 additions and 269 deletions

86
CLAUDE.md Normal file
View File

@@ -0,0 +1,86 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Development Commands
```bash
# Install dependencies
bun install
# Start development server
bun run dev
# Build for production
bun run build
# Preview production build
bun run preview
# Lint code
bun run lint
# Deploy to Cloudflare
bun run deploy
# Generate Cloudflare types
bun run cf-typegen
```
## Architecture Overview
This is a **Nuxt 3 portfolio website** deployed to **Cloudflare Workers** with the following architecture:
### Tech Stack
- **Framework**: Nuxt 3 (SSR/ISR with Cloudflare preset)
- **UI**: Nuxt UI v4 with Tailwind CSS
- **Content**: Nuxt Content with D1 database backend
- **Styling**: SASS (main.css) + Tailwind CSS
- **Database**: Cloudflare D1 (SQLite)
- **Cache**: Cloudflare KV
- **Icons**: Iconify
- **Composables**: VueUse
- **Validation**: Zod
- **Deployment**: Cloudflare Wrangler + NuxtHub
### Key Patterns
1. **Content Collections**: Content is organized into typed collections (projects, experiences, education, skills, contact, languages, hobbies) defined in `content.config.ts` with Zod schemas
2. **Server API Routes**: Data-fetching endpoints in `server/api/` use `queryCollection()` to fetch from D1 and return JSON to the client
3. **Composables**: Shared logic lives in `app/composables/`:
- `content.ts`: Fetches all main content collections
- `projects.ts`: Project status/type color mappings
4. **Component Structure**: Components are organized by domain in `app/components/`:
- `home/`: Homepage-specific components
- `content/`: Content projection components
- Shared components at root level
5. **Pages**: File-based routing with dynamic routes for projects (`pages/projects/[slug].vue`)
6. **Internationalization**: English/French support with content files in appropriate locales
7. **Server-side Resumes**: Static PDF resume endpoints in `server/routes/resumes/` (en.get.ts, fr.get.ts)
### Cloudflare Configuration
- **Binding**: DB (D1), CACHE (KV), ASSETS (static files)
- **Workers preset**: `cloudflare_module`
- **Exported file**: `.output/server/index.mjs`
- **Preview URLs**: Enabled for branch deployments
### Environment Variables
Required for full functionality (see `.env.example`):
- `STUDIO_GITHUB_CLIENT_ID` / `STUDIO_GITHUB_CLIENT_SECRET`: Nuxt Studio integration
- `NUXT_WAKATIME_*`: Coding statistics API keys
- `NUXT_DISCORD_USER_ID`: Discord activity display
- `NUXT_STATUS_PAGE`: Status page URL
### Build Output
- Client assets: `.output/public/`
- Server code: `.output/server/`
- Database migrations: `.output/server/db/migrations/`

View File

@@ -38,8 +38,6 @@ defineOgImageComponent('NuxtSeo', {
theme: '#F43F5E'
})
const { statusColors, typeColors } = useProjectColors()
const formattedDate = computed(() => {
if (!project.value?.publishedAt) return null
return new Date(project.value.publishedAt).toLocaleDateString('en-US', {
@@ -82,14 +80,12 @@ const formattedDate = computed(() => {
<div class="flex flex-wrap items-center gap-2 mb-4">
<UBadge
v-if="project.type"
:color="(typeColors[project.type] || 'neutral') as any"
variant="subtle"
>
{{ project.type }}
</UBadge>
<UBadge
v-if="project.status"
:color="(statusColors[project.status] || 'neutral') as any"
variant="subtle"
>
{{ project.status }}

View File

@@ -1,11 +1,5 @@
<script lang="ts" setup>
const { data: projects } = await useAsyncData('projects', () => {
return queryCollection('projects')
.where('extension', '=', 'md')
.order('favorite', 'DESC')
.order('publishedAt', 'DESC')
.all()
})
import type { ProjectsCollectionItem } from '@nuxt/content'
const head = {
title: 'Engineering & Research Labs',
@@ -28,250 +22,130 @@ defineOgImageComponent('NuxtSeo', {
theme: '#F43F5E'
})
const { statusColors, typeColors } = useProjectColors()
const { data: projects } = await useAsyncData('projects', () => {
return queryCollection('projects')
.where('extension', '=', 'md')
.order('favorite', 'DESC')
.order('publishedAt', 'DESC')
.all()
})
const selectedType = ref<string | null>(null)
const selectedStatus = ref<string | null>(null)
const availableTypes = computed(() => {
const types = new Set<string>()
const grouped_projects = computed(() => {
const groups: Record<string, ProjectsCollectionItem[]> = {}
projects.value?.forEach((project) => {
if (project.type) types.add(project.type)
const group = project.type || 'Other'
if (!groups[group]) {
groups[group] = []
}
groups[group].push(project)
})
return Array.from(types).sort()
})
const availableStatuses = computed(() => {
const statuses = new Set<string>()
projects.value?.forEach((project) => {
if (project.status) statuses.add(project.status)
const orderPriority = ['Personal Project', 'Research Project', 'Academic Project']
const sorted = Object.entries(groups).sort(([keyA], [keyB]) => {
const indexA = orderPriority.indexOf(keyA)
const indexB = orderPriority.indexOf(keyB)
if (indexA === -1 && indexB === -1) return 0
if (indexA === -1) return 1
if (indexB === -1) return -1
return indexA - indexB
})
return Array.from(statuses).sort()
return Object.fromEntries(sorted)
})
const filteredProjects = computed(() => {
if (!projects.value) return []
return projects.value.filter((project) => {
const typeMatch = !selectedType.value || project.type === selectedType.value
const statusMatch = !selectedStatus.value || project.status === selectedStatus.value
return typeMatch && statusMatch
})
})
function clearFilters() {
selectedType.value = null
selectedStatus.value = null
}
const hasActiveFilters = computed(() => !!selectedType.value || !!selectedStatus.value)
const activeFilterCount = computed(() => (selectedType.value ? 1 : 0) + (selectedStatus.value ? 1 : 0))
</script>
<template>
<main class="space-y-8 py-4">
<div class="space-y-4">
<div class="flex flex-col items-center justify-center gap-4">
<h1 class="text-3xl sm:text-4xl font-bold text-neutral-900 dark:text-white font-mono tracking-tight">
Engineering & Research Labs
</h1>
<p class="max-w-3xl leading-relaxed">
Bridging the gap between theoretical models and production systems. Explore my experimental labs, open-source contributions, and engineering work.
Bridging the gap between theoretical models and production systems. <br>Explore my experimental labs, open-source contributions, and engineering work.
</p>
<UButton
size="md"
label="See more open source projects on Github"
variant="soft"
color="neutral"
icon="i-ph-github-logo"
to="https://go.arthurdanjou.fr/github"
/>
</div>
<div class="flex flex-col gap-4">
<div class="flex items-center gap-2 overflow-x-auto w-full whitespace-nowrap pb-2">
<span class="text-sm font-medium text-neutral-700 dark:text-neutral-300 mr-2 min-w-12.5">Type:</span>
<UButton
:variant="!selectedType ? 'solid' : 'ghost'"
color="neutral"
size="sm"
@click="selectedType = null"
>
All
</UButton>
<UButton
v-for="type in availableTypes"
:key="type"
:variant="selectedType === type ? 'subtle' : 'ghost'"
:color="(typeColors[type] || 'neutral') as any"
size="sm"
class="transition-all duration-200"
:class="selectedType === type ? 'ring-1 ring-inset' : ''"
@click="selectedType = selectedType === type ? null : type"
>
{{ type }}
</UButton>
</div>
<div class="flex items-center gap-4 overflow-x-auto flex-nowrap md:flex-wrap w-full whitespace-nowrap pb-2">
<div class="flex gap-2 items-center">
<span class="text-sm font-medium text-neutral-700 dark:text-neutral-300">Status:</span>
<UButton
:variant="!selectedStatus ? 'solid' : 'ghost'"
color="neutral"
size="sm"
@click="selectedStatus = null"
>
All
</UButton>
<UButton
v-for="status in availableStatuses"
:key="status"
:variant="selectedStatus === status ? 'solid' : 'ghost'"
:color="(statusColors[status] || 'neutral') as any"
size="sm"
@click="selectedStatus = selectedStatus === status ? null : status"
>
{{ status }}
</UButton>
</div>
<div class="ml-auto">
<UButton
v-if="hasActiveFilters"
variant="ghost"
color="neutral"
size="sm"
icon="i-ph-x-circle-duotone"
aria-label="Clear filters"
@click="clearFilters"
>
Clear filters ({{ activeFilterCount }})
</UButton>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<UCard
v-for="project in filteredProjects"
:key="project.slug"
class="relative hover:shadow-sm transition-all duration-300 group"
>
<template #header>
<div class="flex items-start gap-4">
<div
class="p-2 rounded-lg shrink-0 flex items-center justify-center"
:class="project.favorite ? 'ring-2 ring-amber-400 text-amber-400' : 'bg-neutral-200 dark:bg-neutral-800 text-neutral-700 dark:text-neutral-300'"
>
<UIcon
:name="project.icon || 'i-ph-code-duotone'"
class="w-6 h-6"
/>
</div>
<div class="flex-1 min-w-0">
<UTooltip
:text="project.title"
:popper="{ placement: 'top-start' }"
class="w-full relative z-10"
>
<NuxtLink
:to="`/projects/${project.slug}`"
class="block focus:outline-none"
>
<h3 class="text-lg font-bold truncate group-hover:text-neutral-900 text-neutral-500 dark:group-hover:text-white transition-colors duration-200">
{{ project.title }}
</h3>
</NuxtLink>
</UTooltip>
<div class="flex items-center gap-2 mt-2 flex-wrap relative z-10">
<UBadge
v-if="project.type"
:color="(typeColors[project.type] || 'neutral') as any"
variant="subtle"
size="xs"
>
{{ project.type }}
</UBadge>
<UBadge
v-if="project.status"
:color="(statusColors[project.status] || 'neutral') as any"
variant="subtle"
size="xs"
>
{{ project.status }}
</UBadge>
<UBadge
v-if="project.favorite"
color="amber"
variant="subtle"
size="xs"
>
</UBadge>
</div>
</div>
</div>
</template>
<p class="text-sm text-neutral-600 dark:text-neutral-400 line-clamp-3 leading-relaxed">
{{ project.description }}
</p>
<template #footer>
<div class="flex items-center justify-between">
<div class="flex flex-wrap gap-1">
<UBadge
v-for="tag in project.tags?.slice(0, 3)"
:key="tag"
color="neutral"
variant="outline"
size="xs"
class="opacity-75"
>
{{ tag }}
</UBadge>
<span
v-if="project.tags && project.tags.length > 3"
class="text-xs text-neutral-400 font-mono ml-1 self-center"
>
+{{ project.tags.length - 3 }}
</span>
</div>
<span
v-if="project.readingTime"
class="text-xs text-neutral-400 font-mono flex items-center gap-1 shrink-0 ml-2"
>
<UIcon
name="i-ph-hourglass-medium-duotone"
class="w-3 h-3"
/>
{{ project.readingTime }}m
</span>
</div>
</template>
<div class="flex flex-col gap-16">
<div
v-for="(projects, group) in grouped_projects"
:key="group"
class="relative"
>
<h1 class="mb-2 font-bold text-7xl text-transparent opacity-15 text-stroke-neutral-400 dark:text-stroke-neutral-500 text-stroke-2 -translate-x-8">
{{ group }}
</h1>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 grid-rows-auto">
<NuxtLink
v-for="project in projects"
:key="project.slug"
:to="`/projects/${project.slug}`"
:aria-label="`Open project: ${project.title}`"
class="absolute inset-0 z-0"
/>
</UCard>
</div>
<div
v-if="filteredProjects.length === 0"
class="text-center py-20 border border-dashed border-neutral-200 dark:border-neutral-800 rounded-xl"
>
<UIcon
name="i-ph-flask-duotone"
class="text-6xl text-neutral-300 dark:text-neutral-700 mb-4"
/>
<h3 class="text-lg font-medium text-neutral-900 dark:text-white">
No experiments found
</h3>
<p class="text-neutral-500 dark:text-neutral-400 mb-6">
No projects match the selected filters.
</p>
<UButton
color="primary"
variant="soft"
@click="clearFilters"
>
Clear Filters
</UButton>
class="hover:bg-[#8881] dark:hover:bg-neutral-700/20 duration-500 rounded-lg transition-colors p-4"
>
<div class="flex justify-center items-center gap-4 z-50">
<div>
<UIcon
:name="project.icon"
size="40"
/>
</div>
<div class="space-y-2">
<div class="flex items-center gap-2">
<h1 class="font-bold">
{{ project.title }}
</h1>
<UTooltip
text="Favorite"
:delay-duration="4"
>
<UBadge
v-if="project.favorite"
color="amber"
variant="subtle"
size="sm"
icon="i-ph-star-four-duotone"
/>
</UTooltip>
<UTooltip
text="In Progress"
:delay-duration="4"
>
<UBadge
v-if="project.status === 'in progress'"
color="blue"
variant="soft"
size="sm"
icon="i-ph-hourglass-duotone"
/>
</UTooltip>
<UTooltip
text="Archived"
:delay-duration="4"
>
<UBadge
v-if="project.status === 'archived'"
color="gray"
variant="soft"
size="sm"
icon="i-ph-archive-duotone"
/>
</UTooltip>
</div>
<p class="italic text-xs text-muted">
{{ project.shortDescription }}
</p>
</div>
</div>
</nuxtlink>
</div>
</div>
</div>
</main>

View File

@@ -13,10 +13,11 @@ export const collections = {
schema: z.object({
slug: z.string(),
title: z.string(),
type: z.string().optional(),
type: z.enum(['Personal Project', 'Academic Project', 'Hackathon', 'Research Project', 'Internship Project']),
description: z.string(),
shortDescription: z.string(),
publishedAt: z.string(),
readingTime: z.number().optional(),
readingTime: z.number(),
tags: z.array(z.string()),
favorite: z.boolean().optional(),
status: z.enum(['active', 'completed', 'archived', 'in progress']),

View File

@@ -1,28 +0,0 @@
---
slug: arthome
title: ArtHome - Browser Homepage
type: Personal Project
description: A customizable browser homepage that lets you organize all your favorite links in one place with categories, tabs, icons and colors.
publishedAt: 2024-09-04
readingTime: 1
status: Archived
tags:
- Nuxt
- Vue.js
- Web
- Productivity
icon: i-ph-house-duotone
---
[**ArtHome**](https://go.arthurdanjou.fr/arthome) is a customizable browser homepage that lets you organize all your favorite links in one place.
Create categories and tabs to group your shortcuts, personalize them with icons and colors, and make the page private if you want to keep your links just for yourself. The interface is clean, responsive, and works across all modern browsers.
## 🛠️ Technology Stack
- **[Nuxt](https://nuxt.com)**: An open-source framework for building performant, full-stack web applications with Vue.
- **[NuxtHub](https://hub.nuxt.com)**: A Cloudflare-powered platform to deploy and scale Nuxt apps globally with minimal latency and full-stack capabilities.
- **[NuxtUI](https://ui.nuxt.com)**: A sleek and flexible component library that helps create beautiful, responsive UIs for Nuxt applications.
- **[ESLint](https://eslint.org)**: A linter that identifies and fixes problems in your JavaScript/TypeScript code.
- **[Drizzle ORM](https://orm.drizzle.team/)**: A lightweight, type-safe ORM built for TypeScript, designed for simplicity and performance.
- **[Zod](https://zod.dev/)**: A TypeScript-first schema declaration and validation library with full static type inference.

View File

@@ -1,8 +1,9 @@
---
slug: artlab
title: ArtLab - Personal HomeLab
type: Infrastructure Project
type: Personal Project
description: A personal homelab environment where I deploy, test, and maintain self-hosted services with privacy-focused networking through VPN and Cloudflare Tunnels.
shortDescription: A personal homelab environment for self-hosting and automation.
publishedAt: 2025-09-04
readingTime: 1
favorite: true
@@ -39,6 +40,6 @@ My homelab is a self-hosted environment where I deploy, test, and maintain perso
- **Beelink EQR6**: AMD Ryzen mini PC, main server host.
- **TP-Link 5-port Switch**: Network connectivity for all devices.
- **UGREEN NASync DXP4800 Plus**: 4-bay NAS, currently populated with 2 × 8TB drives for storage and backups.
- **UGREEN NASync DXP4800 Plus**: 4-bay NAS, currently populated with 2 x 8TB drives for storage and backups.
This homelab is a sandbox for DevOps experimentation, infrastructure reliability, and privacy-respecting digital autonomy.

View File

@@ -3,6 +3,7 @@ slug: artsite
title: ArtSite - Personal Research Hub
type: Personal Project
description: My digital headquarters. A high-performance portfolio built on the Edge using the full Nuxt ecosystem, deployed to Cloudflare Workers via Wrangler.
shortDescription: A modern portfolio and experimental lab built on the Nuxt ecosystem and deployed to Cloudflare Workers.
publishedAt: 2024-06-01
readingTime: 2
favorite: true

View File

@@ -3,6 +3,7 @@ slug: artstudies
title: ArtStudies - Academic Projects Collection
type: Academic Project
description: A curated collection of mathematics and data science projects developed during my academic journey, spanning Bachelor's and Master's studies.
shortDescription: A collection of academic projects in mathematics and data science from my university studies.
publishedAt: 2023-09-01
readingTime: 1
favorite: true

View File

@@ -3,6 +3,7 @@ slug: data-visualisation
title: Data Visualisation Project
type: Academic Project
description: An interactive data visualization project built with R, R Shiny, and ggplot2 for creating dynamic, explorable visualizations.
shortDescription: An interactive data visualization project using R and R Shiny.
publishedAt: 2026-01-05
readingTime: 1
status: Completed

View File

@@ -3,6 +3,7 @@ slug: dl-dropout-reduces-underfitting
title: Dropout Reduces Underfitting
type: Research Project
description: TensorFlow/Keras implementation and reproduction of "Dropout Reduces Underfitting" (Liu et al., 2023). A comparative study of Early and Late Dropout strategies to optimize model convergence.
shortDescription: Reproduction of "Dropout Reduces Underfitting" with TensorFlow/Keras, comparing Early and Late Dropout strategies.
publishedAt: 2024-12-10
readingTime: 6
status: Completed

View File

@@ -3,6 +3,7 @@ slug: glm-bikes
title: Generalized Linear Models for Bikes Prediction
type: Academic Project
description: Predicting the number of bikes rented in a bike-sharing system using Generalized Linear Models and various statistical techniques.
shortDescription: A project applying Generalized Linear Models to predict bike rentals based on environmental and temporal features.
publishedAt: 2025-01-24
readingTime: 1
status: Completed

View File

@@ -0,0 +1,67 @@
---
slug: implied-volatility-modeling
title: Implied Volatility Surface Modeling
type: Academic Project
description: A large-scale statistical study comparing Generalized Linear Models (GLMs) and black-box machine learning architectures to predict the implied volatility of S&P 500 options.
shortDescription: Predicting the SPX volatility surface using GLMs and black-box models on 1.2 million observations.
publishedAt: 2026-02-28
readingTime: 3
status: in progress
tags:
- R
- GLM
- Finance
- Machine Learning
icon: i-ph-graph-duotone
---
This project targets high-precision calibration of the **Implied Volatility Surface** using a large-scale dataset of S&P 500 (SPX) European options.
The core objective is to stress-test classic statistical models against modern predictive algorithms. **Generalized Linear Models (GLMs)** provide a transparent baseline, while more complex "black-box" architectures are evaluated on whether their accuracy gains justify the loss of interpretability in a risk management setting.
## 📊 Dataset & Scale
The modeling is performed on a high-dimensional dataset with over **1.2 million observations**.
- **Target Variable**: `implied_vol_ref` (implied volatility).
- **Features**: Option strike price ($K$), underlying asset price ($S$), and time to maturity ($\tau$).
- **Volume**: A training set of $1,251,307$ rows and a test set of identical size.
## 🛠️ Modeling Methodology
The project follows a rigorous statistical pipeline to compare two modeling philosophies:
### 1. The Statistical Baseline (GLM)
Using R's GLM framework, I implement models with targeted link functions and error distributions (such as **Gamma** or **Inverse Gaussian**) to capture the global structure of the volatility surface. These models serve as the benchmark for transparency and stability.
### 2. The Black-Box Challenge
To capture local non-linearities such as the volatility smile and skew, I explore more complex architectures. Performance is evaluated by **Root Mean Squared Error (RMSE)** relative to the GLM baselines.
### 3. Feature Engineering
Key financial indicators are derived from the raw data:
- **Moneyness**: Calculated as the ratio $K/S$.
- **Temporal Dynamics**: Transformations of time to maturity to linearize the term structure.
## 📈 Evaluation & Reproducibility
Performance is measured strictly via RMSE on the original scale of the target variable. To ensure reproducibility and precise comparisons across model iterations, a fixed random seed is maintained throughout the entire workflow.
```r
set.seed(2025)
TrainData <- read.csv("train_ISF.csv", stringsAsFactors = FALSE)
TestX <- read.csv("test_ISF.csv", stringsAsFactors = FALSE)
rmse_eval <- function(actual, predicted) {
sqrt(mean((actual - predicted)^2))
}
```
## 🔍 Critical Analysis
Beyond pure prediction, the project addresses:
- Model Limits: Identifying market regimes where models fail (e.g., deep out-of-the-money options).
- Interpretability: Quantifying the trade-off between complexity and practical utility in a risk management context.
- Future Extensions: Considering richer dynamics, such as historical volatility or skew-specific targets.

View File

@@ -0,0 +1,55 @@
---
slug: hackathon-cnd
title: "CND Hackathon: Defense-Grade Log Intelligence"
type: Hackathon
description: A high-stakes cybersecurity challenge organized by the French Ministry of Defense (CND). Representing Université Paris-Dauphine, our team spent 3 days in a high-security military fortress developing ML models to detect stealthy cyber threats in firewall logs.
shortDescription: Cybersecurity threat detection within a high-security military environment.
publishedAt: 2025-10-28
readingTime: 4
status: completed
tags:
- Python
- Streamlit
- Cybersecurity
- Machine Learning
- Scikit-learn
icon: i-ph-shield-check-duotone
---
## The Setting: Fort de Mont-Valérien
This wasn't your typical university hackathon. Organized by the **Commissariat au Numérique de Défense (CND)**, the event took place over three intense days within the walls of the **Fort de Mont-Valérien**—a highly secured military fortress.
Working in this environment underscored the real-world stakes of the mission. Our **team of six**, representing **Université Paris-Dauphine**, competed against several elite engineering schools to solve critical defense-related data challenges.
## The Mission: Classifying the "Invisible"
The core task involved processing poorly labeled and noisy firewall logs. In a defense context, a "missing" log or a mislabeled entry can be the difference between a minor system bug and a coordinated intrusion.
### 1. Tactical Log Translation
Firewall logs are often cryptic and inconsistent. We developed a preprocessing pipeline to:
* **Feature Extraction:** Parse raw logs into structured data (headers, flags, payloads).
* **Contextual Labeling:** Distinguish between routine system "bugs" (non-malicious failures) and actual "attacks" (malicious intent).
### 2. Strategic Goal: Recalling the Threat
In military cybersecurity, the cost of a **False Negative** (an undetected attack) is catastrophic.
* **Model Priority:** We optimized our classifiers specifically for **Recall**. We would rather investigate a few system bugs (False Positives) than let a single attack slip through the net.
* **Techniques:** We used ensemble methods (XGBoost/Random Forest) combined with advanced resampling to handle the heavy class imbalance typical of network traffic.
> **Key Achievement:** Our model significantly reduced the rate of undetected threats compared to standard baseline configurations provided at the start of the challenge.
## Deployment & Interaction
To make our findings operational, we built a **Streamlit-based command center**:
* **On-the-Fly Analysis:** Security officers can paste a single log line to get an immediate "Bug vs. Attack" probability score.
* **Bulk Audit:** The interface supports CSV uploads, allowing for the rapid analysis of entire daily log batches to highlight high-risk anomalies.
## Technical Stack
* **Language:** Python
* **ML Library:** Scikit-learn, XGBoost
* **Deployment:** Streamlit
* **Environment:** High-security on-site military infrastructure
---
Representing Dauphine in such a specialized environment was a highlight of my academic year. Would you like me to elaborate on the specific feature engineering techniques we used to "clean" the raw military logs?

View File

@@ -0,0 +1,55 @@
---
slug: hackathon-natixis
title: "Natixis Hackathon: Generative SQL Analytics"
type: Hackathon
description: An intensive 4-week challenge to build an AI-powered data assistant. Our team developed a GenAI agent that transforms natural language into executable SQL queries, interactive visualizations, and natural language insights.
shortDescription: A team-based project building an NL-to-SQL agent with Nuxt, Ollama, and Vercel AI SDK.
publishedAt: 2026-03-07
readingTime: 4
status: completed
tags:
- Nuxt
- Ollama
- Vercel AI SDK
- PostgreSQL
- ETL
icon: i-ph-database-duotone
---
## The Challenge
Organized by **Natixis**, this hackathon followed a unique high-intensity format: **three consecutive Saturdays** of on-site development, bridged by two full weeks of remote collaboration.
Working in a **team of four**, our goal was to bridge the gap between non-technical stakeholders and complex financial databases by creating an autonomous "Data Talk" agent.
## Core Features
### 1. Data Engineering & Schema Design
Before building the AI layer, we had to handle a significant data migration task. I led the effort to:
* **ETL Pipeline:** Convert fragmented datasets from **.xlsx** and **.csv** formats into a structured **SQL database**.
* **Schema Optimization:** Design robust SQL schemas that allow an LLM to easily understand relationships (foreign keys, indexing) for accurate query generation.
### 2. Natural Language to SQL (NL-to-SQL)
Using the **Vercel AI SDK** and **Ollama**, we implemented an agentic workflow:
* **Prompt Engineering:** Fine-tuning the agent to translate complex business questions (e.g., "What was our highest growth margin last quarter?") into valid, optimized SQL.
* **Self-Correction:** If a query fails, the agent analyzes the SQL error and self-corrects the syntax before returning a result.
### 3. Automated Insights & Visualization
Data is only useful if its readable. Our Nuxt application goes beyond raw tables:
* **Dynamic Charts:** The agent automatically determines the best visualization type (Bar, Line, Pie) based on the query result and renders it using interactive components.
* **Narrative Explanations:** A final LLM pass summarizes the data findings in plain English, highlighting anomalies or key trends.
## Technical Stack
* **Frontend/API:** **Nuxt 3** for a seamless, reactive user interface.
* **Orchestration:** **Vercel AI SDK** to manage streams and tool-calling logic.
* **Inference:** **Ollama** for running LLMs locally, ensuring data privacy during development.
* **Storage:** **PostgreSQL** for the converted data warehouse.
## Impact & Results
This project demonstrated that a modern stack (Nuxt + local LLMs) can drastically reduce the time needed for data discovery. By the final Saturday, our team successfully presented a working prototype capable of handling multi-table joins and generating real-time financial dashboards from simple chat prompts.
---
*Curious about the ETL logic or the prompt structure we used? I'd be happy to discuss how we optimized the LLM's SQL accuracy.*

View File

@@ -3,6 +3,7 @@ slug: ml-loan
title: Machine Learning for Loan Prediction
type: Academic Project
description: Predicting loan approval and default risk using machine learning classification techniques.
shortDescription: A project applying machine learning to predict loan approvals and assess default risk.
publishedAt: 2025-01-24
readingTime: 2
status: Completed

View File

@@ -3,6 +3,7 @@ slug: monte-carlo-project
title: Monte Carlo Methods Project
type: Academic Project
description: An implementation of different Monte Carlo methods and algorithms in R, including inverse CDF simulation, accept-reject methods, and stratification techniques.
shortDescription: A project implementing various Monte Carlo methods and algorithms in R.
publishedAt: 2024-11-24
readingTime: 3
status: Completed

View File

@@ -0,0 +1,55 @@
---
slug: n8n-automations
title: n8n Automations
type: Academic Project
description: An academic project exploring the automation of GenAI workflows using n8n and Ollama for self-hosted AI applications, including personalized research agents and productivity hubs.
shortDescription: Automating GenAI workflows with n8n and Ollama in a self-hosted environment.
publishedAt: 2026-03-15
readingTime: 2
status: in progress
tags:
- n8n
- Gemini
- Self-Hosted
- Automation
- RAG
- Productivity
icon: i-ph-plugs-connected-duotone
---
## Overview
This project focuses on designing and implementing autonomous workflows that leverage Large Language Models (LLMs) to streamline productivity and academic research. By orchestrating Generative AI through a self-hosted infrastructure on my **[ArtLab](/projects/artlab)**, I have built a private ecosystem that acts as both a personal assistant and a specialized research agent.
## Key Workflows
### 1. Centralized Productivity Hub
I developed a synchronization engine that bridges **Notion**, **Google Calendar**, and **Todoist**.
* **Contextual Sync:** Academic events, such as course schedules and exam dates, are pulled from Notion and reflected in my calendar and task manager.
* **Daily Briefing:** Every morning, the system triggers a workflow that compiles my schedule, pending tasks, and a local weather report into a single, centralized email summary. This ensures a friction-less start to the day with all critical information in one place.
### 2. Intelligent Research Engine (RSS & RAG)
To stay at the forefront of AI research, I built an automated pipeline for academic and technical monitoring.
* **Multi-Source Fetching:** The system monitors RSS feeds from **arXiv**, **Hugging Face**, **Hacker News**, **selfho.st**, and major industry blogs (OpenAI, Google Research, Meta).
* **Semantic Filtering:** Using LLMs, articles are filtered and ranked based on my specific research profile—focusing on **robust distributed learning**.
* **Knowledge Base:** Relevant papers and posts are automatically stored in a structured Notion database.
* **Interactive Research Agent:** I integrated a chat interface within n8n that allows me to query this collected data. I can request summaries, ask specific technical questions about a paper, or extract the most relevant insights for my current thesis work.
## Technical Architecture
The environment is built to handle complex multi-step chains, moving beyond simple API calls to create context-aware agents.
### Integrated Ecosystem
* **Intelligence Layer:** Integration with **Gemini** (API) and **Ollama** (local) for summarization and semantic sorting.
* **Data Sources:** RSS Feeds, Notion Databases.
* **Notifications & UI:** Gmail for briefings and Discord for real-time system alerts.
## Key Objectives
1. **Privacy-Centric AI:** Ensuring that sensitive academic data and personal schedules remain within a self-hosted or controlled environment.
2. **Academic Efficiency:** Reducing the "noise" of information overload by using AI to surface only the most relevant research papers.
3. **Low-Code Orchestration:** Utilizing n8n to manage complex logic and API interactions without the overhead of maintaining a massive custom codebase.
---
*This project is currently under active development as I refine the RAG (Retrieval-Augmented Generation) logic and optimize the filtering prompts for my research.*

View File

@@ -0,0 +1,52 @@
---
slug: rl-tennis
title: Reinforcement Learning for Tennis Strategy Optimization
type: Academic Project
description: An academic project exploring the application of reinforcement learning to optimize tennis strategies. The project involves training RL agents on Atari Tennis (ALE) to evaluate strategic decision-making through competitive self-play and baseline benchmarking.
shortDescription: Reinforcement learning algorithms applied to Atari tennis matches for strategy optimization and competitive benchmarking.
publishedAt: 2026-03-13
readingTime: 3
status: in progress
tags:
- Reinforcement Learning
- Python
- Gymnasium
- Atari
- ALE
icon: i-ph-lightning-duotone
---
## Overview
This project serves as a practical application of theoretical Reinforcement Learning (RL) principles. The goal is to develop and train autonomous agents capable of mastering the complex dynamics of **Atari Tennis**, using the **Arcade Learning Environment (ALE)** via Farama Foundation's Gymnasium.
Instead of simply reaching a high score, this project focuses on **strategy optimization** and **comparative performance** through a multi-stage tournament architecture.
## Technical Objectives
The project is divided into three core phases:
### 1. Algorithm Implementation
I am implementing several key RL algorithms covered during my academic curriculum to observe their behavioral differences in a high-dimensional state space:
* **Value-Based Methods:** Deep Q-Networks (DQN) and its variants (Double DQN, Dueling DQN).
* **Policy Gradient Methods:** Proximal Policy Optimization (PPO) for more stable continuous action control.
* **Exploration Strategies:** Implementing Epsilon-greedy and Entropy-based exploration to handle the sparse reward signals in tennis rallies.
### 2. The "Grand Slam" Tournament (Self-Play)
To determine the most robust strategy, I developed a competitive framework:
* **Agent vs. Agent:** Different algorithms (e.g., PPO vs. DQN) are pitted against each other in head-to-head matches.
* **Evolutionary Ranking:** Success is measured not just by points won, but by the ability to adapt to the opponent's playstyle (serve-and-volley vs. baseline play).
* **Winner Identification:** The agent with the highest win rate and most stable policy is crowned the "Optimal Strategist."
### 3. Benchmarking Against Atari Baselines
The final "Boss Level" involves taking my best-performing trained agent and testing it against the pre-trained, high-performance algorithms provided by the Atari/ALE benchmarks. This serves as a validation step to measure the efficiency of my custom implementations against industry-standard baselines.
## Tech Stack & Environment
* **Environment:** [ALE (Arcade Learning Environment) - Tennis](https://ale.farama.org/environments/tennis/)
* **Frameworks:** Python, Gymnasium, PyTorch (for neural network backends).
* **Key Challenges:** Handling the long-horizon dependency of a tennis match and the high-frequency input of the Atari RAM/Pixels.
---
*This project is currently in the training phase. I am currently fine-tuning the reward function to discourage "passive" play and reward aggressive net approaches.*

View File

@@ -3,6 +3,7 @@ slug: schelling-segregation-model
title: Schelling Segregation Model
type: Academic Project
description: A Python implementation of the Schelling Segregation Model using statistics and data visualization to analyze spatial segregation patterns.
shortDescription: A project implementing the Schelling Segregation Model in Python.
publishedAt: 2024-05-03
readingTime: 4
status: Completed

View File

@@ -3,6 +3,7 @@ slug: sevetys
title: Data Engineer Internship at Sevetys
type: Internship Project
description: Summary of my internship as a Data Engineer at Sevetys, focusing on data quality, cleaning, standardization, and comprehensive data quality metrics.
shortDescription: A summary of my Data Engineer internship at Sevetys, focusing on data quality and cleaning processes.
publishedAt: 2025-07-31
readingTime: 2
status: Completed

View File

@@ -3,6 +3,7 @@ slug: sl-breast-cancer
title: Breast Cancer Detection
type: Academic Project
description: Prediction of breast cancer presence by comparing several supervised classification models using machine learning techniques.
shortDescription: A project comparing supervised classification models to predict breast cancer presence using machine learning.
publishedAt: 2025-06-06
readingTime: 2
status: Completed