# Project export: Stream

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2025
- Tagline: AI and Blockchain Male Fertility Tracking app
- Devpost: https://devpost.com/software/f-l0643e
- GitHub: http://github.com/friendly304010/sperm
- Video: https://www.youtube.com/embed/b6hNF5C1LhQ?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — Samantha Ouyang (22 commits), Vivian Zhou (7 commits)

## Devpost submission (written by the team)

### Inspiration

Ever since high school, I've been passionate about expanding contraceptive options, particularly for men. I was driven by the belief that everyone, regardless of their sex or gender, should have the power to make informed decisions about family planning and reproductive autonomy. This led me to dive into the development of a non-hormonal male birth control pill. While working in a lab focused on creating a pill that could impact sperm motility, I stumbled upon a sobering realization: the reverse of contraceptive development is infertility. This revelation hit me hard when I learned that 15% of couples in the U.S. today are facing infertility issues—and more than half of these cases are due to male infertility. What's even more troubling is the fact that sperm counts have halved in the last 40 years, yet the conversation around male infertility remains largely under the radar. We wanted to create a fun and engaging way for males to stay proactive about their sperm health. If so far everything that we've tried to get males to track their fertility has failed, then if the one thing that will get them to do so is the male ego, then so be it.

### What it does

Stream is an AI agent that empowers males to improve their sperm health. Users upload a video of their sperm, which undergoes a YOLO model to identify sperm in different frames and gather their average sperm count and sperm motility score. The AI agent then users their sperm metrics to provide personalized recommendations and award them with NFTs (bronze, silver, gold) based on their sperm health. Users strive to earn the Gold NFT to wear as a badge of honour of their health.

### How we built it

Our approach involved leveraging a YOLO (You Only Look Once) pre-trained deep learning model to analyze microscopic video footage of sperm, which had been captured and developed by researchers at the Simula Metropolitan Center for Digital Engineering in Oslo in 2023. By using this model, we could identify sperm counts and score sperm motility, a key aspect of male fertility. We used the EigenLayer AI agent kit to create our AI agent and verify off-chain data (sperm metrics) and bring on-chain. We used React to build the frontend, so that users can upload their sperm videos, track their history, and see how they compete with others on a leaderboard.

### Challenges we ran into

We encountered a lot of challenges with setting up the EigenLayer AI agent kit since the packages were only built for Mac (not Windows or WSL), and there was limited documentation on how to further customize the agent. None of the members on the team have experience with Yolo algorithm and EigenLayer so it was challenging for us to figure out in 10 hours. We didn't have React experience either on frontend, but we managed to build an app overnight!

### Accomplishments we're proud of

As a beginner hacker team, we're proud of being able to run the deep learning CV model YOLO on the sperm videos to accurately track sperm metrics, successfully run the EigenLayer AI agent kit, and create a frontend successfully connected with the backend so users can connect their own wallet and upload their own sperm videos.

### What we learned

While these obstacles were tough, they also provided invaluable learning opportunities. We discovered how to integrate deep learning computer vision models with Large Language Models (LLMs), creating a seamless fusion of artificial intelligence that could track sperm motility while also interacting with users in a meaningful way. We also learned how to work with EigenLayer to create our own AI agents.

### What's next

We plan on making the AI agent even more powerful by being able to bring off-chain health data on-chain to get a richer health history and make more informed recommendations, integrating wearable tech to consistently monitor biometrics and other health markers, booking telehealth consultations or fertility appointments, and placing orders for sperm health supplements.

## README (from the GitHub repository)

# sperm

note: run these commands in their respective folders
- start frontend: `npm run dev`
- start eigen agent: `npm run dev`
- start backend: `python3 sperm.py`

demo: https://youtu.be/b6hNF5C1LhQ?si=mdcD96Tg_f--I2b3


## Detected evidence (automated analysis)

Indexed codebase: 22 recognized source files, 131 KB.
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- PyTorch (technology) — detected in the code
- React (technology) — detected in the code
- Solidity (language) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (65 of 65)

