Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Each folder in this repo is a standalone project. Dive in to see how to solve re
| Recipe | Description |
|--------|-------------|
| [bestbet](./bestbet) | Sports betting odds comparison tool |
| [silicon-signal](./silicon-signal) | Semiconductor supply chain tracker for lifecycle, availability, and lead-time signals |

> More recipes added weekly!

Expand Down
3 changes: 3 additions & 0 deletions research-sentry/.env.local.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Rename to .env.local and add your keys
OPENAI_API_KEY=sk-your-key-here
MINO_API_KEY=your-mino-key-here
5 changes: 5 additions & 0 deletions research-sentry/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.next
.env*.local
.vercel
*.tsbuildinfo
51 changes: 51 additions & 0 deletions research-sentry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Research Sentry

Live link: https://voice-research.vercel.app/

## What it is
Research Sentry is a voice-first academic research co-pilot that scans live portals (ArXiv, PubMed, Semantic Scholar, IEEE Xplore, and more) to assemble verified paper metadata and summaries. It uses the TinyFish Web Agent to automate multi-step portal navigation and extract structured results in real time.

## Demo video
https://voice-research.vercel.app/

## TinyFish API usage (snippet)
```ts
const res = await fetch("https://mino.ai/v1/automation/run-sse", {
method: "POST",
headers: {
"X-API-Key": process.env.MINO_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
url,
goal,
browser_profile: stealth ? "stealth" : "lite",
}),
});
```

## How to run
1. Install deps: `npm install`
2. Create `.env.local`:
```
MINO_API_KEY=your_tinyfish_key
OPENAI_API_KEY=your_openai_key
```
3. Start dev server: `npm run dev`

## Architecture diagram
```mermaid
graph TD
User((User)) -->|Voice/Text| UI[Search Interface]
UI -->|Intent| Parser[Intent Parser GPT-4]
Parser -->|Plan| Engine[Search Engine]
Engine -->|Dispatch| Agent1[TinyFish Agent: ArXiv]
Engine -->|Dispatch| Agent2[TinyFish Agent: PubMed]
Engine -->|Dispatch| Agent3[TinyFish Agent: Scholar]
Agent1 -->|Scraping| Web[Live Web DOM]
Agent2 -->|Scraping| Web
Agent3 -->|Scraping| Web
Web -->|Result| Aggregator[Synthesis & Deduplication]
Aggregator -->|JSON Payload| UI
UI -->|Visuals| Terminal[Live Log Terminal]
```
21 changes: 21 additions & 0 deletions research-sentry/app/api/citations/track/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NextRequest, NextResponse } from 'next/server';
import { analyzeCitationTrend } from '@/lib/citation-tracker';

export async function POST(req: NextRequest) {
try {
const { paper } = await req.json();

if (!paper) {
return NextResponse.json({ error: 'Paper data required' }, { status: 400 });
}

const trackedData = await analyzeCitationTrend(paper);

// In a real app, we would save this to a database here

return NextResponse.json(trackedData);
} catch (error) {
console.error('Citation Tracking API Error:', error);
return NextResponse.json({ error: 'Failed to track citation' }, { status: 500 });
}
}
19 changes: 19 additions & 0 deletions research-sentry/app/api/compare/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { NextRequest, NextResponse } from 'next/server';
import { comparePapers } from '@/lib/comparator';

export async function POST(req: NextRequest) {
try {
const { papers } = await req.json();

if (!papers || papers.length < 2) {
return NextResponse.json({ error: 'Select at least 2 papers to compare' }, { status: 400 });
}

const comparison = await comparePapers(papers);

return NextResponse.json(comparison);
} catch (error) {
console.error('Comparison API Error:', error);
return NextResponse.json({ error: 'Failed to generate comparison' }, { status: 500 });
}
}
21 changes: 21 additions & 0 deletions research-sentry/app/api/conversation/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NextRequest, NextResponse } from 'next/server';
import { generateConversationResponse } from '@/lib/conversation';

export const maxDuration = 60;

export async function POST(req: NextRequest) {
try {
const { history, context } = await req.json();

if (!history || !Array.isArray(history)) {
return NextResponse.json({ error: 'Invalid history format' }, { status: 400 });
}

const response = await generateConversationResponse(history, context);

return NextResponse.json(response);
} catch (error) {
console.error('Conversation API Error:', error);
return NextResponse.json({ error: 'Failed to generate response' }, { status: 500 });
}
}
Loading