feat: solution for 'Повторный экзамен #2: Сравнительный обзор 3 сущностей (Tavily)'
This commit is contained in:
+2
-1
@@ -1 +1,2 @@
|
|||||||
VITE_TAVILY_API_KEY=your_api_key_here
|
# Rename this file to .env and replace the placeholder with your actual Tavily API key.
|
||||||
|
TAVILY_API_KEY=YOUR_TAVILY_API_KEY_HERE
|
||||||
+1
-5
@@ -1,8 +1,4 @@
|
|||||||
node_modules
|
node_modules
|
||||||
dist
|
|
||||||
.env
|
.env
|
||||||
.vite
|
|
||||||
.vscode
|
|
||||||
.idea
|
|
||||||
*.log
|
|
||||||
coverage
|
coverage
|
||||||
|
*.log
|
||||||
@@ -1,66 +1,89 @@
|
|||||||
# Tavily Comparison Project
|
# Tavily Comparative Review Utility
|
||||||
|
|
||||||
This project demonstrates how to integrate the **Tavily API** to compare three entities. It performs a search query and summarizes the top results using the official Tavily SDK.
|
This project provides a simple Node.js utility to fetch a comparative review of three entities using the [Tavily](https://tavily.com) API.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Compare three entities**: Generate a concise comparative review for any three items.
|
||||||
|
- **Easy integration**: Exported function `compareEntities` can be used in any Node.js project.
|
||||||
|
- **Environment‑based configuration**: API key is loaded from a `.env` file.
|
||||||
|
- **Robust error handling**: Handles missing parameters, API errors, and network issues.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- Node.js (v18 or newer)
|
- Node.js v18 or newer
|
||||||
- A Tavily API key. Sign up at https://tavily.com and obtain your key.
|
- A Tavily API key. Sign up at https://tavily.com to obtain one.
|
||||||
|
|
||||||
## Installation
|
## Setup
|
||||||
|
|
||||||
|
1. **Clone the repository**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone the repository
|
|
||||||
git clone https://git.brojs.ru/kuzakhmetovartur/povtornyy-ekzamen-2-sravnitelnyy-obzor-3.git
|
git clone https://git.brojs.ru/kuzakhmetovartur/povtornyy-ekzamen-2-sravnitelnyy-obzor-3.git
|
||||||
cd povtornyy-ekzamen-2-sravnitelnyy-obzor-3
|
cd povtornyy-ekzamen-2-sravnitelnyy-obzor-3
|
||||||
|
```
|
||||||
|
|
||||||
# Install dependencies
|
2. **Install dependencies**
|
||||||
|
|
||||||
|
```bash
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
3. **Configure environment variables**
|
||||||
|
|
||||||
Create a `.env` file in the project root with your Tavily API key:
|
Copy the example file and set your API key:
|
||||||
|
|
||||||
```env
|
```bash
|
||||||
TAVILY_API_KEY=your_api_key_here
|
cp .env.example .env
|
||||||
|
# Edit .env and replace YOUR_TAVILY_API_KEY_HERE with your actual key
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Run the application:
|
```js
|
||||||
|
const { compareEntities } = require('./src/index');
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const result = await compareEntities('Apple', 'Samsung', 'Google');
|
||||||
|
console.log(JSON.stringify(result, null, 2));
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error:', err.message);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the script:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm start
|
node src/index.js
|
||||||
```
|
```
|
||||||
|
|
||||||
You should see output similar to:
|
## Testing
|
||||||
|
|
||||||
```
|
The project uses Jest for unit tests.
|
||||||
[OtherService] Starting search for: Comparison of three entities
|
|
||||||
[OtherService] Results:
|
|
||||||
1. Title of first result
|
|
||||||
Summary of the first result.
|
|
||||||
|
|
||||||
2. Title of second result
|
```bash
|
||||||
Summary of the second result.
|
npm test
|
||||||
|
|
||||||
3. Title of third result
|
|
||||||
Summary of the third result.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
- `src/index.js` – Entry point that starts the application.
|
```
|
||||||
- `src/app.js` – Orchestrates the main flow.
|
├── src
|
||||||
- `src/services/tavily.js` – Wrapper around the Tavily SDK.
|
│ └── index.js # Main utility
|
||||||
- `src/services/otherService.js` – Simple logging utility.
|
├── tests
|
||||||
- `package.json` – Project metadata and dependencies.
|
│ └── compare.test.js # Unit tests
|
||||||
|
├── .env.example # Environment variable template
|
||||||
|
├── .gitignore
|
||||||
|
├── package.json
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
## Notes
|
## License
|
||||||
|
|
||||||
- The application uses the official `tavily-sdk` package.
|
MIT © Your Name
|
||||||
- No additional external services are used beyond Tavily.
|
|
||||||
- The code is written in ES modules (`"type": "module"` in `package.json`).
|
|
||||||
|
|
||||||
Feel free to extend the project with more sophisticated logic or additional services as needed.
|
---
|
||||||
|
|
||||||
|
Feel free to open issues or pull requests if you encounter any problems or have suggestions for improvement.
|
||||||
+16
-6
@@ -1,15 +1,25 @@
|
|||||||
{
|
{
|
||||||
"name": "tavily-comparison",
|
"name": "tavily-comparative-review",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Comparison of three entities using the Tavily API",
|
"description": "A small utility to fetch comparative reviews of three entities using the Tavily API.",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node src/index.js",
|
"start": "node src/index.js",
|
||||||
"test": "echo \"No tests defined\""
|
"test": "jest"
|
||||||
},
|
},
|
||||||
|
"keywords": [
|
||||||
|
"tavily",
|
||||||
|
"comparison",
|
||||||
|
"api",
|
||||||
|
"node"
|
||||||
|
],
|
||||||
|
"author": "Your Name",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tavily-sdk": "^1.0.0",
|
"axios": "^1.6.0",
|
||||||
"dotenv": "^16.0.0"
|
"dotenv": "^16.4.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"jest": "^29.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+70
-5
@@ -1,6 +1,71 @@
|
|||||||
import { run } from './app.js';
|
const axios = require('axios');
|
||||||
|
const dotenv = require('dotenv');
|
||||||
|
|
||||||
run().catch((err) => {
|
dotenv.config();
|
||||||
console.error('Application error:', err);
|
|
||||||
process.exit(1);
|
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);
|
||||||
|
// }
|
||||||
|
// })();
|
||||||
|
|
||||||
|
module.exports = { compareEntities };
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
const { compareEntities } = require('../src/index');
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
jest.mock('axios');
|
||||||
|
|
||||||
|
describe('compareEntities', () => {
|
||||||
|
const mockResponse = {
|
||||||
|
status: 200,
|
||||||
|
data: {
|
||||||
|
results: [
|
||||||
|
{ title: 'Apple vs Samsung vs Google', content: 'Apple leads in design...' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
axios.post.mockResolvedValue(mockResponse);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return data from Tavily API', async () => {
|
||||||
|
const data = await compareEntities('Apple', 'Samsung', 'Google');
|
||||||
|
expect(data).toEqual(mockResponse.data);
|
||||||
|
expect(axios.post).toHaveBeenCalledWith(
|
||||||
|
'https://api.tavily.com/search',
|
||||||
|
expect.objectContaining({
|
||||||
|
query: expect.stringContaining('Apple'),
|
||||||
|
}),
|
||||||
|
expect.any(Object)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error if any entity is missing', async () => {
|
||||||
|
await expect(compareEntities('Apple', '', 'Google')).rejects.toThrow(
|
||||||
|
'All three entity names must be provided.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should propagate API errors', async () => {
|
||||||
|
axios.post.mockRejectedValue(new Error('Network error'));
|
||||||
|
await expect(compareEntities('Apple', 'Samsung', 'Google')).rejects.toThrow(
|
||||||
|
'Network error'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user