```
.DS_Store
backend/.env
backend/requirements.txt
backend/sperm.py
eigen-agent/.env.example
eigen-agent/.gitignore
eigen-agent/Formfile
eigen-agent/package.json
eigen-agent/README.md
eigen-agent/src/agent/createAgent.ts
eigen-agent/src/index.ts
eigen-agent/src/server.ts
eigen-agent/src/witness.ts
eigen-agent/tsconfig.json
frontend/.gitignore
frontend/eslint.config.js
frontend/index.html
frontend/package.json
frontend/README.md
frontend/src/App.css
frontend/src/App.jsx
frontend/src/FileDropPage.jsx
frontend/src/HomePage.jsx
frontend/src/index.css
frontend/src/LeaderboardPage.jsx
frontend/src/main.jsx
frontend/vite.config.js
hardhat/.env copy
hardhat/.env.example
hardhat/.gitignore
hardhat/artifacts/@openzeppelin/contracts/access/Ownable.sol/Ownable.dbg.json
hardhat/artifacts/@openzeppelin/contracts/access/Ownable.sol/Ownable.json
hardhat/artifacts/@openzeppelin/contracts/token/ERC721/ERC721.sol/ERC721.dbg.json
hardhat/artifacts/@openzeppelin/contracts/token/ERC721/ERC721.sol/ERC721.json
hardhat/artifacts/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol/IERC721Metadata.dbg.json
hardhat/artifacts/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol/IERC721Metadata.json
hardhat/artifacts/@openzeppelin/contracts/token/ERC721/IERC721.sol/IERC721.dbg.json
hardhat/artifacts/@openzeppelin/contracts/token/ERC721/IERC721.sol/IERC721.json
hardhat/artifacts/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol/IERC721Receiver.dbg.json
hardhat/artifacts/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol/IERC721Receiver.json
hardhat/artifacts/@openzeppelin/contracts/utils/Address.sol/Address.dbg.json
hardhat/artifacts/@openzeppelin/contracts/utils/Address.sol/Address.json
hardhat/artifacts/@openzeppelin/contracts/utils/Context.sol/Context.dbg.json
hardhat/artifacts/@openzeppelin/contracts/utils/Context.sol/Context.json
hardhat/artifacts/@openzeppelin/contracts/utils/Counters.sol/Counters.dbg.json
hardhat/artifacts/@openzeppelin/contracts/utils/Counters.sol/Counters.json
hardhat/artifacts/@openzeppelin/contracts/utils/introspection/ERC165.sol/ERC165.dbg.json
hardhat/artifacts/@openzeppelin/contracts/utils/introspection/ERC165.sol/ERC165.json
hardhat/artifacts/@openzeppelin/contracts/utils/introspection/IERC165.sol/IERC165.dbg.json
hardhat/artifacts/@openzeppelin/contracts/utils/introspection/IERC165.sol/IERC165.json
hardhat/artifacts/@openzeppelin/contracts/utils/math/Math.sol/Math.dbg.json
hardhat/artifacts/@openzeppelin/contracts/utils/math/Math.sol/Math.json
hardhat/artifacts/@openzeppelin/contracts/utils/math/SignedMath.sol/SignedMath.dbg.json
hardhat/artifacts/@openzeppelin/contracts/utils/math/SignedMath.sol/SignedMath.json
hardhat/artifacts/@openzeppelin/contracts/utils/Strings.sol/Strings.dbg.json
hardhat/artifacts/@openzeppelin/contracts/utils/Strings.sol/Strings.json
hardhat/artifacts/build-info/e3f38ea2d1ecedb7a714b6fcca525331.json
hardhat/artifacts/contracts/SpermAnalysisNFT.sol/SpermAnalysisNFT.dbg.json
hardhat/artifacts/contracts/SpermAnalysisNFT.sol/SpermAnalysisNFT.json
hardhat/cache/solidity-files-cache.json
hardhat/contracts/SpermAnalysisNFT.sol
hardhat/hardhat.config.js
hardhat/package.json
hardhat/scripts/deploy.js
README.md
```

### Dependencies

- backend/requirements.txt: fastapi@>=0.109.0, gitpython@>=3.1.30, huggingface-hub@>=0.19.0, numpy@>=1.24.0, opencv-python@>=4.8.0, python-multipart@>=0.0.9, setuptools@>=70.0.0, torch@>=2.0.0, ultralytics@>=8.0.0, uvicorn@>=0.27.0
- eigen-agent/package.json: @layr-labs/agentkit@latest, @layr-labs/agentkit-eigenda@latest, @layr-labs/agentkit-opacity@latest, @layr-labs/agentkit-witnesschain@latest, @types/cors@^2.8.17, @types/express@^4.17.21, @types/node@^20.11.24, @typescript-eslint/eslint-plugin@^7.1.0, @typescript-eslint/parser@^7.1.0, axios@^1.7.9, cors@^2.8.5, dotenv@^16.4.5, eigenda-sdk@latest, eslint@^8.57.0, express@^4.18.3, prettier@^3.2.5, tsx@^4.19.2, typescript@^5.3.3
- frontend/package.json: @eslint/js@^9.19.0, @types/react@^19.0.8, @types/react-dom@^19.0.3, @vitejs/plugin-react-swc@^3.5.0, axios@^1.7.9, eslint@^9.19.0, eslint-plugin-react@^7.37.4, eslint-plugin-react-hooks@^5.0.0, eslint-plugin-react-refresh@^0.4.18, globals@^15.14.0, react@^19.0.0, react-dom@^19.0.0, recharts@^2.15.1, vite@^6.1.0
- hardhat/package.json: @nomicfoundation/hardhat-toolbox@^5.0.0, @openzeppelin/contracts@^4.9.6, dotenv@^16.4.7, hardhat@^2.22.18

