using AiAgent.Core.Agents.Base; using AiAgent.Core.Helpers; using AiAgent.Core.Infrastructure; using AiAgent.Core.Models; using Microsoft.SemanticKernel.Agents; using Mscc.GenerativeAI; using System.Text.Json; using static AiAgent.Core.Constant.ProgramConstants; using static AiAgent.Core.Helpers.GenericHelper; namespace AiAgent.Core.Agents; public static class Agents { public static async Task TimeKeeper() { // Make sure that everything is in place in order to do some work var results = BaseAgent.CheckDocuments("1 - Time Keeper", null, TodayDate, null); // Set the date and time for each article independently if (results == Results.Success) await File.WriteAllTextAsync(TodayDate, DateTime.Now.ToString("dddd dd MMMM yyyy HH:mm:ss")); // Keep the same value throughout the process var articleDateTime = DateTime.Parse(File.ReadAllText(TodayDate)); Log($"- Article timestamp is {articleDateTime}"); return Results.Success; } public static async Task RunNewsFinder() { // Make sure that everything is in place in order to do some work var results = BaseAgent.CheckDocuments("2 - News Finder", RSSSourcesWorld, FetchedArticle, null); // Anything but success is necessary if (results != Results.Success) return results; Log($"- Fetching already published articles from the list of RSS feeds..."); var reader = new RssReader(); var articles = await reader.FetchArticlesAsync(RSSSourcesWorld); var response = string.Join("\n\n", articles.Select(a => a.ToDetailedString())); // Check for problems if (string.IsNullOrEmpty(response)) return Results.Failed; Log($"-> Completed scanning RSS feeds and saving to: {FetchedArticle}"); await File.WriteAllTextAsync(FetchedArticle, response); Log($"-> Completed reading the [{articles.Count}] RSS feeds"); return Results.Success; } public static async Task RunArchiveRetriever() { // Make sure that everything is in place in order to do some work var results = BaseAgent.CheckDocuments("3 - Archivist", null, PublishedArticles, null); // Anything but success is necessary if (results != Results.Success) return results; Log($"- Reading the archive of published articles from GitHub Gist..."); string archiveFile = Path.GetFileName(PublishedArticles); string? archive = await GitHubGistHelper.ReadGistContentAsync(ArchiveID, archiveFile); // Check for problems if (string.IsNullOrEmpty(archive)) return Results.Failed; Log($"-> Completed reading the archive to: {PublishedArticles}"); File.WriteAllText(PublishedArticles, archive ?? ""); return Results.Success; } public static async Task RunNewsFeedAggregator() { GenerativeModel model = new() { Model = GeminiFlashLatest, UseGoogleSearch = false, UseGrounding = false, UseJsonMode = false, UseCodeExecution = false, Timeout = TimeSpan.FromMinutes(60) }; GenerationConfig config = new() { Temperature = 0.0f, CandidateCount = 1, ThinkingConfig = new ThinkingConfig { ThinkingBudget = 15000, //-1, //24576, //32000, IncludeThoughts = true, } }; // Launch the average worker with the default average base configuration return await BaseAgent.RunAsync(model, config, ApiKeys.GetNextFreeKey(), "4a - News Aggregator", AggregateNewsFeed, AggregatedArticles, [FetchedArticle]); } public static async Task RunNewsAggregatesFiltering() { GenerativeModel model = new() { Model = GeminiFlashLatest, UseGoogleSearch = false, UseGrounding = false, UseJsonMode = false, UseCodeExecution = false, Timeout = TimeSpan.FromMinutes(60) }; GenerationConfig config = new() { Temperature = 0.0f, CandidateCount = 1, ThinkingConfig = new ThinkingConfig { ThinkingBudget = -1, //24576, //32000, IncludeThoughts = true, } }; // Launch the average worker with the default average base configuration return await BaseAgent.RunAsync(model, config, ApiKeys.GetNextFreeKey(), "4b - News Filtering", FilterNewsAggregated, FilteredArticles, [AggregatedArticles, TodayDate, PublishedArticles]); } public static async Task RunArticlePicker() { GenerativeModel model = new() { Model = GeminiFlashLatest, UseGoogleSearch = false, UseGrounding = false, UseJsonMode = false, UseCodeExecution = false, Timeout = TimeSpan.FromMinutes(60) }; GenerationConfig config = new() { Temperature = 0.0f, CandidateCount = 1, ThinkingConfig = new ThinkingConfig { ThinkingBudget = -1, //24576, //32000, IncludeThoughts = true, } }; // Launch the average worker with the default average base configuration return await BaseAgent.RunAsync(model, config, ApiKeys.GetNextFreeKey(), "4c - Article Picker", PickMostCitedArticle, MostCitedArticle, [FilteredArticles]); } //public static async Task RunAssignmentEditor() //{ // //var response = GeminiRPA.Query("What day is it today?"); // GenerativeModel model = new() // { // Model = GeminiFlash25Lite, // UseGoogleSearch = false, // UseGrounding = false, // UseJsonMode = true, // UseCodeExecution = false, // Timeout = TimeSpan.FromMinutes(60) // }; // GenerationConfig config = new() // { // Temperature = 0.0f, // CandidateCount = 1, // ThinkingConfig = new ThinkingConfig // { // ThinkingBudget = -1, //24576, //32000, // IncludeThoughts = true, // }, // ResponseMimeType = "application/json", // ResponseJsonSchema = ArticleClusterSchema.Value, // }; // // Launch the average worker with the default average base configuration // return await BaseAgent.Run(model, config, ApiKeys.GetNextFreeKey(), "4 - Assignment Editor", // FindBestArticle, MostCitedArticle, [FetchedArticle, PublishedArticles]); //} //public static async Task RunAssignmentEditor_Grok() //{ // var result = await Program.QueryAI(Grok4FastReason, FindBestArticle, [FetchedArticle, PublishedArticles], MostCitedArticle); // return Results.Success; //} public static async Task RunJournalist() { GenerativeModel model = new() { Model = GeminiFlashLatest, UseGoogleSearch = true, UseGrounding = false, UseJsonMode = false, UseCodeExecution = false }; GenerationConfig config = new() { Temperature = 0.0f, CandidateCount = 1, ThinkingConfig = new ThinkingConfig { ThinkingBudget = -1, //24576, //32000, IncludeThoughts = true, }, //ResponseMimeType = "application/json", //ResponseJsonSchema = ArticleWithReferencesSchema.Value, }; // Launch the average worker with the default average base configuration return await BaseAgent.RunAsync(model, config, ApiKeys.GetNextFreeKey(), "5 - Journalist", ArticleWritingQuery, FullArticle, [MostCitedArticle], ["Title", "Summary", "Sections", "Conclusion", "References"]); } public static async Task RunJournalis2t() { GenerativeModel model = new() { Model = GeminiFlashLatest, UseGoogleSearch = true, UseGrounding = false, UseJsonMode = false, UseCodeExecution = false }; GenerationConfig config = new() { Temperature = 0.0f, CandidateCount = 1, ThinkingConfig = new ThinkingConfig { ThinkingBudget = -1, //24576, //32000, IncludeThoughts = true, } }; var input = File.ReadAllText(MostCitedArticle); var options = new JsonDocumentOptions { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip }; JsonDocument doc = JsonDocument.Parse(input, options); JsonElement root = doc.RootElement; root.TryGetProperty("selectedArticle", out JsonElement selectedArticle); selectedArticle.TryGetProperty("title", out JsonElement title); selectedArticle.TryGetProperty("description", out JsonElement description); selectedArticle.TryGetProperty("source_urls", out JsonElement source_urls); root.TryGetProperty("supportingTitles", out JsonElement supportingTitles); string textToInsert = $"* Title: {title.GetString()}\n" + $"* Summary: {description.GetString()}\n" + $"* References: \n - {string.Join("\n - ", source_urls.EnumerateArray().Select(url => url.GetString()))}\n"; var prompt = File.ReadAllText(ArticleWritingQuery); var delimiter = "extensive web research."; var finalPrompt = prompt.Split(delimiter)[0] + delimiter + "\n\n" + textToInsert + "\n\n" + prompt.Split(delimiter)[1]; var filename = Path.Combine(Output, "5 - Modified Article Writing Prompt.txt"); File.WriteAllText(filename, finalPrompt); // Launch the average worker with the default average base configuration return await BaseAgent.RunAsync(model, config, ApiKeys.GetNextFreeKey(), "5 - Journalist", filename, FullArticle, [], ["Title", "Summary", "References", "Conclusion"]); } public static async Task RunTitleFinder() { GenerativeModel model = new() { Model = GeminiFlashLatest, UseGoogleSearch = false, UseGrounding = false, UseJsonMode = false, UseCodeExecution = false }; GenerationConfig config = new() { Temperature = 1.5f, CandidateCount = 1, ThinkingConfig = new ThinkingConfig { ThinkingBudget = -1, //24576, //32000, IncludeThoughts = true, } }; // Launch the average worker with the default average base configuration return await BaseAgent.RunAsync(model, config, ApiKeys.GetNextFreeKey(), "5a - Title Finder", FindArticleTitle, ArticleMetadata, [FullArticle], ["Title", "Category", "Description"]); } public static async Task RunFactLister() { GenerativeModel model = new() { Model = GeminiFlash25Lite, UseGoogleSearch = false, UseGrounding = false, UseJsonMode = true, UseCodeExecution = false }; GenerationConfig config = new() { Temperature = 0.0f, CandidateCount = 1, ThinkingConfig = new ThinkingConfig { ThinkingBudget = -1, //24576, //32000, IncludeThoughts = true, }, ResponseMimeType = "application/json", ResponseJsonSchema = HistoricalAndFindingsSchema.Value, }; // Launch the average worker with the default average base configuration return await BaseAgent.RunAsync(model, config, ApiKeys.GetNextFreeKey(), "6 - Fact Lister", FindArticleFactList, ArticleFactList, [FullArticle], ["historicalContext", "recentFindings"]); } public static async Task RunGeminiFactChecker() { GenerativeModel model = new() { Model = GeminiFlashLatest, UseGoogleSearch = true, UseGrounding = false, UseJsonMode = false, UseCodeExecution = false }; GenerationConfig config = new() { Temperature = 0.0f, CandidateCount = 1, ThinkingConfig = new ThinkingConfig { ThinkingBudget = -1, //24576, //32000, IncludeThoughts = true, }, //ResponseMimeType = "application/json", //ResponseJsonSchema = HistoricalAndFindingsSchema.Value, }; // Launch the average worker with the default average base configuration return await BaseAgent.RunAsync(model, config, ApiKeys.GetNextFreeKey(), "7 - Gemini Fact Checker", FactCheckingQuery, FactedCheckedGemini, [FullArticle, MostCitedArticle], []); } public static async Task RunFactCheckCompiler() { // Make sure that everything is in place in order to do some work var results = BaseAgent.CheckDocuments("8 - Fact-Check Compiler", null, CompiledFactChecks, null); // Anything but success is necessary if (results != Results.Success) return results; // Prepare the output file await File.WriteAllTextAsync(CompiledFactChecks, string.Empty); Log("- Compiling fact-checks from different sources..."); var factCheckFilses = new List { FactedCheckedGemini }; foreach (var file in factCheckFilses) if (File.Exists(file)) { Log($" - Adding fact-checks from: {file}"); await File.AppendAllTextAsync(CompiledFactChecks, await File.ReadAllTextAsync(file)); } return Results.Success; } public static async Task RunIllustratorManager() { GenerativeModel model = new() { Model = GeminiFlash25Lite, UseGoogleSearch = false, UseGrounding = false, UseJsonMode = false, UseCodeExecution = false }; GenerationConfig config = new() { Temperature = 2.0f, CandidateCount = 1, ThinkingConfig = new ThinkingConfig { ThinkingBudget = -1, //24576, //32000, IncludeThoughts = true, } }; // Launch the average worker with the default average base configuration var result = await BaseAgent.RunAsync(model, config, ApiKeys.GetNextFreeKey(), "9a - Illustrator Manager", ImageGenerationPrompt, ImageRequirements, [FullArticle]); return result; } public static async Task RunIllustrator() { // In case there is no datestamp if (ArticleImagePath is null) return Results.Failed; // Make sure that everything is in place in order to do some work var result = BaseAgent.CheckDocuments("9b - Illustrator", ImageRequirements, ArticleImagePath, [TodayDate]); // Anything but success is necessary if (result != Results.Success) return result; //// Get the Abstract section //var articleJson = await File.ReadAllTextAsync(FullArticle); //var articleSections = JsonHelpers.SectionParser(articleJson); //if (articleSections.TryGetValue("Abstract", out string? articleAbstract) is false) // return Results.Failed; //// Remove the references //var cleanAbstract = CleanReferences(articleAbstract); // Compile the prompt var prompt = File.ReadAllText(ImageRequirements); //File.WriteAllText(Path.Combine(Output, "9a - Used Image Generation Prompt.txt"), prompt); //result = await FreepikApiClient.GenerateImage(prompt, "text-to-image", ArticleImagePath); result = await FreepikApiClient.GenerateImage(prompt, "text-to-image/flux-dev", ArticleImagePath); if (result != Results.Success) { prompt = "A vibrant, not offensive, textured, thick wool image in the style of intarsia knitting that could represente the logo of News AI: https://factcheckednews.com/index.html"; result = await FreepikApiClient.GenerateImage(prompt, "text-to-image/flux-dev", ArticleImagePath); } return result; } public static async Task RunPaginist() { // Make sure that everything is in place in order to do some work var results = BaseAgent.CheckDocuments("10 - Paginist", null, FinalHTMLArticle, [ArticleImagePath, FullArticle, ArticleFactList, TodayDate]); // Anything but success is necessary if (results != Results.Success) return results; try { // 1. Read JSON file content Console.WriteLine($"Reading '{FullArticle}'..."); string articleJson = File.ReadAllText(FullArticle); Console.WriteLine($"Reading '{ArticleFactList}'..."); string factListJson = File.ReadAllText(ArticleFactList); // 2. Deserialize JSON to C# objects Console.WriteLine("Deserializing JSON data..."); var jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; Article? articleData = JsonSerializer.Deserialize
(articleJson, jsonOptions); FactList? factListData = JsonSerializer.Deserialize(factListJson, jsonOptions); if (articleData == null || factListData == null) { Console.WriteLine("Error: Failed to deserialize one or both JSON files. Aborting."); return Results.Failed; } // 3. Generate HTML using the builder Console.WriteLine("Building HTML page..."); string htmlContent = HtmlBuilder.BuildArticlePage(articleData, factListData, $"../images/{ArticleImageName}"); // 4. Write the generated HTML to a file File.WriteAllText(FinalHTMLArticle, htmlContent); Console.WriteLine($"Successfully generated HTML file: '{FinalHTMLArticle}'"); Console.WriteLine($"You can open this file in any web browser."); } catch (FileNotFoundException ex) { Console.WriteLine($"Error: Input file not found. Make sure '{ex.FileName}' is in the correct directory."); } catch (JsonException ex) { Console.WriteLine($"Error: Invalid JSON format. Details: {ex.Message}"); } catch (Exception ex) { Console.WriteLine($"An unexpected error occurred: {ex.Message}"); } return Results.Success; } public static async Task RunLandingPageEditor() { // Make sure that everything is in place in order to do some work var results = BaseAgent.CheckDocuments("11 - Uploader", null, WebpageChanges, [FinalHTMLArticle, ArticleImagePath]); // Anything but success is necessary if (results != Results.Success) return results; // Use the new title string articleMetadata = File.ReadAllText(ArticleMetadata); var articleMetadataSections = JsonHelpers.SectionParser(articleMetadata); // Rename the final article with today's date OnlineHtmlFileName = $"{ArticleDateTime:yyyy-MM-dd_HH-mm-ss}_{articleMetadataSections["Category"]}_({articleMetadataSections["Title"].Replace(" ", "_")}).html"; string localArticleFileName = Path.Combine("Output", OnlineHtmlFileName); File.Copy(FinalHTMLArticle, localArticleFileName, true); // Modify the landing page to include the new article Console.Write($"\nUpdating landing page to include new article link... "); var result = await LandingPageEditor.RunAsync(OnlineHtmlFileName, ArticleImageName); Console.Write($"Completed: " + result); File.WriteAllText(WebpageChanges, result); return Results.Success; } public static async Task RunUploader() { // Make sure that everything is in place in order to do some work var results = BaseAgent.CheckDocuments("11 - Uploader", null, null, [FinalHTMLArticle, ArticleImagePath]); // Anything but success is necessary if (results != Results.Success) return results; // Rename the final article with today's date string localArticleFileName = Path.Combine("Output", OnlineHtmlFileName); File.Copy(FinalHTMLArticle, localArticleFileName, true); // Upload the new article to GitHub Console.Write($"\nUploading new article '{OnlineHtmlFileName}' to GitHub... "); var uploadResult = await GitHubHelper.UploadNewFile($"articles/{OnlineHtmlFileName}", localArticleFileName, $"feat: Add new article '{OnlineHtmlFileName}'"); Console.Write($"Completed: " + uploadResult); // Upload the new image to GitHub Console.Write($"\nUploading new image '{ArticleImagePath}' to GitHub... "); var uploadResult2 = await GitHubHelper.UploadFileAsGitBlobAsync($"images/{ArticleImageName}", ArticleImagePath, $"feat: Add new image '{ArticleImagePath}'"); Console.Write($"Completed: " + uploadResult2); return Results.Success; } public static async Task RunArchivistSaver() { // Archive the published article // Save the archive of already published articles Console.Write($"\nUpdating the archive of published articles... "); string article = File.ReadAllText(FullArticle); var articleSections = JsonHelpers.SectionParser(article); string extraContent = "\n\n\nTitle: " + articleSections["Title"] + "\nDate: " + ArticleDateTime.ToString("dddd dd MMMM yyyy HH:mm:ss") + "\nSummary: " + CleanReferences(articleSections["Summary"]); File.AppendAllText(PublishedArticles, extraContent); // Upload the archive to GitHub Gist string archiveFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, PublishedArticles); string archiveFile = Path.GetFileName(archiveFilePath); string newArchive = File.ReadAllText(archiveFilePath); await GitHubGistHelper.UpdateGistAsync(ArchiveID, archiveFile, newArchive); File.AppendAllText(Path.Combine(Archive, "Articles Created.csv"), $"{DateTime.Now},{ArticleDateTime},{article.Split(" ").Length},\"{articleSections["Title"]}\"\n"); return Results.Success; } }