diff --git a/.gitignore b/.gitignore index 18eb117..61139a3 100644 --- a/.gitignore +++ b/.gitignore @@ -59,4 +59,5 @@ deep-research-agent/data/car_price_prediction_.csv # -go-research/external_code/ \ No newline at end of file +go-research/external_code/ +**.pyc diff --git a/app/presentations/deep-research/[slide]/page.tsx b/app/presentations/deep-research/[slide]/page.tsx new file mode 100644 index 0000000..8882265 --- /dev/null +++ b/app/presentations/deep-research/[slide]/page.tsx @@ -0,0 +1,132 @@ +import { getAllSlideSlugs, getSlideBySlug } from '@/lib/presentations/deep-research'; +import { notFound } from 'next/navigation'; +import type { Metadata } from 'next'; + +export const dynamic = 'error'; +export const revalidate = false; + +export async function generateStaticParams() { + return getAllSlideSlugs().map((slide) => ({ slide })); +} + +export async function generateMetadata({ + params, +}: { + params: Promise<{ slide: string }>; +}): Promise { + const { slide } = await params; + const slideData = getSlideBySlug(slide); + if (!slideData) return { title: 'Slide Not Found' }; + + return { + title: `${slideData.title} — Deep Research Agents`, + description: 'Deep Research Agents: Architecture Walkthrough — Foo Cafe Malmö, Feb 2026', + }; +} + +export default async function SlidePage({ + params, +}: { + params: Promise<{ slide: string }>; +}) { + const { slide } = await params; + const slideData = getSlideBySlug(slide); + if (!slideData) notFound(); + + const slideContent = await (async () => { + switch (slide) { + case '01-title': { + const { TitleSlide } = await import('@/components/presentations/deep-research/slides/01-title'); + return ; + } + case '02-group-project': { + const { GroupProjectSlide } = await import('@/components/presentations/deep-research/slides/02-group-project'); + return ; + } + case '03-the-result': { + const { TheResultSlide } = await import('@/components/presentations/deep-research/slides/03-the-result'); + return ; + } + case '04-the-reveal': { + const { TheRevealSlide } = await import('@/components/presentations/deep-research/slides/04-the-reveal'); + return ; + } + case '05-about': { + const { AboutSlide } = await import('@/components/presentations/deep-research/slides/05-about'); + return ; + } + case '06-audience-poll': { + const { AudiencePollSlide } = await import('@/components/presentations/deep-research/slides/06-audience-poll'); + return ; + } + case '07-timeline': { + const { TimelineSlide } = await import('@/components/presentations/deep-research/slides/07-timeline'); + return ; + } + case '08-cot': { + const { CotSlide } = await import('@/components/presentations/deep-research/slides/08-cot'); + return ; + } + case '09-react': { + const { ReactSlide } = await import('@/components/presentations/deep-research/slides/09-react'); + return ; + } + case '09-react-demo': { + const { ReactDemoSlide } = await import('@/components/presentations/deep-research/slides/09-react-demo'); + return ; + } + case '08-storm-intro': { + const { StormIntroSlide } = await import('@/components/presentations/deep-research/slides/08-storm-intro'); + return ; + } + case '09-storm-architecture': { + const { StormArchitectureSlide } = await import('@/components/presentations/deep-research/slides/09-storm-architecture'); + return ; + } + case '10-storm-demo': { + const { StormDemoSlide } = await import('@/components/presentations/deep-research/slides/10-storm-demo'); + return ; + } + case '11-limitation': { + const { LimitationSlide } = await import('@/components/presentations/deep-research/slides/11-limitation'); + return ; + } + case '12-diffusion-insight': { + const { DiffusionInsightSlide } = await import('@/components/presentations/deep-research/slides/12-diffusion-insight'); + return ; + } + case '13-diffusion-architecture': { + const { DiffusionArchitectureSlide } = await import('@/components/presentations/deep-research/slides/13-diffusion-architecture'); + return ; + } + case '14-loop-visualized': { + const { LoopVisualizedSlide } = await import('@/components/presentations/deep-research/slides/14-loop-visualized'); + return ; + } + case '15-parallel-agents': { + const { ParallelAgentsSlide } = await import('@/components/presentations/deep-research/slides/15-parallel-agents'); + return ; + } + case '16-diffusion-demo': { + const { DiffusionDemoSlide } = await import('@/components/presentations/deep-research/slides/16-diffusion-demo'); + return ; + } + case '17-benchmarks': { + const { BenchmarksSlide } = await import('@/components/presentations/deep-research/slides/17-benchmarks'); + return ; + } + case '18-takeaways': { + const { TakeawaysSlide } = await import('@/components/presentations/deep-research/slides/18-takeaways'); + return ; + } + case '19-resources': { + const { ResourcesSlide } = await import('@/components/presentations/deep-research/slides/19-resources'); + return ; + } + default: + notFound(); + } + })(); + + return slideContent; +} diff --git a/app/presentations/deep-research/layout.tsx b/app/presentations/deep-research/layout.tsx new file mode 100644 index 0000000..9297e28 --- /dev/null +++ b/app/presentations/deep-research/layout.tsx @@ -0,0 +1,106 @@ +'use client'; + +import { useRouter, usePathname } from 'next/navigation'; +import { useEffect, useState, useCallback, createContext, useContext } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { + getAllSlides, + getSlideIndex, + getAdjacentSlugs, + getSlideSteps, +} from '@/lib/presentations/deep-research'; + +const SlideStepContext = createContext(0); + +export function useSlideStep() { + return useContext(SlideStepContext); +} + +function SlideStepProvider({ + pathname, + children, +}: { + pathname: string; + children: React.ReactNode; +}) { + const router = useRouter(); + const currentSlug = pathname.split('/').pop() ?? ''; + const { prev, next } = getAdjacentSlugs(currentSlug); + const maxSteps = getSlideSteps(currentSlug); + const [step, setStep] = useState(0); + + const handleKeyDown = useCallback( + (e: KeyboardEvent) => { + if (e.key === 'ArrowRight' || e.key === ' ') { + e.preventDefault(); + if (step < maxSteps) { + setStep((s) => s + 1); + } else if (next) { + router.push(`/presentations/deep-research/${next}`); + } + } else if (e.key === 'ArrowLeft') { + e.preventDefault(); + if (step > 0) { + setStep((s) => s - 1); + } else if (prev) { + router.push(`/presentations/deep-research/${prev}`); + } + } + }, + [step, maxSteps, router, prev, next], + ); + + useEffect(() => { + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [handleKeyDown]); + + return ( + + {children} + + ); +} + +export default function PresentationLayout({ children }: { children: React.ReactNode }) { + const pathname = usePathname(); + const slides = getAllSlides(); + const currentSlug = pathname.split('/').pop() ?? ''; + const currentIndex = getSlideIndex(currentSlug); + + return ( +
+ {/* Progress bar */} +
+
+
+ + {/* Slide content with fade transition — key resets step state on route change */} + + + + {children} + + + + + {/* Slide counter */} +
+ {currentIndex + 1} / {slides.length} +
+
+ ); +} diff --git a/app/presentations/deep-research/page.tsx b/app/presentations/deep-research/page.tsx new file mode 100644 index 0000000..c291cb7 --- /dev/null +++ b/app/presentations/deep-research/page.tsx @@ -0,0 +1,7 @@ +import { redirect } from 'next/navigation'; + +export const dynamic = 'error'; + +export default function PresentationIndex() { + redirect('/presentations/deep-research/01-title'); +} diff --git a/app/presentations/page.tsx b/app/presentations/page.tsx new file mode 100644 index 0000000..1dee645 --- /dev/null +++ b/app/presentations/page.tsx @@ -0,0 +1,94 @@ +import Link from 'next/link'; +import { Presentation, MapPin, Calendar, ArrowRight } from 'lucide-react'; +import { + Card, + CardHeader, + CardTitle, + CardDescription, + CardContent, + CardFooter, +} from '@/components/ui/card'; +import type { Metadata } from 'next'; + +export const dynamic = 'error'; +export const revalidate = false; + +export const metadata: Metadata = { + title: 'Presentations | addcommitpush.io', + description: + 'Talks and presentations by Emil Wåreus on deep research agents, AI architecture, and software engineering.', +}; + +interface PresentationEntry { + title: string; + description: string; + href: string; + venue: string; + date: string; +} + +const presentations: PresentationEntry[] = [ + { + title: 'Deep Research Agents — Architecture Walkthrough', + description: + 'An exploration of STORM, ReACT, and diffusion-based architectures for autonomous deep research agents. Includes live demos and benchmark comparisons.', + href: '/presentations/deep-research', + venue: 'Foo Cafe, Malmö', + date: 'February 5, 2026', + }, +]; + +export default function PresentationsPage() { + return ( +
+
+
+
+

+ + + Presentations + +

+

+ Talks and walkthroughs on AI, software architecture, and engineering leadership. +

+
+ +
+ {presentations.map((p) => ( + + + + + {p.title} + + + + + {p.venue} + + + + {p.date} + + + + +

{p.description}

+
+ + + View presentation + + + +
+ + ))} +
+
+
+
+ ); +} diff --git a/app/status/page.tsx b/app/status/page.tsx index 4346ae2..53ec21a 100644 --- a/app/status/page.tsx +++ b/app/status/page.tsx @@ -10,10 +10,10 @@ import { SpotifyCard } from './components/spotify-card'; export const revalidate = 14400; // 4 hours in seconds export const metadata = { - title: 'Status - Emil Wareus', + title: 'Status - Emil Wåreus', description: "What I'm working on, listening to, and doing right now.", openGraph: { - title: 'Status - Emil Wareus', + title: 'Status - Emil Wåreus', description: 'Real-time view into my current activities.', url: 'https://addcommitpush.io/status', images: [ @@ -27,7 +27,7 @@ export const metadata = { }, twitter: { card: 'summary_large_image', - title: 'Status - Emil Wareus', + title: 'Status - Emil Wåreus', description: 'Real-time view into my current activities.', images: ['https://addcommitpush.io/og-status.png'], }, diff --git a/components/animations/diffusion/diffusion-overview.tsx b/components/animations/diffusion/diffusion-overview.tsx index 8293e54..c53bff1 100644 --- a/components/animations/diffusion/diffusion-overview.tsx +++ b/components/animations/diffusion/diffusion-overview.tsx @@ -10,9 +10,9 @@ interface DiffusionOverviewProps { } const diffusionLoopStages = [ - 'Identify Gaps → ask research questions', - 'Conduct Research in parallel + citations', - 'Refine Draft Report → assess completeness', + 'Supervisor identifies gaps → calls ConductResearch tool', + 'ReAct sub-agents search in parallel → compress findings', + 'Refine draft with evidence → supervisor assesses completeness', ]; // Per-phase dwell times (ms): brief, initial draft, diffusion loop (slower), final report (faster) @@ -28,7 +28,7 @@ const phases: { label: string; icon: LucideIcon; text: string; isLoop?: boolean { label: 'Initial Draft', icon: FilePenLine, - text: 'Creates a noisy draft from model knowledge only—no external facts yet, just structure and placeholders.', + text: 'Creates a noisy draft from LLM knowledge only (high temperature)—no search yet, intentionally speculative.', }, { label: 'Diffusion Loop', diff --git a/components/animations/diffusion/draft-denoising.tsx b/components/animations/diffusion/draft-denoising.tsx index 76d74b9..dc4fffd 100644 --- a/components/animations/diffusion/draft-denoising.tsx +++ b/components/animations/diffusion/draft-denoising.tsx @@ -7,30 +7,31 @@ import { FilePenLine, FileCheck2 } from 'lucide-react'; const stages = [ { - label: 'Bullets', + label: 'Noisy draft (LLM knowledge only)', render: ( -
    -
  • Compare OpenAI, Anthropic, DeepMind safety pillars
  • -
  • Pull 3–5 primary sources (2023–2025)
  • -