### Recent commits (newest first)

- Update README.md
- good
- works
- works
- server works
- fix nav
- navigation working
- update full stack demo v1
- Update README.md
- Update README.md
- eigen agent add
- eigen agent add
- add eigen agent kit to repo
- rstructure folders
- env example
- remove env
- remove env
- blockchain done
- successfully normalized motility
- d

## Key source files (fetched from GitHub, selected and truncated for size)

### backend/requirements.txt

```
fastapi>=0.109.0
uvicorn>=0.27.0
python-multipart>=0.0.9
torch>=2.0.0
huggingface-hub>=0.19.0
opencv-python>=4.8.0
numpy>=1.24.0
gitpython>=3.1.30
setuptools>=70.0.0
ultralytics>=8.0.0
```

### hardhat/package.json

```
{
  "name": "sperm",
  "version": "1.0.0",
  "description": "",
  "main": "hardhat.config.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/friendly304010/sperm.git"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/friendly304010/sperm/issues"
  },
  "homepage": "https://github.com/friendly304010/sperm#readme",
  "devDependencies": {
    "@nomicfoundation/hardhat-toolbox": "^5.0.0",
    "@openzeppelin/contracts": "^4.9.6",
    "dotenv": "^16.4.7",
    "hardhat": "^2.22.18"
  }
}

```

### frontend/package.json

```
{
  "name": "sperm",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "axios": "^1.7.9",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "recharts": "^2.15.1"
  },
  "devDependencies": {
    "@eslint/js": "^9.19.0",
    "@types/react": "^19.0.8",
    "@types/react-dom": "^19.0.3",
    "@vitejs/plugin-react-swc": "^3.5.0",
    "eslint": "^9.19.0",
    "eslint-plugin-react": "^7.37.4",
    "eslint-plugin-react-hooks": "^5.0.0",
    "eslint-plugin-react-refresh": "^0.4.18",
    "globals": "^15.14.0",
    "vite": "^6.1.0"
  }
}

```

### eigen-agent/package.json

```
{
  "name": "eigenai-quickstart",
  "version": "1.0.0",
  "description": "A ready-to-use scaffold for building verifiable AI agents using Layr Labs' AgentKit adapters",
  "main": "dist/index.js",
  "type": "module",
  "scripts": {
    "dev": "tsx watch src/server.ts",
    "build": "tsc",
    "start": "node dist/index.js",
    "example": "ts-node src/index.ts",
    "lint": "eslint . --ext .ts",
    "format": "prettier --write \"src/**/*.ts\""
  },
  "keywords": [
    "ai",
    "agentkit",
    "opacity",
    "eigenda",
    "witnesschain",
    "verifiable-ai"
  ],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "@layr-labs/agentkit": "latest",
    "@layr-labs/agentkit-eigenda": "latest",
    "@layr-labs/agentkit-opacity": "latest",
    "@layr-labs/agentkit-witnesschain": "latest",
    "cors": "^2.8.5",
    "dotenv": "^16.4.5",
    "eigenda-sdk": "latest",
    "express": "^4.18.3"
  },
  "devDependencies": {
    "@types/cors": "^2.8.17",
    "@types/express": "^4.17.21",
    "@types/node": "^20.11.24",
    "@typescript-eslint/eslint-plugin": "^7.1.0",
    "@typescript-eslint/parser": "^7.1.0",
    "axios": "^1.7.9",
    "eslint": "^8.57.0",
    "prettier": "^3.2.5",
    "tsx": "^4.19.2",
    "typescript": "^5.3.3"
  }
}
```

### frontend/src/main.jsx

```javascript
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

```

### eigen-agent/src/index.ts

