mirror of
https://github.com/dzhng/deep-research.git
synced 2025-12-26 04:48:06 +08:00
Merge pull request #116 from aakashadesara/added_api_server
Added API Server
This commit is contained in:
commit
be18bc208d
992
package-lock.json
generated
992
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,7 @@
|
||||
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
||||
"tsx": "tsx --env-file=.env.local",
|
||||
"start": "tsx --env-file=.env.local src/run.ts",
|
||||
"api": "tsx --env-file=.env.local src/api.ts",
|
||||
"docker": "tsx src/run.ts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
@ -14,8 +15,11 @@
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"@ianvs/prettier-plugin-sort-imports": "^4.4.1",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^22.13.0",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"prettier": "^3.4.2",
|
||||
"tsx": "^4.19.2",
|
||||
"typescript": "^5.7.3"
|
||||
@ -24,9 +28,12 @@
|
||||
"@ai-sdk/openai": "^1.1.9",
|
||||
"@mendable/firecrawl-js": "^1.16.0",
|
||||
"ai": "^4.1.17",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.18.3",
|
||||
"js-tiktoken": "^1.0.17",
|
||||
"lodash-es": "^4.17.21",
|
||||
"p-limit": "^6.2.0",
|
||||
"uuid": "^9.0.1",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
144
src/api.ts
Normal file
144
src/api.ts
Normal file
@ -0,0 +1,144 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
import cors from 'cors';
|
||||
import * as fs from 'fs/promises';
|
||||
import { deepResearch, writeFinalReport, writeFinalAnswer } from './deep-research';
|
||||
import { generateFeedback } from './feedback';
|
||||
import { OutputManager } from './output-manager';
|
||||
|
||||
const app = express();
|
||||
const port = process.env.PORT || 3051;
|
||||
|
||||
// Middleware
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
// Initialize output manager
|
||||
const output = new OutputManager();
|
||||
|
||||
// Helper function for consistent logging
|
||||
function log(...args: any[]) {
|
||||
output.log(...args);
|
||||
}
|
||||
|
||||
// API endpoint to run research
|
||||
app.post('/api/research', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { query, followUpAnswers = [], depth = 3, breadth = 3 } = req.body;
|
||||
|
||||
if (!query) {
|
||||
return res.status(400).json({ error: 'Query is required' });
|
||||
}
|
||||
|
||||
log(`Creating research plan for: ${query}`);
|
||||
|
||||
// Generate follow-up questions
|
||||
const followUpQuestions = await generateFeedback({
|
||||
query,
|
||||
numQuestions: 0
|
||||
});
|
||||
|
||||
// Combine all information for deep research
|
||||
let combinedQuery = `Initial Query: ${query}`;
|
||||
|
||||
if (followUpQuestions.length > 0 && followUpAnswers.length > 0) {
|
||||
combinedQuery += `\nFollow-up Questions and Answers:\n${
|
||||
followUpQuestions
|
||||
.slice(0, followUpAnswers.length)
|
||||
.map((q: string, i: number) => `Q: ${q}\nA: ${followUpAnswers[i]}`)
|
||||
.join('\n')
|
||||
}`;
|
||||
}
|
||||
|
||||
log('\nResearching your topic...');
|
||||
log('\nStarting research with progress tracking...\n');
|
||||
|
||||
// Track progress
|
||||
let currentProgress = {};
|
||||
|
||||
const { learnings, visitedUrls } = await deepResearch({
|
||||
query: combinedQuery,
|
||||
breadth,
|
||||
depth,
|
||||
onProgress: (progress) => {
|
||||
output.updateProgress(progress);
|
||||
currentProgress = progress;
|
||||
},
|
||||
});
|
||||
|
||||
log(`\n\nLearnings:\n\n${learnings.join('\n')}`);
|
||||
log(`\n\nVisited URLs (${visitedUrls.length}):\n\n${visitedUrls.join('\n')}`);
|
||||
log('Writing final report...');
|
||||
|
||||
const report = await writeFinalReport({
|
||||
prompt: combinedQuery,
|
||||
learnings,
|
||||
visitedUrls,
|
||||
});
|
||||
|
||||
// Save report to file with timestamp
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
const reportFilename = `output-${timestamp}.md`;
|
||||
// await fs.writeFile(reportFilename, report, 'utf-8');
|
||||
|
||||
const answer = await writeFinalAnswer({
|
||||
prompt: combinedQuery,
|
||||
learnings,
|
||||
report,
|
||||
});
|
||||
|
||||
// Save answer to file
|
||||
const answerFilename = `answer-${timestamp}.md`;
|
||||
// await fs.writeFile(answerFilename, answer, 'utf-8');
|
||||
|
||||
// Return the results
|
||||
return res.json({
|
||||
success: true,
|
||||
report,
|
||||
answer,
|
||||
learnings,
|
||||
visitedUrls,
|
||||
reportFilename,
|
||||
answerFilename
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
console.error('Error in research API:', error);
|
||||
return res.status(500).json({
|
||||
error: 'An error occurred during research',
|
||||
message: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// API endpoint to get follow-up questions
|
||||
app.post('/api/questions', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { query, numQuestions = 3 } = req.body;
|
||||
|
||||
if (!query) {
|
||||
return res.status(400).json({ error: 'Query is required' });
|
||||
}
|
||||
|
||||
const followUpQuestions = await generateFeedback({
|
||||
query,
|
||||
numQuestions
|
||||
});
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
questions: followUpQuestions
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
console.error('Error generating questions:', error);
|
||||
return res.status(500).json({
|
||||
error: 'An error occurred while generating questions',
|
||||
message: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Start the server
|
||||
app.listen(port, () => {
|
||||
console.log(`Deep Research API running on port ${port}`);
|
||||
});
|
||||
|
||||
export default app;
|
||||
@ -160,6 +160,37 @@ export async function writeFinalReport({
|
||||
return res.object.reportMarkdown + urlsSection;
|
||||
}
|
||||
|
||||
export async function writeFinalAnswer({
|
||||
prompt,
|
||||
learnings,
|
||||
report,
|
||||
}: {
|
||||
prompt: string;
|
||||
learnings: string[];
|
||||
report: string;
|
||||
}) {
|
||||
const res = await generateObject({
|
||||
model: o3MiniModel,
|
||||
system: systemPrompt(),
|
||||
prompt: `Given the following prompt from the user, write a final answer on the topic using the learnings from research. Follow the format specified in the prompt. Do not yap or babble or include any other text than the answer besides the format specified in the prompt. Keep the answer as concise as possible - usually it should be just a few words or maximum a sentence. Try to follow the format specified in the prompt (for example, if the prompt is using Latex, the answer should be in Latex. If the prompt gives multiple answer choices, the answer should be one of the choices).
|
||||
|
||||
<prompt>${prompt}</prompt>
|
||||
|
||||
<report>${report}</report>
|
||||
|
||||
<format>
|
||||
<answer>
|
||||
</answer>
|
||||
</format>
|
||||
`,
|
||||
schema: z.object({
|
||||
answer: z.string().describe('The final answer'),
|
||||
}),
|
||||
});
|
||||
|
||||
return res.object.answer;
|
||||
}
|
||||
|
||||
export async function deepResearch({
|
||||
query,
|
||||
breadth,
|
||||
|
||||
14
src/run.ts
14
src/run.ts
@ -1,7 +1,8 @@
|
||||
import * as fs from 'fs/promises';
|
||||
import * as readline from 'readline';
|
||||
|
||||
import { deepResearch, writeFinalReport } from './deep-research';
|
||||
|
||||
import { deepResearch, writeFinalAnswer, writeFinalReport } from './deep-research';
|
||||
import { generateFeedback } from './feedback';
|
||||
import { OutputManager } from './output-manager';
|
||||
|
||||
@ -50,6 +51,7 @@ async function run() {
|
||||
// Generate follow-up questions
|
||||
const followUpQuestions = await generateFeedback({
|
||||
query: initialQuery,
|
||||
numQuestions: 0
|
||||
});
|
||||
|
||||
log(
|
||||
@ -101,6 +103,16 @@ ${followUpQuestions.map((q: string, i: number) => `Q: ${q}\nA: ${answers[i]}`).j
|
||||
console.log(`\n\nFinal Report:\n\n${report}`);
|
||||
console.log('\nReport has been saved to output.md');
|
||||
rl.close();
|
||||
|
||||
const answer = await writeFinalAnswer({
|
||||
prompt: combinedQuery,
|
||||
learnings,
|
||||
report,
|
||||
});
|
||||
|
||||
console.log(`\n\nFinal Answer:\n\n${answer}`);
|
||||
|
||||
await fs.writeFile('answer.md', answer, 'utf-8');
|
||||
}
|
||||
|
||||
run().catch(console.error);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user