150 lines
4.5 KiB
Dart
150 lines
4.5 KiB
Dart
import 'dart:convert';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:news/utils/api.dart';
|
|
|
|
/// AI Service Module using Google Gemini API
|
|
/// This module contains all AI-related API calls for the application
|
|
|
|
class GeminiService {
|
|
static const String _geminiModel = "gemini-2.0-flash"; // Or gemini-1.5-pro
|
|
|
|
/// Helper function to call Gemini API
|
|
static Future<Map<String, dynamic>> _callGeminiAPI(String prompt, String apiKey) async {
|
|
try {
|
|
final requestBody = {
|
|
"contents": [
|
|
{
|
|
"parts": [
|
|
{"text": prompt}
|
|
]
|
|
}
|
|
]
|
|
};
|
|
|
|
final url = Uri.parse("${Api.geminiMetaInfoApi}$_geminiModel:generateContent?key=$apiKey");
|
|
|
|
final response = await http.post(
|
|
url,
|
|
headers: {"Content-Type": "application/json"},
|
|
body: jsonEncode(requestBody),
|
|
);
|
|
|
|
if (response.statusCode != 200) {
|
|
final errorData = jsonDecode(response.body);
|
|
throw Exception(errorData["error"]?["message"] ?? "Failed to generate content");
|
|
}
|
|
|
|
return jsonDecode(response.body);
|
|
} catch (e) {
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// Generate content
|
|
static Future<String> generateContent({
|
|
String? title,
|
|
String? category,
|
|
String? language,
|
|
String? languageCode,
|
|
required String apiKey,
|
|
}) async {
|
|
try {
|
|
String fullPrompt = "You are a skilled news article writer. Create engaging and informative content.";
|
|
|
|
if (title != null) {
|
|
fullPrompt += "\n\nWrite an article with the title: \"$title\"";
|
|
}
|
|
|
|
if (category != null) {
|
|
fullPrompt += "\nCategory: $category";
|
|
}
|
|
|
|
if (language != null && languageCode != null) {
|
|
fullPrompt += "\n\nIMPORTANT: Generate all content in $language language ($languageCode). The response MUST be in $language.";
|
|
}
|
|
|
|
fullPrompt += "\n\nRequest: \n\nArticle:";
|
|
|
|
final response = await _callGeminiAPI(fullPrompt, apiKey);
|
|
|
|
return response["candidates"][0]["content"]["parts"][0]["text"];
|
|
} catch (e) {
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// Generate meta info
|
|
static Future<Map<String, dynamic>> generateMetaInfo({required String title, String? language, String? languageCode, required String apiKey}) async {
|
|
try {
|
|
String languageInstruction = "";
|
|
if (language != null && languageCode != null) {
|
|
languageInstruction = "\n\nIMPORTANT: Generate all content in $language language ($languageCode). The response MUST be in same language as title.";
|
|
}
|
|
|
|
final prompt = """
|
|
You are an SEO expert. Generate meta title, description, keywords, and a slug for this news article titled: "$title".$languageInstruction
|
|
|
|
Return ONLY a JSON object with these fields:
|
|
- meta_title
|
|
- meta_description
|
|
- meta_keywords
|
|
- slug
|
|
|
|
Response must be valid JSON.
|
|
""";
|
|
|
|
final response = await _callGeminiAPI(prompt, apiKey);
|
|
final responseText = response["candidates"][0]["content"]["parts"][0]["text"].trim();
|
|
|
|
try {
|
|
return jsonDecode(responseText);
|
|
} catch (_) {
|
|
final match = RegExp(r"\{[\s\S]*\}").firstMatch(responseText);
|
|
if (match != null) {
|
|
return jsonDecode(match.group(0)!);
|
|
}
|
|
return {
|
|
"meta_title": title,
|
|
"meta_description": "Read about $title in our latest news article.",
|
|
"meta_keywords": title.toLowerCase().split(" ").join(","),
|
|
"slug": title.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]+'), "-").replaceAll(RegExp(r'^-|-$'), ""),
|
|
};
|
|
}
|
|
} catch (e) {
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// Summarize description
|
|
static Future<String> summarizeDescription(String description, String apiKey, {String language = "English", String languageCode = "en"}) async {
|
|
try {
|
|
if (description.trim().isEmpty) return "";
|
|
|
|
final cleanContent = description.replaceAll(RegExp(r"<[^>]*>"), "").trim();
|
|
if (cleanContent.isEmpty) return "";
|
|
|
|
final prompt = """
|
|
You are a skilled content summarizer. Summarize the following news content:
|
|
|
|
Content: "$cleanContent"
|
|
|
|
Instructions:
|
|
- 200-250 words
|
|
- Maintain key facts
|
|
- Professional news style
|
|
- No explanations, only summary
|
|
- IMPORTANT: Generate in $language ($languageCode).
|
|
|
|
Summary:""";
|
|
|
|
final response = await _callGeminiAPI(prompt, apiKey);
|
|
final summary = response["candidates"][0]["content"]["parts"][0]["text"].trim();
|
|
|
|
String finalSummary = summary.replaceAll(RegExp(r"^['\']+|['\']+$"), '').trim();
|
|
return finalSummary;
|
|
} catch (e) {
|
|
return description.replaceAll(RegExp(r"<[^>]*>"), "").substring(0, 150) + "...";
|
|
}
|
|
}
|
|
}
|