```typescript
import { createAgent } from './agent/createAgent.ts';

async function main() {
  try {
    console.log('🤖 Initializing AI Agent...');
    const agent = await createAgent();

    // Example 1: Generate verifiable text
    console.log('\n📝 Generating verifiable text...');
    const prompt = 'What is the capital of France?';
    const textResult = await agent.generateVerifiableText(prompt);
    console.log('Response:', textResult.content);
    console.log('Proof available:', !!textResult.proof);

    // Example 2: Log custom information
    console.log('\n📊 Logging custom information...');
    await agent.logInfo('Demo Completed', {
      timestamp: new Date().toISOString(),
      status: 'success'
    });

    console.log('\n✅ Demo completed successfully!');
  } catch (error) {
    console.error('❌ Error:', error);
    process.exit(1);
  }
}

main(); 
```

### eigen-agent/src/server.ts

```typescript
import express from 'express';
import cors from 'cors';
import { createAgent } from './agent/createAgent.js';
import dotenv from 'dotenv';

dotenv.config();

const app = express();
const port = process.env.PORT || 3000;

let agent: Awaited<ReturnType<typeof createAgent>>;

// Initialize the agent
createAgent().then(a => {
  agent = a;
  console.log('Agent initialized successfully');
}).catch(error => {
  console.error('Failed to initialize agent:', error);
  process.exit(1);
});

// Middleware
app.use(cors());
app.use(express.json());

// Health check endpoint
app.get('/health', (req, res) => {
  res.json({ status: 'ok', agentReady: !!agent });
});

// Generate verifiable text endpoint
app.post('/api/generate', async (req, res) => {
  try {
    if (!agent) {
      return res.status(503).json({ error: 'Agent not ready' });
    }

    const { prompt } = req.body;
    
    if (!prompt) {
      return res.status(400).json({ error: 'Prompt is required' });
    }

    console.log('Generating text for prompt:', prompt);
    const result = await agent.generateVerifiableText(prompt);
    console.log('Generation result:', result);
    res.json(result);
  } catch (error) {
    console.error('Error generating text:', error);
    res.status(500).json({ 
      error: 'Failed to generate text',
      details: error instanceof Error ? error.message : String(error)
    });
  }
});

// Start server
app.listen(port, () => {
  console.log(`🚀 Server running at http://localhost:${port}`);
  console.log('Available endpoints:');
  console.log('  GET  /health');
  console.log('  POST /api/generate');
}); 
```

### frontend/src/App.jsx

```javascript
import './App.css';
import { LineChart, Line, XAxis, YAxis, ResponsiveContainer, Tooltip } from 'recharts';
import { useState } from 'react';
import LeaderboardPage from './LeaderboardPage';
import FileDropPage from './FileDropPage';
import HomePage from './HomePage';

const healthData = [
  { time: '1d', value: 65 },
  { time: '2d', value: 70 },
  { time: '3d', value: 62 },
  { time: '4d', value: 75 },
  { time: '5d', value: 78 },
];

// Define page states
const PAGES = {
  HOME: 'HOME',
  ANALYSIS: 'ANALYSIS',
  LEADERBOARD: 'LEADERBOARD'
};

function App() {
  const [currentPage, setCurrentPage] = useState(PAGES.HOME);

  const renderContent = () => {
    switch (currentPage) {
      case PAGES.LEADERBOARD:
        return <LeaderboardPage onBack={() => setCurrentPage(PAGES.HOME)} />;
      case PAGES.ANALYSIS:
        return <FileDropPage onBack={() => setCurrentPage(PAGES.HOME)} />;
      default:
        return <HomePage onStartTest={() => setCurrentPage(PAGES.ANALYSIS)} />;
    }
  };

  return (
    <div className="app-container">
      {renderContent()}
      <nav className="bottom-nav">
        <button 
          className="nav-button home-button"
          onClick={() => setCurrentPage(PAGES.ANALYSIS)}
        >
          <svg viewBox="0 0 24 24" className="nav-icon">
            <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
          </svg>
        </button>
        <button 
          className="nav-button camera-button"
          onClick={() => setCurrentPage(PAGES.HOME)}
        >
          <svg viewBox="0 0 24 24" className="nav-icon">
            <circle cx="12" cy="12" r="10"></circle>
          </svg>
        </button>
        <button 
          className="nav-button stats-button"
          onClick={() => setCurrentPage(PAGES.LEADERBOARD)}
        >
            <svg viewBox="0 0 24 24" className="nav-icon">
              <path d="M2 2v20h20"></path>
              <path d="M6 16l6-8 6 8"></path>
            </svg>
          </button>
        </nav>
      </div>
    );
  }

  export default App;

```

### frontend/vite.config.js

```javascript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
})

```

### hardhat/hardhat.config.js

```javascript
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();

module.exports = {
  solidity: "0.8.19",
  networks: {
    sepolia: {
      url: process.env.SEPOLIA_URL || "",
      accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
    }
  }
}; 
```

[12 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]