diff --git a/.env.example b/.env.example index 422f08b..5ee6398 100644 --- a/.env.example +++ b/.env.example @@ -1,18 +1,18 @@ # API Keys Configuration # Copy this file to .env and fill in your actual keys -# OpenAI API (for AI features) -# Get from: https://platform.openai.com/api-keys -OPENAI_API_KEY=your_openai_api_key_here +# DeepSeek API (recommended - cost effective) +# Get from: https://platform.deepseek.com/api_keys +DEEPSEEK_API_KEY=sk_your_deepseek_api_key_here -# Anthropic API (alternative AI provider) +# OpenAI API (alternative) +# Get from: https://platform.openai.com/api-keys +OPENAI_API_KEY=sk_your_openai_api_key_here + +# Anthropic API (alternative) # Get from: https://console.anthropic.com/ ANTHROPIC_API_KEY=your_anthropic_api_key_here -# Google AI API (Gemini) -# Get from: https://makersuite.google.com/app/apikey -GOOGLE_AI_API_KEY=your_google_ai_api_key_here - # Firebase Configuration # These will be in your Firebase console FIREBASE_API_KEY=your_firebase_api_key diff --git a/DEEPSEEK_SETUP.md b/DEEPSEEK_SETUP.md new file mode 100644 index 0000000..0649fdd --- /dev/null +++ b/DEEPSEEK_SETUP.md @@ -0,0 +1,246 @@ +# 🤖 DeepSeek AI - Настройка + +## Что такое DeepSeek? + +**DeepSeek** - это мощная китайская нейросеть с открытым API, которая: +- 🚀 По качеству на уровне GPT-4 +- 💰 В 10-20 раз дешевле OpenAI +- 🌐 Поддерживает русский язык +- 🔑 Даёт бесплатные токены при регистрации + +--- + +## 📝 Как получить API ключ + +### Шаг 1: Регистрация + +1. Перейди на https://platform.deepseek.com +2. Нажми **Sign Up** или **Login** +3. Зарегистрируйся через email или Google + +### Шаг 2: Получение API ключа + +1. После входа перейди в **API Keys**: https://platform.deepseek.com/api_keys +2. Нажми **Create API Key** +3. Скопируй ключ (начинается с `sk-`) + +**Важно:** Сохрани ключ в надёжном месте! Показать его можно только один раз. + +### Шаг 3: Пополнение баланса (опционально) + +DeepSeek даёт **бесплатные токены** новым пользователям (~$2-5). + +Для пополнения: +1. Перейди в **Billing**: https://platform.deepseek.com/billing +2. Выбери способ оплаты +3. Минимальное пополнение: ~$10 + +--- + +## 💰 Тарифы + +| Модель | Цена (за 1M токенов) | +|--------|---------------------| +| **DeepSeek Chat** | ~$0.14 (input) / ~$0.28 (output) | +| **DeepSeek Coder** | ~$0.14 (input) / ~$0.28 (output) | + +**Для сравнения:** +- GPT-4: ~$30 / $60 за 1M токенов +- DeepSeek: ~$0.14 / $0.28 за 1M токенов + +**Экономия: в 200+ раз!** 💰 + +--- + +## 🔧 Настройка в проекте + +### 1. Создай файл `.env` + +Скопируй `.env.example` в `.env`: +```bash +cp .env.example .env +``` + +### 2. Добавь API ключ + +Открой `.env` и вставь ключ: +``` +DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx +``` + +### 3. Обновите `lib/core/deepseek_service.dart` + +```dart +final deepSeekService = DeepSeekService( + apiKey: 'sk-xxxxxxxxxxxxxxxxxxxxxxxx', // Твой ключ +); +``` + +Или используй пакет `flutter_dotenv`: + +```dart +// В main.dart +import 'package:flutter_dotenv/flutter_dotenv.dart'; + +Future main() async { + await dotenv.load(fileName: ".env"); + + final deepSeekService = DeepSeekService( + apiKey: dotenv.env['DEEPSEEK_API_KEY'], + ); + + runApp(...); +} +``` + +--- + +## 📱 Примеры использования + +### Генерация подзадач + +```dart +import 'package:nanaxe_crm/core/deepseek_service.dart'; + +final subtasks = await deepSeekService.generateSubtasks( + title: 'Создать CRM систему', + description: 'Разработка CRM для управления задачами команды с AI', +); + +print(subtasks); +// [ +// {"title": "Анализ требований", "estimatedHours": 2, "description": "..."}, +// {"title": "Проектирование", "estimatedHours": 3, "description": "..."}, +// ... +// ] +``` + +### Приоритизация задачи + +```dart +final priority = await deepSeekService.prioritizeTask( + title: 'Срочный баг', + description: 'Пользователи не могут войти', + dueDate: DateTime.now().add(Duration(hours: 2)), +); + +print(priority); // TaskPriority.urgent +``` + +### Умное планирование + +```dart +final schedule = await deepSeekService.smartSchedule( + tasks: myTasks, + startDate: DateTime.now(), + endDate: DateTime.now().add(Duration(days: 7)), + availableHoursPerDay: 6, +); + +print(schedule); +// [ +// {"taskId": 0, "date": "2026-03-27", "hours": 3}, +// {"taskId": 1, "date": "2026-03-27", "hours": 3}, +// ... +// ] +``` + +### Генерация mind-карты проекта + +```dart +final mindMapNodes = await deepSeekService.generateMindMap( + projectName: 'CRM App', + description: 'Мобильное CRM приложение с AI планированием', +); + +print(mindMapNodes); +// [ +// {"level": 0, "title": "CRM App", "parentId": null}, +// {"level": 1, "title": "Планирование", "parentId": 0}, +// {"level": 1, "title": "Разработка", "parentId": 0}, +// ... +// ] +``` + +--- + +## 🆘 Troubleshooting + +### Ошибка: "Invalid API key" + +``` +1. Проверь что ключ начинается с 'sk-' +2. Убедись что нет лишних пробелов +3. Проверь что ключ активен в Dashboard +``` + +### Ошибка: "Insufficient balance" + +``` +1. Проверь баланс в Billing: https://platform.deepseek.com/billing +2. Пополни счёт +3. Или используй mock данные (по умолчанию) +``` + +### Ошибка: "Rate limit exceeded" + +``` +DeepSeek ограничивает запросы. Подожди немного или увеличь лимиты в настройках. +``` + +--- + +## 📊 Статистика использования + +Для отслеживания расходов: + +1. Перейди в **Usage**: https://platform.deepseek.com/usage +2. Просматривай статистику по дням/моделям +3. Настрой алерты при достижении лимита + +--- + +## 🔒 Безопасность + +**Никогда не публикуй API ключ в GitHub!** + +✅ Правильно: +```bash +# Добавить .env в .gitignore +echo ".env" >> .gitignore +``` + +✅ Использовать в коде: +```dart +// Загружать из .env файла +final apiKey = dotenv.env['DEEPSEEK_API_KEY']; +``` + +❌ Неправильно: +```dart +// Хардкод ключа в коде +final apiKey = 'sk-xxxxx'; // НЕ ДЕЛАЙ ТАК! +``` + +--- + +## 📚 Ресурсы + +- **Официальная документация**: https://platform.deepseek.com/docs +- **API Reference**: https://platform.deepseek.com/api-docs +- **Цены**: https://platform.deepseek.com/pricing +- **Dashboard**: https://platform.deepseek.com + +--- + +## 🎯 Следующие шаги + +1. ✅ Получить API ключ +2. ✅ Добавить в `.env` +3. ✅ Протестировать генерацию подзадач +4. ✅ Интегрировать в UI приложения +5. ✅ Настроить отслеживание расходов + +--- + +**DeepSeek готов к работе! 🚀** diff --git a/lib/core/deepseek_service.dart b/lib/core/deepseek_service.dart new file mode 100644 index 0000000..3e7672d --- /dev/null +++ b/lib/core/deepseek_service.dart @@ -0,0 +1,375 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; +import '../models/task_model.dart'; + +class DeepSeekService { + final String? apiKey; + final String baseUrl = 'https://api.deepseek.com'; + + DeepSeekService({this.apiKey}); + + // Генерация подзадач из основной задачи + Future>> generateSubtasks({ + required String title, + required String description, + }) async { + if (apiKey == null || apiKey!.isEmpty) { + return _mockGenerateSubtasks(title, description); + } + + try { + final response = await http.post( + Uri.parse('$baseUrl/v1/chat/completions'), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $apiKey', + }, + body: jsonEncode({ + 'model': 'deepseek-chat', + 'messages': [ + { + 'role': 'system', + 'content': 'You are a task planning assistant. Break down tasks into logical subtasks. Return ONLY a JSON array.', + }, + { + 'role': 'user', + 'content': 'Break down this task into 3-7 subtasks:\n\nTitle: $title\nDescription: $description\n\nReturn ONLY a JSON array with format: [{"title": "...", "estimatedHours": 0, "description": "..."}]', + }, + ], + 'temperature': 0.7, + 'max_tokens': 2000, + }), + ); + + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + final content = data['choices'][0]['message']['content']; + + // Извлекаем JSON из ответа + final jsonMatch = RegExp(r'\[.*\]', dotAll: true).firstMatch(content); + if (jsonMatch != null) { + final List subtasks = jsonDecode(jsonMatch.group(0)!); + return subtasks.cast>(); + } + } else { + print('DeepSeek API Error: ${response.statusCode} - ${response.body}'); + } + } catch (e) { + print('DeepSeek Service Error: $e'); + } + + return _mockGenerateSubtasks(title, description); + } + + // AI приоритизация задач + Future prioritizeTask({ + required String title, + required String description, + DateTime? dueDate, + }) async { + if (apiKey == null || apiKey!.isEmpty) { + return _mockPrioritizeTask(dueDate); + } + + try { + final response = await http.post( + Uri.parse('$baseUrl/v1/chat/completions'), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $apiKey', + }, + body: jsonEncode({ + 'model': 'deepseek-chat', + 'messages': [ + { + 'role': 'system', + 'content': 'You are a task prioritization assistant. Determine task priority: low, medium, high, or urgent. Reply with ONE word only.', + }, + { + 'role': 'user', + 'content': 'What priority should this task have?\n\nTitle: $title\nDescription: $description\nDue: ${dueDate ?? "Not specified"}\n\nReply with ONE word: low, medium, high, or urgent', + }, + ], + 'temperature': 0.3, + 'max_tokens': 10, + }), + ); + + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + final content = data['choices'][0]['message']['content'].trim().toLowerCase(); + + switch (content) { + case 'urgent': return TaskPriority.urgent; + case 'high': return TaskPriority.high; + case 'low': return TaskPriority.low; + default: return TaskPriority.medium; + } + } + } catch (e) { + print('DeepSeek Service Error: $e'); + } + + return _mockPrioritizeTask(dueDate); + } + + // Генерация описания задачи + Future generateTaskDescription({ + required String title, + String? context, + }) async { + if (apiKey == null || apiKey!.isEmpty) { + return 'Описание для задачи: $title'; + } + + try { + final response = await http.post( + Uri.parse('$baseUrl/v1/chat/completions'), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $apiKey', + }, + body: jsonEncode({ + 'model': 'deepseek-chat', + 'messages': [ + { + 'role': 'system', + 'content': 'You are a task description writer. Write clear, concise task descriptions in Russian. Keep it under 100 words.', + }, + { + 'role': 'user', + 'content': 'Write a brief description for this task:\n\nTitle: $title\nContext: ${context ?? "No additional context"}\n\nWrite in Russian.', + }, + ], + 'temperature': 0.5, + 'max_tokens': 500, + }), + ); + + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + return data['choices'][0]['message']['content']; + } + } catch (e) { + print('DeepSeek Service Error: $e'); + } + + return 'Описание для задачи: $title'; + } + + // Умное планирование - распределение задач по времени + Future>> smartSchedule({ + required List tasks, + required DateTime startDate, + required DateTime endDate, + required int availableHoursPerDay, + }) async { + if (apiKey == null || apiKey!.isEmpty) { + return _mockSchedule(tasks, startDate, endDate, availableHoursPerDay); + } + + try { + final tasksJson = tasks.map((t) => { + 'title': t.title, + 'estimatedHours': t.estimatedHours, + 'priority': t.priority.name, + 'dueDate': t.dueDate?.toIso8601String(), + }).toList(); + + final response = await http.post( + Uri.parse('$baseUrl/v1/chat/completions'), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $apiKey', + }, + body: jsonEncode({ + 'model': 'deepseek-chat', + 'messages': [ + { + 'role': 'system', + 'content': 'You are a smart scheduling assistant. Distribute tasks across available days optimally. Return ONLY a JSON array.', + }, + { + 'role': 'user', + 'content': ''' +Schedule these tasks optimally: +${jsonEncode(tasksJson)} + +Available hours per day: $availableHoursPerDay +Start date: ${startDate.toIso8601String()} +End date: ${endDate.toIso8601String()} + +Return JSON array: [{"taskId": index, "date": "YYYY-MM-DD", "hours": 0}] +''', + }, + ], + 'temperature': 0.5, + 'max_tokens': 3000, + }), + ); + + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + final content = data['choices'][0]['message']['content']; + + final jsonMatch = RegExp(r'\[.*\]', dotAll: true).firstMatch(content); + if (jsonMatch != null) { + final List schedule = jsonDecode(jsonMatch.group(0)!); + return schedule.map((s) { + return { + 'taskId': s['taskId'], + 'date': DateTime.parse(s['date']), + 'hours': s['hours'], + }; + }).toList(); + } + } + } catch (e) { + print('DeepSeek Service Error: $e'); + } + + return _mockSchedule(tasks, startDate, endDate, availableHoursPerDay); + } + + // AI анализ проекта - генерация mind-карты + Future>> generateMindMap({ + required String projectName, + required String description, + }) async { + if (apiKey == null || apiKey!.isEmpty) { + return _mockMindMap(projectName); + } + + try { + final response = await http.post( + Uri.parse('$baseUrl/v1/chat/completions'), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $apiKey', + }, + body: jsonEncode({ + 'model': 'deepseek-chat', + 'messages': [ + { + 'role': 'system', + 'content': 'You are a project structure assistant. Create a mind-map structure for projects. Return ONLY a JSON array of nodes.', + }, + { + 'role': 'user', + 'content': ''' +Create a mind-map structure for this project: + +Project: $projectName +Description: $description + +Return JSON array of nodes with format: +[{"level": 0, "title": "Project Name", "parentId": null}, {"level": 1, "title": "Phase 1", "parentId": 0}, ...] +''', + }, + ], + 'temperature': 0.7, + 'max_tokens': 3000, + }), + ); + + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + final content = data['choices'][0]['message']['content']; + + final jsonMatch = RegExp(r'\[.*\]', dotAll: true).firstMatch(content); + if (jsonMatch != null) { + final List nodes = jsonDecode(jsonMatch.group(0)!); + return nodes.cast>(); + } + } + } catch (e) { + print('DeepSeek Service Error: $e'); + } + + return _mockMindMap(projectName); + } + + // Mock данные для тестирования + List> _mockGenerateSubtasks(String title, String description) { + return [ + {'title': 'Анализ требований', 'estimatedHours': 2, 'description': 'Изучить требования и документацию'}, + {'title': 'Проектирование', 'estimatedHours': 3, 'description': 'Спроектировать архитектуру решения'}, + {'title': 'Реализация', 'estimatedHours': 8, 'description': 'Написать код'}, + {'title': 'Тестирование', 'estimatedHours': 3, 'description': 'Протестировать функциональность'}, + {'title': 'Документирование', 'estimatedHours': 2, 'description': 'Создать документацию'}, + ]; + } + + TaskPriority _mockPrioritizeTask(DateTime? dueDate) { + if (dueDate == null) return TaskPriority.medium; + + final daysUntilDue = dueDate.difference(DateTime.now()).inDays; + + if (daysUntilDue <= 1) return TaskPriority.urgent; + if (daysUntilDue <= 3) return TaskPriority.high; + if (daysUntilDue <= 7) return TaskPriority.medium; + return TaskPriority.low; + } + + List> _mockSchedule( + List tasks, + DateTime startDate, + DateTime endDate, + int availableHoursPerDay, + ) { + final schedule = >[]; + var currentDate = startDate; + var remainingHours = availableHoursPerDay; + + for (var i = 0; i < tasks.length; i++) { + if (currentDate.isAfter(endDate)) break; + + final task = tasks[i]; + if (task.estimatedHours <= remainingHours) { + schedule.add({ + 'taskId': i, + 'date': currentDate, + 'hours': task.estimatedHours, + }); + remainingHours -= task.estimatedHours; + } else { + currentDate = currentDate.add(const Duration(days: 1)); + remainingHours = availableHoursPerDay; + + if (currentDate.isAfter(endDate)) break; + + schedule.add({ + 'taskId': i, + 'date': currentDate, + 'hours': task.estimatedHours > remainingHours + ? remainingHours + : task.estimatedHours, + }); + } + } + + return schedule; + } + + List> _mockMindMap(String projectName) { + return [ + {'level': 0, 'title': projectName, 'parentId': null}, + {'level': 1, 'title': 'Планирование', 'parentId': 0}, + {'level': 1, 'title': 'Разработка', 'parentId': 0}, + {'level': 1, 'title': 'Тестирование', 'parentId': 0}, + {'level': 1, 'title': 'Деплой', 'parentId': 0}, + {'level': 2, 'title': 'Анализ', 'parentId': 1}, + {'level': 2, 'title': 'Дизайн', 'parentId': 1}, + {'level': 2, 'title': 'Frontend', 'parentId': 2}, + {'level': 2, 'title': 'Backend', 'parentId': 2}, + {'level': 2, 'title': 'Unit тесты', 'parentId': 3}, + {'level': 2, 'title': 'E2E тесты', 'parentId': 3}, + ]; + } +} + +// Глобальный экземпляр сервиса +final deepSeekService = DeepSeekService( + // API ключ можно получить на https://platform.deepseek.com + apiKey: null, // Добавить ключ здесь или через .env +);