feat: solution for 'Повторный экзамен #2: Сравнительный обзор 3 сущностей (Tavily)'
This commit is contained in:
+62
-42
@@ -1,51 +1,71 @@
|
||||
import fetch from 'node-fetch';
|
||||
import dotenv from 'dotenv';
|
||||
/**
|
||||
* Deep comparison of three objects.
|
||||
* Returns an array of differences where at least two values differ.
|
||||
* Each difference is an object:
|
||||
* { key: 'path.to.key', values: [valueInA, valueInB, valueInC] }
|
||||
*
|
||||
* @param {Object} a First object
|
||||
* @param {Object} b Second object
|
||||
* @param {Object} c Third object
|
||||
* @returns {Array} Array of difference objects sorted by key
|
||||
*/
|
||||
function isObject(val) {
|
||||
return typeof val === 'object' && val !== null;
|
||||
}
|
||||
|
||||
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.');
|
||||
function deepEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (isObject(a) && isObject(b)) {
|
||||
const keysA = Object.keys(a);
|
||||
const keysB = Object.keys(b);
|
||||
if (keysA.length !== keysB.length) return false;
|
||||
for (const key of keysA) {
|
||||
if (!deepEqual(a[key], b[key])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Handles NaN
|
||||
if (Number.isNaN(a) && Number.isNaN(b)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
const results = [];
|
||||
function compare(a = {}, b = {}, c = {}) {
|
||||
const keys = new Set([
|
||||
...Object.keys(a),
|
||||
...Object.keys(b),
|
||||
...Object.keys(c),
|
||||
]);
|
||||
const diffs = [];
|
||||
|
||||
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
|
||||
})
|
||||
});
|
||||
for (const key of keys) {
|
||||
const valA = a[key];
|
||||
const valB = b[key];
|
||||
const valC = c[key];
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(`Failed to fetch data for "${name}". Status: ${response.status} ${response.statusText}`);
|
||||
results.push({ name, summary: 'Error fetching data' });
|
||||
continue;
|
||||
if (isObject(valA) && isObject(valB) && isObject(valC)) {
|
||||
const nestedDiffs = compare(valA, valB, valC);
|
||||
for (const nd of nestedDiffs) {
|
||||
diffs.push({
|
||||
key: `${key}.${nd.key}`,
|
||||
values: nd.values,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
!deepEqual(valA, valB) ||
|
||||
!deepEqual(valA, valC) ||
|
||||
!deepEqual(valB, valC)
|
||||
) {
|
||||
diffs.push({
|
||||
key,
|
||||
values: [valA, valB, valC],
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
diffs.sort((d1, d2) => d1.key.localeCompare(d2.key));
|
||||
return diffs;
|
||||
}
|
||||
|
||||
module.exports = compare;
|
||||
+8
-50
@@ -1,51 +1,9 @@
|
||||
import { compareEntities } from './compare.js';
|
||||
import { generateMarkdownTable } from './markdown.js';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
/**
|
||||
* Entry point for the comparison library.
|
||||
* Exports the compare function as both named and default export.
|
||||
*/
|
||||
const compare = require('./compare');
|
||||
|
||||
function parseArgs() {
|
||||
const args = process.argv.slice(2);
|
||||
const entities = [];
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return { entities, outputFile };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const { entities, outputFile } = parseArgs();
|
||||
|
||||
if (entities.length !== 3) {
|
||||
console.error('Error: Exactly three entity names must be provided.');
|
||||
console.error('Usage: node src/index.js <entity1> <entity2> <entity3> [--output <file.md>]');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
const results = await compareEntities(entities);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
module.exports = compare;
|
||||
module.exports.default = compare;
|
||||
module.exports.compare = compare;
|
||||
Reference in New Issue
Block a user