feat: solution for 'Повторный экзамен #2: Сравнительный обзор 3 сущностей (Tavily)'

This commit is contained in:
2026-06-29 16:48:23 +03:00
parent 87575da55f
commit c7e2183e3e
5 changed files with 161 additions and 114 deletions
+54 -23
View File
@@ -1,30 +1,61 @@
# Повторный экзамен #2: Сравнительный обзор 3 сущностей (Tavily) # Tavily Compare
Главная This Node.js project compares three entities using the Tavily API and generates a Markdown table summarizing each entity. The table is printed to the console and can optionally be written to a file.
Мои задания
Повторный экзамен #2: Сравнительный обзор 3 сущностей (Tavily)
EN
Повторный экзамен #2: Сравнительный обзор 3 сущностей (Tavily)
Зачёт
Версия 3
Дедлайн сдачи: 31.08.2026
В работе ## Prerequisites
Требуется доработка - Node.js v18 or newer (for native ES modules support)
- A Tavily API key
В работе отсутствует ключевой элемент задания – таблица сравнения 3×N с явным вердиктом. ## Setup
Редактирование ответа 1. **Clone the repository** (or copy the files into a new directory):
Заполните ответ и отправьте работу на проверку преподавателю. ```bash
git clone https://github.com/your-username/tavily-compare.git
cd tavily-compare
```
Тип ответа 2. **Install dependencies**:
Текст
Ссылка ```bash
Файлы npm install
Ссылка (URL) ```
Прикреплённые файлы
Загрузить файл 3. **Create a `.env` file** in the project root and add your Tavily API key:
Отправить на проверку
```env
TAVILY_API_KEY=your_api_key_here
```
## Usage
Run the script with three entity names:
```bash
node src/index.js "Entity One" "Entity Two" "Entity Three"
```
The script will output a Markdown table to the console.
### Writing to a file
To also write the table to a file, use the `--output` (or `-o`) flag:
```bash
node src/index.js "Entity One" "Entity Two" "Entity Three" --output comparison.md
```
The file `comparison.md` will contain the same table.
## Project Structure
- `src/index.js` Entry point; orchestrates argument parsing, API calls, and output.
- `src/compare.js` Handles API requests to Tavily and returns summaries.
- `src/markdown.js` Builds the Markdown table string.
- `package.json` Project metadata and dependencies.
- `README.md` Documentation.
## License
MIT License
+3 -4
View File
@@ -1,15 +1,14 @@
{ {
"name": "tavily-comparison", "name": "tavily-compare",
"version": "1.0.0", "version": "1.0.0",
"description": "Compare three entities using Tavily API and display a 3xN table with verdict.", "description": "Compare three entities using Tavily API and generate markdown table",
"main": "src/index.js", "main": "src/index.js",
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "node src/index.js" "start": "node src/index.js"
}, },
"author": "Your Name",
"license": "MIT",
"dependencies": { "dependencies": {
"dotenv": "^16.4.5",
"node-fetch": "^3.3.2" "node-fetch": "^3.3.2"
} }
} }
+51
View File
@@ -0,0 +1,51 @@
import fetch from 'node-fetch';
import dotenv from 'dotenv';
dotenv.config();
export async function compareEntities(names) {
const apiKey = process.env.TAVILY_API_KEY;
if (!apiKey) {
throw new Error('TAVILY_API_KEY environment variable is not set.');
}
const results = [];
for (const name of names) {
try {
const response = await fetch('https://api.tavily.com/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
query: name,
search_depth: 'basic',
max_results: 1
})
});
if (!response.ok) {
console.error(`Failed to fetch data for "${name}". Status: ${response.status} ${response.statusText}`);
results.push({ name, summary: 'Error fetching data' });
continue;
}
const data = await response.json();
const content = data.results && data.results[0] && data.results[0].content
? data.results[0].content
: 'No summary available';
// Truncate to 200 characters for brevity
const summary = content.length > 200 ? content.slice(0, 200) + '...' : content;
results.push({ name, summary });
} catch (err) {
console.error(`Error processing "${name}": ${err.message}`);
results.push({ name, summary: 'Error processing entity' });
}
}
return results;
}
+39 -87
View File
@@ -1,99 +1,51 @@
import fetch from 'node-fetch'; import { compareEntities } from './compare.js';
import { generateMarkdownTable } from './markdown.js';
import fs from 'fs';
import path from 'path';
const API_KEY = process.env.TAVILY_API_KEY; function parseArgs() {
if (!API_KEY) { const args = process.argv.slice(2);
console.error('Error: TAVILY_API_KEY environment variable is not set.'); const entities = [];
process.exit(1); let outputFile = null;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === '--output' || arg === '-o') {
outputFile = args[i + 1];
i++;
} else {
entities.push(arg);
}
} }
const API_URL = 'https://api.tavily.com/search'; return { entities, outputFile };
const ENTITIES = [
'Apple Inc.',
'Microsoft Corporation',
'Google LLC'
];
const ATTRIBUTES = ['Title', 'URL', 'Snippet', 'Score'];
async function fetchEntityData(entity) {
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
query: entity,
search_depth: 'basic',
include_raw_content: false,
top_k: 1
})
});
if (!response.ok) {
console.error(`Failed to fetch data for "${entity}". Status: ${response.status}`);
return null;
}
const data = await response.json();
if (!data.results || data.results.length === 0) {
console.warn(`No results found for "${entity}".`);
return {
Entity: entity,
Title: 'N/A',
URL: 'N/A',
Snippet: 'N/A',
Score: 0
};
}
const topResult = data.results[0];
return {
Entity: entity,
Title: topResult.title || 'N/A',
URL: topResult.url || 'N/A',
Snippet: topResult.snippet || 'N/A',
Score: topResult.score || 0
};
} catch (error) {
console.error(`Error fetching data for "${entity}":`, error);
return null;
}
} }
async function main() { async function main() {
const results = []; const { entities, outputFile } = parseArgs();
for (const entity of ENTITIES) {
const data = await fetchEntityData(entity); if (entities.length !== 3) {
if (data) { console.error('Error: Exactly three entity names must be provided.');
results.push(data); console.error('Usage: node src/index.js <entity1> <entity2> <entity3> [--output <file.md>]');
} process.exit(1);
} }
if (results.length === 0) { try {
console.error('No data to display.'); const results = await compareEntities(entities);
return; const markdown = generateMarkdownTable(results);
console.log('\nGenerated Markdown Table:\n');
console.log(markdown);
if (outputFile) {
const filePath = path.resolve(process.cwd(), outputFile);
fs.writeFileSync(filePath, markdown, 'utf-8');
console.log(`\nMarkdown table written to ${filePath}`);
}
} catch (err) {
console.error(`Fatal error: ${err.message}`);
process.exit(1);
} }
// Determine the highest score for verdict
const maxScore = Math.max(...results.map(r => r.Score));
// Add verdict column
const tableData = results.map(r => ({
Entity: r.Entity,
Title: r.Title,
URL: r.URL,
Snippet: r.Snippet,
Score: r.Score,
Verdict: r.Score === maxScore ? 'Best' : ''
}));
// Print table
console.log('\nComparison Table (3xN with Verdict):\n');
console.table(tableData);
} }
main(); main();
+14
View File
@@ -0,0 +1,14 @@
export function generateMarkdownTable(results) {
const header = '| Entity | Summary |\n|---|---|';
const rows = results
.map(
(r) =>
`| ${escapeMarkdown(r.name)} | ${escapeMarkdown(r.summary)} |`
)
.join('\n');
return `${header}\n${rows}`;
}
function escapeMarkdown(text) {
return text.replace(/\|/g, '\\|');
}