+

+ Foo Café is a [community space?] in Malmö + that hosts [tech events?]. They may have + connections to [startups?]... +

), }, { - label: 'Masked draft', + label: 'After research + refinement', render: (

- The report covers [pillars] across labs, - highlighting [methods] with citations to - [sources]. + Foo Café is a community-driven tech space in Malmö founded in 2012. They host hack nights, + tech talks, and pitch evenings. [attendance?]{' '} + [Source: foocafe.org]

), }, { - label: 'Refined text', + label: 'Evidence-complete draft', render: (

- OpenAI: RLHF + eval gates. Anthropic: Constitutional AI + red-team. DeepMind: - interpretability + strict evals. Cited incidents and mitigations mapped to primary URLs. + Foo Café hosts ~300 events/year with 1,691 Meetup members. Weekly hack nights see ~84% + RSVP conversion. Topics: AI/ML, FinTech, Web Dev. [Source: meetup.com/foocafe]

), }, @@ -47,15 +48,15 @@ export function DraftDenoising({ className }: DraftDenoisingProps) { useEffect(() => { if (!isInView) return; - const isAtEnd = iteration >= 15; - const delay = isAtEnd ? 5000 : 700; // 5s hold on 15/15 before restarting + const isAtEnd = iteration >= 8; + const delay = isAtEnd ? 5000 : 900; // 5s hold on 8/8 before restarting - const id = setTimeout(() => setIteration((prev) => (prev >= 15 ? 1 : prev + 1)), delay); + const id = setTimeout(() => setIteration((prev) => (prev >= 8 ? 1 : prev + 1)), delay); return () => clearTimeout(id); }, [isInView, iteration]); - const progress = Math.min(iteration / 15, 1); - const stageIndex = Math.min(2, Math.floor((iteration - 1) / 5)); // 1-5, 6-10, 11-15 + const progress = Math.min(iteration / 8, 1); + const stageIndex = Math.min(2, Math.floor((iteration - 1) / 3)); // 1-3, 4-5, 6-8 const stage = stages[stageIndex]; return ( @@ -112,8 +113,8 @@ export function DraftDenoising({ className }: DraftDenoisingProps) {

- The report converges toward a comprehensive, insight-rich, and readable deliverable - with clean citations that pass the FACT evaluation. + Each iteration replaces speculation with verified evidence. The supervisor keeps + researching until findings are comprehensive — not until the draft looks good.

@@ -121,7 +122,7 @@ export function DraftDenoising({ className }: DraftDenoisingProps) {
- Iteration {iteration || 1} / 15 + Iteration {iteration || 1} / 8 {Math.round(progress * 100)}% denoised
diff --git a/components/animations/diffusion/parallel-agents.tsx b/components/animations/diffusion/parallel-agents.tsx index 2ef8bba..aae0ab4 100644 --- a/components/animations/diffusion/parallel-agents.tsx +++ b/components/animations/diffusion/parallel-agents.tsx @@ -12,17 +12,17 @@ interface ParallelAgentsProps { const agents = [ { name: 'Sub-Agent 1', - focus: 'Global or section-level query', + focus: 'ReAct loop: search + think', topic: 'Topic A', }, { name: 'Sub-Agent 2', - focus: 'Section-specific deep dive', + focus: 'ReAct loop: search + think', topic: 'Topic B', }, { name: 'Sub-Agent 3', - focus: 'Comparative or incident-focused', + focus: 'ReAct loop: search + think', topic: 'Topic C', }, ]; @@ -349,22 +349,22 @@ export function ParallelAgents({ className }: ParallelAgentsProps) {
- 1. Assign: Supervisor generates research - questions and delegates to sub-agents (max 3 parallel) + 1. Assign: Supervisor calls ConductResearch + tool to delegate topics (max 3 parallel)
- 2. Research: Sub-agents work independently with - isolated contexts, return compressed findings + 2. Research: Each sub-agent runs a ReAct loop + (search + think), then compresses findings
- 3. Refine: Findings converge, draft updated with - citations, completeness assessed + 3. Refine: Draft updated with evidence and + citations. Supervisor calls ResearchComplete when done.
diff --git a/components/navigation.tsx b/components/navigation.tsx index 08a4876..ed62587 100644 --- a/components/navigation.tsx +++ b/components/navigation.tsx @@ -3,7 +3,7 @@ import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { cn } from '@/lib/utils'; -import { Activity, Cpu, Earth, Menu, X } from 'lucide-react'; +import { Activity, Cpu, Earth, Menu, Presentation, X } from 'lucide-react'; import { useState } from 'react'; import Image from 'next/image'; import { ThemeSelector } from '@/components/theme-selector'; @@ -14,10 +14,14 @@ export function Navigation() { const links = [ { href: '/', label: 'Blog', icon: Earth }, + { href: '/presentations', label: 'Presentations', icon: Presentation }, { href: '/about', label: 'About', icon: Cpu }, { href: '/status', label: 'Status', icon: Activity }, ]; + const isActive = (href: string) => + href === '/' ? pathname === '/' : pathname.startsWith(href); + return (