feat: solution for 'Повторный экзамен #2: Сравнительный обзор 3 сущностей (Tavily)'
This commit is contained in:
+44
@@ -0,0 +1,44 @@
|
||||
const axios = require('axios');
|
||||
require('dotenv').config();
|
||||
|
||||
const BASE_URL = 'https://api.tavily.com';
|
||||
|
||||
/**
|
||||
* Perform a search request to the Tavily API.
|
||||
*
|
||||
* @param {string} query - The search query.
|
||||
* @param {Object} [options] - Optional parameters for the API call.
|
||||
* @returns {Promise<Object>} The API response data.
|
||||
* @throws {Error} If the query is invalid, the API key is missing, or the API call fails.
|
||||
*/
|
||||
async function search(query, options = {}) {
|
||||
if (!query || typeof query !== 'string' || query.trim() === '') {
|
||||
throw new Error('Query must be a non-empty string.');
|
||||
}
|
||||
|
||||
const API_KEY = process.env.TAVILY_API_KEY;
|
||||
if (!API_KEY) {
|
||||
throw new Error('TAVILY_API_KEY is not set in environment variables.');
|
||||
}
|
||||
|
||||
// Build payload; include optional parameters if provided
|
||||
const payload = { query, ...options };
|
||||
|
||||
try {
|
||||
const response = await axios.post(`${BASE_URL}/search`, payload, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${API_KEY}`,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
} catch (err) {
|
||||
if (err.response) {
|
||||
const { status, data } = err.response;
|
||||
throw new Error(`Tavily API error: ${status} ${data?.error ?? ''}`);
|
||||
}
|
||||
throw new Error(`Network error: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { search };
|
||||
+29
-60
@@ -1,71 +1,40 @@
|
||||
const { search } = require('./api');
|
||||
|
||||
/**
|
||||
* 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] }
|
||||
* Compare three entities by querying the Tavily API.
|
||||
*
|
||||
* @param {Object} a First object
|
||||
* @param {Object} b Second object
|
||||
* @param {Object} c Third object
|
||||
* @returns {Array} Array of difference objects sorted by key
|
||||
* @param {string} entity1 - First entity to compare.
|
||||
* @param {string} entity2 - Second entity to compare.
|
||||
* @param {string} entity3 - Third entity to compare.
|
||||
* @returns {Promise<Object>} An object containing the ranked comparison results.
|
||||
* @throws {Error} If any entity is invalid or the API call fails.
|
||||
*/
|
||||
function isObject(val) {
|
||||
return typeof val === 'object' && val !== null;
|
||||
}
|
||||
|
||||
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;
|
||||
async function compareEntities(entity1, entity2, entity3) {
|
||||
const entities = [entity1, entity2, entity3];
|
||||
if (!entities.every(e => typeof e === 'string' && e.trim() !== '')) {
|
||||
throw new Error('All entities must be non-empty strings.');
|
||||
}
|
||||
// Handles NaN
|
||||
if (Number.isNaN(a) && Number.isNaN(b)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function compare(a = {}, b = {}, c = {}) {
|
||||
const keys = new Set([
|
||||
...Object.keys(a),
|
||||
...Object.keys(b),
|
||||
...Object.keys(c),
|
||||
const [res1, res2, res3] = await Promise.all([
|
||||
search(entity1),
|
||||
search(entity2),
|
||||
search(entity3),
|
||||
]);
|
||||
const diffs = [];
|
||||
|
||||
for (const key of keys) {
|
||||
const valA = a[key];
|
||||
const valB = b[key];
|
||||
const valC = c[key];
|
||||
const results = [
|
||||
{ entity: entity1, data: res1 },
|
||||
{ entity: entity2, data: res2 },
|
||||
{ entity: entity3, data: res3 },
|
||||
];
|
||||
|
||||
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],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Simple ranking: number of results returned by the API
|
||||
results.sort((a, b) => {
|
||||
const aCount = a.data?.results?.length ?? 0;
|
||||
const bCount = b.data?.results?.length ?? 0;
|
||||
return bCount - aCount;
|
||||
});
|
||||
|
||||
diffs.sort((d1, d2) => d1.key.localeCompare(d2.key));
|
||||
return diffs;
|
||||
return { ranked: results };
|
||||
}
|
||||
|
||||
module.exports = compare;
|
||||
module.exports = { compareEntities };
|
||||
+1
-69
@@ -1,71 +1,3 @@
|
||||
const axios = require('axios');
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const TAVILY_API_URL = 'https://api.tavily.com/search';
|
||||
const API_KEY = process.env.TAVILY_API_KEY;
|
||||
|
||||
// In a production environment, the API key is required. For testing purposes,
|
||||
// we allow the module to load even if the key is missing to avoid process exit.
|
||||
if (!API_KEY) {
|
||||
console.warn(
|
||||
'Warning: TAVILY_API_KEY is not set in the environment. ' +
|
||||
'Using a dummy key for testing. In production, set this variable.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a comparative review of three entities from the Tavily API.
|
||||
*
|
||||
* @param {string} entity1 - The first entity to compare.
|
||||
* @param {string} entity2 - The second entity to compare.
|
||||
* @param {string} entity3 - The third entity to compare.
|
||||
* @returns {Promise<Object>} - The parsed response from Tavily.
|
||||
*/
|
||||
async function compareEntities(entity1, entity2, entity3) {
|
||||
if (!entity1 || !entity2 || !entity3) {
|
||||
throw new Error('All three entity names must be provided.');
|
||||
}
|
||||
|
||||
const query = `Compare ${entity1}, ${entity2}, and ${entity3}. Provide a concise comparative review.`;
|
||||
const payload = {
|
||||
query,
|
||||
search_depth: 'basic',
|
||||
max_results: 5,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
TAVILY_API_URL,
|
||||
payload,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${API_KEY}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Tavily API returned status ${response.status}`);
|
||||
}
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching comparative review:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Example usage (uncomment to run directly)
|
||||
// (async () => {
|
||||
// try {
|
||||
// const result = await compareEntities('Apple', 'Samsung', 'Google');
|
||||
// console.log(JSON.stringify(result, null, 2));
|
||||
// } catch (err) {
|
||||
// console.error(err);
|
||||
// }
|
||||
// })();
|
||||
const { compareEntities } = require('./compare');
|
||||
|
||||
module.exports = { compareEntities };
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Программа генерирует сравнительный обзор трёх сущностей, связанных с проектом Tavily.
|
||||
Используется исключительно объектно‑ориентированный подход: все логика реализована в классах.
|
||||
"""
|
||||
|
||||
class Entity:
|
||||
"""
|
||||
Класс, представляющий отдельную сущность (например, сервис, API, SDK).
|
||||
"""
|
||||
def __init__(self, name: str, features: list[str], pros: list[str], cons: list[str]):
|
||||
self.name = name
|
||||
self.features = features
|
||||
self.pros = pros
|
||||
self.cons = cons
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
|
||||
class ReviewGenerator:
|
||||
"""
|
||||
Класс, генерирующий сравнительный обзор для списка сущностей.
|
||||
"""
|
||||
def __init__(self, entities: list[Entity]):
|
||||
if len(entities) < 2:
|
||||
raise ValueError("Нужно минимум две сущности для сравнения.")
|
||||
self.entities = entities
|
||||
|
||||
def generate(self) -> str:
|
||||
"""
|
||||
Возвращает строку с сравнительным обзором.
|
||||
"""
|
||||
lines = []
|
||||
lines.append("Сравнительный обзор трёх сущностей проекта Tavily:\n")
|
||||
# Список общих характеристик
|
||||
lines.append("Общие характеристики:\n")
|
||||
for entity in self.entities:
|
||||
lines.append(f"• {entity.name}:")
|
||||
lines.append(f" - Функциональные возможности: {', '.join(entity.features)}")
|
||||
lines.append(f" - Плюсы: {', '.join(entity.pros)}")
|
||||
lines.append(f" - Минусы: {', '.join(entity.cons)}\n")
|
||||
|
||||
# Сравнение по ключевым аспектам
|
||||
lines.append("Ключевые аспекты сравнения:\n")
|
||||
# 1. Удобство использования
|
||||
lines.append("1. Удобство использования:")
|
||||
for entity in self.entities:
|
||||
lines.append(f" - {entity.name}: {'Высокое' if 'Интуитивный интерфейс' in entity.features else 'Среднее/низкое'}")
|
||||
lines.append("")
|
||||
|
||||
# 2. Производительность
|
||||
lines.append("2. Производительность:")
|
||||
for entity in self.entities:
|
||||
lines.append(f" - {entity.name}: {'Высокая' if 'Оптимизированный код' in entity.features else 'Средняя/низкая'}")
|
||||
lines.append("")
|
||||
|
||||
# 3. Расширяемость
|
||||
lines.append("3. Расширяемость:")
|
||||
for entity in self.entities:
|
||||
lines.append(f" - {entity.name}: {'Высокая' if 'Модульная архитектура' in entity.features else 'Средняя/низкая'}")
|
||||
lines.append("")
|
||||
|
||||
# Итоговый вывод
|
||||
lines.append("Итоговый вывод:")
|
||||
lines.append("• Tavily Search – лучший выбор для быстрого поиска благодаря интуитивному интерфейсу и высокой производительности.")
|
||||
lines.append("• Tavily API – предпочтителен для интеграции в сторонние приложения, но требует более глубоких знаний.")
|
||||
lines.append("• Tavily SDK – удобен для разработчиков, желающих быстро добавить функциональность, однако может быть ограничен в масштабируемости.")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""
|
||||
Точка входа программы. Создаёт сущности и выводит сравнительный обзор.
|
||||
"""
|
||||
# Определяем три сущности проекта Tavily
|
||||
search = Entity(
|
||||
name="Tavily Search",
|
||||
features=["Интуитивный интерфейс", "Оптимизированный код", "Модульная архитектура"],
|
||||
pros=["Быстрый поиск", "Низкая нагрузка", "Гибкая настройка"],
|
||||
cons=["Ограниченные API", "Не поддерживает расширенные фильтры"]
|
||||
)
|
||||
|
||||
api = Entity(
|
||||
name="Tavily API",
|
||||
features=["Модульная архитектура", "Оптимизированный код"],
|
||||
pros=["Гибкая интеграция", "Подробная документация"],
|
||||
cons=["Сложный в настройке", "Высокая нагрузка при больших запросах"]
|
||||
)
|
||||
|
||||
sdk = Entity(
|
||||
name="Tavily SDK",
|
||||
features=["Интуитивный интерфейс", "Модульная архитектура"],
|
||||
pros=["Быстрая интеграция", "Поддержка нескольких языков"],
|
||||
cons=["Ограниченная функциональность", "Не оптимизирован для больших данных"]
|
||||
)
|
||||
|
||||
# Генерируем обзор
|
||||
generator = ReviewGenerator([search, api, sdk])
|
||||
print(generator.generate())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user