From d986551e050afe5ed4c622f136e7c76fb4805040 Mon Sep 17 00:00:00 2001 From: carlfeynman Date: Sat, 21 Jun 2025 17:06:00 +0530 Subject: [PATCH] adding beifongai project --- README.md | 1 + .../beifong/__init__.py | 0 .../beifong/agents/audio_generate_agent.py | 359 ++++ .../beifong/agents/image_generate_agent.py | 100 + .../beifong/agents/scrape_agent.py | 148 ++ .../beifong/agents/script_agent.py | 135 ++ .../beifong/agents/search_agent.py | 105 ++ .../beifong/bootstrap_demo.py | 75 + .../beifong/celery_worker.py | 13 + .../beifong/db/__init__.py | 0 .../beifong/db/agent_config_v2.py | 169 ++ .../beifong/db/articles.py | 276 +++ .../beifong/db/config.py | 71 + .../beifong/db/connection.py | 27 + .../beifong/db/feeds.py | 179 ++ .../beifong/db/podcast_configs.py | 205 +++ .../beifong/db/podcasts.py | 124 ++ .../beifong/db/tasks.py | 247 +++ .../beifong/integrations/slack/chat.py | 1224 +++++++++++++ .../beifong/main.py | 187 ++ .../beifong/models/__init__.py | 0 .../beifong/models/article_schemas.py | 29 + .../beifong/models/podcast_config_schemas.py | 41 + .../beifong/models/podcast_schemas.py | 81 + .../beifong/models/schemas.py | 0 .../beifong/models/social_media_schemas.py | 56 + .../beifong/models/source_schemas.py | 68 + .../beifong/models/tasks_schemas.py | 118 ++ .../beifong/pack_demo.py | 25 + .../beifong/processors/__init__.py | 0 .../processors/ai_analysis_processor.py | 188 ++ .../beifong/processors/embedding_processor.py | 229 +++ .../processors/faiss_indexing_processor.py | 339 ++++ .../processors/fb_scraper_processor.py | 24 + .../beifong/processors/feed_processor.py | 89 + .../processors/podcast_generator_processor.py | 333 ++++ .../beifong/processors/url_processor.py | 82 + .../beifong/processors/x_scraper_processor.py | 24 + .../beifong/requirements.txt | 236 +++ .../beifong/routers/__init__.py | 0 .../beifong/routers/article_router.py | 54 + .../routers/async_podcast_agent_router.py | 72 + .../beifong/routers/podcast_config_router.py | 103 ++ .../beifong/routers/podcast_router.py | 200 ++ .../beifong/routers/social_media_router.py | 173 ++ .../beifong/routers/source_router.py | 136 ++ .../beifong/routers/task_router.py | 138 ++ .../beifong/ruff.toml | 1 + .../beifong/scheduler.py | 205 +++ .../beifong/services/__init__.py | 0 .../beifong/services/article_service.py | 174 ++ .../services/async_podcast_agent_service.py | 392 ++++ .../beifong/services/celery_app.py | 70 + .../beifong/services/celery_tasks.py | 85 + .../beifong/services/db_init.py | 369 ++++ .../beifong/services/db_service.py | 75 + .../services/internal_session_service.py | 155 ++ .../services/podcast_config_service.py | 177 ++ .../beifong/services/podcast_service.py | 349 ++++ .../beifong/services/social_media_service.py | 603 +++++++ .../beifong/services/source_service.py | 375 ++++ .../beifong/services/task_service.py | 315 ++++ .../beifong/static/images/ponyo.png | Bin 0 -> 317720 bytes .../beifong/static/musics/intro_audio.mp3 | Bin 0 -> 244892 bytes .../beifong/tests/__init__.py | 0 .../beifong/tests/agent_agno_test.py | 12 + .../beifong/tests/embedding_search_test.py | 194 ++ .../beifong/tests/english_sample_0.wav | Bin 0 -> 200444 bytes .../beifong/tests/hindi_sample_0.wav | Bin 0 -> 222044 bytes .../beifong/tests/index_faiss_test.py | 25 + .../beifong/tests/tool_browseruse_test.py | 20 + .../beifong/tests/tts_kokoro_test.py | 60 + .../beifong/tools/__init__.py | 0 .../beifong/tools/browser_crawler.py | 146 ++ .../beifong/tools/embedding_search.py | 153 ++ .../beifong/tools/google_news_discovery.py | 53 + .../beifong/tools/jikan_search.py | 199 ++ .../tools/pipeline/image_generate_agent.py | 81 + .../beifong/tools/pipeline/scrape_agent.py | 136 ++ .../beifong/tools/pipeline/script_agent.py | 120 ++ .../beifong/tools/pipeline/search_agent.py | 90 + .../beifong/tools/search_articles.py | 67 + .../beifong/tools/session_state_manager.py | 204 +++ .../beifong/tools/social/__init__.py | 0 .../beifong/tools/social/browser.py | 90 + .../beifong/tools/social/config.py | 3 + .../beifong/tools/social/db.py | 161 ++ .../beifong/tools/social/fb_post_extractor.py | 415 +++++ .../beifong/tools/social/fb_scraper.py | 127 ++ .../beifong/tools/social/session_setup.py | 9 + .../beifong/tools/social/x_agent.py | 103 ++ .../beifong/tools/social/x_post_extractor.py | 112 ++ .../beifong/tools/social/x_scraper.py | 88 + .../beifong/tools/social_media_search.py | 142 ++ .../beifong/tools/ui_manager.py | 33 + .../beifong/tools/user_source_selection.py | 30 + .../beifong/tools/web_search.py | 99 + .../beifong/tools/wikipedia_search.py | 62 + .../beifong/utils/__init__.py | 0 .../beifong/utils/crawl_url.py | 70 + .../beifong/utils/get_articles.py | 190 ++ .../beifong/utils/load_api_keys.py | 9 + .../beifong/utils/rss_feed_parser.py | 76 + .../beifong/utils/text_to_audio_elevenslab.py | 196 ++ .../beifong/utils/text_to_audio_kokoro.py | 110 ++ .../beifong/utils/text_to_audio_openai.py | 210 +++ .../beifong/utils/translate_podcast.py | 58 + .../beifong/utils/tts_engine_selector.py | 84 + .../ai_news_and_podcast_agents/readme.md | 644 +++++++ .../web/.prettierrc.json | 11 + .../web/build/android-chrome-192x192.png | Bin 0 -> 16697 bytes .../web/build/android-chrome-512x512.png | Bin 0 -> 46977 bytes .../web/build/apple-touch-icon.png | Bin 0 -> 14867 bytes .../web/build/asset-manifest.json | 15 + .../web/build/favicon-16x16.png | Bin 0 -> 773 bytes .../web/build/favicon-32x32.png | Bin 0 -> 1904 bytes .../web/build/favicon.ico | Bin 0 -> 15406 bytes .../web/build/index.html | 1 + .../web/build/manifest.json | 25 + .../web/build/robots.txt | 3 + .../web/build/static/css/main.e6c13ad2.css | 2 + .../build/static/css/main.e6c13ad2.css.map | 1 + .../web/build/static/js/453.258b012d.chunk.js | 2 + .../build/static/js/453.258b012d.chunk.js.map | 1 + .../web/build/static/js/main.5073a5c8.js | 3 + .../static/js/main.5073a5c8.js.LICENSE.txt | 68 + .../web/build/static/js/main.5073a5c8.js.map | 1 + .../web/package.json | 52 + .../web/public/android-chrome-192x192.png | Bin 0 -> 16697 bytes .../web/public/android-chrome-512x512.png | Bin 0 -> 46977 bytes .../web/public/apple-touch-icon.png | Bin 0 -> 14867 bytes .../web/public/favicon-16x16.png | Bin 0 -> 773 bytes .../web/public/favicon-32x32.png | Bin 0 -> 1904 bytes .../web/public/favicon.ico | Bin 0 -> 15406 bytes .../web/public/index.html | 46 + .../web/public/manifest.json | 25 + .../web/public/robots.txt | 3 + .../ai_news_and_podcast_agents/web/readme.md | 58 + .../web/src/App.css | 39 + .../ai_news_and_podcast_agents/web/src/App.js | 142 ++ .../web/src/App.test.js | 8 + .../src/components/ActivePodcastPreview.js | 654 +++++++ .../web/src/components/AssetPannelToggle.js | 131 ++ .../web/src/components/AudioConfirmation.js | 210 +++ .../web/src/components/BannerConfirmation.js | 310 ++++ .../web/src/components/ChatMessage.js | 442 +++++ .../web/src/components/FinalPresentation.js | 436 +++++ .../web/src/components/Footer.js | 51 + .../web/src/components/LanguageSelector.js | 168 ++ .../web/src/components/Navbar.js | 252 +++ .../web/src/components/PodcastConfigForm.js | 327 ++++ .../web/src/components/ProgressIndicator.js | 81 + .../web/src/components/ScriptConfirmation.js | 482 +++++ .../web/src/components/Sidebar.js | 688 +++++++ .../web/src/components/SourceSelection.js | 299 +++ .../web/src/components/SourceSelector.js | 151 ++ .../web/src/components/TaskForm.js | 235 +++ .../components/WebSearchRecordingPlayer.js | 221 +++ .../src/components/social/AnalyticsCards.js | 316 ++++ .../src/components/social/DateRangeFilter.js | 214 +++ .../web/src/components/social/FeedTab.js | 100 + .../web/src/components/social/Filters.js | 265 +++ .../web/src/components/social/Pagination.js | 158 ++ .../src/components/social/PostDetailPanel.js | 469 +++++ .../web/src/components/social/PostItem.js | 351 ++++ .../src/components/social/SessionSetupTab.js | 176 ++ .../web/src/components/social/StatsTab.js | 1099 +++++++++++ .../web/src/icons/Spoot.js | 1606 +++++++++++++++++ .../web/src/index.css | 13 + .../web/src/index.js | 13 + .../web/src/pages/ArticleDetail.js | 347 ++++ .../web/src/pages/Articles.js | 611 +++++++ .../web/src/pages/Home.js | 488 +++++ .../web/src/pages/PodcastDetail.js | 960 ++++++++++ .../web/src/pages/Podcasts.js | 849 +++++++++ .../web/src/pages/SocialMedia.js | 361 ++++ .../web/src/pages/SocialMediaDetail.js | 584 ++++++ .../web/src/pages/SourceDetail.js | 553 ++++++ .../web/src/pages/SourceEdit.js | 398 ++++ .../web/src/pages/Sources.js | 798 ++++++++ .../web/src/pages/StudioChat.js | 1117 ++++++++++++ .../web/src/pages/StudioLanding.js | 291 +++ .../web/src/pages/Voyager.js | 1149 ++++++++++++ .../web/src/reportWebVitals.js | 13 + .../web/src/services/api.js | 265 +++ .../web/src/setupTests.js | 1 + 186 files changed, 33718 insertions(+) create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/__init__.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/audio_generate_agent.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/image_generate_agent.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/scrape_agent.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/script_agent.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/search_agent.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/bootstrap_demo.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/celery_worker.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/__init__.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/agent_config_v2.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/articles.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/config.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/connection.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/feeds.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/podcast_configs.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/podcasts.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/tasks.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/integrations/slack/chat.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/main.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/__init__.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/article_schemas.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/podcast_config_schemas.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/podcast_schemas.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/schemas.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/social_media_schemas.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/source_schemas.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/tasks_schemas.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/pack_demo.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/__init__.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/ai_analysis_processor.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/embedding_processor.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/faiss_indexing_processor.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/fb_scraper_processor.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/feed_processor.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/podcast_generator_processor.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/url_processor.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/x_scraper_processor.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/requirements.txt create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/__init__.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/article_router.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/async_podcast_agent_router.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/podcast_config_router.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/podcast_router.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/social_media_router.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/source_router.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/task_router.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/ruff.toml create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/scheduler.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/__init__.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/article_service.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/async_podcast_agent_service.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/celery_app.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/celery_tasks.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/db_init.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/db_service.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/internal_session_service.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/podcast_config_service.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/podcast_service.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/social_media_service.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/source_service.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/task_service.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/static/images/ponyo.png create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/static/musics/intro_audio.mp3 create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/__init__.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/agent_agno_test.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/embedding_search_test.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/english_sample_0.wav create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/hindi_sample_0.wav create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/index_faiss_test.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/tool_browseruse_test.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/tts_kokoro_test.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/__init__.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/browser_crawler.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/embedding_search.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/google_news_discovery.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/jikan_search.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/pipeline/image_generate_agent.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/pipeline/scrape_agent.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/pipeline/script_agent.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/pipeline/search_agent.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/search_articles.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/session_state_manager.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/social/__init__.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/social/browser.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/social/config.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/social/db.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/social/fb_post_extractor.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/social/fb_scraper.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/social/session_setup.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/social/x_agent.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/social/x_post_extractor.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/social/x_scraper.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/social_media_search.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/ui_manager.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/user_source_selection.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/web_search.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tools/wikipedia_search.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/utils/__init__.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/utils/crawl_url.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/utils/get_articles.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/utils/load_api_keys.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/utils/rss_feed_parser.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/utils/text_to_audio_elevenslab.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/utils/text_to_audio_kokoro.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/utils/text_to_audio_openai.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/utils/translate_podcast.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/utils/tts_engine_selector.py create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/readme.md create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/.prettierrc.json create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/android-chrome-192x192.png create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/android-chrome-512x512.png create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/apple-touch-icon.png create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/asset-manifest.json create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/favicon-16x16.png create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/favicon-32x32.png create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/favicon.ico create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/index.html create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/manifest.json create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/robots.txt create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/static/css/main.e6c13ad2.css create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/static/css/main.e6c13ad2.css.map create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/static/js/453.258b012d.chunk.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/static/js/453.258b012d.chunk.js.map create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/static/js/main.5073a5c8.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/static/js/main.5073a5c8.js.LICENSE.txt create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/build/static/js/main.5073a5c8.js.map create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/package.json create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/public/android-chrome-192x192.png create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/public/android-chrome-512x512.png create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/public/apple-touch-icon.png create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/public/favicon-16x16.png create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/public/favicon-32x32.png create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/public/favicon.ico create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/public/index.html create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/public/manifest.json create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/public/robots.txt create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/readme.md create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/App.css create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/App.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/App.test.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/ActivePodcastPreview.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/AssetPannelToggle.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/AudioConfirmation.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/BannerConfirmation.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/ChatMessage.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/FinalPresentation.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/Footer.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/LanguageSelector.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/Navbar.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/PodcastConfigForm.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/ProgressIndicator.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/ScriptConfirmation.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/Sidebar.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/SourceSelection.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/SourceSelector.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/TaskForm.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/WebSearchRecordingPlayer.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/social/AnalyticsCards.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/social/DateRangeFilter.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/social/FeedTab.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/social/Filters.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/social/Pagination.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/social/PostDetailPanel.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/social/PostItem.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/social/SessionSetupTab.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/components/social/StatsTab.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/icons/Spoot.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/index.css create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/index.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/pages/ArticleDetail.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/pages/Articles.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/pages/Home.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/pages/PodcastDetail.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/pages/Podcasts.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/pages/SocialMedia.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/pages/SocialMediaDetail.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/pages/SourceDetail.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/pages/SourceEdit.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/pages/Sources.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/pages/StudioChat.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/pages/StudioLanding.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/pages/Voyager.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/reportWebVitals.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/services/api.js create mode 100644 advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/web/src/setupTests.js diff --git a/README.md b/README.md index 153ae59..d6b1f7c 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ A curated collection of **Awesome LLM apps built with RAG, AI Agents, Multi-agen * [🧠 AI Mental Wellbeing Agent](advanced_ai_agents/multi_agent_apps/ai_mental_wellbeing_agent/) * [📑 AI Meeting Agent](advanced_ai_agents/single_agent_apps/ai_meeting_agent/) * [🧬 AI Self-Evolving Agent](advanced_ai_agents/multi_agent_apps/ai_Self-Evolving_agent/) +* [🎧 AI Social Media News and Podcast Agent](advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agent/) ### 🎮 Autonomous Game Playing Agents diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/__init__.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/audio_generate_agent.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/audio_generate_agent.py new file mode 100644 index 0000000..71e2e23 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/audio_generate_agent.py @@ -0,0 +1,359 @@ +from agno.agent import Agent +import os +from datetime import datetime +import tempfile +import numpy as np +import soundfile as sf +from typing import Any, Dict, List, Optional, Tuple +from utils.load_api_keys import load_api_key +from openai import OpenAI +from scipy import signal + + +PODCASTS_FOLDER = "podcasts" +PODCAST_AUDIO_FOLDER = os.path.join(PODCASTS_FOLDER, "audio") +PODCAST_MUSIC_FOLDER = os.path.join('static', "musics") +OPENAI_VOICES = {1: "alloy", 2: "echo", 3: "fable", 4: "onyx", 5: "nova", 6: "shimmer"} +DEFAULT_VOICE_MAP = {1: "alloy", 2: "nova"} +TTS_MODEL = "gpt-4o-mini-tts" +INTRO_MUSIC_FILE = os.path.join(PODCAST_MUSIC_FOLDER, "intro_audio.mp3") +OUTRO_MUSIC_FILE = os.path.join(PODCAST_MUSIC_FOLDER, "intro_audio.mp3") + + +def resample_audio_scipy(audio, original_sr, target_sr): + if original_sr == target_sr: + return audio + resampling_ratio = target_sr / original_sr + num_samples = int(len(audio) * resampling_ratio) + resampled = signal.resample(audio, num_samples) + return resampled + + +def create_silence_audio(silence_duration: float, sampling_rate: int) -> np.ndarray: + if sampling_rate <= 0: + print(f"Invalid sampling rate ({sampling_rate}) for silence generation") + return np.zeros(0, dtype=np.float32) + return np.zeros(int(sampling_rate * silence_duration), dtype=np.float32) + + +def combine_audio_segments(audio_segments: List[np.ndarray], silence_duration: float, sampling_rate: int) -> np.ndarray: + if not audio_segments: + return np.zeros(0, dtype=np.float32) + silence = create_silence_audio(silence_duration, sampling_rate) + combined_segments = [] + for i, segment in enumerate(audio_segments): + combined_segments.append(segment) + if i < len(audio_segments) - 1: + combined_segments.append(silence) + combined = np.concatenate(combined_segments) + max_amp = np.max(np.abs(combined)) + if max_amp > 0: + combined = combined / max_amp * 0.95 + return combined + + +def process_audio_file(temp_path: str) -> Optional[Tuple[np.ndarray, int]]: + try: + from pydub import AudioSegment + + audio_segment = AudioSegment.from_mp3(temp_path) + channels = audio_segment.channels + sample_width = audio_segment.sample_width + frame_rate = audio_segment.frame_rate + samples = np.array(audio_segment.get_array_of_samples()) + if channels == 2: + samples = samples.reshape(-1, 2).mean(axis=1) + max_possible_value = float(2 ** (8 * sample_width - 1)) + samples = samples.astype(np.float32) / max_possible_value + return samples, frame_rate + except ImportError: + print("Pydub not available, falling back to soundfile") + except Exception as e: + print(f"Pydub processing failed: {e}") + try: + audio_np, samplerate = sf.read(temp_path) + return audio_np, samplerate + except Exception as e: + print(f"Failed to process audio with soundfile: {e}") + try: + from pydub import AudioSegment + + sound = AudioSegment.from_mp3(temp_path) + wav_path = temp_path.replace(".mp3", ".wav") + sound.export(wav_path, format="wav") + audio_np, samplerate = sf.read(wav_path) + os.unlink(wav_path) + return audio_np, samplerate + except Exception as e: + print(f"All audio processing methods failed: {e}") + return None + + +def resample_audio(audio, orig_sr, target_sr): + try: + import librosa + + return librosa.resample(audio, orig_sr=orig_sr, target_sr=target_sr) + except ImportError: + print("Librosa not available for resampling") + return audio + except Exception as e: + print(f"Resampling failed: {e}") + return audio + + +def text_to_speech_openai( + client: OpenAI, + text: str, + speaker_id: int, + voice_map: Dict[int, str] = None, + model: str = TTS_MODEL, +) -> Optional[Tuple[np.ndarray, int]]: + if not text.strip(): + print("Empty text provided, skipping TTS generation") + return None + voice_map = voice_map or DEFAULT_VOICE_MAP + voice = voice_map.get(speaker_id) + if not voice: + if speaker_id in OPENAI_VOICES: + voice = OPENAI_VOICES[speaker_id] + else: + voice = next(iter(voice_map.values()), "alloy") + print(f"No voice mapping for speaker {speaker_id}, using {voice}") + try: + print(f"Generating TTS for speaker {speaker_id} using voice '{voice}'") + response = client.audio.speech.create( + model=model, + voice=voice, + input=text, + response_format="mp3", + ) + audio_data = response.content + if not audio_data: + print("OpenAI TTS returned empty response") + return None + print(f"Received {len(audio_data)} bytes from OpenAI TTS") + temp_file = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) + temp_path = temp_file.name + temp_file.close() + with open(temp_path, "wb") as f: + f.write(audio_data) + try: + return process_audio_file(temp_path) + finally: + if os.path.exists(temp_path): + os.unlink(temp_path) + except Exception as e: + print(f"OpenAI TTS API error: {e}") + import traceback + + traceback.print_exc() + return None + + +def create_podcast( + script: Any, + output_path: str, + tts_engine: str = "openai", + language_code: str = "en", + silence_duration: float = 0.7, + voice_map: Dict[int, str] = None, + model: str = TTS_MODEL, +) -> Optional[str]: + if tts_engine.lower() != "openai": + print(f"Only OpenAI TTS engine is available in this standalone version. Requested: {tts_engine}") + return None + try: + api_key = load_api_key("OPENAI_API_KEY") + if not api_key: + print("No OpenAI API key provided") + return None + client = OpenAI(api_key=api_key) + print("OpenAI client initialized") + except Exception as e: + print(f"Failed to initialize OpenAI client: {e}") + return None + output_path = os.path.abspath(output_path) + os.makedirs(os.path.dirname(output_path), exist_ok=True) + if voice_map is None: + voice_map = DEFAULT_VOICE_MAP.copy() + model_to_use = model + if model == "tts-1" and language_code == "en": + model_to_use = "tts-1-hd" + print(f"Using high-definition TTS model for English: {model_to_use}") + generated_segments = [] + sampling_rate_detected = None + + if hasattr(script, "entries"): + entries = script.entries + else: + entries = script + + print(f"Processing {len(entries)} script entries") + for i, entry in enumerate(entries): + if hasattr(entry, "speaker"): + speaker_id = entry.speaker + entry_text = entry.text + else: + speaker_id = entry["speaker"] + entry_text = entry["text"] + print(f"Processing entry {i + 1}/{len(entries)}: Speaker {speaker_id}") + result = text_to_speech_openai( + client=client, + text=entry_text, + speaker_id=speaker_id, + voice_map=voice_map, + model=model_to_use, + ) + if result: + segment_audio, segment_rate = result + if sampling_rate_detected is None: + sampling_rate_detected = segment_rate + print(f"Using sample rate: {sampling_rate_detected} Hz") + elif sampling_rate_detected != segment_rate: + print(f"Sample rate mismatch: {sampling_rate_detected} vs {segment_rate}") + try: + segment_audio = resample_audio(segment_audio, segment_rate, sampling_rate_detected) + print(f"Resampled to {sampling_rate_detected} Hz") + except Exception as e: + sampling_rate_detected = segment_rate + print(f"Resampling failed: {e}") + generated_segments.append(segment_audio) + else: + print(f"Failed to generate audio for entry {i + 1}") + if not generated_segments: + print("No audio segments were generated") + return None + if sampling_rate_detected is None: + print("Could not determine sample rate") + return None + print(f"Combining {len(generated_segments)} audio segments") + full_audio = combine_audio_segments(generated_segments, silence_duration, sampling_rate_detected) + if full_audio.size == 0: + print("Combined audio is empty") + return None + + try: + if os.path.exists(INTRO_MUSIC_FILE): + intro_music, intro_sr = sf.read(INTRO_MUSIC_FILE) + print(f"Loaded intro music: {len(intro_music) / intro_sr:.1f} seconds") + + if intro_music.ndim == 2: + intro_music = np.mean(intro_music, axis=1) + + if intro_sr != sampling_rate_detected: + intro_music = resample_audio_scipy(intro_music, intro_sr, sampling_rate_detected) + + full_audio = np.concatenate([intro_music, full_audio]) + print("Added intro music") + + if os.path.exists(OUTRO_MUSIC_FILE): + outro_music, outro_sr = sf.read(OUTRO_MUSIC_FILE) + print(f"Loaded outro music: {len(outro_music) / outro_sr:.1f} seconds") + + if outro_music.ndim == 2: + outro_music = np.mean(outro_music, axis=1) + + if outro_sr != sampling_rate_detected: + outro_music = resample_audio_scipy(outro_music, outro_sr, sampling_rate_detected) + + full_audio = np.concatenate([full_audio, outro_music]) + print("Added outro music") + + except Exception as e: + print(f"Could not add intro/outro music: {e}") + print("Continuing without background music") + + print(f"Writing audio to {output_path}") + try: + sf.write(output_path, full_audio, sampling_rate_detected) + except Exception as e: + print(f"Failed to write audio file: {e}") + return None + if os.path.exists(output_path): + file_size = os.path.getsize(output_path) + print(f"Audio file created: {output_path} ({file_size / 1024:.1f} KB)") + return output_path + else: + print(f"Failed to create audio file at {output_path}") + return None + + +def audio_generate_agent_run(agent: Agent) -> str: + """ + Generate an audio file for the podcast using the selected TTS engine. + + Args: + agent: The agent instance + + Returns: + A message with the result of audio generation + """ + from services.internal_session_service import SessionService + + session_id = agent.session_id + session = SessionService.get_session(session_id) + session_state = session["state"] + script_data = session_state.get("generated_script", {}) + if not script_data or (isinstance(script_data, dict) and not script_data.get("sections")): + error_msg = "Cannot generate audio: No podcast script data found. Please generate a script first." + print(error_msg) + return error_msg + if isinstance(script_data, dict): + podcast_title = script_data.get("title", "Your Podcast") + else: + podcast_title = "Your Podcast" + session_state["stage"] = "audio" + audio_dir = PODCAST_AUDIO_FOLDER + audio_filename = f"podcast_{datetime.now().strftime('%Y%m%d_%H%M%S')}.wav" + audio_path = os.path.join(audio_dir, audio_filename) + try: + if isinstance(script_data, dict) and "sections" in script_data: + speaker_map = {"ALEX": 1, "MORGAN": 2} + script_entries = [] + for section in script_data.get("sections", []): + for dialog in section.get("dialog", []): + speaker = dialog.get("speaker", "ALEX") + text = dialog.get("text", "") + + if text and speaker in speaker_map: + script_entries.append({"text": text, "speaker": speaker_map[speaker]}) + if not script_entries: + error_msg = "Cannot generate audio: No dialog found in the script." + print(error_msg) + return error_msg + + selected_language = session_state.get("selected_language", {"code": "en", "name": "English"}) + language_code = selected_language.get("code", "en") + language_name = selected_language.get("name", "English") + tts_engine = "openai" + if tts_engine == "openai" and not load_api_key("OPENAI_API_KEY"): + error_msg = "Cannot generate audio: OpenAI API key not found." + print(error_msg) + return error_msg + print(f"Generating podcast audio using {tts_engine} TTS engine in {language_name} language") + full_audio_path = create_podcast( + script=script_entries, + output_path=audio_path, + tts_engine=tts_engine, + language_code=language_code, + ) + if not full_audio_path: + error_msg = f"Failed to generate podcast audio with {tts_engine} TTS engine." + print(error_msg) + return error_msg + + audio_url = f"{os.path.basename(full_audio_path)}" + session_state["audio_url"] = audio_url + session_state["show_audio_for_confirmation"] = True + SessionService.save_session(session_id, session_state) + print(f"Successfully generated podcast audio: {full_audio_path}") + return f"I've generated the audio for your '{podcast_title}' podcast using {tts_engine.capitalize()} voices in {language_name}. You can listen to it in the player below. What do you think? If it sounds good, click 'Sounds Great!' to complete your podcast." + else: + error_msg = "Cannot generate audio: Script is not in the expected format." + print(error_msg) + return error_msg + except Exception as e: + error_msg = f"Error generating podcast audio: {str(e)}" + print(error_msg) + return f"I encountered an error while generating the podcast audio: {str(e)}. Please try again or let me know if you'd like to proceed without audio." \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/image_generate_agent.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/image_generate_agent.py new file mode 100644 index 0000000..a108896 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/image_generate_agent.py @@ -0,0 +1,100 @@ +from agno.agent import Agent +from agno.models.openai import OpenAIChat +from agno.tools.dalle import DalleTools +from textwrap import dedent +import json +from dotenv import load_dotenv +import uuid +from db.agent_config_v2 import PODCAST_IMG_DIR +import os +import requests + + +load_dotenv() + +IMAGE_GENERATION_AGENT_DESCRIPTION = "You are an AI agent that can generate images using DALL-E." +IMAGE_GENERATION_AGENT_INSTRUCTIONS = dedent(""" + When the user asks you to create an image, use the `create_image` tool to create the image. + Create a modern, eye-catching podcast cover images that represents a podcast given podcast topic. + Create 3 images for the given podcast topic. + + IMPORTANT INSTRUCTIONS: + - DO NOT include ANY text in the image + - DO NOT include any words, titles, or lettering + - Create a purely visual and symbolic representation + - Use imagery that represents the specific topics mentioned + - I like Studio Ghibli flavor if possible + - The image should work well as a podcast cover thumbnail + - Create a clean, professional design suitable for a podcast + - AGAIN, DO NOT INCLUDE ANY TEXT + """) + + +def download_images(image_urls): + local_image_filenames = [] + try: + if image_urls: + for image_url in image_urls: + unique_id = str(uuid.uuid4()) + filename = f"podcast_banner_{unique_id}.png" + os.makedirs(PODCAST_IMG_DIR, exist_ok=True) + print(f"Downloading image: {filename}") + response = requests.get(image_url, timeout=30) + response.raise_for_status() + image_path = os.path.join(PODCAST_IMG_DIR, filename) + with open(image_path, "wb") as f: + f.write(response.content) + local_image_filenames.append(filename) + print(f"Successfully downloaded: {filename}") + except requests.exceptions.RequestException as e: + print(f"Error downloading images (network): {e}") + except Exception as e: + print(f"Error downloading images: {e}") + + return local_image_filenames + + +def image_generation_agent_run(agent: Agent, query: str) -> str: + """ + Image Generation Agent that takes the generated_script (internally from session_state) and creates a images for the given podcast script. + + Args: + agent: The agent instance + query: any custom preferences for the image generation + Returns: + Response status + """ + from services.internal_session_service import SessionService + + session_id = agent.session_id + session = SessionService.get_session(session_id) + session_state = session["state"] + print("Image Generation Agent input: ", query) + + try: + image_agent = Agent( + model=OpenAIChat(id="gpt-4o"), + tools=[DalleTools()], + description=IMAGE_GENERATION_AGENT_DESCRIPTION, + instructions=IMAGE_GENERATION_AGENT_INSTRUCTIONS, + markdown=True, + show_tool_calls=True, + session_id=agent.session_id, + ) + image_agent.run(f"query: {query},\n podcast script: {json.dumps(session_state['generated_script'])}", session_id=agent.session_id) + images = image_agent.get_images() + image_urls = [] + if images and isinstance(images, list): + for image_response in images: + image_url = image_response.url + image_urls.append(image_url) + local_image_filenames = download_images(image_urls) + session_state["banner_images"] = local_image_filenames + if local_image_filenames: + session_state["banner_url"] = local_image_filenames[0] + except Exception as e: + print(f"Error in Image Generation Agent: {e}") + return "Error in Image Generation Agent" + session_state["stage"] = "image" + SessionService.save_session(session_id, session_state) + return "Required banner images for the podcast are generated successfully." \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/scrape_agent.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/scrape_agent.py new file mode 100644 index 0000000..84e7310 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/scrape_agent.py @@ -0,0 +1,148 @@ +from agno.agent import Agent +from agno.models.openai import OpenAIChat +from pydantic import BaseModel, Field +from dotenv import load_dotenv +from tools.browser_crawler import create_browser_crawler +from textwrap import dedent + + +load_dotenv() + + +class ScrapedContent(BaseModel): + url: str = Field(..., description="The URL of the search result") + description: str = Field(description="The description of the search result") + full_text: str = Field( + ..., + description="The full text of the given source URL, if not available or not applicable keep it empty", + ) + published_date: str = Field( + ..., + description="The published date of the content in ISO format, if not available keep it empty", + ) + + +SCRAPE_AGENT_DESCRIPTION = "You are a helpful assistant that can scrape the URL for full content." +SCRAPE_AGENT_INSTRUCTIONS = dedent(""" + You are a content verification and formatting assistant. + + You will receive a batch of pre-scraped content from various URLs along with a search query. + Your job is to: + + 1. VERIFY RELEVANCE: Ensure each piece of content is relevant to the given query + 2. QUALITY CONTROL: Filter out low-quality, duplicate, or irrelevant content + 3. FORMAT CONSISTENCY: Ensure all content follows a consistent format + 4. LENGTH OPTIMIZATION: Keep content at reasonable length - not too long, not too short + 5. CLEAN TEXT: Remove any formatting artifacts, ads, or navigation elements from scraped content + + For each piece of content, return: + - full_text: The cleaned, relevant text content (or empty if not relevant/low quality) + - published_date: The publication date in ISO format (or empty if not available) + + Note: Some content may be fallback descriptions (when scraping failed) - treat these appropriately and don't penalize them for being shorter. + + IMPORTANT: Focus on quality over quantity. It's better to return fewer high-quality, relevant pieces than many low-quality ones. + """) + + +def crawl_urls_batch(search_results): + url_to_search_results = {} + unique_urls = [] + for search_result in search_results: + if not search_result.get("url", False): + continue + if not search_result.get("is_scrapping_required", True): + continue + if not search_result.get('original_url'): + search_result['original_url'] = search_result['url'] + url = search_result["url"] + if url not in url_to_search_results: + url_to_search_results[url] = [] + unique_urls.append(url) + url_to_search_results[url].append(search_result) + browser_crawler = create_browser_crawler() + scraped_results = browser_crawler.scrape_urls(unique_urls) + url_to_scraped = {result["original_url"]: result for result in scraped_results} + updated_search_results = [] + successful_scrapes = 0 + failed_scrapes = 0 + for search_result in search_results: + original_url = search_result["url"] + scraped = url_to_scraped.get(original_url, {}) + updated_result = search_result.copy() + updated_result["original_url"] = original_url + if scraped.get("success", False): + updated_result["url"] = scraped.get("final_url", original_url) + updated_result["full_text"] = scraped.get("full_text", "") + updated_result["published_date"] = scraped.get("published_date", "") + successful_scrapes += 1 + else: + updated_result["url"] = original_url + updated_result["full_text"] = search_result.get("description", "") + updated_result["published_date"] = "" + failed_scrapes += 1 + updated_search_results.append(updated_result) + return updated_search_results, successful_scrapes, failed_scrapes + + +def verify_content_with_agent(agent, query, search_results, use_agent=True): + if not use_agent: + return search_results + verified_search_results = [] + for _, search_result in enumerate(search_results): + content_for_verification = { + "url": search_result["url"], + "description": search_result.get("description", ""), + "full_text": search_result["full_text"], + "published_date": search_result["published_date"], + } + search_result["agent_verified"] = False + try: + scrape_agent = Agent( + model=OpenAIChat(id="gpt-4o-mini"), + instructions=SCRAPE_AGENT_INSTRUCTIONS, + description=SCRAPE_AGENT_DESCRIPTION, + use_json_mode=True, + session_id=agent.session_id, + response_model=ScrapedContent, + ) + response = scrape_agent.run( + f"Query: {query}\n" + f"Verify and format this scraped content. " + f"Keep content relevant to the query and ensure quality: {content_for_verification}", + session_id=agent.session_id, + ) + verified_item = response.to_dict()["content"] + search_result["full_text"] = verified_item.get("full_text", search_result["full_text"]) + search_result["published_date"] = verified_item.get("published_date", search_result["published_date"]) + search_result["agent_verified"] = True + except Exception as _: + pass + verified_search_results.append(search_result) + return verified_search_results + + +def scrape_agent_run( + agent: Agent, + query: str, +) -> str: + """ + Scrape Agent that takes the search_results (internaly from search_results) and scrapes each URL for full content, making sure those contents are of high quality and relevant to the topic. + Args: + agent: The agent instance + query: The search query + Returns: + Response status + """ + print("Scrape Agent Input:", query) + session_id = agent.session_id + from services.internal_session_service import SessionService + + session = SessionService.get_session(session_id) + current_state = session["state"] + updated_results, _, _ = crawl_urls_batch(current_state["search_results"]) + verified_results = verify_content_with_agent(agent, query, updated_results, use_agent=False) + current_state["search_results"] = verified_results + SessionService.save_session(session_id, current_state) + has_results = "search_results" in current_state and current_state["search_results"] + return f"Scraped {len(current_state['search_results'])} sources with full content relevant to '{query}'{' and updated the full text and published date in the search_results items' if has_results else ''}." \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/script_agent.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/script_agent.py new file mode 100644 index 0000000..11d7ef9 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/script_agent.py @@ -0,0 +1,135 @@ +from agno.agent import Agent +from agno.models.openai import OpenAIChat +from pydantic import BaseModel, Field +from typing import List, Optional +from dotenv import load_dotenv +from textwrap import dedent +from datetime import datetime + +load_dotenv() + + +class Dialog(BaseModel): + speaker: str = Field(..., description="The speaker name (SHOULD BE 'ALEX' OR 'MORGAN')") + text: str = Field( + ..., + description="The spoken text content for this speaker based on the requested langauge, default is English", + ) + + +class Section(BaseModel): + type: str = Field(..., description="The section type (intro, headlines, article, outro)") + title: Optional[str] = Field(None, description="Optional title for the section (required for article type)") + dialog: List[Dialog] = Field(..., description="List of dialog exchanges between speakers") + + +class PodcastScript(BaseModel): + title: str = Field(..., description="The podcast episode title with date") + sections: List[Section] = Field(..., description="List of podcast sections (intro, headlines, articles, outro)") + + +PODCAST_AGENT_DESCRIPTION = "You are a helpful assistant that can generate engaging podcast scripts for the given sources." +PODCAST_AGENT_INSTRUCTIONS = dedent(""" + You are a helpful assistant that can generate engaging podcast scripts for the given source content and query. + For given content, create an engaging podcast script that should be at least 15 minutes worth of content and your allowed enhance the script beyond given sources if you know something additional info will be interesting to the discussion or not enough conents available. + You use the provided sources to ground your podcast script generation process. Keep it engaging and interesting. + + IMPORTANT: Generate the entire script in the provided language. basically only text field needs to be in requested language, + + CONTENT GUIDELINES [THIS IS EXAMPLE YOU CAN CHANGE THE GUIDELINES ANYWAY BASED ON THE QUERY OR TOPIC DISCUSSED]: + - Provide insightful analysis that helps the audience understand the significance + - Include discussions on potential implications and broader context of each story + - Explain complex concepts in an accessible but thorough manner + - Make connections between current and relevant historical developments when applicable + - Provide comparisons and contrasts with similar stories or trends when relevant + + PERSONALITY NOTES [THIS IS EXAMPLE YOU CAN CHANGE THE PERSONALITY OF ALEX AND MORGAN ANYWAY BASED ON THE QUERY OR TOPIC DISCUSSED]: + - Alex is more analytical and fact-focused + * Should reference specific details and data points + * Should explain complex topics clearly + * Should identify key implications of stories + - Morgan is more focused on human impact, social context, and practical applications + * Should analyze broader implications + * Should consider ethical implications and real-world applications + - Include natural, conversational banter and smooth transitions between topics + - Each article discussion should go beyond the basic summary to provide valuable insights + - Maintain a conversational but informed tone that would appeal to a general audience + + IMPORTNAT: + - MAKE SURE PODCAST SCRIPS ARE AT LEAST 15 MINUTES LONG WHICH MEANS YOU NEED TO HAVE DETAILED DISCUSSIONS OFFCOURSE KEEP IT INTERESTING AND ENGAGING. + """) + + +def format_search_results_for_podcast( + search_results: List[dict], +) -> tuple[str, List[str]]: + created_at = datetime.now().strftime("%B %d, %Y at %I:%M %p") + structured_content = [] + structured_content.append(f"PODCAST CREATION: {created_at}\n") + sources = [] + for idx, search_result in enumerate(search_results): + try: + if search_result.get("confirmed", False): + sources.append(search_result["url"]) + structured_content.append( + f""" + SOURCE {idx + 1}: + Title: {search_result['title']} + URL: {search_result['url']} + Content: {search_result.get('full_text') or search_result.get('description', '')} + ---END OF SOURCE {idx + 1}--- + """.strip() + ) + except Exception as e: + print(f"Error processing search result: {e}") + content_texts = "\n\n".join(structured_content) + return content_texts, sources + + +def podcast_script_agent_run( + agent: Agent, + query: str, + language_name: str, +) -> str: + """ + Podcast Script Agent that takes the search_results (internally from search_results) and creates a podcast script for the given query and language. + + Args: + agent: The agent instance + query: The search query + language_name: The language the podcast script should be. + Returns: + Response status + """ + from services.internal_session_service import SessionService + session_id = agent.session_id + session = SessionService.get_session(session_id) + session_state = session["state"] + + print("Podcast Script Agent Input:", query) + content_texts, sources = format_search_results_for_podcast(session_state.get("search_results", [])) + if not content_texts: + return "No confirmed sources found to generate podcast script." + + podcast_script_agent = Agent( + model=OpenAIChat(id="gpt-4o-mini"), + instructions=PODCAST_AGENT_INSTRUCTIONS, + description=PODCAST_AGENT_DESCRIPTION, + use_json_mode=True, + response_model=PodcastScript, + session_id=agent.session_id, + ) + response = podcast_script_agent.run( + f"query: {query}\n language_name: {language_name}\n content_texts: {content_texts}\n, IMPORTANT: texts should be in {language_name} language.", + session_id=agent.session_id, + ) + response_dict = response.to_dict() + response_dict = response_dict["content"] + response_dict["sources"] = sources + session_state["generated_script"] = response_dict + session_state['stage'] = 'script' + SessionService.save_session(session_id, session_state) + + if not session_state["generated_script"] and not session_state["generated_script"].get("sections"): + return "Failed to generate podcast script." + return f"Generated podcast script for '{query}' with {len(sources)} confirmed sources." \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/search_agent.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/search_agent.py new file mode 100644 index 0000000..c193b60 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/agents/search_agent.py @@ -0,0 +1,105 @@ +from typing import List +from agno.agent import Agent +from agno.models.openai import OpenAIChat +from pydantic import BaseModel, Field +from dotenv import load_dotenv +from agno.tools.duckduckgo import DuckDuckGoTools +from textwrap import dedent +from tools.wikipedia_search import wikipedia_search +from tools.google_news_discovery import google_news_discovery_run +from tools.jikan_search import jikan_search +from tools.embedding_search import embedding_search +from tools.social_media_search import social_media_search, social_media_trending_search +from tools.search_articles import search_articles +from tools.web_search import run_browser_search + + +load_dotenv() + + +class ReturnItem(BaseModel): + url: str = Field(..., description="The URL of the search result") + title: str = Field(..., description="The title of the search result") + description: str = Field(..., description="A brief description or summary of the search result content") + source_name: str = Field( + ..., + description="The name/type of the source (e.g., 'wikipedia', 'general', or any reputable source tag)", + ) + tool_used: str = Field( + ..., + description="The tools used to generate the search results, unknown if not used or not applicable", + ) + published_date: str = Field( + ..., + description="The published date of the content in ISO format, if not available keep it empty", + ) + is_scrapping_required: bool = Field( + ..., + description="Set to True if the content need scraping, False otherwise, default keep it True if not sure", + ) + + +class SearchResults(BaseModel): + items: List[ReturnItem] = Field(..., description="A list of search result items") + + +SEARCH_AGENT_DESCRIPTION = "You are a helpful assistant that can search the web for information." +SEARCH_AGENT_INSTRUCTIONS = dedent(""" + You are a helpful assistant that can search the web or any other sources for information. + You should create topic for the search from the given query instead of blindly apply the query to the search tools. + For a given topic, your job is to search the web or any other sources and return the top 5 to 10 sources about the topic. + Keep the search sources of high quality and reputable, and sources should be relevant to the asked topic. + Sources should be from diverse platforms with no duplicates. + IMPORTANT: User queries might be fuzzy or misspelled. Understand the user's intent and act accordingly. + IMPORTANT: The output source_name field can be one of ["wikipedia", "general", or any source tag used"]. + IMPORTANT: You have access to different search tools use them when appropriate which one is best for the given search query. Don't use particular tool if not required. + IMPORTANT: Make sure you are able to detect what tool to use and use it available tool tags = ["google_news_discovery", "duckduckgo", "wikipedia_search", "jikan_search", "social_media_search", "social_media_trending_search", "browser_search", "unknown"]. + IMPORTANT: If query is news related please prefere google news over other news tools. + IMPORTANT: If returned sources are not of high quality or not relevant to the asked topic, don't include them in the returned sources. + IMPORTANT: Never include dates to the search query unless user explicitly asks for it. + IMPORTANT: You are allowed to use appropriate tools to get the best results even the single tool return enough results diverse check is better. + IMPORTANT: You have access to browser agent for searching as well use it when other source can't suitable for the given tasks but input should detailed instruction to the run_browser_search agent to get the best results and also use it conservatively because it's expensive process. + """) + + +def search_agent_run(agent: Agent, query: str) -> str: + """ + Search Agent which searches the web and other sources for relevant sources about the given topic or query. + Args: + agent: The agent instance + query: The search query + Returns: + A formatted string response with the search results (link and gist only) + """ + print("Search Agent Input:", query) + session_id = agent.session_id + from services.internal_session_service import SessionService + + session = SessionService.get_session(session_id) + current_state = session["state"] + search_agent = Agent( + model=OpenAIChat(id="gpt-4o-mini"), + instructions=SEARCH_AGENT_INSTRUCTIONS, + description=SEARCH_AGENT_DESCRIPTION, + use_json_mode=True, + response_model=SearchResults, + tools=[ + google_news_discovery_run, + DuckDuckGoTools(), + wikipedia_search, + jikan_search, + embedding_search, + social_media_search, + social_media_trending_search, + search_articles, + run_browser_search, + ], + session_id=session_id, + ) + response = search_agent.run(query, session_id=session_id) + response_dict = response.to_dict() + current_state["stage"] = "search" + current_state["search_results"] = response_dict["content"]["items"] + SessionService.save_session(session_id, current_state) + has_results = "search_results" in current_state and current_state["search_results"] + return f"Found {len(response_dict['content']['items'])} sources about {query} {'and added to the search_results' if has_results else ''}" \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/bootstrap_demo.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/bootstrap_demo.py new file mode 100644 index 0000000..f95fc7e --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/bootstrap_demo.py @@ -0,0 +1,75 @@ +import os +import sys +import tempfile +import requests +import zipfile +from tqdm import tqdm + +DEMO_URL = "https://github.com/arun477/beifong/releases/download/v1.2.0/demo_content.zip" +TARGET_DIRS = ["databases", "podcasts"] + + +def ensure_empty(dir_path): + """check if directory is empty (or create it). exit if not empty.""" + if os.path.exists(dir_path): + if os.listdir(dir_path): + print(f"✗ '{dir_path}' is not empty. aborting.") + sys.exit(1) + else: + os.makedirs(dir_path, exist_ok=True) + + +def download_file(url, dest_path): + """stream-download a file from url to dest_path with progress bar.""" + print("↓ downloading demo content...") + + response = requests.get(url, stream=True) + response.raise_for_status() + + + total_size = int(response.headers.get('content-length', 0)) + block_size = 8192 + + with open(dest_path, "wb") as f: + with tqdm(total=total_size, unit='B', unit_scale=True, desc="Download Progress") as pbar: + for chunk in response.iter_content(chunk_size=block_size): + if chunk: + f.write(chunk) + pbar.update(len(chunk)) + + +def extract_zip(zip_path, extract_to): + """extract zip file into extract_to (project root) with progress bar.""" + print("✂ extracting demo content...") + + with zipfile.ZipFile(zip_path, "r") as zip_ref: + total_files = len(zip_ref.infolist()) + + with tqdm(total=total_files, desc="Extraction Progress") as pbar: + for file in zip_ref.infolist(): + zip_ref.extract(file, extract_to) + pbar.update(1) + + +def main(): + print("populating demo folders…") + for d in TARGET_DIRS: + ensure_empty(d) + + with tempfile.TemporaryDirectory() as tmp: + tmp_zip = os.path.join(tmp, "demo_content.zip") + download_file(DEMO_URL, tmp_zip) + extract_zip(tmp_zip, os.getcwd()) + + print("✓ demo folders populated successfully.") + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + print("\n✗ Download cancelled by user.") + sys.exit(1) + except Exception as e: + print(f"\n✗ Error: {e}") + sys.exit(1) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/celery_worker.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/celery_worker.py new file mode 100644 index 0000000..67f66ef --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/celery_worker.py @@ -0,0 +1,13 @@ +from services.celery_tasks import app + +worker_options = [ + "worker", + "--loglevel=INFO", + "--concurrency=4", + "--hostname=beifong_worker@%h", + "--pool=threads", +] + +if __name__ == "__main__": + print("Starting Beifong podcast agent workers...") + app.worker_main(worker_options) \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/__init__.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/agent_config_v2.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/agent_config_v2.py new file mode 100644 index 0000000..19aaa5d --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/agent_config_v2.py @@ -0,0 +1,169 @@ +from agno.storage.sqlite import SqliteStorage +from db.config import get_agent_session_db_path +import json + +AGENT_MODEL = "gpt-4o" +AVAILABLE_LANGS = [ + {"code": "en", "name": "English"}, + {"code": "zh", "name": "Chinese"}, + {"code": "de", "name": "German"}, + {"code": "es", "name": "Spanish"}, + {"code": "ru", "name": "Russian"}, + {"code": "ko", "name": "Korean"}, + {"code": "fr", "name": "French"}, + {"code": "ja", "name": "Japanese"}, + {"code": "pt", "name": "Portuguese"}, + {"code": "tr", "name": "Turkish"}, + {"code": "pl", "name": "Polish"}, + {"code": "ca", "name": "Catalan"}, + {"code": "nl", "name": "Dutch"}, + {"code": "ar", "name": "Arabic"}, + {"code": "sv", "name": "Swedish"}, + {"code": "it", "name": "Italian"}, + {"code": "id", "name": "Indonesian"}, + {"code": "hi", "name": "Hindi"}, + {"code": "fi", "name": "Finnish"}, + {"code": "vi", "name": "Vietnamese"}, + {"code": "he", "name": "Hebrew"}, + {"code": "uk", "name": "Ukrainian"}, + {"code": "el", "name": "Greek"}, + {"code": "ms", "name": "Malay"}, + {"code": "cs", "name": "Czech"}, + {"code": "ro", "name": "Romanian"}, + {"code": "da", "name": "Danish"}, + {"code": "hu", "name": "Hungarian"}, + {"code": "ta", "name": "Tamil"}, + {"code": "no", "name": "Norwegian"}, + {"code": "th", "name": "Thai"}, + {"code": "ur", "name": "Urdu"}, + {"code": "hr", "name": "Croatian"}, + {"code": "bg", "name": "Bulgarian"}, + {"code": "lt", "name": "Lithuanian"}, + {"code": "la", "name": "Latin"}, + {"code": "mi", "name": "Maori"}, + {"code": "ml", "name": "Malayalam"}, + {"code": "cy", "name": "Welsh"}, + {"code": "sk", "name": "Slovak"}, + {"code": "te", "name": "Telugu"}, + {"code": "fa", "name": "Persian"}, + {"code": "lv", "name": "Latvian"}, + {"code": "bn", "name": "Bengali"}, + {"code": "sr", "name": "Serbian"}, + {"code": "az", "name": "Azerbaijani"}, + {"code": "sl", "name": "Slovenian"}, + {"code": "kn", "name": "Kannada"}, + {"code": "et", "name": "Estonian"}, + {"code": "mk", "name": "Macedonian"}, + {"code": "br", "name": "Breton"}, + {"code": "eu", "name": "Basque"}, + {"code": "is", "name": "Icelandic"}, + {"code": "hy", "name": "Armenian"}, + {"code": "ne", "name": "Nepali"}, + {"code": "mn", "name": "Mongolian"}, + {"code": "bs", "name": "Bosnian"}, + {"code": "kk", "name": "Kazakh"}, + {"code": "sq", "name": "Albanian"}, + {"code": "sw", "name": "Swahili"}, + {"code": "gl", "name": "Galician"}, + {"code": "mr", "name": "Marathi"}, + {"code": "pa", "name": "Punjabi"}, + {"code": "si", "name": "Sinhala"}, + {"code": "km", "name": "Khmer"}, + {"code": "sn", "name": "Shona"}, + {"code": "yo", "name": "Yoruba"}, + {"code": "so", "name": "Somali"}, + {"code": "af", "name": "Afrikaans"}, + {"code": "oc", "name": "Occitan"}, + {"code": "ka", "name": "Georgian"}, + {"code": "be", "name": "Belarusian"}, + {"code": "tg", "name": "Tajik"}, + {"code": "sd", "name": "Sindhi"}, + {"code": "gu", "name": "Gujarati"}, + {"code": "am", "name": "Amharic"}, + {"code": "yi", "name": "Yiddish"}, + {"code": "lo", "name": "Lao"}, + {"code": "uz", "name": "Uzbek"}, + {"code": "fo", "name": "Faroese"}, + {"code": "ht", "name": "Haitian creole"}, + {"code": "ps", "name": "Pashto"}, + {"code": "tk", "name": "Turkmen"}, + {"code": "nn", "name": "Nynorsk"}, + {"code": "mt", "name": "Maltese"}, + {"code": "sa", "name": "Sanskrit"}, + {"code": "lb", "name": "Luxembourgish"}, + {"code": "my", "name": "Myanmar"}, + {"code": "bo", "name": "Tibetan"}, + {"code": "tl", "name": "Tagalog"}, + {"code": "mg", "name": "Malagasy"}, + {"code": "as", "name": "Assamese"}, + {"code": "tt", "name": "Tatar"}, + {"code": "haw", "name": "Hawaiian"}, + {"code": "ln", "name": "Lingala"}, + {"code": "ha", "name": "Hausa"}, + {"code": "ba", "name": "Bashkir"}, + {"code": "jw", "name": "Javanese"}, + {"code": "su", "name": "Sundanese"}, + {"code": "yue", "name": "Cantonese"}, +] + +TOGGLE_UI_STATES = [ + "show_sources_for_selection", + "show_script_for_confirmation", + "show_banner_for_confirmation", + "show_audio_for_confirmation", +] + +AGENT_DESCRIPTION = "You are name is Beifong, a helpful assistant that guides users to choose best sources for the podcast and allow them to generate the podcast script." + +# sacred commandments, touch these with devotion. +AGENT_INSTRUCTIONS = [ + "Guide users to choose the best sources for the podcast and allow them to generate the podcast script and images for the podcast and audio for the podcast.", + "1. Make sure you get the intent of the topic from the user. It can be fuzzy and contain spelling mistakes from the user, so act as intent detection and get clarification only if needed.", + "1a. Keep this phase as quick as possible. Try to avoid too many back and forth conversations. Try to infer the intent if you're confident you can go right to the next search phase without confirming with the user. The less back and forth, the better for the user experience.", + "2. Once you understand the intent of the topic, use the available search tools (we have search agent where you can pass the query and along with appropriate prompt search agent has lot of search tools and api access which you don't have so you can instruct if needed) to get diverse and high-quality sources for the topic. and also make sure give appropriate short title for the chat and update the chat title using update_chat_title tool", + "2a. Once we receive the search results. do the full scraping using appropriate scraping tool to get the full text of the each source.", + "2b. Don't do back and forth during this source collection process with the user. Either you have the results or not, then inform the user and ask the user if they want to try again or give more details about the topic.", + "3. Once you have the results, ask the user to pick which sources they want to use for the podcast. You don't have to list out the found sources; just tell them to pick from the list of sources that will be visible in the UI.", + "4. User do the selection by selecting the index of sources from the list of sources that will be visible in the UI. so response will be a list of indices of sources selected by the user. sometime user will also send prefered language for the podcast along with the selection." + "4a. You have to use user_source_selection tool to update the user selection. and after this point immediately switch off any UI states.", + "4b. If user sends prefered language for the podcast along with the selection, you have to use update_language tool to update the user language if not leave it default is english. and after this point immediately switch off any UI states.", + "5. Once you we have the confimed selection from the user let's immediatly call the podcast script agent to generate the podcast script for the given sources and query and perfered language (pass full lanage name).", + "5a. Once podcast script is ready switch on the podcast_script UI state and so user will see if the generated podcast script is fine or not, you dont' have to show the script active podasshow_script_for_confirmation will take care of it.", + "6. Once you got the confirmation from the user (throug UI) let's immediatly call the image generation agent to generate the image for the given podcast script.", + "6a. Once image generation successfully generated switch on the image UI state and so user will see if the generated image is fine or not, you just ask user to confirm the image through UI. show_banner_for_confirmation will take care of it." + "7. Once you got the confirmation from the user (throug UI) let's immediatly call the audio generation agent to generate the audio for the given podcast script.", + "7a. Once audio generation successfully generated switch on the audio UI state and so user will see if the generated audio is fine or not, you just ask user to confirm the audio through UI. show_audio_for_confirmation will take care of it." + "8. Once you got the confirmation from the user for audio (throug UI) let's immediatly call the mark_session_finished tool to mark the session as finished and if finish is successful then no further conversation are allowed and only new session can be started.", + "8a. It's important mark_session_finished should be called only when we have all the stages search->selection->script->image->audio are completed." + "APPENDIX:", + "1. You can enable appropriate UI states using the ui_manager tool, which takes [state_type, active] as input, and it takes care of the appropriate UI state for activating appropriate UI state.", + f"1a. Available UI state types: {TOGGLE_UI_STATES}", + "1b. During the conversation, at any place you feel a UI state is not necessary, you can disable it using the ui_manager tool by setting active to False. For switching off all states, pass all to False.", + f"2. Supported Languges: {json.dumps(AVAILABLE_LANGS)}", + "3. Search Agent has a lot off tools, so you can instruct the search query as prompt to get the best results as because search agent has lot of tools you can instruct instead of directly passing the query to search agent when required.", + "4. You are not allowed to include year or date in your seach query construction for the search agent unless that request explicilty with yeear or date come for the users.", +] +DB_PATH = "databases" +PODCAST_DIR = "podcasts" +PODCAST_IMG_DIR = PODCAST_DIR + "/images" +PODCAST_AUIDO_DIR = PODCAST_DIR + "/audio" +PODCAST_RECORDINGS_DIR = PODCAST_DIR + "/recordings" + +INITIAL_SESSION_STATE = { + "search_results": [], + "show_sources_for_selection": False, + "show_script_for_confirmation": False, + "generated_script": {}, + "selected_language": {"code": "en", "name": "English"}, + "available_languages": AVAILABLE_LANGS, + "banner_images": [], + "banner_url": "", + "audio_url": "", + "title": "Untitled", + "created_at": "", + "finished": False, + "show_banner_for_confirmation": False, + "show_audio_for_confirmation": False, +} + +STORAGE = SqliteStorage(table_name="podcast_sessions", db_file=get_agent_session_db_path()) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/articles.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/articles.py new file mode 100644 index 0000000..75db55a --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/articles.py @@ -0,0 +1,276 @@ +import json +from datetime import datetime +from .connection import db_connection, execute_query + + +def store_crawled_article(tracking_db_path, entry, raw_content, metadata): + metadata_json = json.dumps(metadata) + query = """ + INSERT INTO crawled_articles + (entry_id, source_id, feed_id, title, url, published_date, raw_content, metadata) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + """ + try: + params = ( + entry["id"], + entry.get("source_id"), + entry.get("feed_id"), + entry.get("title", ""), + entry.get("link", ""), + entry.get("published_date", datetime.now().isoformat()), + raw_content, + metadata_json, + ) + execute_query(tracking_db_path, query, params) + return True + except Exception: + return False + + +def update_entry_status(tracking_db_path, entry_id, status): + query = """ + UPDATE feed_entries + SET crawl_attempts = crawl_attempts + 1, crawl_status = ? + WHERE id = ? + """ + return execute_query(tracking_db_path, query, (status, entry_id)) + + +def get_unprocessed_articles(tracking_db_path, limit=5, max_attempts=1): + reset_stuck_articles(tracking_db_path) + query = """ + SELECT id, entry_id, source_id, feed_id, title, url, published_date, raw_content, metadata, ai_attempts + FROM crawled_articles + WHERE (ai_status = 'pending' OR ai_status = 'error') + AND ai_attempts < ? + AND processed = 0 + ORDER BY published_date DESC + LIMIT ? + """ + articles = execute_query(tracking_db_path, query, (max_attempts, limit), fetch=True) + for article in articles: + if article.get("metadata"): + try: + article["metadata"] = json.loads(article["metadata"]) + except json.JSONDecodeError: + article["metadata"] = {} + if articles: + article_ids = [a["id"] for a in articles] + mark_articles_as_processing(tracking_db_path, article_ids) + return articles + + +def reset_stuck_articles(tracking_db_path): + query = """ + UPDATE crawled_articles + SET ai_status = 'pending' + WHERE ai_status = 'processing' + """ + return execute_query(tracking_db_path, query) + + +def mark_articles_as_processing(tracking_db_path, article_ids): + if not article_ids: + return 0 + with db_connection(tracking_db_path) as conn: + cursor = conn.cursor() + placeholders = ",".join(["?"] * len(article_ids)) + query = f""" + UPDATE crawled_articles + SET ai_status = 'processing' + WHERE id IN ({placeholders}) + """ + cursor.execute(query, article_ids) + conn.commit() + return cursor.rowcount + + +def save_article_categories(tracking_db_path, article_id, categories): + if not categories: + return 0 + with db_connection(tracking_db_path) as conn: + cursor = conn.cursor() + cursor.execute( + """ + DELETE FROM article_categories + WHERE article_id = ? + """, + (article_id,), + ) + count = 0 + for category in categories: + try: + cursor.execute( + """ + INSERT INTO article_categories (article_id, category_name) + VALUES (?, ?) + """, + (article_id, category.lower().strip()), + ) + count += 1 + except Exception as _: + pass + conn.commit() + return count + + +def get_article_categories(tracking_db_path, article_id): + query = """ + SELECT category_name + FROM article_categories + WHERE article_id = ? + """ + results = execute_query(tracking_db_path, query, (article_id,), fetch=True) + return [row["category_name"] for row in results] + + +def update_article_status(tracking_db_path, article_id, results=None, success=False, error_message=None): + with db_connection(tracking_db_path) as conn: + cursor = conn.cursor() + cursor.execute( + """ + UPDATE crawled_articles + SET ai_attempts = ai_attempts + 1 + WHERE id = ? + """, + (article_id,), + ) + if success and results: + categories = [] + if "categories" in results: + if isinstance(results["categories"], str): + try: + categories = json.loads(results["categories"]) + except json.JSONDecodeError: + categories = [c.strip() for c in results["categories"].split(",") if c.strip()] + elif isinstance(results["categories"], list): + categories = results["categories"] + cursor.execute( + """ + UPDATE crawled_articles + SET summary = ?, content = ?, processed = 1, ai_status = 'success' + WHERE id = ? + """, + (results.get("summary", ""), results.get("content", ""), article_id), + ) + conn.commit() + if categories: + save_article_categories(tracking_db_path, article_id, categories) + else: + cursor.execute( + """ + UPDATE crawled_articles + SET ai_status = 'error', ai_error = ? + WHERE id = ? + """, + (error_message, article_id), + ) + cursor.execute( + """ + UPDATE crawled_articles + SET ai_status = 'failed' + WHERE id = ? AND ai_attempts >= 3 + """, + (article_id,), + ) + conn.commit() + return cursor.rowcount + + +def get_articles_by_date_range(tracking_db_path, start_date=None, end_date=None, limit=None, offset=0): + query_parts = [ + "SELECT ca.id, ca.feed_id, ca.source_id, ca.title, ca.url, ca.published_date,", + "ca.summary, ca.content", + "FROM crawled_articles ca", + "WHERE ca.processed = 1", + "AND ca.ai_status = 'success'", + ] + query_params = [] + if start_date: + query_parts.append("AND ca.published_date >= ?") + query_params.append(start_date) + if end_date: + query_parts.append("AND ca.published_date <= ?") + query_params.append(end_date) + query_parts.append("ORDER BY ca.published_date DESC") + if limit is not None: + query_parts.append("LIMIT ? OFFSET ?") + query_params.append(limit) + query_params.append(offset) + query = " ".join(query_parts) + return execute_query(tracking_db_path, query, tuple(query_params), fetch=True) + + +def get_article_by_id(tracking_db_path, article_id): + query = """ + SELECT id, entry_id, source_id, feed_id, title, url, published_date, + raw_content, content, summary, metadata, ai_status, ai_error, + ai_attempts, crawled_date, processed + FROM crawled_articles + WHERE id = ? + """ + article = execute_query(tracking_db_path, query, (article_id,), fetch=True, fetch_one=True) + if article: + if article.get("metadata"): + try: + article["metadata"] = json.loads(article["metadata"]) + except json.JSONDecodeError: + article["metadata"] = {} + article["categories"] = get_article_categories(tracking_db_path, article_id) + return article + + +def get_articles_by_category(tracking_db_path, category, limit=20, offset=0): + query = """ + SELECT ca.id, ca.title, ca.url, ca.published_date, ca.summary + FROM crawled_articles ca + JOIN article_categories ac ON ca.id = ac.article_id + WHERE ac.category_name = ? + AND ca.processed = 1 + AND ca.ai_status = 'success' + ORDER BY ca.published_date DESC + LIMIT ? OFFSET ? + """ + return execute_query(tracking_db_path, query, (category, limit, offset), fetch=True) + + +def get_article_stats(tracking_db_path): + query = """ + SELECT + COUNT(*) as total_articles, + SUM(CASE WHEN processed = 1 THEN 1 ELSE 0 END) as processed_articles, + SUM(CASE WHEN ai_status = 'pending' THEN 1 ELSE 0 END) as pending_articles, + SUM(CASE WHEN ai_status = 'processing' THEN 1 ELSE 0 END) as processing_articles, + SUM(CASE WHEN ai_status = 'success' THEN 1 ELSE 0 END) as success_articles, + SUM(CASE WHEN ai_status = 'error' THEN 1 ELSE 0 END) as error_articles, + SUM(CASE WHEN ai_status = 'failed' THEN 1 ELSE 0 END) as failed_articles + FROM crawled_articles + """ + return execute_query(tracking_db_path, query, fetch=True, fetch_one=True) + + +def get_categories_with_counts(tracking_db_path, limit=20): + query = """ + SELECT category_name, COUNT(*) as article_count + FROM article_categories + GROUP BY category_name + ORDER BY article_count DESC + LIMIT ? + """ + return execute_query(tracking_db_path, query, (limit,), fetch=True) + + +def get_articles_with_source_info(tracking_db_path, limit=20, offset=0): + query = """ + SELECT ca.id, ca.title, ca.url, ca.published_date, ca.summary, + ft.feed_url, s.name as source_name + FROM crawled_articles ca + LEFT JOIN feed_tracking ft ON ca.feed_id = ft.feed_id + LEFT JOIN source_feeds sf ON ca.feed_id = sf.id + LEFT JOIN sources s ON sf.source_id = s.id + WHERE ca.processed = 1 + AND ca.ai_status = 'success' + ORDER BY ca.published_date DESC + LIMIT ? OFFSET ? + """ + return execute_query(tracking_db_path, query, (limit, offset), fetch=True) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/config.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/config.py new file mode 100644 index 0000000..9aeacbf --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/config.py @@ -0,0 +1,71 @@ +import os +from pathlib import Path +from dotenv import load_dotenv + +env_path = Path(".") / ".env" +load_dotenv(dotenv_path=env_path) +DEFAULT_DB_PATHS = { + "sources_db": "databases/sources.db", + "tracking_db": "databases/feed_tracking.db", + "podcasts_db": "databases/podcasts.db", + "tasks_db": "databases/tasks.db", + "agent_session_db": "databases/agent_sessions.db", + "faiss_index_db": "databases/faiss/article_index.faiss", + "faiss_mapping_file": "databases/faiss/article_id_map.npy", + "internal_sessions_db": "databases/internal_sessions.db", + "social_media_db": "databases/social_media.db", + "slack_sessions_db": "databases/slack_sessions.db", +} + + +def get_db_path(db_name): + env_var = f"{db_name.upper()}_PATH" + path = os.environ.get(env_var, DEFAULT_DB_PATHS.get(db_name)) + db_dir = os.path.dirname(path) + os.makedirs(db_dir, exist_ok=True) + return path + + +def get_sources_db_path(): + return get_db_path("sources_db") + + +def get_tracking_db_path(): + return get_db_path("tracking_db") + + +def get_podcasts_db_path(): + return get_db_path("podcasts_db") + + +def get_tasks_db_path(): + return get_db_path("tasks_db") + + +def get_agent_session_db_path(): + return get_db_path("agent_session_db") + + +def get_faiss_db_path(): + return get_db_path("faiss_index_db"), get_db_path("faiss_mapping_file") + + +def get_internal_sessions_db_path(): + return get_db_path("internal_sessions_db") + + +def get_social_media_db_path(): + return get_db_path("social_media_db") + + +def get_browser_session_path(): + return "browsers/playwright_persistent_profile" + +def get_slack_sessions_db_path(): + return get_db_path("slack_sessions_db") + +DB_PATH = "databases" +PODCAST_DIR = "podcasts" +PODCAST_IMG_DIR = PODCAST_DIR + "/images" +PODCAST_AUIDO_DIR = PODCAST_DIR + "/audio" +PODCAST_RECORDINGS_DIR = PODCAST_DIR + "/recordings" diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/connection.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/connection.py new file mode 100644 index 0000000..9c8ce0a --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/connection.py @@ -0,0 +1,27 @@ +import sqlite3 +from contextlib import contextmanager + + +@contextmanager +def db_connection(db_path): + conn = sqlite3.connect(db_path) + conn.row_factory = sqlite3.Row + try: + yield conn + finally: + conn.close() + + +def execute_query(db_path, query, params=(), fetch=False, fetch_one=False): + with db_connection(db_path) as conn: + cursor = conn.cursor() + cursor.execute(query, params) + + if fetch_one: + result = cursor.fetchone() + return dict(result) if result else None + elif fetch: + return [dict(row) for row in cursor.fetchall()] + else: + conn.commit() + return cursor.lastrowid diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/feeds.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/feeds.py new file mode 100644 index 0000000..1271baa --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/feeds.py @@ -0,0 +1,179 @@ +from datetime import datetime +import sqlite3 +from .connection import db_connection, execute_query + + +def get_active_feeds(sources_db_path, limit=None, offset=0): + if limit: + query = """ + SELECT sf.id, sf.source_id, sf.feed_url, sf.feed_type, sf.last_crawled, + s.name as source_name + FROM source_feeds sf + JOIN sources s ON sf.source_id = s.id + WHERE sf.is_active = 1 AND s.is_active = 1 + LIMIT ? OFFSET ? + """ + return execute_query(sources_db_path, query, (limit, offset), fetch=True) + else: + query = """ + SELECT sf.id, sf.source_id, sf.feed_url, sf.feed_type, sf.last_crawled, + s.name as source_name + FROM source_feeds sf + JOIN sources s ON sf.source_id = s.id + WHERE sf.is_active = 1 AND s.is_active = 1 + """ + return execute_query(sources_db_path, query, fetch=True) + + +def count_active_feeds(sources_db_path): + query = """ + SELECT COUNT(*) as count + FROM source_feeds sf + JOIN sources s ON sf.source_id = s.id + WHERE sf.is_active = 1 AND s.is_active = 1 + """ + result = execute_query(sources_db_path, query, fetch=True, fetch_one=True) + return result["count"] if result else 0 + + +def get_feed_tracking_info(tracking_db_path, feed_id): + query = "SELECT * FROM feed_tracking WHERE feed_id = ?" + return execute_query(tracking_db_path, query, (feed_id,), fetch=True, fetch_one=True) + + +def update_feed_tracking(tracking_db_path, feed_id, etag, modified, entry_hash): + query = """ + UPDATE feed_tracking + SET last_processed = ?, last_etag = ?, last_modified = ?, entry_hash = ? + WHERE feed_id = ? + """ + params = (datetime.now().isoformat(), etag, modified, entry_hash, feed_id) + return execute_query(tracking_db_path, query, params) + + +def store_feed_entries(tracking_db_path, feed_id, source_id, entries): + count = 0 + with db_connection(tracking_db_path) as conn: + cursor = conn.cursor() + for entry in entries: + try: + cursor.execute( + """ + INSERT INTO feed_entries + (feed_id, source_id, entry_id, title, link, published_date, content, summary) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + """, + ( + feed_id, + source_id, + entry.get("entry_id", ""), + entry.get("title", ""), + entry.get("link", ""), + entry.get("published_date", datetime.now().isoformat()), + entry.get("content", ""), + entry.get("summary", ""), + ), + ) + count += 1 + except sqlite3.IntegrityError: + pass + conn.commit() + return count + + +def update_tracking_info(tracking_db_path, feeds): + with db_connection(tracking_db_path) as conn: + cursor = conn.cursor() + for feed in feeds: + cursor.execute( + """ + INSERT OR IGNORE INTO feed_tracking + (feed_id, source_id, feed_url, last_processed) + VALUES (?, ?, ?, NULL) + """, + (feed["id"], feed["source_id"], feed["feed_url"]), + ) + conn.commit() + + +def get_uncrawled_entries(tracking_db_path, limit=20, max_attempts=3): + reset_stuck_entries(tracking_db_path) + query = """ + SELECT e.id, e.feed_id, e.source_id, e.title, e.link, e.published_date, + e.crawl_attempts, e.entry_id as original_entry_id + FROM feed_entries e + WHERE (e.crawl_status = 'pending' OR e.crawl_status = 'failed') + AND e.crawl_attempts < ? + AND e.link IS NOT NULL + AND e.link != '' + AND NOT EXISTS ( + SELECT 1 FROM crawled_articles ca WHERE ca.url = e.link + ) + ORDER BY e.published_date DESC + LIMIT ? + """ + entries = execute_query(tracking_db_path, query, (max_attempts, limit), fetch=True) + if entries: + entry_ids = [e["id"] for e in entries] + mark_entries_as_processing(tracking_db_path, entry_ids) + return entries + + +def reset_stuck_entries(tracking_db_path): + query = """ + UPDATE feed_entries + SET crawl_status = 'pending' + WHERE crawl_status = 'processing' + """ + return execute_query(tracking_db_path, query) + + +def mark_entries_as_processing(tracking_db_path, entry_ids): + if not entry_ids: + return 0 + with db_connection(tracking_db_path) as conn: + cursor = conn.cursor() + placeholders = ",".join(["?"] * len(entry_ids)) + query = f""" + UPDATE feed_entries + SET crawl_status = 'processing' + WHERE id IN ({placeholders}) + """ + cursor.execute(query, entry_ids) + conn.commit() + return cursor.rowcount + + +def ensure_feed_tracking_exists(tracking_db_path, feed_id, source_id, feed_url): + query = """ + INSERT OR IGNORE INTO feed_tracking + (feed_id, source_id, feed_url, last_processed) + VALUES (?, ?, ?, NULL) + """ + return execute_query(tracking_db_path, query, (feed_id, source_id, feed_url)) + + +def get_feed_sources_with_categories(sources_db_path): + query = """ + SELECT sf.id, sf.source_id, sf.feed_url, sf.feed_type, sf.last_crawled, + s.name as source_name, c.name as category_name + FROM source_feeds sf + JOIN sources s ON sf.source_id = s.id + LEFT JOIN source_categories sc ON s.id = sc.source_id + LEFT JOIN categories c ON sc.category_id = c.id + WHERE sf.is_active = 1 AND s.is_active = 1 + """ + return execute_query(sources_db_path, query, fetch=True) + + +def get_feed_stats(tracking_db_path): + query = """ + SELECT + COUNT(*) as total_entries, + SUM(CASE WHEN crawl_status = 'pending' THEN 1 ELSE 0 END) as pending_entries, + SUM(CASE WHEN crawl_status = 'processing' THEN 1 ELSE 0 END) as processing_entries, + SUM(CASE WHEN crawl_status = 'success' THEN 1 ELSE 0 END) as success_entries, + SUM(CASE WHEN crawl_status = 'failed' THEN 1 ELSE 0 END) as failed_entries + FROM feed_entries + """ + return execute_query(tracking_db_path, query, fetch=True, fetch_one=True) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/podcast_configs.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/podcast_configs.py new file mode 100644 index 0000000..c520454 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/podcast_configs.py @@ -0,0 +1,205 @@ +import sqlite3 +from typing import List, Dict, Any, Optional +from datetime import datetime + + +def get_podcast_config(db_path: str, config_id: int) -> Optional[Dict[str, Any]]: + conn = sqlite3.connect(db_path) + conn.row_factory = sqlite3.Row + try: + cursor = conn.cursor() + cursor.execute( + """ + SELECT id, name, description, prompt, time_range_hours, limit_articles, + is_active, tts_engine, language_code, podcast_script_prompt, + image_prompt, created_at, updated_at + FROM podcast_configs + WHERE id = ? + """, + (config_id,), + ) + row = cursor.fetchone() + if not row: + return None + config = dict(row) + config["is_active"] = bool(config.get("is_active", 0)) + return config + except Exception as e: + print(f"Error fetching podcast config: {e}") + return None + finally: + conn.close() + + +def get_all_podcast_configs(db_path: str, active_only: bool = False) -> List[Dict[str, Any]]: + conn = sqlite3.connect(db_path) + conn.row_factory = sqlite3.Row + try: + cursor = conn.cursor() + if active_only: + query = """ + SELECT id, name, description, prompt, time_range_hours, limit_articles, + is_active, tts_engine, language_code, podcast_script_prompt, + image_prompt, created_at, updated_at + FROM podcast_configs + WHERE is_active = 1 + ORDER BY name + """ + cursor.execute(query) + else: + query = """ + SELECT id, name, description, prompt, time_range_hours, limit_articles, + is_active, tts_engine, language_code, podcast_script_prompt, + image_prompt, created_at, updated_at + FROM podcast_configs + ORDER BY name + """ + cursor.execute(query) + configs = [] + for row in cursor.fetchall(): + config = dict(row) + config["is_active"] = bool(config.get("is_active", 0)) + configs.append(config) + return configs + except Exception as e: + print(f"Error fetching podcast configs: {e}") + return [] + finally: + conn.close() + + +def create_podcast_config( + db_path: str, + name: str, + prompt: str, + description: Optional[str] = None, + time_range_hours: int = 24, + limit_articles: int = 20, + is_active: bool = True, + tts_engine: str = "kokoro", + language_code: str = "en", + podcast_script_prompt: Optional[str] = None, + image_prompt: Optional[str] = None, +) -> Optional[int]: + conn = sqlite3.connect(db_path) + try: + cursor = conn.cursor() + now = datetime.now().isoformat() + cursor.execute( + """ + INSERT INTO podcast_configs + (name, description, prompt, time_range_hours, limit_articles, + is_active, tts_engine, language_code, podcast_script_prompt, + image_prompt, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """, + ( + name, + description, + prompt, + time_range_hours, + limit_articles, + 1 if is_active else 0, + tts_engine, + language_code, + podcast_script_prompt, + image_prompt, + now, + now, + ), + ) + conn.commit() + return cursor.lastrowid + except Exception as e: + conn.rollback() + print(f"Error creating podcast config: {e}") + return None + finally: + conn.close() + + +def update_podcast_config(db_path: str, config_id: int, updates: Dict[str, Any]) -> bool: + conn = sqlite3.connect(db_path) + try: + cursor = conn.cursor() + cursor.execute("SELECT 1 FROM podcast_configs WHERE id = ?", (config_id,)) + if not cursor.fetchone(): + return False + if not updates: + return True + set_clauses = [] + params = [] + set_clauses.append("updated_at = ?") + params.append(datetime.now().isoformat()) + allowed_fields = [ + "name", + "description", + "prompt", + "time_range_hours", + "limit_articles", + "is_active", + "tts_engine", + "language_code", + "podcast_script_prompt", + "image_prompt", + ] + for field, value in updates.items(): + if field in allowed_fields: + if field == "is_active": + value = 1 if value else 0 + set_clauses.append(f"{field} = ?") + params.append(value) + params.append(config_id) + query = f""" + UPDATE podcast_configs + SET {", ".join(set_clauses)} + WHERE id = ? + """ + cursor.execute(query, tuple(params)) + conn.commit() + return True + except Exception as e: + conn.rollback() + print(f"Error updating podcast config: {e}") + return False + finally: + conn.close() + + +def delete_podcast_config(db_path: str, config_id: int) -> bool: + conn = sqlite3.connect(db_path) + try: + cursor = conn.cursor() + cursor.execute("DELETE FROM podcast_configs WHERE id = ?", (config_id,)) + conn.commit() + + return cursor.rowcount > 0 + except Exception as e: + conn.rollback() + print(f"Error deleting podcast config: {e}") + return False + finally: + conn.close() + + +def toggle_podcast_config(db_path: str, config_id: int, is_active: bool) -> bool: + conn = sqlite3.connect(db_path) + try: + cursor = conn.cursor() + now = datetime.now().isoformat() + cursor.execute( + """ + UPDATE podcast_configs + SET is_active = ?, updated_at = ? + WHERE id = ? + """, + (1 if is_active else 0, now, config_id), + ) + conn.commit() + return cursor.rowcount > 0 + except Exception as e: + conn.rollback() + print(f"Error toggling podcast config: {e}") + return False + finally: + conn.close() diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/podcasts.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/podcasts.py new file mode 100644 index 0000000..221aebf --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/podcasts.py @@ -0,0 +1,124 @@ +import json +from datetime import datetime +from typing import Dict, Any, Optional +from .connection import execute_query + + +def store_podcast( + podcasts_db_path: str, + podcast_data: Dict[str, Any], + audio_path: Optional[str], + banner_path: Optional[str], + tts_engine: str = "kokoro", + language_code: str = "en", +) -> int: + today = datetime.now().strftime("%Y-%m-%d") + podcast_json = json.dumps(podcast_data) + audio_generated = 1 if audio_path else 0 + sources_json = json.dumps(podcast_data.get("sources", [])) + query = """ + INSERT INTO podcasts + (title, date, content_json, audio_generated, audio_path, banner_img_path, + tts_engine, language_code, sources_json, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """ + params = ( + podcast_data.get("title", f"Podcast {today}"), + today, + podcast_json, + audio_generated, + audio_path, + banner_path, + tts_engine, + language_code, + sources_json, + datetime.now().isoformat(), + ) + return execute_query(podcasts_db_path, query, params) + + +def get_podcast(podcasts_db_path: str, podcast_id: int) -> Optional[Dict[str, Any]]: + query = """ + SELECT id, title, date, content_json, audio_generated, audio_path, banner_img_path, + tts_engine, language_code, sources_json, created_at + FROM podcasts + WHERE id = ? + """ + podcast = execute_query(podcasts_db_path, query, (podcast_id,), fetch=True, fetch_one=True) + if podcast: + if podcast.get("content_json"): + try: + podcast["content"] = json.loads(podcast["content_json"]) + except json.JSONDecodeError: + podcast["content"] = {} + + if podcast.get("sources_json"): + try: + podcast["sources"] = json.loads(podcast["sources_json"]) + except json.JSONDecodeError: + podcast["sources"] = [] + return podcast + + +def get_recent_podcasts(podcasts_db_path: str, limit: int = 10) -> list: + query = """ + SELECT id, title, date, audio_generated, audio_path, banner_img_path, + tts_engine, language_code, sources_json, created_at + FROM podcasts + ORDER BY date DESC, created_at DESC + LIMIT ? + """ + podcasts = execute_query(podcasts_db_path, query, (limit,), fetch=True) + for podcast in podcasts: + if podcast.get("sources_json"): + try: + podcast["sources"] = json.loads(podcast["sources_json"]) + except json.JSONDecodeError: + podcast["sources"] = [] + return podcasts + + +def update_podcast_audio(podcasts_db_path: str, podcast_id: int, audio_path: str) -> bool: + query = """ + UPDATE podcasts + SET audio_path = ?, audio_generated = 1 + WHERE id = ? + """ + rows_affected = execute_query(podcasts_db_path, query, (audio_path, podcast_id)) + return rows_affected > 0 + + +def update_podcast_banner(podcasts_db_path: str, podcast_id: int, banner_path: str) -> bool: + query = """ + UPDATE podcasts + SET banner_img_path = ? + WHERE id = ? + """ + rows_affected = execute_query(podcasts_db_path, query, (banner_path, podcast_id)) + return rows_affected > 0 + + +def update_podcast_metadata( + podcasts_db_path: str, podcast_id: int, tts_engine: Optional[str] = None, language_code: Optional[str] = None, sources_json: Optional[str] = None +) -> bool: + update_parts = [] + params = [] + if tts_engine is not None: + update_parts.append("tts_engine = ?") + params.append(tts_engine) + if language_code is not None: + update_parts.append("language_code = ?") + params.append(language_code) + if sources_json is not None: + update_parts.append("sources_json = ?") + params.append(sources_json) + if not update_parts: + return False + query = f""" + UPDATE podcasts + SET {", ".join(update_parts)} + WHERE id = ? + """ + params.append(podcast_id) + rows_affected = execute_query(podcasts_db_path, query, tuple(params)) + return rows_affected > 0 diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/tasks.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/tasks.py new file mode 100644 index 0000000..c44eb3c --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/db/tasks.py @@ -0,0 +1,247 @@ +from datetime import datetime, timedelta +from .connection import execute_query, db_connection + + +def create_task( + tasks_db_path, + name, + command, + frequency, + frequency_unit, + description=None, + enabled=True, +): + query = """ + INSERT INTO tasks + (name, description, command, frequency, frequency_unit, enabled, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?) + """ + params = ( + name, + description, + command, + frequency, + frequency_unit, + 1 if enabled else 0, + datetime.now().isoformat(), + ) + return execute_query(tasks_db_path, query, params) + + +def is_task_running(tasks_db_path, task_id): + query = """ + SELECT 1 + FROM task_executions + WHERE task_id = ? AND status = 'running' + LIMIT 1 + """ + try: + result = execute_query(tasks_db_path, query, (task_id,), fetch=True, fetch_one=True) + return result is not None # True if a running entry exists + except Exception as e: + print(f"Database error checking running status for task {task_id}: {e}") + return True + + +def get_task(tasks_db_path, task_id): + query = """ + SELECT id, name, description, command, frequency, frequency_unit, enabled, last_run, created_at + FROM tasks + WHERE id = ? + """ + return execute_query(tasks_db_path, query, (task_id,), fetch=True, fetch_one=True) + + +def get_all_tasks(tasks_db_path, include_disabled=False): + if include_disabled: + query = """ + SELECT id, name, description, command, frequency, frequency_unit, enabled, last_run, created_at + FROM tasks + ORDER BY name + """ + return execute_query(tasks_db_path, query, fetch=True) + else: + query = """ + SELECT id, name, description, command, frequency, frequency_unit, enabled, last_run, created_at + FROM tasks + WHERE enabled = 1 + ORDER BY name + """ + return execute_query(tasks_db_path, query, fetch=True) + + +def update_task(tasks_db_path, task_id, updates): + allowed_fields = [ + "name", + "description", + "command", + "frequency", + "frequency_unit", + "enabled", + ] + + set_clauses = [] + params = [] + for field, value in updates.items(): + if field in allowed_fields: + if field == "enabled": + value = 1 if value else 0 + set_clauses.append(f"{field} = ?") + params.append(value) + if not set_clauses: + return 0 + query = f""" + UPDATE tasks + SET {", ".join(set_clauses)} + WHERE id = ? + """ + params.append(task_id) + return execute_query(tasks_db_path, query, tuple(params)) + + +def delete_task(tasks_db_path, task_id): + query = """ + DELETE FROM tasks + WHERE id = ? + """ + return execute_query(tasks_db_path, query, (task_id,)) + + +def update_task_last_run(tasks_db_path, task_id, timestamp=None): + if timestamp is None: + timestamp = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") + query = """ + UPDATE tasks + SET last_run = ? + WHERE id = ? + """ + return execute_query(tasks_db_path, query, (timestamp, task_id)) + + +def create_task_execution(tasks_db_path, task_id, status, error_message=None, output=None): + start_time = datetime.now().isoformat() + query = """ + INSERT INTO task_executions + (task_id, start_time, status, error_message, output) + VALUES (?, ?, ?, ?, ?) + """ + params = (task_id, start_time, status, error_message, output) + execute_query(tasks_db_path, query, params) + try: + with db_connection(tasks_db_path) as conn: + cursor = conn.cursor() + cursor.execute(query, params) + conn.commit() + return cursor.lastrowid # Return the ID of the inserted row + except Exception as e: + print(f"Database error in create_task_execution: {e}") + return None # + + +def update_task_execution(tasks_db_path, execution_id, status, error_message=None, output=None): + end_time = datetime.now().isoformat() + query = """ + UPDATE task_executions + SET end_time = ?, status = ?, error_message = ?, output = ? + WHERE id = ? + """ + params = (end_time, status, error_message, output, execution_id) + return execute_query(tasks_db_path, query, params) + + +def get_recent_task_executions(tasks_db_path, task_id=None, limit=10): + if task_id: + query = """ + SELECT id, task_id, start_time, end_time, status, error_message, output + FROM task_executions + WHERE task_id = ? + ORDER BY start_time DESC + LIMIT ? + """ + params = (task_id, limit) + else: + query = """ + SELECT id, task_id, start_time, end_time, status, error_message, output + FROM task_executions + ORDER BY start_time DESC + LIMIT ? + """ + params = (limit,) + return execute_query(tasks_db_path, query, params, fetch=True) + + +def get_task_execution(tasks_db_path, execution_id): + query = """ + SELECT id, task_id, start_time, end_time, status, error_message, output + FROM task_executions + WHERE id = ? + """ + return execute_query(tasks_db_path, query, (execution_id,), fetch=True, fetch_one=True) + + +def mark_task_disabled(tasks_db_path, task_id): + query = """ + UPDATE tasks + SET enabled = 0 + WHERE id = ? + """ + return execute_query(tasks_db_path, query, (task_id,)) + + +def mark_task_enabled(tasks_db_path, task_id): + query = """ + UPDATE tasks + SET enabled = 1 + WHERE id = ? + """ + return execute_query(tasks_db_path, query, (task_id,)) + + +def get_task_stats(tasks_db_path): + query = """ + SELECT + COUNT(*) as total_tasks, + SUM(CASE WHEN enabled = 1 THEN 1 ELSE 0 END) as active_tasks, + SUM(CASE WHEN enabled = 0 THEN 1 ELSE 0 END) as disabled_tasks, + SUM(CASE WHEN last_run IS NULL THEN 1 ELSE 0 END) as never_run_tasks + FROM tasks + """ + return execute_query(tasks_db_path, query, fetch=True, fetch_one=True) + + +def get_execution_stats(tasks_db_path, days=7): + cutoff_date = (datetime.now() - timedelta(days=days)).isoformat() + query = """ + SELECT + COUNT(*) as total_executions, + COALESCE(SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END), 0) as successful_executions, + COALESCE(SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END), 0) as failed_executions, + COALESCE(SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END), 0) as running_executions, + COALESCE(AVG(CASE WHEN end_time IS NOT NULL + THEN (julianday(end_time) - julianday(start_time)) * 86400.0 + ELSE NULL END), 0) as avg_execution_time_seconds + FROM task_executions + WHERE start_time >= ? + """ + return execute_query(tasks_db_path, query, (cutoff_date,), fetch=True, fetch_one=True) + + +def get_pending_tasks(tasks_db_path): + query = """ + SELECT id, name, description, command, frequency, frequency_unit, enabled, last_run + FROM tasks + WHERE enabled = 1 + AND ( + last_run IS NULL + OR + CASE frequency_unit + WHEN 'minutes' THEN datetime(last_run, '+' || frequency || ' minutes') <= datetime('now', 'localtime') + WHEN 'hours' THEN datetime(last_run, '+' || frequency || ' hours') <= datetime('now', 'localtime') + WHEN 'days' THEN datetime(last_run, '+' || frequency || ' days') <= datetime('now', 'localtime') + ELSE datetime(last_run, '+' || frequency || ' seconds') <= datetime('now', 'localtime') + END + ) + ORDER BY last_run + """ + tasks = execute_query(tasks_db_path, query, fetch=True) + return tasks diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/integrations/slack/chat.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/integrations/slack/chat.py new file mode 100644 index 0000000..aecea79 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/integrations/slack/chat.py @@ -0,0 +1,1224 @@ +import os +import re +import sqlite3 +import asyncio +import aiohttp +import json +from concurrent.futures import ThreadPoolExecutor +from slack_bolt import App +from slack_bolt.adapter.socket_mode import SocketModeHandler +from dotenv import load_dotenv +from typing import Dict, List +from datetime import datetime +from db.config import get_slack_sessions_db_path + +load_dotenv() + +app = App(token=os.environ["SLACK_BOT_TOKEN"]) +# local url works but banner images won't work in slack unless it's https with proper domain +# you can use ngrok to port forward local url to https and replace this local url with ngrok url +API_BASE_URL = os.environ.get("API_BASE_URL", "http://localhost:7000") +executor = ThreadPoolExecutor(max_workers=10) +active_sessions: Dict[str, Dict] = {} +DB_PATH = get_slack_sessions_db_path() + + +def send_error_message(thread_key: str, error_message: str): + print(f"Error for {thread_key}: {error_message}") + asyncio.create_task(send_slack_message(thread_key, f"❌ {error_message}")) + + +def init_db(): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute(""" + CREATE TABLE IF NOT EXISTS thread_sessions ( + thread_key TEXT PRIMARY KEY, + session_id TEXT NOT NULL, + channel_id TEXT NOT NULL, + user_id TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + cursor.execute(""" + CREATE TABLE IF NOT EXISTS session_state ( + session_id TEXT PRIMARY KEY, + state_data TEXT, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + conn.commit() + conn.close() + + +def save_session_mapping(thread_key: str, session_id: str, channel_id: str, user_id: str = None): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute( + "INSERT OR REPLACE INTO thread_sessions (thread_key, session_id, channel_id, user_id, updated_at) VALUES (?, ?, ?, ?, ?)", + (thread_key, session_id, channel_id, user_id, datetime.now().isoformat()), + ) + conn.commit() + conn.close() + + +def get_session_info(thread_key: str): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute( + "SELECT session_id, channel_id, user_id FROM thread_sessions WHERE thread_key = ?", + (thread_key,), + ) + result = cursor.fetchone() + conn.close() + return result if result else None + + +def save_session_state(session_id: str, state_data): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + if isinstance(state_data, str): + json_data = state_data + else: + json_data = json.dumps(state_data) + cursor.execute( + "INSERT OR REPLACE INTO session_state (session_id, state_data, updated_at) VALUES (?, ?, ?)", + (session_id, json_data, datetime.now().isoformat()), + ) + conn.commit() + conn.close() + + +def get_session_state(session_id: str): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute("SELECT state_data FROM session_state WHERE session_id = ?", (session_id,)) + result = cursor.fetchone() + conn.close() + if result: + try: + return json.loads(result[0]) + except: + return {} + return {} + + +class PodcastAgentClient: + def __init__(self, base_url: str): + self.base_url = base_url + self.timeout = aiohttp.ClientTimeout(total=30) + + async def create_session(self, session_id=None): + try: + async with aiohttp.ClientSession(timeout=self.timeout) as session: + payload = {"session_id": session_id} if session_id else {} + async with session.post(f"{self.base_url}/api/podcast-agent/session", json=payload) as resp: + resp.raise_for_status() + return await resp.json() + except Exception as e: + print(f"API create_session error: {e}") + raise + + async def chat(self, session_id: str, message: str): + try: + async with aiohttp.ClientSession(timeout=self.timeout) as session: + payload = {"session_id": session_id, "message": message} + async with session.post(f"{self.base_url}/api/podcast-agent/chat", json=payload) as resp: + resp.raise_for_status() + return await resp.json() + except Exception as e: + print(f"API chat error: {e}") + raise + + async def check_status(self, session_id: str, task_id=None): + try: + async with aiohttp.ClientSession(timeout=self.timeout) as session: + payload = {"session_id": session_id} + if task_id: + payload["task_id"] = task_id + async with session.post(f"{self.base_url}/api/podcast-agent/status", json=payload) as resp: + resp.raise_for_status() + return await resp.json() + except Exception as e: + print(f"API check_status error: {e}") + raise + + +api_client = PodcastAgentClient(API_BASE_URL) + + +def get_thread_key(message, is_dm=False): + if is_dm: + return f"dm_{message['channel']}_{message['user']}" + else: + return message.get("thread_ts", message["ts"]) + + +async def get_or_create_session(thread_key: str, channel_id: str, user_id: str = None): + session_info = get_session_info(thread_key) + if not session_info: + response = await api_client.create_session(thread_key) + session_id = response["session_id"] + save_session_mapping(thread_key, session_id, channel_id, user_id) + print(f"Created new session: {session_id} for thread: {thread_key}") + return session_id + else: + return session_info[0] + + +def run_async_in_thread(coro): + def run(): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + try: + return loop.run_until_complete(coro) + finally: + loop.close() + + future = executor.submit(run) + return future + + +async def poll_for_completion(session_id: str, thread_key: str, task_id=None): + print(f"Starting polling for session: {session_id}, task: {task_id}") + max_polls = 60 + poll_count = 0 + active_sessions[session_id] = { + "thread_key": thread_key, + "task_id": task_id, + "start_time": datetime.now(), + } + try: + while poll_count < max_polls: + try: + status_response = await api_client.check_status(session_id, task_id) + if status_response.get("session_state"): + save_session_state(session_id, status_response.get("session_state")) + if not status_response.get("is_processing", True): + await send_completion_message(thread_key, status_response) + break + if poll_count % 10 == 0 and poll_count > 0: + process_type = status_response.get("process_type", "request") + await send_slack_message( + thread_key, + f"🔄 Still processing {process_type}... ({poll_count * 3}s elapsed)", + ) + await asyncio.sleep(3) + poll_count += 1 + except Exception as e: + print(f"Polling error: {e}") + await send_slack_message( + thread_key, + "❌ Something went wrong while processing your request. Please try again.", + ) + break + finally: + if session_id in active_sessions: + del active_sessions[session_id] + + +def start_background_polling(session_id: str, thread_key: str, task_id=None): + if session_id in active_sessions: + print(f"Replacing existing poll for session: {session_id}") + future = run_async_in_thread(poll_for_completion(session_id, thread_key, task_id)) + active_sessions[session_id] = { + "thread_key": thread_key, + "task_id": task_id, + "future": future, + "start_time": datetime.now(), + } + + +async def send_completion_message(thread_key: str, status_response): + response_text = status_response.get("response", "Task completed!") + session_state = status_response.get("session_state") + if session_state: + try: + state_data = json.loads(session_state) if isinstance(session_state, str) else session_state + if state_data.get("show_sources_for_selection") and state_data.get("search_results"): + await send_source_selection_blocks(thread_key, state_data, response_text) + elif state_data.get("show_script_for_confirmation") and state_data.get("generated_script"): + await send_script_confirmation_blocks(thread_key, state_data, response_text) + elif state_data.get("show_banner_for_confirmation") and state_data.get("banner_url"): + await send_banner_confirmation_blocks(thread_key, state_data, response_text) + elif state_data.get("show_audio_for_confirmation") and state_data.get("audio_url"): + await send_audio_confirmation_blocks(thread_key, state_data, response_text) + elif state_data.get("podcast_generated"): + await send_final_presentation_blocks(thread_key, state_data, response_text) + else: + await send_slack_message(thread_key, response_text) + except Exception as e: + print(f"Error parsing session state: {e}") + await send_slack_message(thread_key, response_text) + else: + await send_slack_message(thread_key, response_text) + + +async def send_source_selection_blocks(thread_key: str, state_data: dict, response_text: str): + sources = state_data.get("search_results", []) + languages = state_data.get("available_languages", [{"code": "en", "name": "English"}]) + session_info = get_session_info(thread_key) + if session_info: + save_session_state(session_info[0], state_data) + source_options = [] + for i, source in enumerate(sources[:10]): + title = source.get("title", f"Source {i + 1}") + if len(title) > 70: + title = title[:67] + "..." + source_options.append( + { + "text": {"type": "plain_text", "text": f"{i + 1}. {title}"}, + "value": str(i), + } + ) + language_options = [] + for lang in languages: + language_options.append( + { + "text": {"type": "plain_text", "text": lang["name"]}, + "value": lang["code"], + } + ) + blocks = [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": f"*📋 Source Selection*\n{response_text}", + }, + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": f"Found *{len(sources)}* sources. Select the ones you'd like to use for your podcast:", + }, + }, + ] + if source_options: + blocks.append( + { + "type": "section", + "block_id": "source_selection_block", + "text": {"type": "mrkdwn", "text": "*Select Sources:*"}, + "accessory": { + "type": "checkboxes", + "action_id": "source_selection", + "options": source_options, + "initial_options": source_options, + }, + } + ) + if len(sources) > 10: + blocks.append( + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": f"_Showing first 10 sources. {len(sources) - 10} more available._", + } + ], + } + ) + blocks.extend( + [ + { + "type": "section", + "block_id": "language_selection_block", + "text": {"type": "mrkdwn", "text": "*Select Language:*"}, + "accessory": { + "type": "static_select", + "action_id": "language_selection", + "placeholder": {"type": "plain_text", "text": "Choose language"}, + "options": language_options, + "initial_option": language_options[0] if language_options else None, + }, + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": {"type": "plain_text", "text": "✅ Confirm Selection"}, + "style": "primary", + "action_id": "confirm_sources", + "value": thread_key, + } + ], + }, + ] + ) + await send_slack_blocks(thread_key, blocks, "📋 Source Selection") + + +def format_script_for_slack_snippet(script_data) -> str: + if isinstance(script_data, dict): + lines = [] + title = script_data.get("title", "Podcast Script") + lines.append(f"PODCAST: {title}") + lines.append("=" * (len(title) + 10)) + lines.append("") + sections = script_data.get("sections", []) + for i, section in enumerate(sections): + section_type = section.get("type", "Unknown").upper() + section_title = section.get("title", "") + if section_title: + lines.append(f"SECTION [{section_type}] {section_title}") + else: + lines.append(f"SECTION [{section_type}]") + lines.append("-" * 50) + lines.append("") + if section.get("dialog"): + for j, dialog in enumerate(section["dialog"]): + speaker = dialog.get("speaker", "SPEAKER") + text = dialog.get("text", "") + lines.append(f"SPEAKER {speaker}:") + if len(text) > 70: + words = text.split() + current_line = " " + for word in words: + if len(current_line + word) > 70: + lines.append(current_line) + current_line = " " + word + else: + current_line += " " + word if current_line != " " else word + if current_line.strip(): + lines.append(current_line) + else: + lines.append(f" {text}") + lines.append("") + if i < len(sections) - 1: + lines.append("") + return "\n".join(lines) + return str(script_data) if script_data else "Script content not available" + + +async def send_script_confirmation_blocks(thread_key: str, state_data: dict, response_text: str): + script = state_data.get("generated_script", {}) + title = script.get("title", "Podcast Script") if isinstance(script, dict) else "Podcast Script" + full_script_text = format_script_for_slack_snippet(script) + if len(full_script_text) > 2500: + full_script_text = full_script_text[:2400] + "\n\n... (script continues)\n\nFull script will be available after approval." + section_count = len(script.get("sections", [])) if isinstance(script, dict) else 0 + dialog_count = 0 + if isinstance(script, dict) and script.get("sections"): + for section in script["sections"]: + dialog_count += len(section.get("dialog", [])) + header_text = f"*📝 Script Review*\n{response_text}\n\n*{title}*\nGenerated {section_count} sections with {dialog_count} dialogue exchanges" + blocks = [ + { + "type": "section", + "text": {"type": "mrkdwn", "text": header_text}, + }, + {"type": "section", "text": {"type": "mrkdwn", "text": f"```{full_script_text}```"}}, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": {"type": "plain_text", "text": "✅ Approve Script"}, + "style": "primary", + "action_id": "approve_script", + "value": thread_key, + }, + { + "type": "button", + "text": {"type": "plain_text", "text": "🔄 Request Changes"}, + "action_id": "request_script_changes", + "value": thread_key, + }, + ], + }, + ] + await send_slack_blocks(thread_key, blocks, "📝 Script Review") + + +async def send_banner_confirmation_blocks(thread_key: str, state_data: dict, response_text: str): + banner_url = state_data.get("banner_url") + banner_images = state_data.get("banner_images", []) + image_url = None + if banner_images: + image_url = f"{API_BASE_URL}/podcast_img/{banner_images[0]}" + elif banner_url: + image_url = f"{API_BASE_URL}/podcast_img/{banner_url}" + blocks = [ + { + "type": "section", + "text": {"type": "mrkdwn", "text": f"*🎨 Banner Review*\n{response_text}"}, + } + ] + if image_url: + blocks.append({"type": "image", "image_url": image_url, "alt_text": "Podcast Banner"}) + if len(banner_images) > 1: + blocks.append( + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": f"_Showing 1 of {len(banner_images)} generated banners_", + } + ], + } + ) + blocks.append( + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": {"type": "plain_text", "text": "✅ Approve Banner"}, + "style": "primary", + "action_id": "approve_banner", + "value": thread_key, + } + ], + } + ) + await send_slack_blocks(thread_key, blocks, "🎨 Banner Review") + + +async def send_audio_confirmation_blocks(thread_key: str, state_data: dict, response_text: str): + audio_url = state_data.get("audio_url") + full_audio_url = f"{API_BASE_URL}/audio/{audio_url}" if audio_url else None + blocks = [ + { + "type": "section", + "text": {"type": "mrkdwn", "text": f"*🎵 Audio Review*\n{response_text}"}, + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Your podcast audio has been generated! 🎧\n\n_Note: Click the download link to listen to your podcast audio._", + }, + }, + ] + action_elements = [] + if full_audio_url: + action_elements.append( + { + "type": "button", + "text": {"type": "plain_text", "text": "⬇️ Download Audio"}, + "url": full_audio_url, + "action_id": "download_audio", + } + ) + action_elements.append( + { + "type": "button", + "text": {"type": "plain_text", "text": "✅ Sounds Great!"}, + "style": "primary", + "action_id": "approve_audio", + "value": thread_key, + } + ) + blocks.append({"type": "actions", "elements": action_elements}) + await send_slack_blocks(thread_key, blocks, f"🎵 Audio Review") + + +async def send_final_presentation_blocks(thread_key: str, state_data: dict, response_text: str): + script = state_data.get("generated_script", {}) + podcast_title = script.get("title") if isinstance(script, dict) else None + if not podcast_title: + podcast_title = state_data.get("podcast_info", {}).get("topic", "Your Podcast") + audio_url = state_data.get("audio_url") + banner_url = state_data.get("banner_url") + banner_images = state_data.get("banner_images", []) + full_audio_url = f"{API_BASE_URL}/audio/{audio_url}" if audio_url else None + full_banner_url = None + if banner_images: + full_banner_url = f"{API_BASE_URL}/podcast_img/{banner_images[0]}" + elif banner_url: + full_banner_url = f"{API_BASE_URL}/podcast_img/{banner_url}" + blocks = [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": f"*🎉 Podcast Complete!*\n{response_text}", + }, + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": f"*{podcast_title}*\n\nYour podcast has been successfully created with all assets! 🎊", + }, + }, + ] + if full_banner_url: + blocks.append( + { + "type": "image", + "image_url": full_banner_url, + "alt_text": f"Banner for {podcast_title}", + } + ) + action_elements = [] + if full_audio_url: + action_elements.append( + { + "type": "button", + "text": {"type": "plain_text", "text": "🎵 Download Audio"}, + "url": full_audio_url, + "action_id": "download_final_audio", + } + ) + action_elements.append( + { + "type": "button", + "text": {"type": "plain_text", "text": "🎙️ Create New Podcast"}, + "style": "primary", + "action_id": "new_podcast", + "value": thread_key, + } + ) + blocks.append({"type": "actions", "elements": action_elements}) + await send_slack_blocks(thread_key, blocks, "🎉 Podcast Complete!") + + +async def send_slack_blocks(thread_key: str, blocks: list, fallback_text: str = "Interactive elements loaded"): + try: + session_info = get_session_info(thread_key) + if not session_info: + print(f"No session info found for thread: {thread_key}") + return + session_id, channel_id, user_id = session_info + if thread_key.startswith("dm_"): + app.client.chat_postMessage(channel=channel_id, blocks=blocks, text=fallback_text) + else: + app.client.chat_postMessage( + channel=channel_id, + blocks=blocks, + text=fallback_text, + thread_ts=thread_key, + ) + print(f"Sent interactive blocks to {thread_key}") + except Exception as e: + print(f"Error sending Slack blocks: {e}") + await send_slack_message( + thread_key, + "Interactive elements failed to load. Please continue with text responses.", + ) + + +async def send_slack_message(thread_key: str, text: str): + try: + session_info = get_session_info(thread_key) + if not session_info: + print(f"No session info found for thread: {thread_key}") + return + session_id, channel_id, user_id = session_info + if len(text) > 3800: + chunks = [text[i : i + 3800] for i in range(0, len(text), 3800)] + for i, chunk in enumerate(chunks): + if i == 0: + if thread_key.startswith("dm_"): + app.client.chat_postMessage(channel=channel_id, text=chunk) + else: + app.client.chat_postMessage(channel=channel_id, text=chunk, thread_ts=thread_key) + else: + if thread_key.startswith("dm_"): + app.client.chat_postMessage(channel=channel_id, text=f"...continued:\n{chunk}") + else: + app.client.chat_postMessage( + channel=channel_id, + text=f"...continued:\n{chunk}", + thread_ts=thread_key, + ) + else: + if thread_key.startswith("dm_"): + app.client.chat_postMessage(channel=channel_id, text=text) + else: + app.client.chat_postMessage(channel=channel_id, text=text, thread_ts=thread_key) + print(f"Sent message to {thread_key}: {text[:50]}...") + except Exception as e: + print(f"Error sending Slack message: {e}") + + +def clean_text(text, bot_id): + text = re.sub(f"<@{bot_id}>", "", text).strip() + return text + + +def format_script_for_slack(script_data) -> List[str]: + if isinstance(script_data, dict): + chunks = [] + current_chunk = "" + title = script_data.get("title", "Podcast Script") + current_chunk += f"*{title}*\n\n" + sections = script_data.get("sections", []) + for i, section in enumerate(sections): + section_text = f"*Section {i + 1}: {section.get('type', 'Unknown').title()}*" + if section.get("title"): + section_text += f" - {section['title']}" + section_text += "\n\n" + if section.get("dialog"): + for dialog in section["dialog"]: + speaker = dialog.get("speaker", "Speaker") + text = dialog.get("text", "") + dialog_text = f"*{speaker}:* {text}\n\n" + if len(current_chunk + section_text + dialog_text) > 3500: + if current_chunk.strip(): + chunks.append(current_chunk.strip()) + current_chunk = section_text + dialog_text + else: + current_chunk += section_text + dialog_text + section_text = "" + else: + current_chunk += section_text + current_chunk += "\n---\n\n" + if current_chunk.strip(): + chunks.append(current_chunk.strip()) + return chunks if chunks else ["Script content could not be formatted."] + elif isinstance(script_data, str): + try: + parsed_data = json.loads(script_data) + if isinstance(parsed_data, dict): + return format_script_for_slack(parsed_data) + except (json.JSONDecodeError, TypeError): + pass + text = script_data + if len(text) <= 3500: + return [text] + else: + return [text[i : i + 3500] for i in range(0, len(text), 3500)] + else: + try: + text = str(script_data) + if len(text) <= 3500: + return [text] + else: + return [text[i : i + 3500] for i in range(0, len(text), 3500)] + except Exception as e: + print(f"Error converting script data to string: {e}") + return ["Error: Could not format script data for display."] + + +@app.action("source_selection") +def handle_source_selection(ack, body, logger): + ack() + + +@app.action("language_selection") +def handle_language_selection(ack, body, logger): + ack() + + +@app.action("confirm_sources") +def handle_confirm_sources(ack, body, client): + ack() + + def process_confirmation(): + try: + thread_key = body["actions"][0]["value"] + user_id = body["user"]["id"] + selected_sources = [] + selected_language = "en" + if "state" in body and "values" in body["state"]: + values = body["state"]["values"] + if "source_selection_block" in values and "source_selection" in values["source_selection_block"]: + source_data = values["source_selection_block"]["source_selection"] + if "selected_options" in source_data and source_data["selected_options"]: + selected_sources = [int(opt["value"]) for opt in source_data["selected_options"]] + if "language_selection_block" in values and "language_selection" in values["language_selection_block"]: + lang_data = values["language_selection_block"]["language_selection"] + if "selected_option" in lang_data and lang_data["selected_option"]: + selected_language = lang_data["selected_option"]["value"] + session_info = get_session_info(thread_key) + if not session_info: + client.chat_postMessage( + channel=body["channel"]["id"], + thread_ts=thread_key if not thread_key.startswith("dm_") else None, + text="❌ Session not found. Please start a new conversation.", + ) + return + session_id = session_info[0] + state_data = get_session_state(session_id) + languages = state_data.get("available_languages", [{"code": "en", "name": "English"}]) + language_name = next( + (lang["name"] for lang in languages if lang["code"] == selected_language), + "English", + ) + sources = state_data.get("search_results", []) + if selected_sources: + source_indices = [str(i + 1) for i in selected_sources] + selected_source_titles = [sources[i].get("title", f"Source {i + 1}") for i in selected_sources if i < len(sources)] + message = f"I've selected sources {', '.join(source_indices)} and I want the podcast in {language_name}." + else: + source_indices = [str(i + 1) for i in range(len(sources))] + selected_source_titles = [source.get("title", f"Source {i + 1}") for i, source in enumerate(sources)] + message = f"I want the podcast in {language_name} using all available sources." + try: + confirmation_blocks = create_confirmation_blocks( + selected_sources, + selected_source_titles, + language_name, + len(sources), + ) + client.chat_update( + channel=body["channel"]["id"], + ts=body["message"]["ts"], + blocks=confirmation_blocks, + text="✅ Selection Confirmed", + ) + print(f"Updated interactive message to confirmation state for {thread_key}") + except Exception as e: + print(f"Error updating message: {e}") + client.chat_postMessage( + channel=body["channel"]["id"], + thread_ts=thread_key if not thread_key.startswith("dm_") else None, + text=f"🔄 Processing your selection: {message}\n\n_Generating podcast script..._", + ) + asyncio.run(process_source_confirmation(thread_key, message)) + except Exception as e: + print(f"Error in confirm_sources: {e}") + client.chat_postMessage( + channel=body["channel"]["id"], + thread_ts=thread_key if not thread_key.startswith("dm_") else None, + text="❌ Error processing your selection. Please try again.", + ) + + executor.submit(process_confirmation) + + +def create_confirmation_blocks(selected_sources, selected_source_titles, language_name, total_sources): + if selected_sources: + source_text = "" + for i, (idx, title) in enumerate(zip(selected_sources, selected_source_titles)): + if i < 3: + short_title = title[:50] + "..." if len(title) > 50 else title + source_text += f"• *{idx}.* {short_title}\n" + elif i == 3: + remaining = len(selected_sources) - 3 + source_text += f"• _...and {remaining} more sources_\n" + break + source_summary = f"*Selected {len(selected_sources)} of {total_sources} sources:*\n{source_text}" + else: + source_summary = f"*Selected all {total_sources} sources*" + blocks = [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*✅ Selection Confirmed*\n_Your preferences have been saved and processing has started._", + }, + }, + {"type": "section", "text": {"type": "mrkdwn", "text": source_summary}}, + { + "type": "section", + "text": {"type": "mrkdwn", "text": f"*Language:* {language_name} 🌐"}, + }, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": f"_Confirmed at {datetime.now().strftime('%H:%M')} • Processing script generation..._", + } + ], + }, + ] + return blocks + + +async def process_source_confirmation(thread_key: str, message: str): + try: + session_info = get_session_info(thread_key) + if not session_info: + return + session_id = session_info[0] + chat_response = await api_client.chat(session_id, message) + if chat_response.get("is_processing"): + task_id = chat_response.get("task_id") + start_background_polling(session_id, thread_key, task_id) + else: + response_text = chat_response.get("response", "Selection processed!") + await send_slack_message(thread_key, response_text) + except Exception as e: + print(f"Error processing source confirmation: {e}") + await send_slack_message(thread_key, "❌ Error processing your selection. Please try again.") + + +@app.action("request_script_changes") +def handle_request_script_changes(ack, body, client): + ack() + + def process_request(): + try: + thread_key = body["actions"][0]["value"] + change_blocks = [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*🔄 Changes Requested*\n_Please describe what changes you'd like to make to the script._", + }, + } + ] + try: + client.chat_update( + channel=body["channel"]["id"], + ts=body["message"]["ts"], + blocks=change_blocks, + text="Changes Requested", + ) + except Exception as e: + print(f"Error updating script message: {e}") + client.chat_postMessage( + channel=body["channel"]["id"], + thread_ts=thread_key if not thread_key.startswith("dm_") else None, + text="What specific changes would you like me to make to the script? For example:\n• Adjust the tone or style\n• Add more detail on certain topics\n• Change the dialogue flow\n• Modify the structure", + ) + except Exception as e: + print(f"Error in request_script_changes: {e}") + + executor.submit(process_request) + + +@app.action("approve_script") +def handle_approve_script(ack, body, client): + ack() + + def process_approval(): + try: + thread_key = body["actions"][0]["value"] + approval_blocks = [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*✅ Script Approved*\n_Script has been approved and banner generation is starting._", + }, + }, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": f"_Approved at {datetime.now().strftime('%H:%M')} • Processing banner generation..._", + } + ], + }, + ] + try: + client.chat_update( + channel=body["channel"]["id"], + ts=body["message"]["ts"], + blocks=approval_blocks, + text="✅ Script Approved", + ) + except Exception as e: + print(f"Error updating script message: {e}") + client.chat_postMessage( + channel=body["channel"]["id"], + thread_ts=thread_key if not thread_key.startswith("dm_") else None, + text="🔄 Script approved! Generating banner images...", + ) + asyncio.run(process_approval_action(body, "I approve this script. It looks good!")) + except Exception as e: + print(f"Error in approve_script: {e}") + + executor.submit(process_approval) + + +@app.action("approve_banner") +def handle_approve_banner(ack, body, client): + ack() + + def process_approval(): + try: + thread_key = body["actions"][0]["value"] + approval_blocks = [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*✅ Banner Approved*\n_Banner has been approved and audio generation is starting._", + }, + }, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": f"_Approved at {datetime.now().strftime('%H:%M')} • Processing audio generation..._", + } + ], + }, + ] + try: + client.chat_update( + channel=body["channel"]["id"], + ts=body["message"]["ts"], + blocks=approval_blocks, + text="✅ Banner Approved", + ) + except Exception as e: + print(f"Error updating banner message: {e}") + client.chat_postMessage( + channel=body["channel"]["id"], + thread_ts=thread_key if not thread_key.startswith("dm_") else None, + text="🔄 Banner approved! Generating podcast audio...", + ) + asyncio.run(process_approval_action(body, "I approve this banner. It looks good!")) + except Exception as e: + print(f"Error in approve_banner: {e}") + + executor.submit(process_approval) + + +@app.action("approve_audio") +def handle_approve_audio(ack, body, client): + ack() + + def process_approval(): + try: + thread_key = body["actions"][0]["value"] + approval_blocks = [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*✅ Audio Approved*\n_Audio has been approved and your podcast is being finalized._", + }, + }, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": f"_Approved at {datetime.now().strftime('%H:%M')} • Finalizing podcast..._", + } + ], + }, + ] + try: + client.chat_update( + channel=body["channel"]["id"], + ts=body["message"]["ts"], + blocks=approval_blocks, + text="✅ Audio Approved", + ) + except Exception as e: + print(f"Error updating audio message: {e}") + client.chat_postMessage( + channel=body["channel"]["id"], + thread_ts=thread_key if not thread_key.startswith("dm_") else None, + text="🔄 Audio approved! Finalizing your podcast...", + ) + asyncio.run(process_approval_action(body, "The audio sounds great! I'm happy with the final podcast.")) + except Exception as e: + print(f"Error in approve_audio: {e}") + + executor.submit(process_approval) + + +@app.action("new_podcast") +def handle_new_podcast(ack, body, client): + ack() + + def start_new(): + try: + old_thread_key = body["actions"][0]["value"] + channel_id = body["channel"]["id"] + user_id = body["user"]["id"] + import time + + new_thread_key = f"new_{channel_id}_{user_id}_{int(time.time())}" + client.chat_postMessage( + channel=channel_id, + text="🎙️ *Welcome to AI Podcast Studio!*\n\nI'll help you create a professional podcast from your trusted sources. What topic would you like to create a podcast about?", + ) + print(f"Started new podcast conversation: {new_thread_key}") + except Exception as e: + print(f"Error starting new podcast: {e}") + client.chat_postMessage( + channel=body["channel"]["id"], + text="❌ Error starting new podcast. Please try sending a new message.", + ) + + executor.submit(start_new) + + +async def process_approval_action(body, approval_message: str): + try: + thread_key = body["actions"][0]["value"] + app.client.chat_postMessage( + channel=body["channel"]["id"], + thread_ts=thread_key if not thread_key.startswith("dm_") else None, + text=f"✅ {approval_message}\n🔄 Processing next step...", + ) + session_info = get_session_info(thread_key) + if not session_info: + await send_slack_message(thread_key, "❌ Session not found.") + return + session_id = session_info[0] + chat_response = await api_client.chat(session_id, approval_message) + if chat_response.get("is_processing"): + task_id = chat_response.get("task_id") + start_background_polling(session_id, thread_key, task_id) + else: + response_text = chat_response.get("response", "Approved! Processing next step...") + await send_slack_message(thread_key, response_text) + except Exception as e: + print(f"Error processing approval: {e}") + await send_slack_message(thread_key, "❌ Error processing approval. Please try again.") + + +@app.event("app_mention") +def handle_app_mention(event, say, client): + bot_info = client.auth_test() + bot_id = bot_info["user_id"] + user_input = clean_text(event["text"], bot_id) + thread_key = event["ts"] + channel_id = event["channel"] + user_id = event["user"] + + def handle_async(): + asyncio.run(handle_user_message(thread_key, user_input, say, channel_id, user_id, is_mention=True)) + + executor.submit(handle_async) + + +@app.message("") +def handle_message(message, say, client): + if message.get("bot_id"): + return + if message.get("text", "").startswith("<@"): + return + user_input = message["text"] + channel_type = message.get("channel_type", "") + is_dm = channel_type == "im" + channel_id = message["channel"] + user_id = message["user"] + thread_key = get_thread_key(message, is_dm) + + def handle_async(): + asyncio.run(handle_user_message(thread_key, user_input, say, channel_id, user_id, is_dm=is_dm)) + + executor.submit(handle_async) + + +async def handle_user_message( + thread_key: str, + user_input: str, + say, + channel_id: str, + user_id: str, + is_mention=False, + is_dm=False, +): + try: + session_id = await get_or_create_session(thread_key, channel_id, user_id) + session_state = get_session_state(session_id) + if session_state.get("podcast_generated") and session_state.get("stage") == "complete": + script = session_state.get("generated_script", {}) + podcast_title = script.get("title") if isinstance(script, dict) else "Your Podcast" + podcast_id = session_state.get("podcast_id", "") + completion_queries = ["download", "script", "audio", "banner", "share", "link", "asset", "file"] + is_asset_query = any(query in user_input.lower() for query in completion_queries) + if is_asset_query: + completion_message = ( + f"🎉 *'{podcast_title}' is complete!*\n\n" + "💡 *Looking for your podcast assets?*\n" + "All download links and assets were provided in the completion message above. " + "Please scroll up to find:\n" + "• Audio download link\n" + "• Banner images\n" + "• Complete script\n" + f"• Podcast ID: `{podcast_id}`\n\n" + "To create a **new podcast**, please start a fresh chat with me. 🎙️" + ) + else: + completion_message = ( + f"🎉 *'{podcast_title}' is complete!*\n\n" + "This podcast session has finished successfully. To create a new podcast:\n\n" + "• **Start a new chat** with me\n" + "• Each podcast needs a fresh conversation\n" + "• Your completed podcast assets remain available above\n\n" + "Ready to create another amazing podcast? 🎙️✨" + ) + if not is_dm and not is_mention: + say(text=completion_message, thread_ts=thread_key) + else: + say(text=completion_message) + print(f"Session {session_id} is complete - prevented API call for: {user_input[:50]}...") + return + if session_id in active_sessions: + active_session = active_sessions[session_id] + start_time = active_session.get("start_time", datetime.now()) + elapsed_minutes = (datetime.now() - start_time).total_seconds() / 60 + current_stage = session_state.get("stage", "unknown") + process_type = active_session.get("process_type", "your request") + stage_messages = { + "search": "🔍 Searching for relevant sources", + "scraping": "📰 Gathering full content from sources", + "script": "📝 Generating podcast script", + "banner": "🎨 Creating banner images", + "image": "🎨 Creating banner images", + "audio": "🎵 Generating podcast audio", + } + stage_message = stage_messages.get(current_stage, f"🔄 Processing {process_type}") + progress_message = ( + f"⏳ *Still working on your podcast...*\n\n" + f"{stage_message}\n" + f"⏱️ Running for {elapsed_minutes:.1f} minutes\n\n" + f"_Please wait while I complete this step. This can take several minutes for high-quality results._" + ) + if current_stage == "search": + progress_message += "\n\n💡 *Currently:* Finding the best sources across multiple platforms" + elif current_stage == "script": + progress_message += "\n\n💡 *Currently:* Crafting engaging dialogue and content structure" + elif current_stage in ["banner", "image"]: + progress_message += "\n\n💡 *Currently:* Generating professional banner designs" + elif current_stage == "audio": + progress_message += "\n\n💡 *Currently:* Creating high-quality voice narration" + if not is_dm and not is_mention: + say(text=progress_message, thread_ts=thread_key) + else: + say(text=progress_message) + print(f"Session {session_id} already processing ({current_stage}) - prevented API call for: {user_input[:50]}...") + return + print(f"Processing message for session {session_id}: {user_input[:50]}...") + chat_response = await api_client.chat(session_id, user_input) + if chat_response.get("response"): + response_text = chat_response["response"] + if not is_dm and not is_mention: + say(text=response_text, thread_ts=thread_key) + else: + say(text=response_text) + if chat_response.get("is_processing"): + task_id = chat_response.get("task_id") + start_background_polling(session_id, thread_key, task_id) + processing_msg = "🔄 Processing your request... This may take a moment." + if not is_dm and not is_mention: + say(text=processing_msg, thread_ts=thread_key) + else: + say(text=processing_msg) + else: + await send_completion_message(thread_key, chat_response) + except Exception as e: + print(f"Error handling message: {e}") + if "timeout" in str(e).lower(): + error_msg = "⏱️ Request timed out. The system might be busy. Please try again in a moment." + elif "connection" in str(e).lower(): + error_msg = "🔌 Connection issue. Please check your connection and try again." + else: + error_msg = "❌ Sorry, I encountered an error processing your request. Please try again." + if not is_dm and not is_mention: + say(text=error_msg, thread_ts=thread_key) + else: + say(text=error_msg) + + +init_db() + +if __name__ == "__main__": + handler = SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]) + print("⚡️ Podcast Bot is running! Press Ctrl+C to stop.") + print(f"🎙️ Connected to API at: {API_BASE_URL}") + handler.start() \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/main.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/main.py new file mode 100644 index 0000000..130b62b --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/main.py @@ -0,0 +1,187 @@ +from fastapi import FastAPI, Request, Response +from fastapi.middleware.cors import CORSMiddleware +from fastapi.staticfiles import StaticFiles +from fastapi.responses import FileResponse, StreamingResponse +import uvicorn +import os +import aiofiles +from contextlib import asynccontextmanager +from routers import article_router, podcast_router, source_router, task_router, podcast_config_router, async_podcast_agent_router, social_media_router +from services.db_init import init_databases +from dotenv import load_dotenv + + +load_dotenv() + +CLIENT_BUILD_PATH = os.environ.get( + "CLIENT_BUILD_PATH", + "../web/build", +) + + +@asynccontextmanager +async def lifespan(app: FastAPI): + print("Starting up application...") + os.makedirs("databases", exist_ok=True) + os.makedirs("browsers", exist_ok=True) + os.makedirs("podcasts/audio", exist_ok=True) + os.makedirs("podcasts/images", exist_ok=True) + os.makedirs("podcasts/recordings", exist_ok=True) + await init_databases() + if not os.path.exists(CLIENT_BUILD_PATH): + print(f"WARNING: React client build path not found: {CLIENT_BUILD_PATH}") + print("Application startup complete!") + yield + print("Shutting down application...") + print("Shutdown complete") + + +app = FastAPI(title="Beifong API", description="Beifong API", version="1.0.0", lifespan=lifespan) + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +app.include_router(article_router.router, prefix="/api/articles", tags=["articles"]) +app.include_router(source_router.router, prefix="/api/sources", tags=["sources"]) +app.include_router(podcast_router.router, prefix="/api/podcasts", tags=["podcasts"]) +app.include_router(task_router.router, prefix="/api/tasks", tags=["tasks"]) +app.include_router(podcast_config_router.router, prefix="/api/podcast-configs", tags=["podcast-configs"]) +app.include_router(async_podcast_agent_router.router, prefix="/api/podcast-agent", tags=["podcast-agent"]) +app.include_router(social_media_router.router, prefix="/api/social-media", tags=["social-media"]) + + +@app.get("/stream-audio/{filename}") +async def stream_audio(filename: str, request: Request): + audio_path = os.path.join("podcasts/audio", filename) + if not os.path.exists(audio_path): + return Response(status_code=404, content="Audio file not found") + file_size = os.path.getsize(audio_path) + range_header = request.headers.get("Range", "").strip() + start = 0 + end = file_size - 1 + if range_header: + try: + range_data = range_header.replace("bytes=", "").split("-") + start = int(range_data[0]) if range_data[0] else 0 + end = int(range_data[1]) if len(range_data) > 1 and range_data[1] else file_size - 1 + except ValueError: + return Response(status_code=400, content="Invalid range header") + end = min(end, file_size - 1) + content_length = end - start + 1 + headers = { + "Accept-Ranges": "bytes", + "Content-Range": f"bytes {start}-{end}/{file_size}", + "Content-Length": str(content_length), + "Content-Disposition": f"inline; filename={filename}", + "Content-Type": "audio/wav", + } + + async def file_streamer(): + async with aiofiles.open(audio_path, "rb") as f: + await f.seek(start) + remaining = content_length + chunk_size = 64 * 1024 + while remaining > 0: + chunk = await f.read(min(chunk_size, remaining)) + if not chunk: + break + remaining -= len(chunk) + yield chunk + + status_code = 206 if range_header else 200 + return StreamingResponse(file_streamer(), status_code=status_code, headers=headers) + + +@app.get("/stream-recording/{session_id}/{filename}") +async def stream_recording(session_id: str, filename: str, request: Request): + recording_path = os.path.join("podcasts/recordings", session_id, filename) + if not os.path.exists(recording_path): + return Response(status_code=404, content="Recording video not found") + file_size = os.path.getsize(recording_path) + range_header = request.headers.get("Range", "").strip() + start = 0 + end = file_size - 1 + if range_header: + try: + range_data = range_header.replace("bytes=", "").split("-") + start = int(range_data[0]) if range_data[0] else 0 + end = int(range_data[1]) if len(range_data) > 1 and range_data[1] else file_size - 1 + except ValueError: + return Response(status_code=400, content="Invalid range header") + end = min(end, file_size - 1) + content_length = end - start + 1 + headers = { + "Accept-Ranges": "bytes", + "Content-Range": f"bytes {start}-{end}/{file_size}", + "Content-Length": str(content_length), + "Content-Disposition": f"inline; filename={filename}", + "Content-Type": "video/webm", + } + + async def file_streamer(): + async with aiofiles.open(recording_path, "rb") as f: + await f.seek(start) + remaining = content_length + chunk_size = 64 * 1024 + while remaining > 0: + chunk = await f.read(min(chunk_size, remaining)) + if not chunk: + break + remaining -= len(chunk) + yield chunk + + status_code = 206 if range_header else 200 + return StreamingResponse(file_streamer(), status_code=status_code, headers=headers) + + +app.mount("/audio", StaticFiles(directory="podcasts/audio"), name="audio") +app.mount("/server_static", StaticFiles(directory="static"), name="server_static") +app.mount("/podcast_img", StaticFiles(directory="podcasts/images"), name="podcast_img") +if os.path.exists(os.path.join(CLIENT_BUILD_PATH, "static")): + app.mount("/static", StaticFiles(directory=os.path.join(CLIENT_BUILD_PATH, "static")), name="react_static") + + +@app.get("/favicon.ico") +async def favicon(): + favicon_path = os.path.join(CLIENT_BUILD_PATH, "favicon.ico") + if os.path.exists(favicon_path): + return FileResponse(favicon_path) + return {"detail": "Favicon not found"} + + +@app.get("/manifest.json") +async def manifest(): + manifest_path = os.path.join(CLIENT_BUILD_PATH, "manifest.json") + if os.path.exists(manifest_path): + return FileResponse(manifest_path) + return {"detail": "Manifest not found"} + + +@app.get("/logo{rest_of_path:path}") +async def logo(rest_of_path: str): + logo_path = os.path.join(CLIENT_BUILD_PATH, f"logo{rest_of_path}") + if os.path.exists(logo_path): + return FileResponse(logo_path) + return {"detail": "Logo not found"} + + +@app.get("/{full_path:path}") +async def serve_react(full_path: str, request: Request): + if full_path.startswith("api/") or request.url.path.startswith("/api/"): + return {"detail": "Not Found"} + index_path = os.path.join(CLIENT_BUILD_PATH, "index.html") + if os.path.exists(index_path): + return FileResponse(index_path) + else: + return {"detail": "React client not found. Build the client or set the correct CLIENT_BUILD_PATH."} + + +if __name__ == "__main__": + port = int(os.environ.get("PORT", 7000)) + uvicorn.run("main:app", host="0.0.0.0", port=port, reload=False, timeout_keep_alive=120, timeout_graceful_shutdown=120) \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/__init__.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/article_schemas.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/article_schemas.py new file mode 100644 index 0000000..6de8f20 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/article_schemas.py @@ -0,0 +1,29 @@ +from pydantic import BaseModel, ConfigDict +from typing import Optional, List, Dict, Any + + +class ArticleBase(BaseModel): + title: str + url: Optional[str] = None + published_date: str + summary: Optional[str] = None + content: Optional[str] = None + categories: Optional[List[str]] = [] + source_name: Optional[str] = None + + +class Article(ArticleBase): + id: int + metadata: Optional[Dict[str, Any]] = {} + + model_config = ConfigDict(from_attributes=True) + + +class PaginatedArticles(BaseModel): + items: List[Article] + total: int + page: int + per_page: int + total_pages: int + has_next: bool + has_prev: bool \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/podcast_config_schemas.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/podcast_config_schemas.py new file mode 100644 index 0000000..0e8fc1e --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/podcast_config_schemas.py @@ -0,0 +1,41 @@ +from pydantic import BaseModel, Field +from typing import Optional + + +class PodcastConfigBase(BaseModel): + name: str + prompt: str + description: Optional[str] = None + time_range_hours: int = Field(24, ge=1, le=168) + limit_articles: int = Field(20, ge=5, le=50) + is_active: bool = True + tts_engine: str = "kokoro" + language_code: str = "en" + podcast_script_prompt: Optional[str] = None + image_prompt: Optional[str] = None + + +class PodcastConfig(PodcastConfigBase): + id: int + created_at: Optional[str] = None + updated_at: Optional[str] = None + + class Config: + from_attributes = True + + +class PodcastConfigCreate(PodcastConfigBase): + pass + + +class PodcastConfigUpdate(BaseModel): + name: Optional[str] = None + prompt: Optional[str] = None + description: Optional[str] = None + time_range_hours: Optional[int] = Field(None, ge=1, le=168) + limit_articles: Optional[int] = Field(None, ge=5, le=50) + is_active: Optional[bool] = None + tts_engine: Optional[str] = None + language_code: Optional[str] = None + podcast_script_prompt: Optional[str] = None + image_prompt: Optional[str] = None diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/podcast_schemas.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/podcast_schemas.py new file mode 100644 index 0000000..a372d8e --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/podcast_schemas.py @@ -0,0 +1,81 @@ +from pydantic import BaseModel +from typing import Optional, List, Dict, Any, Union + + +class PodcastBase(BaseModel): + title: str + date: str + audio_generated: bool = False + banner_img: Optional[str] = None + identifier: str + language_code: Optional[str] = "en" + tts_engine: Optional[str] = "kokoro" + + +class Podcast(PodcastBase): + id: int + created_at: Optional[str] = None + audio_path: Optional[str] = None + + class Config: + from_attributes = True + + +class PodcastContent(BaseModel): + title: str + sections: List[Dict[str, Any]] + + +class PodcastSource(BaseModel): + title: Optional[str] = None + url: Optional[str] = None + source: Optional[str] = None + + @classmethod + def __get_validators__(cls): + yield cls.validate + + @classmethod + def validate(cls, v): + if isinstance(v, str): + return cls(url=v) + if isinstance(v, dict): + return cls(**v) + raise ValueError("Source must be a string or a dict") + + +class PodcastDetail(BaseModel): + podcast: Podcast + content: PodcastContent + audio_url: Optional[str] = None + sources: Optional[List[Union[PodcastSource, str]]] = None + banner_images: Optional[List[str]] = None + + +class PodcastCreate(BaseModel): + title: str + date: Optional[str] = None + content: Dict[str, Any] + sources: Optional[List[Union[Dict[str, str], str]]] = None + language_code: Optional[str] = "en" + tts_engine: Optional[str] = "kokoro" + + +class PodcastUpdate(BaseModel): + title: Optional[str] = None + date: Optional[str] = None + content: Optional[Dict[str, Any]] = None + audio_generated: Optional[bool] = None + sources: Optional[List[Union[Dict[str, str], str]]] = None + language_code: Optional[str] = None + tts_engine: Optional[str] = None + + +class PaginatedPodcasts(BaseModel): + items: List[Podcast] + total: int + page: int + per_page: int + total_pages: int + has_next: bool + has_prev: bool diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/schemas.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/schemas.py new file mode 100644 index 0000000..e69de29 diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/social_media_schemas.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/social_media_schemas.py new file mode 100644 index 0000000..67c3b08 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/social_media_schemas.py @@ -0,0 +1,56 @@ +from typing import List, Optional +from pydantic import BaseModel +from datetime import datetime + +class PostBase(BaseModel): + post_id: str + platform: str + user_display_name: Optional[str] = None + user_handle: Optional[str] = None + user_profile_pic_url: Optional[str] = None + post_timestamp: Optional[str] = None + post_display_time: Optional[str] = None + post_url: Optional[str] = None + post_text: Optional[str] = None + post_mentions: Optional[str] = None + +class PostEngagement(BaseModel): + replies: Optional[int] = None + retweets: Optional[int] = None + likes: Optional[int] = None + bookmarks: Optional[int] = None + views: Optional[int] = None + +class MediaItem(BaseModel): + type: str + url: str + +class Post(PostBase): + engagement: Optional[PostEngagement] = None + media: Optional[List[MediaItem]] = None + media_count: Optional[int] = 0 + is_ad: Optional[bool] = False + sentiment: Optional[str] = None + categories: Optional[List[str]] = None + tags: Optional[List[str]] = None + analysis_reasoning: Optional[str] = None + created_at: Optional[datetime] = None + updated_at: Optional[datetime] = None + +class PaginatedPosts(BaseModel): + items: List[Post] + total: int + page: int + per_page: int + total_pages: int + has_next: bool + has_prev: bool + +class PostFilterParams(BaseModel): + platform: Optional[str] = None + user_handle: Optional[str] = None + sentiment: Optional[str] = None + category: Optional[str] = None + date_from: Optional[str] = None + date_to: Optional[str] = None + search: Optional[str] = None \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/source_schemas.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/source_schemas.py new file mode 100644 index 0000000..e36af08 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/source_schemas.py @@ -0,0 +1,68 @@ +from pydantic import BaseModel +from typing import Optional, List + + +class SourceFeed(BaseModel): + id: int + feed_url: str + feed_type: str + description: Optional[str] = None + is_active: bool + created_at: str + last_crawled: Optional[str] = None + + +class SourceFeedCreate(BaseModel): + feed_url: str + feed_type: str = "main" + description: Optional[str] = None + is_active: bool = True + + +class SourceBase(BaseModel): + name: str + url: Optional[str] = None + categories: Optional[List[str]] = [] + description: Optional[str] = None + is_active: bool = True + + +class Source(SourceBase): + id: int + created_at: Optional[str] = None + last_crawled: Optional[str] = None + + class Config: + from_attributes = True + + +class SourceCreate(SourceBase): + feeds: Optional[List[SourceFeedCreate]] = [] + + +class SourceUpdate(BaseModel): + name: Optional[str] = None + url: Optional[str] = None + categories: Optional[List[str]] = None + description: Optional[str] = None + is_active: Optional[bool] = None + + +class SourceWithFeeds(Source): + feeds: List[SourceFeed] = [] + + +class PaginatedSources(BaseModel): + items: List[Source] + total: int + page: int + per_page: int + total_pages: int + has_next: bool + has_prev: bool + + +class Category(BaseModel): + id: int + name: str + description: Optional[str] = None diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/tasks_schemas.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/tasks_schemas.py new file mode 100644 index 0000000..dbf03fb --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/models/tasks_schemas.py @@ -0,0 +1,118 @@ +from pydantic import BaseModel, validator +from typing import Optional, List, Dict, Any +from enum import Enum + + +class TaskType(str, Enum): + feed_processor = "feed_processor" + url_crawler = "url_crawler" + ai_analyzer = "ai_analyzer" + podcast_generator = "podcast_generator" + embedding_processor = "embedding_processor" + faiss_indexer = "faiss_indexer" + social_x_scraper = "social_x_scraper" + social_fb_scraper = "social_fb_scraper" + + +TASK_TYPES = { + "feed_processor": { + "name": "Feed Processor", + "command": "python -m processors.feed_processor", + "description": "Processes RSS feeds and stores new entries", + }, + "url_crawler": {"name": "URL Crawler", "command": "python -m processors.url_processor", "description": "Crawls URLs and extracts content"}, + "ai_analyzer": { + "name": "AI Analyzer", + "command": "python -m processors.ai_analysis_processor", + "description": "Analyzes article content using AI", + }, + "podcast_generator": { + "name": "Podcast Generator", + "command": "python -m processors.podcast_generator_processor", + "description": "Generates podcasts from articles", + }, + "embedding_processor": { + "name": "Embedding Processor", + "command": "python -m processors.embedding_processor", + "description": "Generates embeddings for processed articles using OpenAI", + }, + "faiss_indexer": { + "name": "FAISS Indexer", + "command": "python -m processors.faiss_indexing_processor", + "description": "Updates FAISS vector index with new article embeddings", + }, + "social_x_scraper": { + "name": "X.com Scraper", + "command": "python -m processors.x_scraper_processor", + "description": "Scrapes X.com profiles and analyzes sentiment", + }, + "social_fb_scraper": { + "name": "Facebook.com Scraper", + "command": "python -m processors.fb_scraper_processor", + "description": "Scrapes Facebook.com profiles and analyzes sentiment", + }, +} + + +class TaskBase(BaseModel): + name: str + task_type: TaskType + frequency: int + frequency_unit: str + description: Optional[str] = None + enabled: bool = True + + @validator("task_type") + def set_command_from_type(cls, v): + if v not in TASK_TYPES: + raise ValueError(f"Invalid task type: {v}") + return v + + +class Task(TaskBase): + id: int + command: str + last_run: Optional[str] = None + created_at: Optional[str] = None + + class Config: + from_attributes = True + + +class TaskCreate(TaskBase): + pass + + +class TaskUpdate(BaseModel): + name: Optional[str] = None + task_type: Optional[TaskType] = None + frequency: Optional[int] = None + frequency_unit: Optional[str] = None + description: Optional[str] = None + enabled: Optional[bool] = None + + +class TaskExecution(BaseModel): + id: int + task_id: int + task_name: Optional[str] = None + start_time: str + end_time: Optional[str] = None + status: str + error_message: Optional[str] = None + output: Optional[str] = None + + +class PaginatedTaskExecutions(BaseModel): + items: List[TaskExecution] + total: int + page: int + per_page: int + total_pages: int + has_next: bool + has_prev: bool + + +class TaskStats(BaseModel): + tasks: Dict[str, int] + executions: Dict[str, Any] diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/pack_demo.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/pack_demo.py new file mode 100644 index 0000000..4ab64b1 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/pack_demo.py @@ -0,0 +1,25 @@ +import os +import zipfile + +SOURCE_DIRS = ["databases", "podcasts"] +OUTPUT_ZIP = "demo_content.zip" + + +def create_zip(source_dirs, output_zip): + print("packing.....") + """zip up each source directory into a single archive.""" + with zipfile.ZipFile(output_zip, "w", zipfile.ZIP_DEFLATED) as z: + for src in source_dirs: + if not os.path.isdir(src): + print(f"✗ source '{src}' not found, skipping.") + continue + for root, _, files in os.walk(src): + for file in files: + full_path = os.path.join(root, file) + arcname = os.path.relpath(full_path, os.getcwd()) + z.write(full_path, arcname) + print(f"✓ created '{output_zip}' containing: {', '.join(source_dirs)}") + + +if __name__ == "__main__": + create_zip(SOURCE_DIRS, OUTPUT_ZIP) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/__init__.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/ai_analysis_processor.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/ai_analysis_processor.py new file mode 100644 index 0000000..1d44d5d --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/ai_analysis_processor.py @@ -0,0 +1,188 @@ +import json +import time +import random +import argparse +from bs4 import BeautifulSoup +from openai import OpenAI +from db.config import get_tracking_db_path +from db.articles import get_unprocessed_articles, update_article_status +from utils.load_api_keys import load_api_key + +WEB_PAGE_ANALYSE_MODEL = "gpt-4o" +MODEL_INSTRUCTION = "You are a helpful assistant that analyzes articles and extracts structured information." + + +def extract_clean_text(raw_html, max_tokens=8000): + soup = BeautifulSoup(raw_html, "html.parser") + for element in soup(["script", "style", "nav", "header", "footer", "aside"]): + element.decompose() + text = soup.get_text(separator="\n", strip=True) + lines = [line.strip() for line in text.splitlines() if line.strip()] + text = "\n".join(lines) + approx_tokens = len(text) / 4 + if approx_tokens > max_tokens: + text = text[: max_tokens * 4] + return text + + +def process_article_with_ai(client, article, max_tokens=8000): + clean_text = extract_clean_text(article["raw_content"], max_tokens) + metadata = article.get("metadata", {}) + title = article["title"] + url = article["url"] + description = "" + if metadata and isinstance(metadata, dict): + if "description" in metadata: + description = metadata["description"] + elif "og" in metadata and "description" in metadata["og"]: + description = metadata["og"]["description"] + try: + response = client.chat.completions.create( + model=WEB_PAGE_ANALYSE_MODEL, + response_format={"type": "json_object"}, + messages=[ + { + "role": "system", + "content": MODEL_INSTRUCTION, + }, + { + "role": "user", + "content": f""" + Analyze this article and provide a structured output with three components: + + 1. A list of 3-5 relevant categories for this article + 2. A concise 2-3 sentence summary of the article + 3. The extracted main article content, removing any navigation, ads, or irrelevant elements + + Article Title: {title} + Article URL: {url} + Description: {description} + + Article Text: + {clean_text} + + Provide your response as a JSON object with these keys: + - categories: an array of 3-5 relevant categories (as strings) + - summary: a 2-3 sentence summary of the article + - content: the cleaned main article content + """, + }, + ], + temperature=0.3, + max_tokens=1500, + ) + response_json = json.loads(response.choices[0].message.content) + categories = response_json.get("categories", []) + if isinstance(categories, str): + categories = [cat.strip() for cat in categories.split(",") if cat.strip()] + results = { + "categories": categories, + "summary": response_json.get("summary", ""), + "content": response_json.get("content", ""), + } + return results, True, None + except Exception as e: + error_message = str(e) + print(f"Error processing article with AI: {error_message}") + return None, False, error_message + + +def analyze_articles(tracking_db_path=None, openai_api_key=None, batch_size=5, delay_range=(1, 3)): + if tracking_db_path is None: + tracking_db_path = get_tracking_db_path() + if openai_api_key is None: + raise ValueError("OpenAI API key is required") + client = OpenAI(api_key=openai_api_key) + articles = get_unprocessed_articles(tracking_db_path, limit=batch_size) + stats = {"total_articles": len(articles), "success_count": 0, "failed_count": 0} + for i, article in enumerate(articles): + article_id = article["id"] + title = article["title"] + attempt = article.get("ai_attempts", 0) + 1 + print(f"[{i + 1}/{len(articles)}] Processing article: {title} (Attempt {attempt})") + results, success, error_message = process_article_with_ai(client, article) + update_article_status(tracking_db_path, article_id, results, success, error_message) + if success: + categories_display = ", ".join(results["categories"]) + print(f"Successfully processed article ID {article_id}") + print(f"Categories: {categories_display}") + print(f"Summary: {results['summary'][:100]}..." if len(results["summary"]) > 100 else f"Summary: {results['summary']}") + stats["success_count"] += 1 + else: + print(f"Failed to process article ID {article_id}: {error_message}") + stats["failed_count"] += 1 + + if i < len(articles) - 1: + delay = random.uniform(delay_range[0], delay_range[1]) + time.sleep(delay) + return stats + + +def print_stats(stats): + print("\nAI Analysis Statistics:") + print(f"Total articles processed: {stats['total_articles']}") + print(f"Successfully analyzed: {stats['success_count']}") + print(f"Failed: {stats['failed_count']}") + + +def analyze_in_batches( + tracking_db_path=None, + openai_api_key=None, + batch_size=20, + total_batches=1, + delay_between_batches=10, +): + if tracking_db_path is None: + tracking_db_path = get_tracking_db_path() + if openai_api_key is None: + raise ValueError("OpenAI API key is required") + total_stats = {"total_articles": 0, "success_count": 0, "failed_count": 0} + for i in range(total_batches): + print(f"\nProcessing batch {i + 1}/{total_batches}") + batch_stats = analyze_articles( + tracking_db_path=tracking_db_path, + openai_api_key=openai_api_key, + batch_size=batch_size, + ) + total_stats["total_articles"] += batch_stats["total_articles"] + total_stats["success_count"] += batch_stats["success_count"] + total_stats["failed_count"] += batch_stats["failed_count"] + if batch_stats["total_articles"] == 0: + print("No more articles to process") + break + if i < total_batches - 1: + print(f"Waiting {delay_between_batches} seconds before next batch...") + time.sleep(delay_between_batches) + return total_stats + + +def parse_arguments(): + parser = argparse.ArgumentParser(description="Process articles with AI analysis") + parser.add_argument("--api_key", help="OpenAI API Key (overrides environment variables)") + parser.add_argument( + "--batch_size", + type=int, + default=10, + help="Number of articles to process in each batch", + ) + parser.add_argument( + "--total_batches", + type=int, + default=1, + help="Total number of batches to process", + ) + return parser.parse_args() + + +if __name__ == "__main__": + args = parse_arguments() + api_key = args.api_key or load_api_key() + if not api_key: + print("Error: No OpenAI API key provided. Please provide via --api_key or set OPENAI_API_KEY in .env file") + exit(1) + stats = analyze_in_batches( + openai_api_key=api_key, + batch_size=args.batch_size, + total_batches=args.total_batches, + ) + print_stats(stats) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/embedding_processor.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/embedding_processor.py new file mode 100644 index 0000000..18b9ed4 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/embedding_processor.py @@ -0,0 +1,229 @@ +import time +import argparse +import random +import numpy as np +from openai import OpenAI +from db.config import get_tracking_db_path +from db.connection import db_connection, execute_query +from utils.load_api_keys import load_api_key + +EMBEDDING_MODEL = "text-embedding-3-small" + +def create_embedding_table(tracking_db_path): + with db_connection(tracking_db_path) as conn: + cursor = conn.cursor() + cursor.execute(""" + SELECT name FROM sqlite_master + WHERE type='table' AND name='article_embeddings' + """) + table_exists = cursor.fetchone() is not None + if not table_exists: + cursor.execute(""" + CREATE TABLE IF NOT EXISTS article_embeddings ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + article_id INTEGER NOT NULL, + embedding BLOB NOT NULL, + embedding_model TEXT NOT NULL, + created_at TEXT NOT NULL, + in_faiss_index INTEGER DEFAULT 0, + FOREIGN KEY (article_id) REFERENCES crawled_articles(id) + ) + """) + cursor.execute(""" + CREATE INDEX IF NOT EXISTS idx_article_embeddings_article_id + ON article_embeddings(article_id) + """) + cursor.execute(""" + CREATE INDEX IF NOT EXISTS idx_article_embeddings_in_faiss + ON article_embeddings(in_faiss_index) + """) + conn.commit() + print("Article embeddings table created successfully.") + else: + print("Article embeddings table already exists.") + + +def get_articles_without_embeddings(tracking_db_path, limit=20): + query = """ + SELECT ca.id, ca.title, ca.summary, ca.content + FROM crawled_articles ca + WHERE ca.processed = 1 + AND ca.ai_status = 'success' + AND NOT EXISTS ( + SELECT 1 FROM article_embeddings ae + WHERE ae.article_id = ca.id + ) + ORDER BY ca.published_date DESC + LIMIT ? + """ + return execute_query(tracking_db_path, query, (limit,), fetch=True) + + +def mark_articles_as_processing(tracking_db_path, article_ids): + if not article_ids: + return 0 + try: + with db_connection(tracking_db_path) as conn: + cursor = conn.cursor() + cursor.execute("PRAGMA table_info(crawled_articles)") + columns = [col[1] for col in cursor.fetchall()] + if "embedding_status" in columns: + placeholders = ",".join(["?"] * len(article_ids)) + query = f""" + UPDATE crawled_articles + SET embedding_status = 'processing' + WHERE id IN ({placeholders}) + """ + cursor.execute(query, article_ids) + conn.commit() + return cursor.rowcount + else: + print("Note: embedding_status column doesn't exist in crawled_articles table.") + print("Skipping article marking step (this is non-critical).") + return len(article_ids) + except Exception as e: + print(f"Error marking articles as processing: {str(e)}") + print("Continuing without marking articles (this is non-critical).") + return 0 + + +def generate_embedding(client, text, model=EMBEDDING_MODEL): + try: + response = client.embeddings.create(input=text, model=model) + return response.data[0].embedding, model + except Exception as e: + print(f"Error generating embedding: {str(e)}") + return None, None + + +def prepare_article_text(article): + title = article.get("title", "") + summary = article.get("summary", "") + content = article.get("content", "") + full_text = f"Title: {title}\n\nSummary: {summary}\n\nContent: {content}" + return full_text + + +def store_embedding(tracking_db_path, article_id, embedding, model): + from datetime import datetime + import sqlite3 + embedding_blob = np.array(embedding, dtype=np.float32).tobytes() + query = """ + INSERT INTO article_embeddings + (article_id, embedding, embedding_model, created_at, in_faiss_index) + VALUES (?, ?, ?, ?, 0) + """ + params = (article_id, embedding_blob, model, datetime.now().isoformat()) + try: + execute_query(tracking_db_path, query, params) + return True + except sqlite3.IntegrityError: + print(f"Warning: Embedding already exists for article {article_id}") + return False + except Exception as e: + print(f"Error storing embedding: {str(e)}") + return False + + +def process_articles_for_embedding(tracking_db_path=None, openai_api_key=None, batch_size=20, delay_range=(1, 3)): + if tracking_db_path is None: + tracking_db_path = get_tracking_db_path() + if openai_api_key is None: + raise ValueError("OpenAI API key is required") + create_embedding_table(tracking_db_path) + client = OpenAI(api_key=openai_api_key) + articles = get_articles_without_embeddings(tracking_db_path, limit=batch_size) + if not articles: + print("No articles found that need embeddings") + return {"total_articles": 0, "success_count": 0, "failed_count": 0} + article_ids = [article["id"] for article in articles] + mark_articles_as_processing(tracking_db_path, article_ids) + stats = {"total_articles": len(articles), "success_count": 0, "failed_count": 0} + for i, article in enumerate(articles): + article_id = article["id"] + try: + print(f"[{i + 1}/{len(articles)}] Generating embedding for article {article_id}: {article['title']}") + text = prepare_article_text(article) + embedding, model = generate_embedding(client, text) + if embedding: + success = store_embedding(tracking_db_path, article_id, embedding, model) + if success: + print(f"Successfully stored embedding for article {article_id}") + stats["success_count"] += 1 + else: + print(f"Failed to store embedding for article {article_id}") + stats["failed_count"] += 1 + else: + print(f"Failed to generate embedding for article {article_id}") + stats["failed_count"] += 1 + except Exception as e: + print(f"Error processing article {article_id}: {str(e)}") + stats["failed_count"] += 1 + if i < len(articles) - 1: + delay = random.uniform(delay_range[0], delay_range[1]) + time.sleep(delay) + return stats + + +def print_stats(stats): + print("\nEmbedding Generation Statistics:") + print(f"Total articles processed: {stats['total_articles']}") + print(f"Successfully embedded: {stats['success_count']}") + print(f"Failed: {stats['failed_count']}") + + +def parse_arguments(): + parser = argparse.ArgumentParser(description="Generate embeddings for processed articles") + parser.add_argument("--api_key", help="OpenAI API Key (overrides environment variables)") + parser.add_argument( + "--batch_size", + type=int, + default=20, + help="Number of articles to process in each batch", + ) + return parser.parse_args() + + +def process_in_batches( + tracking_db_path=None, + openai_api_key=None, + batch_size=20, + total_batches=1, + delay_between_batches=10, +): + if tracking_db_path is None: + tracking_db_path = get_tracking_db_path() + if openai_api_key is None: + raise ValueError("OpenAI API key is required") + total_stats = {"total_articles": 0, "success_count": 0, "failed_count": 0} + for i in range(total_batches): + print(f"\nProcessing batch {i + 1}/{total_batches}") + batch_stats = process_articles_for_embedding( + tracking_db_path=tracking_db_path, + openai_api_key=openai_api_key, + batch_size=batch_size, + ) + total_stats["total_articles"] += batch_stats["total_articles"] + total_stats["success_count"] += batch_stats["success_count"] + total_stats["failed_count"] += batch_stats["failed_count"] + if batch_stats["total_articles"] == 0: + print("No more articles to process") + break + if i < total_batches - 1: + print(f"Waiting {delay_between_batches} seconds before next batch...") + time.sleep(delay_between_batches) + return total_stats + + +if __name__ == "__main__": + args = parse_arguments() + api_key = args.api_key or load_api_key() + if not api_key: + print("Error: No OpenAI API key provided. Please provide via --api_key or set OPENAI_API_KEY in .env file") + exit(1) + stats = process_in_batches( + openai_api_key=api_key, + batch_size=args.batch_size, + total_batches=3, + ) + print_stats(stats) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/faiss_indexing_processor.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/faiss_indexing_processor.py new file mode 100644 index 0000000..cb90176 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/faiss_indexing_processor.py @@ -0,0 +1,339 @@ +import os +import time +import argparse +import numpy as np +import faiss +from db.config import get_tracking_db_path, get_faiss_db_path +from db.connection import db_connection, execute_query + + +def initialize_faiss_index(dimension=1536, index_path=None, index_type="hnsw", n_list=100): + if index_path and os.path.exists(index_path): + print(f"Loading existing FAISS index from {index_path}") + try: + index = faiss.read_index(index_path) + print(f"Loaded index with {index.ntotal} vectors") + return index + except Exception as e: + print(f"Error loading FAISS index: {str(e)}") + print("Creating a new index instead") + print(f"Creating new FAISS index with dimension {dimension}, type: {index_type}") + if index_type == "flat": + return faiss.IndexFlatL2(dimension) + + elif index_type == "ivfflat": + quantizer = faiss.IndexFlatL2(dimension) + index = faiss.IndexIVFFlat(quantizer, dimension, n_list) + print("Training IVF index with random vectors...") + train_size = max(10000, n_list * 10) + train_vectors = np.random.random((train_size, dimension)).astype(np.float32) + index.train(train_vectors) + index.nprobe = min(10, n_list // 10) + return index + elif index_type == "ivfpq": + quantizer = faiss.IndexFlatL2(dimension) + m = 16 + bits = 8 + index = faiss.IndexIVFPQ(quantizer, dimension, n_list, m, bits) + print("Training IVF-PQ index with random vectors...") + train_size = max(10000, n_list * 10) + train_vectors = np.random.random((train_size, dimension)).astype(np.float32) + index.train(train_vectors) + index.nprobe = min(10, n_list // 10) + return index + elif index_type == "hnsw": + m = 32 + ef_construction = 100 + index = faiss.IndexHNSWFlat(dimension, m) + index.hnsw.efConstruction = ef_construction + index.hnsw.efSearch = 64 + return index + else: + print(f"Unknown index type '{index_type}', falling back to IVF Flat") + quantizer = faiss.IndexFlatL2(dimension) + index = faiss.IndexIVFFlat(quantizer, dimension, n_list) + print("Training IVF index with random vectors...") + train_size = max(10000, n_list * 10) + train_vectors = np.random.random((train_size, dimension)).astype(np.float32) + index.train(train_vectors) + index.nprobe = min(10, n_list // 10) + return index + + +def save_faiss_index(index, index_path): + try: + index_dir = os.path.dirname(index_path) + os.makedirs(index_dir, exist_ok=True) + temp_path = f"{index_path}.tmp" + faiss.write_index(index, temp_path) + os.replace(temp_path, index_path) + print(f"FAISS index saved to {index_path}") + return True + except Exception as e: + print(f"Error saving FAISS index: {str(e)}") + return False + + +def save_id_mapping(id_map, mapping_path): + try: + mapping_dir = os.path.dirname(mapping_path) + os.makedirs(mapping_dir, exist_ok=True) + np.save(mapping_path, np.array(id_map)) + print(f"ID mapping saved to {mapping_path}") + return True + except Exception as e: + print(f"Error saving ID mapping: {str(e)}") + try: + print("Trying alternative save method...") + simple_path = os.path.join(mapping_dir, "article_id_map.npy") + np.save(simple_path, np.array(id_map)) + print(f"ID mapping saved to {simple_path} (alternative path)") + return True + except Exception as e: + print(f"Alternative save method also failed: {str(e)}") + return False + + +def load_id_mapping(mapping_path): + if os.path.exists(mapping_path): + try: + return np.load(mapping_path).tolist() + except Exception as e: + print(f"Error loading ID mapping: {str(e)}") + return [] + + +def get_embeddings_not_in_index(tracking_db_path, limit=100): + query = """ + SELECT ae.id, ae.article_id, ae.embedding, ae.embedding_model + FROM article_embeddings ae + WHERE ae.in_faiss_index = 0 + LIMIT ? + """ + return execute_query(tracking_db_path, query, (limit,), fetch=True) + + +def mark_embeddings_as_indexed(tracking_db_path, embedding_ids): + if not embedding_ids: + return 0 + with db_connection(tracking_db_path) as conn: + cursor = conn.cursor() + placeholders = ",".join(["?"] * len(embedding_ids)) + query = f""" + UPDATE article_embeddings + SET in_faiss_index = 1 + WHERE id IN ({placeholders}) + """ + cursor.execute(query, embedding_ids) + conn.commit() + return cursor.rowcount + + +def add_embeddings_to_index(embeddings_data, faiss_index, id_map): + if not embeddings_data: + return 0, [] + embeddings = [] + article_ids = [] + embedding_ids = [] + for data in embeddings_data: + try: + embedding_blob = data["embedding"] + embedding = np.frombuffer(embedding_blob, dtype=np.float32) + + if embedding.shape[0] != faiss_index.d: + print(f"Embedding dimension mismatch: expected {faiss_index.d}, got {embedding.shape[0]}") + continue + embeddings.append(embedding) + article_ids.append(data["article_id"]) + embedding_ids.append(data["id"]) + except Exception as e: + print(f"Error processing embedding {data['id']}: {str(e)}") + if not embeddings: + return 0, [] + try: + embeddings_array = np.vstack(embeddings).astype(np.float32) + faiss_index.add(embeddings_array) + for article_id in article_ids: + id_map.append(article_id) + print(f"Added {len(embeddings)} embeddings to FAISS index") + return len(embeddings), embedding_ids + except Exception as e: + print(f"Error adding embeddings to FAISS index: {str(e)}") + return 0, [] + + +def process_embeddings_for_indexing( + tracking_db_path=None, + index_path=None, + mapping_path=None, + batch_size=100, + index_type="ivfflat", + n_list=100, +): + if tracking_db_path is None: + tracking_db_path = get_tracking_db_path() + index_dir = os.path.dirname(index_path) + os.makedirs(index_dir, exist_ok=True) + id_map = load_id_mapping(mapping_path) + with db_connection(tracking_db_path) as conn: + cursor = conn.cursor() + cursor.execute(""" + SELECT name FROM sqlite_master + WHERE type='table' AND name='article_embeddings' + """) + table_exists = cursor.fetchone() is not None + + if not table_exists: + print("article_embeddings table does not exist. Please run embedding_processor first.") + return {"processed": 0, "added": 0, "errors": 0, "total_vectors": 0, "status": "table_missing"} + sample_query = """ + SELECT embedding FROM article_embeddings LIMIT 1 + """ + sample = execute_query(tracking_db_path, sample_query, fetch=True, fetch_one=True) + if not sample: + print("No embeddings found in the database") + default_dimension = 1536 + print(f"Using default dimension: {default_dimension}") + + faiss_index = initialize_faiss_index( + dimension=default_dimension, index_path=index_path if os.path.exists(index_path) else None, index_type=index_type, n_list=n_list + ) + return { + "processed": 0, + "added": 0, + "errors": 0, + "total_vectors": faiss_index.ntotal if hasattr(faiss_index, "ntotal") else 0, + "status": "no_embeddings", + } + embedding_dimension = len(np.frombuffer(sample["embedding"], dtype=np.float32)) + print(f"Detected embedding dimension: {embedding_dimension}") + faiss_index = initialize_faiss_index(dimension=embedding_dimension, index_path=index_path, index_type=index_type, n_list=n_list) + embeddings_data = get_embeddings_not_in_index(tracking_db_path, limit=batch_size) + if not embeddings_data: + print("No new embeddings to add to the index") + return {"processed": 0, "added": 0, "errors": 0, "total_vectors": faiss_index.ntotal, "status": "no_new_embeddings"} + added_count, embedding_ids = add_embeddings_to_index(embeddings_data, faiss_index, id_map) + if added_count > 0: + save_faiss_index(faiss_index, index_path) + save_id_mapping(id_map, mapping_path) + marked_count = mark_embeddings_as_indexed(tracking_db_path, embedding_ids) + print(f"Marked {marked_count} embeddings as indexed in the database") + stats = { + "processed": len(embeddings_data), + "added": added_count, + "errors": len(embeddings_data) - added_count, + "total_vectors": faiss_index.ntotal, + "index_type": index_type, + "status": "success", + } + return stats + + +def process_in_batches( + tracking_db_path=None, + index_path=None, + mapping_path=None, + batch_size=100, + total_batches=5, + delay_between_batches=2, + index_type="ivfflat", + n_list=100, +): + if tracking_db_path is None: + tracking_db_path = get_tracking_db_path() + total_stats = {"processed": 0, "added": 0, "errors": 0, "index_type": index_type} + for i in range(total_batches): + print(f"\nProcessing batch {i + 1}/{total_batches}") + batch_stats = process_embeddings_for_indexing( + tracking_db_path=tracking_db_path, + index_path=index_path, + mapping_path=mapping_path, + batch_size=batch_size, + index_type=index_type, + n_list=n_list, + ) + total_stats["processed"] += batch_stats["processed"] + total_stats["added"] += batch_stats["added"] + total_stats["errors"] += batch_stats["errors"] + if "total_vectors" in batch_stats: + total_stats["total_vectors"] = batch_stats["total_vectors"] + if batch_stats["processed"] == 0: + print("No more embeddings to process") + break + if i < total_batches - 1: + print(f"Waiting {delay_between_batches} seconds before next batch...") + time.sleep(delay_between_batches) + return total_stats + + +def print_stats(stats): + print("\nFAISS Indexing Statistics:") + print(f"Total embeddings processed: {stats['processed']}") + print(f"Successfully added to index: {stats['added']}") + print(f"Errors: {stats['errors']}") + if "total_vectors" in stats: + print(f"Total vectors in index: {stats['total_vectors']}") + if "index_type" in stats: + print(f"Index type: {stats['index_type']}") + if stats["index_type"] == "flat": + print("Index performance: Most accurate but slowest for large datasets") + elif stats["index_type"] == "ivfflat": + print("Index performance: Good balance of accuracy and speed") + elif stats["index_type"] == "ivfpq": + print("Index performance: Memory efficient, good for very large datasets") + elif stats["index_type"] == "hnsw": + print("Index performance: Excellent search speed with good accuracy") + + +def parse_arguments(): + parser = argparse.ArgumentParser(description="Process embeddings and add to FAISS index") + parser.add_argument( + "--batch_size", + type=int, + default=100, + help="Number of embeddings to process in each batch", + ) + parser.add_argument( + "--index_path", + default="databases/faiss/article_index.faiss", + help="Path to save the FAISS index", + ) + parser.add_argument( + "--mapping_path", + default="databases/faiss/article_id_map.npy", + help="Path to save the ID mapping file", + ) + parser.add_argument( + "--index_type", + choices=["flat", "ivfflat", "ivfpq", "hnsw"], + default="hnsw", + help="Type of FAISS index to create", + ) + parser.add_argument( + "--n_list", + type=int, + default=100, + help="Number of clusters for IVF-based indexes", + ) + parser.add_argument( + "--total_batches", + type=int, + default=5, + help="Total number of batches to process", + ) + return parser.parse_args() + + +if __name__ == "__main__": + args = parse_arguments() + index_path, mapping_path = get_faiss_db_path() + index_path = args.index_path or index_path + mapping_path = args.mapping_path or mapping_path + stats = process_in_batches( + batch_size=args.batch_size, + index_path=index_path, + mapping_path=mapping_path, + total_batches=args.total_batches, + index_type=args.index_type, + n_list=args.n_list, + ) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/fb_scraper_processor.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/fb_scraper_processor.py new file mode 100644 index 0000000..77bd3a2 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/fb_scraper_processor.py @@ -0,0 +1,24 @@ + +import sys +from datetime import datetime +from db.config import get_social_media_db_path +from tools.social.fb_scraper import crawl_facebook_feed + + +def main(): + print(f"Starting facebook.com feed scraping at {datetime.now().isoformat()}") + db_path = get_social_media_db_path() + + try: + print("Running facebook.com feed scraper") + posts = crawl_facebook_feed("https://facebook.com", db_file=db_path) + print(f"facebook.com scraping completed at {datetime.now().isoformat()}") + print(f"Collected {posts} posts from feed") + + except Exception as e: + print(f"Error executing facebook.com feed scraper: {str(e)}") + sys.exit(1) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/feed_processor.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/feed_processor.py new file mode 100644 index 0000000..bca056d --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/feed_processor.py @@ -0,0 +1,89 @@ +import time +import random +from utils.rss_feed_parser import get_feed_data +from db.config import get_sources_db_path, get_tracking_db_path +from db.feeds import ( + get_active_feeds, + count_active_feeds, + get_feed_tracking_info, + update_feed_tracking, + store_feed_entries, + update_tracking_info, +) + + +def fetch_and_process_feeds(sources_db_path=None, tracking_db_path=None, delay_between_feeds=2, batch_size=100): + if sources_db_path is None: + sources_db_path = get_sources_db_path() + if tracking_db_path is None: + tracking_db_path = get_tracking_db_path() + total_feeds = count_active_feeds(sources_db_path) + stats = { + "total_feeds": total_feeds, + "processed_feeds": 0, + "new_entries": 0, + "unchanged_feeds": 0, + "failed_feeds": 0, + } + offset = 0 + while offset < total_feeds: + feeds = get_active_feeds(sources_db_path, limit=batch_size, offset=offset) + if not feeds: + break + update_tracking_info(tracking_db_path, feeds) + for feed in feeds: + feed_id = feed["id"] + source_id = feed["source_id"] + feed_url = feed["feed_url"] + tracking_info = get_feed_tracking_info(tracking_db_path, feed_id) + etag = tracking_info.get("last_etag") if tracking_info else None + modified = tracking_info.get("last_modified") if tracking_info else None + last_hash = tracking_info.get("entry_hash") if tracking_info else None + try: + feed_data = get_feed_data(feed_url, etag=etag, modified=modified) + if not feed_data["is_rss_feed"]: + print(f"Feed {feed_url} is not a valid RSS feed") + stats["failed_feeds"] += 1 + continue + if feed_data["status"] == 304: + print(f"Feed {feed_url} not modified since last check") + stats["unchanged_feeds"] += 1 + continue + current_hash = feed_data["current_hash"] + if last_hash and current_hash == last_hash: + print(f"Feed {feed_url} content unchanged based on hash") + stats["unchanged_feeds"] += 1 + continue + parsed_entries = feed_data["parsed_entries"] + if parsed_entries: + new_entries = store_feed_entries(tracking_db_path, feed_id, source_id, parsed_entries) + stats["new_entries"] += new_entries + print(f"Stored {new_entries} new entries from {feed_url}") + update_feed_tracking( + tracking_db_path, + feed_id, + feed_data["etag"], + feed_data["modified"], + current_hash, + ) + stats["processed_feeds"] += 1 + except Exception as e: + print(f"Error processing feed {feed_url}: {str(e)}") + stats["failed_feeds"] += 1 + time.sleep(random.uniform(1, delay_between_feeds)) + offset += batch_size + return stats + + +def print_stats(stats): + print("\nFeed Processing Statistics:") + print(f"Total feeds: {stats['total_feeds']}") + print(f"Processed feeds: {stats['processed_feeds']}") + print(f"Unchanged feeds: {stats['unchanged_feeds']}") + print(f"Failed feeds: {stats['failed_feeds']}") + print(f"New entries: {stats['new_entries']}") + + +if __name__ == "__main__": + stats = fetch_and_process_feeds() + print_stats(stats) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/podcast_generator_processor.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/podcast_generator_processor.py new file mode 100644 index 0000000..e921651 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/podcast_generator_processor.py @@ -0,0 +1,333 @@ +import os +import json +from datetime import datetime +from typing import List, Dict, Any, Optional +from tools.pipeline.search_agent import search_agent_run +from tools.pipeline.scrape_agent import scrape_agent_run +from tools.pipeline.script_agent import script_agent_run +from tools.pipeline.image_generate_agent import image_generation_agent_run +from db.config import get_tracking_db_path, get_podcasts_db_path, get_tasks_db_path +from db.podcast_configs import get_podcast_config, get_all_podcast_configs +from db.agent_config_v2 import AVAILABLE_LANGS +from utils.tts_engine_selector import generate_podcast_audio +from utils.load_api_keys import load_api_key +from tools.session_state_manager import _save_podcast_to_database_sync + +PODCAST_ASSETS_DIR = "podcasts" + + +def get_language_name(language_code: str) -> str: + language_map = {lang["code"]: lang["name"] for lang in AVAILABLE_LANGS} + return language_map.get(language_code, "English") + + +def convert_script_to_audio_format(podcast_data: Dict[str, Any]) -> Dict[str, List[Dict[str, Any]]]: + speaker_map = {"ALEX": 1, "MORGAN": 2} + dict_entries = [] + for section in podcast_data.get("sections", []): + for dialog in section.get("dialog", []): + speaker = dialog.get("speaker", "ALEX") + text = dialog.get("text", "") + if text and speaker in speaker_map: + dict_entries.append({"text": text, "speaker": speaker_map[speaker]}) + return {"entries": dict_entries} + + +def generate_podcast_from_prompt_v2( + prompt: str, + openai_api_key: str, + tracking_db_path: Optional[str] = None, + podcasts_db_path: Optional[str] = None, + output_dir: str = PODCAST_ASSETS_DIR, + tts_engine: str = "kokoro", + language_code: str = "en", + podcast_script_prompt: Optional[str] = None, + image_prompt: Optional[str] = None, + debug: bool = False, +) -> Dict[str, Any]: + if tracking_db_path is None: + tracking_db_path = get_tracking_db_path() + if podcasts_db_path is None: + podcasts_db_path = get_podcasts_db_path() + os.makedirs(output_dir, exist_ok=True) + images_dir = os.path.join(output_dir, "images") + os.makedirs(images_dir, exist_ok=True) + print(f"Starting enhanced podcast generation for prompt: {prompt}") + try: + search_results = search_agent_run(prompt) + if not search_results: + print(f"WARNING: No search results found for prompt: {prompt}") + return {"error": "No search results found"} + print(f"Found {len(search_results)} search results") + if debug: + print("Search results:", json.dumps(search_results[:2], indent=2)) + except Exception as e: + print(f"ERROR: Search agent failed: {e}") + return {"error": f"Search agent failed: {str(e)}"} + try: + scraped_results = scrape_agent_run(prompt, search_results) + if not scraped_results: + print("WARNING: No content could be scraped") + return {"error": "No content could be scraped"} + confirmed_results = [] + for result in scraped_results: + if result.get("full_text") and len(result["full_text"].strip()) > 100: + result["confirmed"] = True + confirmed_results.append(result) + if not confirmed_results: + print("WARNING: No high-quality content available after scraping") + return {"error": "No high-quality content available"} + print(f"Successfully scraped {len(confirmed_results)} high-quality articles") + if debug: + print("Sample scraped content:", confirmed_results[0].get("full_text", "")[:200]) + except Exception as e: + print(f"ERROR: Scrape agent failed: {e}") + return {"error": f"Scrape agent failed: {str(e)}"} + try: + language_name = get_language_name(language_code) + podcast_data = script_agent_run(query=prompt, search_results=confirmed_results, language_name=language_name) + if not podcast_data or not isinstance(podcast_data, dict): + print("ERROR: Failed to generate podcast script") + return {"error": "Failed to generate podcast script"} + if not podcast_data.get("sections"): + print("ERROR: Generated podcast script is missing required sections") + return {"error": "Invalid podcast script structure"} + print(f"Generated script with {len(podcast_data['sections'])} sections") + if debug: + print("Script title:", podcast_data.get("title", "No title")) + except Exception as e: + print(f"ERROR: Script agent failed: {e}") + return {"error": f"Script agent failed: {str(e)}"} + banner_filenames = [] + banner_url = None + try: + image_query = image_prompt if image_prompt else prompt + image_result = image_generation_agent_run(image_query, podcast_data) + if image_result and image_result.get("banner_images"): + banner_filenames = image_result["banner_images"] + banner_url = image_result.get("banner_url") + print(f"Generated {len(banner_filenames)} banner images") + else: + print("WARNING: No images were generated") + except Exception as e: + print(f"ERROR: Image generation failed: {e}") + audio_filename = None + full_audio_path = None + try: + audio_format = convert_script_to_audio_format(podcast_data) + audio_filename = f"podcast_{datetime.now().strftime('%Y%m%d_%H%M%S')}.wav" + audio_path = os.path.join(output_dir, "audio", audio_filename) + + class DictPodcastScript: + def __init__(self, entries): + self.entries = entries + + def __iter__(self): + return iter(self.entries) + + script_obj = DictPodcastScript(audio_format["entries"]) + full_audio_path = generate_podcast_audio( + script=script_obj, + output_path=audio_path, + tts_engine=tts_engine, + language_code=language_code, + ) + if full_audio_path: + print(f"Generated podcast audio: {full_audio_path}") + else: + print("ERROR: Failed to generate audio") + audio_filename = None + except Exception as e: + print(f"ERROR: Error generating audio: {e}") + import traceback + + traceback.print_exc() + audio_filename = None + try: + session_state = { + "generated_script": podcast_data, + "banner_url": banner_url, + "banner_images": banner_filenames, + "audio_url": full_audio_path, + "tts_engine": tts_engine, + "selected_language": {"code": language_code, "name": get_language_name(language_code)}, + "podcast_info": {"topic": prompt}, + } + success, message, podcast_id = _save_podcast_to_database_sync(session_state) + if success: + print(f"Stored podcast data with ID: {podcast_id}") + else: + print(f"ERROR: Failed to save to database: {message}") + podcast_id = 0 + except Exception as e: + print(f"ERROR: Error storing podcast data: {e}") + podcast_id = 0 + if audio_filename: + frontend_audio_path = os.path.join(output_dir, audio_filename).replace("\\", "/") + else: + frontend_audio_path = None + if banner_url: + frontend_banner_path = banner_url.replace("\\", "/") + else: + frontend_banner_path = None + return { + "podcast_id": podcast_id, + "title": podcast_data.get("title", "Podcast"), + "audio_path": frontend_audio_path, + "banner_path": frontend_banner_path, + "banner_images": banner_filenames, + "script": podcast_data, + "tts_engine": tts_engine, + "language": language_code, + "sources_count": len(confirmed_results), + "processing_stats": { + "search_results": len(search_results), + "scraped_results": len(scraped_results), + "confirmed_results": len(confirmed_results), + "images_generated": len(banner_filenames), + "audio_generated": bool(audio_filename), + }, + } + + +def generate_podcast_from_config_v2( + config_id: int, + openai_api_key: str, + tracking_db_path: Optional[str] = None, + podcasts_db_path: Optional[str] = None, + tasks_db_path: Optional[str] = None, + output_dir: str = PODCAST_ASSETS_DIR, + debug: bool = False, +) -> Dict[str, Any]: + if tracking_db_path is None: + tracking_db_path = get_tracking_db_path() + if podcasts_db_path is None: + podcasts_db_path = get_podcasts_db_path() + if tasks_db_path is None: + tasks_db_path = get_tasks_db_path() + config = get_podcast_config(tasks_db_path, config_id) + if not config: + print(f"ERROR: Podcast configuration not found: {config_id}") + return {"error": f"Podcast configuration not found: {config_id}"} + prompt = config.get("prompt", "") + time_range_hours = config.get("time_range_hours", 24) + limit_articles = config.get("limit_articles", 20) + tts_engine = config.get("tts_engine", "elevenlabs") + language_code = config.get("language_code", "en") + podcast_script_prompt = config.get("podcast_script_prompt") + image_prompt = config.get("image_prompt") + print(f"Generating podcast with enhanced config: {config.get('name', 'Unnamed')}") + print(f"Prompt: {prompt}") + print(f"Time range: {time_range_hours} hours") + print(f"Limit: {limit_articles} articles") + print(f"TTS Engine: {tts_engine}") + print(f"Language: {language_code}") + return generate_podcast_from_prompt_v2( + prompt=prompt, + openai_api_key=openai_api_key, + tracking_db_path=tracking_db_path, + podcasts_db_path=podcasts_db_path, + output_dir=output_dir, + tts_engine=tts_engine, + language_code=language_code, + podcast_script_prompt=podcast_script_prompt, + image_prompt=image_prompt, + debug=debug, + ) + + +def process_all_active_configs_v2( + openai_api_key: str, + tracking_db_path: Optional[str] = None, + podcasts_db_path: Optional[str] = None, + tasks_db_path: Optional[str] = None, + output_dir: str = PODCAST_ASSETS_DIR, + debug: bool = False, +) -> List[Dict[str, Any]]: + if tracking_db_path is None: + tracking_db_path = get_tracking_db_path() + if podcasts_db_path is None: + podcasts_db_path = get_podcasts_db_path() + if tasks_db_path is None: + tasks_db_path = get_tasks_db_path() + configs = get_all_podcast_configs(tasks_db_path, active_only=True) + if not configs: + print("WARNING: No active podcast configurations found") + return [{"error": "No active podcast configurations found"}] + results = [] + total_configs = len(configs) + print(f"Processing {total_configs} active podcast configurations with enhanced pipeline...") + for i, config in enumerate(configs, 1): + config_id = config["id"] + config_name = config["name"] + print(f"\n[{i}/{total_configs}] Processing podcast configuration {config_id}: {config_name}") + try: + result = generate_podcast_from_config_v2( + config_id=config_id, + openai_api_key=openai_api_key, + tracking_db_path=tracking_db_path, + podcasts_db_path=podcasts_db_path, + tasks_db_path=tasks_db_path, + output_dir=output_dir, + debug=debug, + ) + result["config_id"] = config_id + result["config_name"] = config_name + results.append(result) + if "error" not in result: + stats = result.get("processing_stats", {}) + print(f"Success - Podcast ID: {result.get('podcast_id', 'Unknown')}") + print(f"Sources: {stats.get('confirmed_results', 0)} articles processed") + print(f"Images: {stats.get('images_generated', 0)} generated") + print(f"Audio: {'Yes' if stats.get('audio_generated') else 'No'}") + else: + print(f"Failed: {result['error']}") + except Exception as e: + print(f"ERROR: Error generating podcast for config {config_id}: {e}") + results.append({"config_id": config_id, "config_name": config_name, "error": str(e)}) + return results + + +def main(): + openai_api_key = load_api_key() + tasks_db_path = get_tasks_db_path() + if not openai_api_key: + print("ERROR: No OpenAI API key provided. Please set OPENAI_API_KEY environment variable.") + return 1 + output_dir = PODCAST_ASSETS_DIR + debug = False + print("Starting Enhanced Agent-Based Podcast Generation System") + print("=" * 60) + results = process_all_active_configs_v2( + openai_api_key=openai_api_key, + tasks_db_path=tasks_db_path, + output_dir=output_dir, + debug=debug, + ) + print("\n" + "=" * 60) + print("PODCAST GENERATION RESULTS SUMMARY") + print("=" * 60) + successful = 0 + failed = 0 + for result in results: + config_id = result.get("config_id", "Unknown") + config_name = result.get("config_name", "Unknown") + if "error" in result: + print(f"Config {config_id} ({config_name}): {result['error']}") + failed += 1 + else: + podcast_id = result.get("podcast_id", "Unknown") + stats = result.get("processing_stats", {}) + print(f"Config {config_id} ({config_name}): Success") + print(f"Podcast ID: {podcast_id}") + print(f"Sources: {stats.get('confirmed_results', 0)} articles") + print(f"Images: {stats.get('images_generated', 0)} generated") + successful += 1 + print("=" * 60) + print(f"FINAL STATS: {successful} successful, {failed} failed out of {len(results)} total") + print("=" * 60) + return 0 + + +if __name__ == "__main__": + exit(main()) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/url_processor.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/url_processor.py new file mode 100644 index 0000000..ec67720 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/url_processor.py @@ -0,0 +1,82 @@ +from db.config import get_tracking_db_path +from db.feeds import get_uncrawled_entries +from db.articles import store_crawled_article, update_entry_status +from utils.crawl_url import get_web_data + + +def crawl_pending_entries(tracking_db_path=None, batch_size=20, delay_range=(1, 3), max_attempts=3): + if tracking_db_path is None: + tracking_db_path = get_tracking_db_path() + entries = get_uncrawled_entries(tracking_db_path, limit=batch_size, max_attempts=max_attempts) + stats = { + "total_entries": len(entries), + "success_count": 0, + "failed_count": 0, + "skipped_count": 0, + } + for entry in entries: + entry_id = entry["id"] + url = entry["link"] + if not url or url.strip() == "": + update_entry_status(tracking_db_path, entry_id, "skipped") + stats["skipped_count"] += 1 + continue + print(f"Crawling URL: {url}") + try: + web_data = get_web_data(url) + if not web_data or not web_data["raw_html"]: + print(f"No content retrieved for {url}") + update_entry_status(tracking_db_path, entry_id, "failed") + stats["failed_count"] += 1 + continue + success = store_crawled_article(tracking_db_path, entry, web_data["raw_html"], web_data["metadata"]) + if success: + update_entry_status(tracking_db_path, entry_id, "success") + stats["success_count"] += 1 + print(f"Successfully crawled: {url}") + else: + update_entry_status(tracking_db_path, entry_id, "failed") + stats["failed_count"] += 1 + print(f"Failed to store: {url} (likely duplicate)") + except Exception as e: + print(f"Error crawling {url}: {str(e)}") + update_entry_status(tracking_db_path, entry_id, "failed") + stats["failed_count"] += 1 + return stats + + +def print_stats(stats): + print("\nCrawl Statistics:") + print(f"Total entries processed: {stats['total_entries']}") + print(f"Successfully crawled: {stats['success_count']}") + print(f"Failed: {stats['failed_count']}") + print(f"Skipped (no URL): {stats['skipped_count']}") + + +def crawl_in_batches(tracking_db_path=None, batch_size=20, total_batches=5, delay_between_batches=10): + if tracking_db_path is None: + tracking_db_path = get_tracking_db_path() + total_stats = { + "total_entries": 0, + "success_count": 0, + "failed_count": 0, + "skipped_count": 0, + } + for i in range(total_batches): + print(f"\nProcessing batch {i + 1}/{total_batches}") + batch_stats = crawl_pending_entries(tracking_db_path=tracking_db_path, batch_size=batch_size) + total_stats["total_entries"] += batch_stats["total_entries"] + total_stats["success_count"] += batch_stats["success_count"] + total_stats["failed_count"] += batch_stats["failed_count"] + total_stats["skipped_count"] += batch_stats["skipped_count"] + if batch_stats["total_entries"] == 0: + print("No more entries to process") + break + if i < total_batches - 1: + print(f"Waiting {delay_between_batches} seconds before next batch...") + return total_stats + + +if __name__ == "__main__": + stats = crawl_in_batches(batch_size=20, total_batches=50) + print_stats(stats) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/x_scraper_processor.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/x_scraper_processor.py new file mode 100644 index 0000000..a98e9ba --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/processors/x_scraper_processor.py @@ -0,0 +1,24 @@ + +import sys +from datetime import datetime +from db.config import get_social_media_db_path +from tools.social.x_scraper import crawl_x_profile + + +def main(): + print(f"Starting X.com feed scraping at {datetime.now().isoformat()}") + db_path = get_social_media_db_path() + + try: + print("Running X.com feed scraper") + posts = crawl_x_profile("home", db_file=db_path) + print(f"X.com feed scraping completed at {datetime.now().isoformat()}") + print(f"Collected {posts} posts from feed") + + except Exception as e: + print(f"Error executing X.com feed scraper: {str(e)}") + sys.exit(1) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/requirements.txt b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/requirements.txt new file mode 100644 index 0000000..fe092be --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/requirements.txt @@ -0,0 +1,236 @@ +addict==2.4.0 +agno==1.4.2 +aiofiles==24.1.0 +aiohappyeyeballs==2.6.1 +aiohttp==3.11.18 +aioredis==2.0.1 +aiosignal==1.3.2 +aiosqlite==0.21.0 +amqp==5.3.1 +annotated-types==0.7.0 +anthropic==0.49.0 +anyio==4.9.0 +APScheduler==3.11.0 +asgi-csrf==0.11 +asgiref==3.8.1 +async-timeout==5.0.1 +attrs==25.3.0 +audioread==3.0.1 +babel==2.17.0 +backoff==2.2.1 +beautifulsoup4==4.13.4 +billiard==4.2.1 +blis==1.2.1 +boto3==1.38.3 +botocore==1.38.3 +browser-use==0.2.4 +cachetools==5.5.2 +catalogue==2.0.10 +celery==5.5.2 +certifi==2025.1.31 +cffi==1.17.1 +charset-normalizer==3.4.1 +click==8.1.8 +click-default-group==1.2.4 +click-didyoumean==0.3.1 +click-plugins==1.1.1 +click-repl==0.3.0 +cloudpathlib==0.21.0 +colorama==0.4.6 +confection==0.1.5 +csvw==3.5.1 +curated-tokenizers==0.0.9 +curated-transformers==0.1.1 +cymem==2.0.11 +Cython==3.0.12 +datasette==0.65.1 +defusedxml==0.7.1 +distro==1.9.0 +dlinfo==2.0.0 +dnspython==2.7.0 +docopt==0.6.2 +docstring_parser==0.16 +duckduckgo_search==8.0.1 +elevenlabs==1.57.0 +espeakng-loader==0.2.4 +faiss-cpu==1.9.0.post1 +fastapi==0.115.12 +feedparser==6.0.11 +filelock==3.18.0 +filetype==1.2.0 +flexcache==0.3 +flexparser==0.4 +flower==2.0.1 +frozenlist==1.6.0 +fsspec==2025.3.2 +gitdb==4.0.12 +GitPython==3.1.44 +gnews==0.4.1 +google-ai-generativelanguage==0.6.17 +google-api-core==2.24.2 +google-auth==2.39.0 +googleapis-common-protos==1.70.0 +greenlet==3.2.0 +grpcio==1.72.0rc1 +grpcio-status==1.72.0rc1 +h11==0.14.0 +h2==4.2.0 +hpack==4.1.0 +httpcore==1.0.8 +httpx==0.28.1 +huggingface-hub==0.30.2 +humanize==4.12.3 +hupper==1.12.1 +hyperframe==6.1.0 +idna==3.10 +isodate==0.7.2 +itsdangerous==2.2.0 +janus==2.0.0 +Jinja2==3.1.6 +jiter==0.9.0 +jmespath==1.0.1 +joblib==1.4.2 +jsonpatch==1.33 +jsonpointer==3.0.0 +jsonschema==4.23.0 +jsonschema-specifications==2025.4.1 +kokoro==0.9.4 +kombu==5.5.3 +langchain==0.3.22 +langchain-anthropic==0.3.3 +langchain-aws==0.2.19 +langchain-core==0.3.49 +langchain-deepseek==0.1.3 +langchain-google-genai==2.1.2 +langchain-ollama==0.3.0 +langchain-openai==0.3.11 +langchain-text-splitters==0.3.7 +langcodes==3.5.0 +langsmith==0.3.31 +language-tags==1.2.0 +language_data==1.3.0 +lazy_loader==0.4 +librosa==0.11.0 +linkify-it-py==2.0.3 +llvmlite==0.44.0 +loguru==0.7.3 +lxml==5.4.0 +lxml_html_clean==0.4.2 +marisa-trie==1.2.1 +markdown-it-py==3.0.0 +markdownify==1.1.0 +MarkupSafe==3.0.2 +mdit-py-plugins==0.4.2 +mdurl==0.1.2 +mem0ai==0.1.93 +mergedeep==1.3.4 +misaki==0.9.4 +monotonic==1.6 +mpmath==1.3.0 +msgpack==1.1.0 +multidict==6.4.3 +murmurhash==1.0.12 +networkx==3.4.2 +newspaper4k==0.9.3.1 +nltk==3.9.1 +num2words==0.5.14 +numba==0.61.2 +numpy==1.26.4 +ollama==0.4.7 +openai==1.74.0 +orjson==3.10.16 +packaging==24.2 +pandas==2.2.3 +phonemizer-fork==3.3.2 +pillow==11.2.1 +pip-chill==1.0.3 +playwright==1.52.0 +pluggy==1.6.0 +pooch==1.8.2 +portalocker==2.10.1 +posthog==3.25.0 +preshed==3.0.9 +primp==0.15.0 +prometheus_client==0.22.0 +propcache==0.3.1 +proto-plus==1.26.1 +protobuf==6.30.2 +psycopg2-binary==2.9.10 +pyasn1==0.6.1 +pyasn1_modules==0.4.2 +pycparser==2.22 +pydantic==2.10.6 +pydantic-settings==2.9.1 +pydantic_core==2.27.2 +pydub==0.25.1 +pyee==13.0.0 +PyJWT==2.9.0 +pyparsing==3.2.3 +pyperclip==1.9.0 +python-dotenv==1.1.0 +python-multipart==0.0.20 +pytz==2024.2 +PyYAML==6.0.2 +qdrant-client==1.14.2 +rdflib==7.1.4 +redis==5.3.0 +referencing==0.36.2 +regex==2024.11.6 +requests==2.32.3 +requests-file==2.1.0 +requests-toolbelt==1.0.0 +rfc3986==1.5.0 +rich==14.0.0 +rpds-py==0.24.0 +rsa==4.9.1 +s3transfer==0.12.0 +safetensors==0.5.3 +scikit-learn==1.6.1 +scipy==1.15.2 +screeninfo==0.8.1 +segments==2.3.0 +sentence-transformers==4.1.0 +sgmllib3k==1.0.0 +shellingham==1.5.4 +smart-open==7.1.0 +smmap==5.0.2 +sniffio==1.3.1 +soundfile==0.13.1 +soupsieve==2.6 +soxr==0.5.0.post1 +spacy==3.8.5 +spacy-curated-transformers==0.3.0 +spacy-legacy==3.0.12 +spacy-loggers==1.0.5 +SQLAlchemy==2.0.40 +srsly==2.5.1 +starlette==0.46.2 +sympy==1.14.0 +tabulate==0.9.0 +tenacity==9.1.2 +textual==3.2.0 +thinc==8.3.4 +threadpoolctl==3.6.0 +tiktoken==0.9.0 +tldextract==5.3.0 +tokenizers==0.21.1 +tomli==2.2.1 +torch==2.2.2 +tqdm==4.67.1 +transformers==4.51.3 +typer==0.15.2 +typing-inspection==0.4.0 +tzdata==2025.2 +tzlocal==5.3.1 +uc-micro-py==1.0.3 +uritemplate==4.1.1 +urllib3==2.4.0 +uvicorn==0.34.2 +vine==5.1.0 +wasabi==1.1.3 +weasel==0.4.1 +websockets==15.0.1 +widgetsnbextension==4.0.14 +wrapt==1.17.2 +yarl==1.20.0 +zstandard==0.23.0 \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/__init__.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/article_router.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/article_router.py new file mode 100644 index 0000000..2589823 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/article_router.py @@ -0,0 +1,54 @@ +from fastapi import APIRouter, Query +from typing import List, Optional, Dict, Any +from models.article_schemas import Article, PaginatedArticles +from services.article_service import article_service + +router = APIRouter() + + +@router.get("/", response_model=PaginatedArticles) +async def read_articles( + page: int = Query(1, ge=1, description="Page number"), + per_page: int = Query(10, ge=1, le=100, description="Items per page"), + source: Optional[str] = Query(None, description="Filter by source name"), + category: Optional[str] = Query(None, description="Filter by category"), + date_from: Optional[str] = Query(None, description="Filter by start date (format: YYYY-MM-DD)"), + date_to: Optional[str] = Query(None, description="Filter by end date (format: YYYY-MM-DD)"), + search: Optional[str] = Query(None, description="Search in title and summary"), +): + """ + Get all articles with pagination and filtering. + + - **page**: Page number (starting from 1) + - **per_page**: Number of items per page (max 100) + - **source**: Filter by source name + - **category**: Filter by category + - **date_from**: Filter by start date (format: YYYY-MM-DD) + - **date_to**: Filter by end date (format: YYYY-MM-DD) + - **search**: Search in title and summary + """ + return await article_service.get_articles( + page=page, per_page=per_page, source=source, category=category, date_from=date_from, date_to=date_to, search=search + ) + + +@router.get("/{article_id}", response_model=Article) +async def read_article(article_id: int): + """ + Get a specific article by ID. + + - **article_id**: ID of the article to retrieve + """ + return await article_service.get_article(article_id=article_id) + + +@router.get("/sources/list", response_model=List[str]) +async def read_sources(): + """Get all available sources.""" + return await article_service.get_sources() + + +@router.get("/categories/list", response_model=List[Dict[str, Any]]) +async def read_categories(): + """Get all available categories with article counts.""" + return await article_service.get_categories() diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/async_podcast_agent_router.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/async_podcast_agent_router.py new file mode 100644 index 0000000..eba119e --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/async_podcast_agent_router.py @@ -0,0 +1,72 @@ +from fastapi import APIRouter +from typing import Optional +from pydantic import BaseModel +from services.async_podcast_agent_service import podcast_agent_service + +router = APIRouter() + + +class SessionRequest(BaseModel): + session_id: Optional[str] = None + + +class ChatRequest(BaseModel): + session_id: str + message: str + + +class ChatResponse(BaseModel): + session_id: str + response: str + stage: str + session_state: str + is_processing: bool = False + process_type: Optional[str] = None + task_id: Optional[str] = None + browser_recording_path: Optional[str] = None + + +class StatusRequest(BaseModel): + session_id: str + task_id: Optional[str] = None + + +@router.post("/session") +async def create_session(request: SessionRequest = None): + """Create or reuse a session with the podcast agent""" + return await podcast_agent_service.create_session(request) + + +@router.post("/chat", response_model=ChatResponse) +async def chat(request: ChatRequest): + """Send a message to the podcast agent and get a response""" + return await podcast_agent_service.chat(request) + + +@router.post("/status", response_model=ChatResponse) +async def check_status(request: StatusRequest): + """Check if a result is available for the session""" + return await podcast_agent_service.check_result_status(request) + + +@router.get("/sessions") +async def list_sessions(page: int = 1, per_page: int = 10): + """List all saved podcast sessions with pagination""" + return await podcast_agent_service.list_sessions(page, per_page) + + +@router.get("/session_history") +async def get_session_history(session_id: str): + """Get the complete message history for a session""" + return await podcast_agent_service.get_session_history(session_id) + + +@router.delete("/session/{session_id}") +async def delete_session(session_id: str): + """Delete a podcast session and all its data""" + return await podcast_agent_service.delete_session(session_id) + +@router.get("/languages") +async def get_supported_languages(): + """Get the list of supported languages""" + return await podcast_agent_service.get_supported_languages() diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/podcast_config_router.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/podcast_config_router.py new file mode 100644 index 0000000..511c302 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/podcast_config_router.py @@ -0,0 +1,103 @@ +from fastapi import APIRouter, Query, Path, Body, status +from typing import List +from models.podcast_config_schemas import PodcastConfig, PodcastConfigCreate, PodcastConfigUpdate +from services.podcast_config_service import podcast_config_service + + +router = APIRouter() + + +@router.get("/", response_model=List[PodcastConfig]) +async def get_podcast_configs( + active_only: bool = Query(False, description="Include only active podcast configs"), +): + """ + Get all podcast configurations with optional filtering. + + - **active_only**: If true, includes only active podcast configurations + """ + return await podcast_config_service.get_all_configs(active_only=active_only) + + +@router.get("/{config_id}", response_model=PodcastConfig) +async def get_podcast_config( + config_id: int = Path(..., description="The ID of the podcast config to retrieve"), +): + """ + Get a specific podcast configuration by ID. + + - **config_id**: The ID of the podcast configuration to retrieve + """ + return await podcast_config_service.get_config(config_id=config_id) + + +@router.post("/", response_model=PodcastConfig, status_code=status.HTTP_201_CREATED) +async def create_podcast_config(config_data: PodcastConfigCreate = Body(...)): + """ + Create a new podcast configuration. + + - **config_data**: Data for the new podcast configuration + """ + return await podcast_config_service.create_config( + name=config_data.name, + prompt=config_data.prompt, + description=config_data.description, + time_range_hours=config_data.time_range_hours, + limit_articles=config_data.limit_articles, + is_active=config_data.is_active, + tts_engine=config_data.tts_engine, + language_code=config_data.language_code, + podcast_script_prompt=config_data.podcast_script_prompt, + image_prompt=config_data.image_prompt, + ) + + +@router.put("/{config_id}", response_model=PodcastConfig) +async def update_podcast_config( + config_id: int = Path(..., description="The ID of the podcast config to update"), + config_data: PodcastConfigUpdate = Body(...), +): + """ + Update an existing podcast configuration. + + - **config_id**: The ID of the podcast configuration to update + - **config_data**: Updated data for the podcast configuration + """ + updates = {k: v for k, v in config_data.dict().items() if v is not None} + return await podcast_config_service.update_config(config_id=config_id, updates=updates) + + +@router.delete("/{config_id}") +async def delete_podcast_config( + config_id: int = Path(..., description="The ID of the podcast config to delete"), +): + """ + Delete a podcast configuration. + + - **config_id**: The ID of the podcast configuration to delete + """ + return await podcast_config_service.delete_config(config_id=config_id) + + +@router.post("/{config_id}/enable", response_model=PodcastConfig) +async def enable_podcast_config( + config_id: int = Path(..., description="The ID of the podcast config to enable"), +): + """ + Enable a podcast configuration. + + - **config_id**: The ID of the podcast configuration to enable + """ + return await podcast_config_service.toggle_config(config_id=config_id, enable=True) + + +@router.post("/{config_id}/disable", response_model=PodcastConfig) +async def disable_podcast_config( + config_id: int = Path(..., description="The ID of the podcast config to disable"), +): + """ + Disable a podcast configuration. + + - **config_id**: The ID of the podcast configuration to disable + """ + return await podcast_config_service.toggle_config(config_id=config_id, enable=False) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/podcast_router.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/podcast_router.py new file mode 100644 index 0000000..2dcc19a --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/podcast_router.py @@ -0,0 +1,200 @@ +from fastapi import APIRouter, HTTPException, File, UploadFile, Body, Query, Path +from fastapi.responses import FileResponse +from typing import List, Optional +import os +from datetime import datetime +from models.podcast_schemas import Podcast, PodcastDetail, PodcastCreate, PodcastUpdate, PaginatedPodcasts +from services.podcast_service import podcast_service + +router = APIRouter() + + +@router.get("/", response_model=PaginatedPodcasts) +async def get_podcasts( + page: int = Query(1, ge=1, description="Page number"), + per_page: int = Query(10, ge=1, le=100, description="Items per page"), + search: Optional[str] = Query(None, description="Search in title"), + date_from: Optional[str] = Query(None, description="Filter by date from (YYYY-MM-DD)"), + date_to: Optional[str] = Query(None, description="Filter by date to (YYYY-MM-DD)"), + language_code: Optional[str] = Query(None, description="Filter by language code"), + tts_engine: Optional[str] = Query(None, description="Filter by TTS engine"), + has_audio: Optional[bool] = Query(None, description="Filter by audio availability"), +): + """ + Get a paginated list of podcasts with optional filtering. + """ + return await podcast_service.get_podcasts( + page=page, + per_page=per_page, + search=search, + date_from=date_from, + date_to=date_to, + language_code=language_code, + tts_engine=tts_engine, + has_audio=has_audio, + ) + + +@router.get("/formats", response_model=List[str]) +async def get_podcast_formats(): + """ + Get a list of available podcast formats for filtering. + """ + return await podcast_service.get_podcast_formats() + + +@router.get("/language-codes", response_model=List[str]) +async def get_language_codes(): + """ + Get a list of available language codes for filtering. + """ + return await podcast_service.get_language_codes() + + +@router.get("/tts-engines", response_model=List[str]) +async def get_tts_engines(): + """ + Get a list of available TTS engines for filtering. + """ + return await podcast_service.get_tts_engines() + + +@router.get("/{podcast_id}", response_model=PodcastDetail) +async def get_podcast(podcast_id: int = Path(..., description="The ID of the podcast to retrieve")): + """ + Get detailed information about a specific podcast. + + Parameters: + - **podcast_id**: The ID of the podcast to retrieve + + Returns the podcast metadata and content. + """ + podcast = await podcast_service.get_podcast(podcast_id) + content = await podcast_service.get_podcast_content(podcast_id) + audio_url = await podcast_service.get_podcast_audio_url(podcast) + sources = podcast.get("sources", []) + if "sources" in podcast: + del podcast["sources"] + banner_images = podcast.get("banner_images", []) + if "banner_images" in podcast: + del podcast["banner_images"] + return {"podcast": podcast, "content": content, "audio_url": audio_url, "sources": sources, "banner_images": banner_images} + + +@router.get("/by-identifier/{identifier}", response_model=PodcastDetail) +async def get_podcast_by_identifier(identifier: str = Path(..., description="The unique identifier of the podcast")): + """ + Get detailed information about a specific podcast using string identifier. + + Parameters: + - **identifier**: The unique identifier of the podcast to retrieve + + Returns the podcast metadata and content. + """ + podcast = await podcast_service.get_podcast_by_identifier(identifier) + podcast_id = int(podcast["id"]) + content = await podcast_service.get_podcast_content(podcast_id) + audio_url = await podcast_service.get_podcast_audio_url(podcast) + sources = podcast.get("sources", []) + if "sources" in podcast: + del podcast["sources"] + banner_images = podcast.get("banner_images", []) + if "banner_images" in podcast: + del podcast["banner_images"] + return {"podcast": podcast, "content": content, "audio_url": audio_url, "sources": sources, "banner_images": banner_images} + + +@router.post("/", response_model=Podcast) +async def create_podcast(podcast_data: PodcastCreate = Body(...)): + """ + Create a new podcast. + + Parameters: + - **podcast_data**: Podcast data including title, date, content, sources, language_code, and tts_engine + + Returns the created podcast metadata. + """ + date = podcast_data.date or datetime.now().strftime("%Y-%m-%d") + return await podcast_service.create_podcast( + title=podcast_data.title, + date=date, + content=podcast_data.content, + sources=podcast_data.sources, + language_code=podcast_data.language_code, + tts_engine=podcast_data.tts_engine, + ) + + +@router.put("/{podcast_id}", response_model=Podcast) +async def update_podcast(podcast_id: int = Path(..., description="The ID of the podcast to update"), podcast_data: PodcastUpdate = Body(...)): + """ + Update an existing podcast's metadata and/or content. + + Parameters: + - **podcast_id**: The ID of the podcast to update + - **podcast_data**: Updated data for the podcast + + Returns the updated podcast metadata. + """ + update_data = {k: v for k, v in podcast_data.dict().items() if v is not None} + return await podcast_service.update_podcast(podcast_id, update_data) + + +@router.delete("/{podcast_id}") +async def delete_podcast(podcast_id: int = Path(..., description="The ID of the podcast to delete")): + """ + Delete a podcast. + + Parameters: + - **podcast_id**: The ID of the podcast to delete + + Returns a success message. + """ + success = await podcast_service.delete_podcast(podcast_id) + if success: + return {"message": f"Podcast {podcast_id} deleted successfully"} + return {"message": "No podcast was deleted"} + + +@router.post("/{podcast_id}/audio", response_model=Podcast) +async def upload_audio(podcast_id: int = Path(..., description="The ID of the podcast"), file: UploadFile = File(...)): + """ + Upload an audio file for a podcast. + + Parameters: + - **podcast_id**: The ID of the podcast to attach the audio to + - **file**: The audio file to upload + + Returns the updated podcast. + """ + return await podcast_service.upload_podcast_audio(podcast_id, file) + + +@router.post("/{podcast_id}/banner", response_model=Podcast) +async def upload_banner(podcast_id: int = Path(..., description="The ID of the podcast"), file: UploadFile = File(...)): + """ + Upload a banner image for a podcast. + + Parameters: + - **podcast_id**: The ID of the podcast to attach the banner to + - **file**: The image file to upload + + Returns the updated podcast. + """ + return await podcast_service.upload_podcast_banner(podcast_id, file) + + +@router.get("/audio/{filename}") +async def get_audio_file(filename: str = Path(..., description="The filename of the audio file")): + """ + Get the audio file for a podcast. + + Parameters: + - **filename**: The filename of the audio file to retrieve + + Returns the audio file as a download. + """ + audio_path = os.path.join("podcasts", "audio", filename) + if not os.path.exists(audio_path): + raise HTTPException(status_code=404, detail="Audio file not found") + return FileResponse(audio_path) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/social_media_router.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/social_media_router.py new file mode 100644 index 0000000..fb91314 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/social_media_router.py @@ -0,0 +1,173 @@ +from fastapi import APIRouter, Query +from typing import List, Optional, Dict, Any +from services.social_media_service import social_media_service +from models.social_media_schemas import PaginatedPosts, Post +import threading +from tools.social.browser import setup_session_multi + +router = APIRouter() + + +@router.get("/", response_model=PaginatedPosts) +async def read_posts( + page: int = Query(1, ge=1, description="Page number"), + per_page: int = Query(10, ge=1, le=100, description="Items per page"), + platform: Optional[str] = Query(None, description="Filter by platform (e.g., x.com, instagram)"), + user_handle: Optional[str] = Query(None, description="Filter by user handle"), + sentiment: Optional[str] = Query(None, description="Filter by sentiment"), + category: Optional[str] = Query(None, description="Filter by category"), + date_from: Optional[str] = Query(None, description="Filter by start date (format: YYYY-MM-DD)"), + date_to: Optional[str] = Query(None, description="Filter by end date (format: YYYY-MM-DD)"), + search: Optional[str] = Query(None, description="Search in post text, user display name, or handle"), +): + """ + Get all social media posts with pagination and filtering. + """ + return await social_media_service.get_posts( + page=page, + per_page=per_page, + platform=platform, + user_handle=user_handle, + sentiment=sentiment, + category=category, + date_from=date_from, + date_to=date_to, + search=search, + ) + + +@router.get("/{post_id}", response_model=Post) +async def read_post(post_id: str): + """ + Get a specific social media post by ID. + """ + return await social_media_service.get_post(post_id=post_id) + + +@router.get("/platforms/list", response_model=List[str]) +async def read_platforms(): + """Get all available platforms.""" + return await social_media_service.get_platforms() + + +@router.get("/sentiments/list", response_model=List[Dict[str, Any]]) +async def read_sentiments( + date_from: Optional[str] = Query(None, description="Filter by start date (format: YYYY-MM-DD)"), + date_to: Optional[str] = Query(None, description="Filter by end date (format: YYYY-MM-DD)"), +): + """Get sentiment distribution with post counts.""" + return await social_media_service.get_sentiments(date_from=date_from, date_to=date_to) + + +@router.get("/users/top", response_model=List[Dict[str, Any]]) +async def read_top_users( + platform: Optional[str] = Query(None, description="Filter by platform"), + limit: int = Query(10, ge=1, le=50, description="Number of top users to return"), + date_from: Optional[str] = Query(None, description="Filter by start date (format: YYYY-MM-DD)"), + date_to: Optional[str] = Query(None, description="Filter by end date (format: YYYY-MM-DD)"), +): + """Get top users by post count.""" + return await social_media_service.get_top_users(platform=platform, limit=limit, date_from=date_from, date_to=date_to) + + +@router.get("/categories/list", response_model=List[Dict[str, Any]]) +async def read_categories( + date_from: Optional[str] = Query(None, description="Filter by start date (format: YYYY-MM-DD)"), + date_to: Optional[str] = Query(None, description="Filter by end date (format: YYYY-MM-DD)"), +): + """Get all categories with post counts.""" + return await social_media_service.get_categories(date_from=date_from, date_to=date_to) + + +@router.get("/users/sentiment", response_model=List[Dict[str, Any]]) +async def read_user_sentiment( + limit: int = Query(10, ge=1, le=50, description="Number of users to return"), + platform: Optional[str] = Query(None, description="Filter by platform"), + date_from: Optional[str] = Query(None, description="Filter by start date (format: YYYY-MM-DD)"), + date_to: Optional[str] = Query(None, description="Filter by end date (format: YYYY-MM-DD)"), +): + """Get users with their sentiment breakdown.""" + return await social_media_service.get_user_sentiment(limit=limit, platform=platform, date_from=date_from, date_to=date_to) + + +@router.get("/categories/sentiment", response_model=List[Dict[str, Any]]) +async def read_category_sentiment( + date_from: Optional[str] = Query(None, description="Filter by start date (format: YYYY-MM-DD)"), + date_to: Optional[str] = Query(None, description="Filter by end date (format: YYYY-MM-DD)"), +): + """Get sentiment distribution by category.""" + return await social_media_service.get_category_sentiment(date_from=date_from, date_to=date_to) + + +@router.get("/topic/trends", response_model=List[Dict[str, Any]]) +async def read_trending_topics( + limit: int = Query(10, ge=1, le=50, description="Number of topics to return"), + date_from: Optional[str] = Query(None, description="Filter by start date (format: YYYY-MM-DD)"), + date_to: Optional[str] = Query(None, description="Filter by end date (format: YYYY-MM-DD)"), +): + """Get trending topics with sentiment breakdown.""" + return await social_media_service.get_trending_topics(date_from=date_from, date_to=date_to, limit=limit) + + +@router.get("/trends/time", response_model=List[Dict[str, Any]]) +async def read_sentiment_over_time( + platform: Optional[str] = Query(None, description="Filter by platform"), + date_from: Optional[str] = Query(None, description="Filter by start date (format: YYYY-MM-DD)"), + date_to: Optional[str] = Query(None, description="Filter by end date (format: YYYY-MM-DD)"), +): + """Get sentiment trends over time.""" + return await social_media_service.get_sentiment_over_time(date_from=date_from, date_to=date_to, platform=platform) + + +@router.get("/posts/influential", response_model=List[Dict[str, Any]]) +async def read_influential_posts( + sentiment: Optional[str] = Query(None, description="Filter by sentiment"), + limit: int = Query(5, ge=1, le=20, description="Number of posts to return"), + date_from: Optional[str] = Query(None, description="Filter by start date (format: YYYY-MM-DD)"), + date_to: Optional[str] = Query(None, description="Filter by end date (format: YYYY-MM-DD)"), +): + """Get most influential posts by engagement, optionally filtered by sentiment.""" + return await social_media_service.get_influential_posts(sentiment=sentiment, limit=limit, date_from=date_from, date_to=date_to) + + +@router.get("/engagement/stats", response_model=Dict[str, Any]) +async def read_engagement_stats( + date_from: Optional[str] = Query(None, description="Filter by start date (format: YYYY-MM-DD)"), + date_to: Optional[str] = Query(None, description="Filter by end date (format: YYYY-MM-DD)"), +): + """Get overall engagement statistics.""" + return await social_media_service.get_engagement_stats(date_from=date_from, date_to=date_to) + + +def _run_browser_setup_background(sites: Optional[List[str]] = None): + """Background task to run browser session setup in separate thread.""" + try: + if sites and len(sites) > 1: + setup_session_multi(sites) + elif sites and len(sites) > 0: + setup_session_multi(sites) + else: + default_sites = ["https://x.com", "https://facebook.com"] + setup_session_multi(default_sites) + except Exception as e: + print(f"Browser setup error: {e}") + + +@router.post("/session/setup") +async def setup_browser_session(sites: Optional[List[str]] = Query(None, description="List of sites to setup sessions for")): + """ + Trigger browser session setup in a completely separate thread. + This will open a browser window for manual login to social media platforms. + The API immediately returns while the browser setup runs independently. + """ + thread = threading.Thread( + target=_run_browser_setup_background, + args=(sites,), + daemon=True, + ) + thread.start() + return { + "status": "ok", + "message": "Browser session setup triggered successfully", + "note": "Browser window will open shortly for manual authentication", + } \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/source_router.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/source_router.py new file mode 100644 index 0000000..81e5114 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/source_router.py @@ -0,0 +1,136 @@ +from fastapi import APIRouter, Body, Path, status +from typing import List, Optional, Dict, Any +from models.source_schemas import Source, SourceWithFeeds, Category, PaginatedSources, SourceCreate, SourceUpdate, SourceFeedCreate +from services.source_service import source_service + +router = APIRouter() + + +@router.get("/", response_model=PaginatedSources) +async def read_sources( + page: int = 1, per_page: int = 10, category: Optional[str] = None, search: Optional[str] = None, include_inactive: bool = False +): + """ + Get sources with pagination and filtering. + + - **page**: Page number (starting from 1) + - **per_page**: Number of items per page + - **category**: Filter by source category + - **search**: Search in name and description + - **include_inactive**: Include inactive sources + """ + return await source_service.get_sources(page=page, per_page=per_page, category=category, search=search, include_inactive=include_inactive) + + +@router.get("/categories", response_model=List[Category]) +async def read_categories(): + """Get all available source categories.""" + return await source_service.get_categories() + + +@router.get("/by-category/{category_name}", response_model=List[Source]) +async def read_sources_by_category(category_name: str): + """ + Get sources by category name. + + - **category_name**: Name of the category to filter by + """ + return await source_service.get_source_by_category(category_name=category_name) + + +@router.get("/{source_id}", response_model=SourceWithFeeds) +async def read_source(source_id: int = Path(..., gt=0)): + """ + Get detailed information about a specific source. + + - **source_id**: ID of the source to retrieve + """ + source = await source_service.get_source(source_id=source_id) + feeds = await source_service.get_source_feeds(source_id=source_id) + source_with_feeds = {**source, "feeds": feeds} + return source_with_feeds + + +@router.get("/by-name/{name}", response_model=SourceWithFeeds) +async def read_source_by_name(name: str): + """ + Get detailed information about a specific source by name. + + - **name**: Name of the source to retrieve + """ + source = await source_service.get_source_by_name(name=name) + feeds = await source_service.get_source_feeds(source_id=source["id"]) + source_with_feeds = {**source, "feeds": feeds} + return source_with_feeds + + +@router.post("/", response_model=SourceWithFeeds, status_code=status.HTTP_201_CREATED) +async def create_source(source_data: SourceCreate): + """ + Create a new source. + + - **source_data**: Data for the new source + """ + source = await source_service.create_source(source_data) + feeds = await source_service.get_source_feeds(source_id=source["id"]) + source_with_feeds = {**source, "feeds": feeds} + return source_with_feeds + + +@router.put("/{source_id}", response_model=SourceWithFeeds) +async def update_source(source_data: SourceUpdate, source_id: int = Path(..., gt=0)): + """ + Update an existing source. + + - **source_id**: ID of the source to update + - **source_data**: Updated data for the source + """ + source = await source_service.update_source(source_id, source_data) + feeds = await source_service.get_source_feeds(source_id=source["id"]) + source_with_feeds = {**source, "feeds": feeds} + return source_with_feeds + + +@router.delete("/{source_id}", response_model=Dict[str, Any]) +async def delete_source(source_id: int = Path(..., gt=0), permanent: bool = False): + """ + Delete a source. + + - **source_id**: ID of the source to delete + - **permanent**: If true, permanently deletes the source; otherwise, performs a soft delete + """ + if permanent: + return await source_service.hard_delete_source(source_id) + return await source_service.delete_source(source_id) + + +@router.post("/{source_id}/feeds", response_model=List[Dict[str, Any]]) +async def add_feed(feed_data: SourceFeedCreate, source_id: int = Path(..., gt=0)): + """ + Add a new feed to a source. + + - **source_id**: ID of the source to add the feed to + - **feed_data**: Data for the new feed + """ + return await source_service.add_feed_to_source(source_id, feed_data) + + +@router.put("/feeds/{feed_id}", response_model=Dict[str, Any]) +async def update_feed(feed_data: Dict[str, Any] = Body(...), feed_id: int = Path(..., gt=0)): + """ + Update an existing feed. + + - **feed_id**: ID of the feed to update + - **feed_data**: Updated data for the feed + """ + return await source_service.update_feed(feed_id, feed_data) + + +@router.delete("/feeds/{feed_id}", response_model=Dict[str, str]) +async def delete_feed(feed_id: int = Path(..., gt=0)): + """ + Delete a feed. + + - **feed_id**: ID of the feed to delete + """ + return await source_service.delete_feed(feed_id) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/task_router.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/task_router.py new file mode 100644 index 0000000..eb416ef --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/routers/task_router.py @@ -0,0 +1,138 @@ +from fastapi import APIRouter, Query, Path, Body, status +from typing import List, Optional, Dict +from models.tasks_schemas import Task, TaskCreate, TaskUpdate, TaskStats, TASK_TYPES +from services.task_service import task_service + +router = APIRouter() + + +@router.get("/", response_model=List[Task]) +async def get_tasks( + include_disabled: bool = Query(False, description="Include disabled tasks"), +): + """ + Get all tasks with optional filtering. + + - **include_disabled**: If true, includes disabled tasks + """ + return await task_service.get_tasks(include_disabled=include_disabled) + + +@router.get("/pending", response_model=List[Task]) +async def get_pending_tasks(): + """ + Get tasks that are due to run. + """ + return await task_service.get_pending_tasks() + + +@router.get("/stats", response_model=TaskStats) +async def get_task_stats(): + """ + Get task statistics. + """ + return await task_service.get_stats() + + +@router.get("/executions") +async def get_task_executions( + task_id: Optional[int] = Query(None, description="Filter by task ID"), + page: int = Query(1, ge=1, description="Page number"), + per_page: int = Query(10, ge=1, le=100, description="Items per page"), +): + """ + Get paginated task executions. + + - **task_id**: Filter by task ID + - **page**: Page number (starting from 1) + - **per_page**: Number of items per page (max 100) + """ + return await task_service.get_task_executions(task_id=task_id, page=page, per_page=per_page) + + +@router.get("/types", response_model=Dict[str, Dict[str, str]]) +async def get_task_types(): + """ + Get all available task types. + """ + return TASK_TYPES + + +@router.get("/{task_id}", response_model=Task) +async def get_task( + task_id: int = Path(..., description="The ID of the task to retrieve"), +): + """ + Get a specific task by ID. + + - **task_id**: The ID of the task to retrieve + """ + return await task_service.get_task(task_id=task_id) + + +@router.post("/", response_model=Task, status_code=status.HTTP_201_CREATED) +async def create_task(task_data: TaskCreate = Body(...)): + """ + Create a new task. + + - **task_data**: Data for the new task + """ + return await task_service.create_task( + name=task_data.name, + task_type=task_data.task_type, + frequency=task_data.frequency, + frequency_unit=task_data.frequency_unit, + description=task_data.description, + enabled=task_data.enabled, + ) + + +@router.put("/{task_id}", response_model=Task) +async def update_task( + task_id: int = Path(..., description="The ID of the task to update"), + task_data: TaskUpdate = Body(...), +): + """ + Update an existing task. + + - **task_id**: The ID of the task to update + - **task_data**: Updated data for the task + """ + updates = {k: v for k, v in task_data.dict().items() if v is not None} + return await task_service.update_task(task_id=task_id, updates=updates) + + +@router.delete("/{task_id}") +async def delete_task( + task_id: int = Path(..., description="The ID of the task to delete"), +): + """ + Delete a task. + + - **task_id**: The ID of the task to delete + """ + return await task_service.delete_task(task_id=task_id) + + +@router.post("/{task_id}/enable", response_model=Task) +async def enable_task( + task_id: int = Path(..., description="The ID of the task to enable"), +): + """ + Enable a task. + + - **task_id**: The ID of the task to enable + """ + return await task_service.toggle_task(task_id=task_id, enable=True) + + +@router.post("/{task_id}/disable", response_model=Task) +async def disable_task( + task_id: int = Path(..., description="The ID of the task to disable"), +): + """ + Disable a task. + + - **task_id**: The ID of the task to disable + """ + return await task_service.toggle_task(task_id=task_id, enable=False) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/ruff.toml b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/ruff.toml new file mode 100644 index 0000000..3c00ab7 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/ruff.toml @@ -0,0 +1 @@ +line-length = 150 \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/scheduler.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/scheduler.py new file mode 100644 index 0000000..3cdf432 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/scheduler.py @@ -0,0 +1,205 @@ +import os +import time +import signal +import subprocess +from datetime import datetime +import traceback +from concurrent.futures import ThreadPoolExecutor +from apscheduler.schedulers.background import BackgroundScheduler +from apscheduler.triggers.interval import IntervalTrigger +from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore +from db.config import get_tasks_db_path +from db.connection import db_connection +from db.tasks import ( + get_pending_tasks, + update_task_last_run, + update_task_execution, +) + +running = True +MAX_WORKERS = 5 +DEFAULT_TASK_TIMEOUT = 3600 + + +def cleanup_stuck_tasks(): + tasks_db_path = get_tasks_db_path() + try: + with db_connection(tasks_db_path) as conn: + cursor = conn.cursor() + cursor.execute( + """ + SELECT id FROM task_executions + WHERE status = 'running' + LIMIT 100 + """ + ) + running_executions = [dict(row) for row in cursor.fetchall()] + if running_executions: + print(f"WARNING: Found {len(running_executions)} tasks stuck in 'running' state. Marking as failed.") + for execution in running_executions: + execution_id = execution["id"] + error_message = "Task was interrupted by system shutdown or crash" + cursor.execute( + """ + UPDATE task_executions + SET end_time = ?, status = ?, error_message = ? + WHERE id = ? + """, + (datetime.now().isoformat(), "failed", error_message, execution_id), + ) + print(f"INFO: Marked execution {execution_id} as failed") + conn.commit() + else: + print("INFO: No stuck tasks found") + except Exception as e: + print(f"ERROR: Error cleaning up stuck tasks: {str(e)}") + print(f"ERROR: {traceback.format_exc()}") + + +def execute_task(task_id, command): + tasks_db_path = get_tasks_db_path() + with db_connection(tasks_db_path) as conn: + conn.execute("BEGIN EXCLUSIVE TRANSACTION") + try: + cursor = conn.cursor() + cursor.execute( + """ + SELECT 1 FROM task_executions + WHERE task_id = ? AND status = 'running' + LIMIT 1 + """, + (task_id,), + ) + is_running = cursor.fetchone() is not None + if is_running: + print(f"WARNING: Task {task_id} is already running, skipping this execution") + conn.commit() + return + cursor.execute( + """ + INSERT INTO task_executions + (task_id, start_time, status) + VALUES (?, ?, ?) + """, + (task_id, datetime.now().isoformat(), "running"), + ) + execution_id = cursor.lastrowid + conn.commit() + if not execution_id: + print(f"ERROR: Failed to create execution record for task {task_id}") + return + except Exception as e: + conn.rollback() + print(f"ERROR: Transaction error for task {task_id}: {str(e)}") + return + print(f"INFO: Starting task {task_id}: {command}") + try: + process = subprocess.Popen( + command, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + try: + stdout, stderr = process.communicate(timeout=DEFAULT_TASK_TIMEOUT) + if process.returncode == 0: + status = "success" + error_message = None + print(f"INFO: Task {task_id} completed successfully") + else: + status = "failed" + error_message = stderr if stderr else f"Process exited with code {process.returncode}" + print(f"ERROR: Task {task_id} failed: {error_message}") + except subprocess.TimeoutExpired: + process.kill() + stdout, stderr = process.communicate() + status = "failed" + error_message = f"Task timed out after {DEFAULT_TASK_TIMEOUT} seconds" + print(f"ERROR: Task {task_id} timed out") + output = f"STDOUT:\n{stdout}\n\nSTDERR:\n{stderr}" if stderr else stdout + update_task_execution(tasks_db_path, execution_id, status, error_message, output) + timestamp = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") + update_task_last_run(tasks_db_path, task_id, timestamp) + except Exception as e: + print(f"ERROR: Error executing task {task_id}: {str(e)}") + error_message = traceback.format_exc() + update_task_execution(tasks_db_path, execution_id, "failed", error_message) + timestamp = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") + update_task_last_run(tasks_db_path, task_id, timestamp) + + +def check_for_tasks(): + tasks_db_path = get_tasks_db_path() + try: + print("DEBUG: Checking for pending tasks...") + pending_tasks = get_pending_tasks(tasks_db_path) + if not pending_tasks: + print("DEBUG: No pending tasks found") + return + print(f"INFO: Found {len(pending_tasks)} pending tasks") + with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor: + for task in pending_tasks: + task_id = task["id"] + command = task["command"] + print(f"INFO: Scheduling task {task_id}: {task['name']} (Last run: {task['last_run']})") + executor.submit(execute_task, task_id, command) + except Exception as e: + print(f"ERROR: Error in check_for_tasks: {str(e)}") + print(f"ERROR: {traceback.format_exc()}") + + +def check_missed_tasks(): + tasks_db_path = get_tasks_db_path() + try: + print("INFO: Checking for missed tasks during downtime...") + pending_tasks = get_pending_tasks(tasks_db_path) + if pending_tasks: + print(f"INFO: Found {len(pending_tasks)} tasks to run (including any missed during downtime)") + else: + print("INFO: No missed tasks found") + except Exception as e: + print(f"ERROR: Error checking for missed tasks: {str(e)}") + + +def signal_handler(sig, frame): + global running + print("INFO: Shutdown signal received, stopping scheduler...") + running = False + + +def main(): + global running + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + print("INFO: Starting task scheduler") + tasks_db_path = get_tasks_db_path() + cleanup_stuck_tasks() + scheduler_dir = os.path.dirname(tasks_db_path) + os.makedirs(scheduler_dir, exist_ok=True) + scheduler_db_path = os.path.join(scheduler_dir, "scheduler.sqlite") + jobstores = {"default": SQLAlchemyJobStore(url=f"sqlite:///{scheduler_db_path}")} + scheduler = BackgroundScheduler(jobstores=jobstores, daemon=True) + scheduler.add_job( + check_for_tasks, + IntervalTrigger(minutes=1), + id="check_tasks", + name="Check for pending tasks", + replace_existing=True, + ) + scheduler.start() + print("INFO: Scheduler started, checking for tasks every minute") + check_for_tasks() + check_missed_tasks() + try: + while running: + time.sleep(1) + except (KeyboardInterrupt, SystemExit): + print("INFO: Scheduler interrupted") + finally: + scheduler.shutdown() + print("INFO: Scheduler shutdown complete") + + +if __name__ == "__main__": + main() diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/__init__.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/article_service.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/article_service.py new file mode 100644 index 0000000..c81d9ee --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/article_service.py @@ -0,0 +1,174 @@ +from typing import List, Optional, Dict, Any +from fastapi import HTTPException +import json +from services.db_service import tracking_db, sources_db +from models.article_schemas import Article, PaginatedArticles + + +class ArticleService: + """Service for managing article operations with the new database structure.""" + + async def get_articles( + self, + page: int = 1, + per_page: int = 10, + source: Optional[str] = None, + date_from: Optional[str] = None, + date_to: Optional[str] = None, + search: Optional[str] = None, + category: Optional[str] = None, + ) -> PaginatedArticles: + """Get articles with pagination and filtering.""" + try: + offset = (page - 1) * per_page + query_parts = [ + "SELECT ca.id, ca.title, ca.url, ca.published_date, ca.summary, ca.feed_id", + "FROM crawled_articles ca", + "WHERE ca.processed = 1 AND ca.ai_status = 'success'", + ] + query_params = [] + if source: + source_id_query = "SELECT id FROM sources WHERE name = ?" + source_id_result = await sources_db.execute_query(source_id_query, (source,), fetch=True, fetch_one=True) + if source_id_result and source_id_result.get("id"): + feed_ids_query = "SELECT id FROM source_feeds WHERE source_id = ?" + feed_ids_result = await sources_db.execute_query(feed_ids_query, (source_id_result["id"],), fetch=True) + if feed_ids_result: + feed_ids = [item["id"] for item in feed_ids_result] + placeholders = ",".join(["?" for _ in feed_ids]) + query_parts.append(f"AND ca.feed_id IN ({placeholders})") + query_params.extend(feed_ids) + if category: + query_parts.append(""" + AND EXISTS ( + SELECT 1 FROM article_categories ac + WHERE ac.article_id = ca.id AND ac.category_name = ? + ) + """) + query_params.append(category.lower()) + if date_from: + query_parts.append("AND datetime(ca.published_date) >= datetime(?)") + query_params.append(date_from) + if date_to: + query_parts.append("AND datetime(ca.published_date) <= datetime(?)") + query_params.append(date_to) + if search: + query_parts.append("AND (ca.title LIKE ? OR ca.summary LIKE ?)") + search_param = f"%{search}%" + query_params.extend([search_param, search_param]) + count_query = " ".join(query_parts).replace( + "SELECT ca.id, ca.title, ca.url, ca.published_date, ca.summary, ca.feed_id", + "SELECT COUNT(*)", + ) + total_articles = await tracking_db.execute_query(count_query, tuple(query_params), fetch=True, fetch_one=True) + total_count = total_articles.get("COUNT(*)", 0) if total_articles else 0 + query_parts.append("ORDER BY datetime(ca.published_date) DESC, ca.id DESC") + query_parts.append("LIMIT ? OFFSET ?") + query_params.extend([per_page, offset]) + articles_query = " ".join(query_parts) + articles = await tracking_db.execute_query(articles_query, tuple(query_params), fetch=True) + feed_ids = [article["feed_id"] for article in articles if article.get("feed_id")] + source_names = {} + if feed_ids: + feed_ids_str = ",".join("?" for _ in feed_ids) + source_query = f""" + SELECT sf.id as feed_id, s.name as source_name + FROM source_feeds sf + JOIN sources s ON sf.source_id = s.id + WHERE sf.id IN ({feed_ids_str}) + """ + sources_result = await sources_db.execute_query(source_query, tuple(feed_ids), fetch=True) + source_names = {item["feed_id"]: item["source_name"] for item in sources_result} + for article in articles: + feed_id = article.get("feed_id") + article["source_name"] = source_names.get(feed_id, "Unknown Source") + article.pop("feed_id", None) + article["categories"] = await self.get_article_categories(article["id"]) + total_pages = (total_count + per_page - 1) // per_page if total_count > 0 else 0 + has_next = page < total_pages + has_prev = page > 1 + return PaginatedArticles( + items=articles, + total=total_count, + page=page, + per_page=per_page, + total_pages=total_pages, + has_next=has_next, + has_prev=has_prev, + ) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching articles: {str(e)}") + + async def get_article(self, article_id: int) -> Article: + """Get a specific article by ID.""" + try: + article_query = """ + SELECT id, title, url, published_date, content, summary, feed_id, + metadata, ai_status + FROM crawled_articles + WHERE id = ? AND processed = 1 + """ + article = await tracking_db.execute_query(article_query, (article_id,), fetch=True, fetch_one=True) + if not article: + raise HTTPException(status_code=404, detail="Article not found") + if article.get("feed_id"): + source_query = """ + SELECT s.name as source_name + FROM source_feeds sf + JOIN sources s ON sf.source_id = s.id + WHERE sf.id = ? + """ + source_result = await sources_db.execute_query(source_query, (article["feed_id"],), fetch=True, fetch_one=True) + if source_result: + article["source_name"] = source_result["source_name"] + else: + article["source_name"] = "Unknown Source" + else: + article["source_name"] = "Unknown Source" + article.pop("feed_id", None) + if article.get("metadata"): + try: + article["metadata"] = json.loads(article["metadata"]) + except json.JSONDecodeError: + article["metadata"] = {} + article["categories"] = await self.get_article_categories(article_id) + return article + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching article: {str(e)}") + + async def get_article_categories(self, article_id: int) -> List[str]: + """Get categories for a specific article.""" + query = """ + SELECT category_name + FROM article_categories + WHERE article_id = ? + """ + categories = await tracking_db.execute_query(query, (article_id,), fetch=True) + return [category.get("category_name", "") for category in categories] + + async def get_sources(self) -> List[str]: + """Get all available active sources.""" + query = """ + SELECT DISTINCT name FROM sources + WHERE is_active = 1 + ORDER BY name + """ + result = await sources_db.execute_query(query, fetch=True) + return [row.get("name", "") for row in result if row.get("name")] + + async def get_categories(self) -> List[Dict[str, Any]]: + """Get all categories with article counts.""" + query = """ + SELECT category_name, COUNT(DISTINCT article_id) as article_count + FROM article_categories + GROUP BY category_name + ORDER BY article_count DESC + """ + return await tracking_db.execute_query(query, fetch=True) + + +article_service = ArticleService() diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/async_podcast_agent_service.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/async_podcast_agent_service.py new file mode 100644 index 0000000..2c99b63 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/async_podcast_agent_service.py @@ -0,0 +1,392 @@ +import os +import json +import uuid +from fastapi import status +from fastapi.responses import JSONResponse +import aiosqlite +import glob +from redis.asyncio import ConnectionPool, Redis +from db.config import get_agent_session_db_path +from db.agent_config_v2 import PODCAST_DIR, PODCAST_AUIDO_DIR, PODCAST_IMG_DIR, PODCAST_RECORDINGS_DIR, AVAILABLE_LANGS +from services.celery_tasks import agent_chat +from dotenv import load_dotenv +from services.internal_session_service import SessionService + +load_dotenv() + + +class PodcastAgentService: + def __init__(self): + os.makedirs(PODCAST_DIR, exist_ok=True) + os.makedirs(PODCAST_AUIDO_DIR, exist_ok=True) + os.makedirs(PODCAST_IMG_DIR, exist_ok=True) + os.makedirs(PODCAST_RECORDINGS_DIR, exist_ok=True) + + self.redis_host = os.environ.get("REDIS_HOST", "localhost") + self.redis_port = int(os.environ.get("REDIS_PORT", 6379)) + self.redis_db = int(os.environ.get("REDIS_DB", 0)) + self.redis_pool = ConnectionPool.from_url(f"redis://{self.redis_host}:{self.redis_port}/{self.redis_db + 1}", max_connections=10) + self.redis = Redis(connection_pool=self.redis_pool) + + async def get_active_task(self, session_id): + try: + lock_info = await self.redis.get(f"lock_info:{session_id}") + if lock_info: + try: + lock_data = json.loads(lock_info.decode("utf-8")) + task_id = lock_data.get("task_id") + if task_id: + return task_id + except (json.JSONDecodeError, AttributeError) as e: + print(f"Error parsing lock info: {e}") + return None + except Exception as e: + print(f"Error checking active task: {e}") + return None + + async def create_session(self, request=None): + if request and request.session_id: + session_id = request.session_id + try: + db_path = get_agent_session_db_path() + async with aiosqlite.connect(db_path) as conn: + async with conn.execute("SELECT 1 FROM podcast_sessions WHERE session_id = ?", (session_id,)) as cursor: + row = await cursor.fetchone() + exists = row is not None + if exists: + return {"session_id": session_id} + except Exception as e: + print(f"Error checking session existence: {e}") + new_session_id = str(uuid.uuid4()) + return {"session_id": new_session_id} + + async def chat(self, request): + try: + session_id = request.session_id + task_id = await self.get_active_task(session_id) + if task_id: + return { + "session_id": session_id, + "response": "Your request is already being processed.", + "stage": "processing", + "session_state": "{}", + "is_processing": True, + "process_type": "chat", + "task_id": task_id, + } + task = agent_chat.delay(request.session_id, request.message) + return { + "session_id": request.session_id, + "response": "Your request is being processed.", + "stage": "processing", + "session_state": "{}", + "is_processing": True, + "process_type": "chat", + "task_id": task.id, + } + except Exception as e: + return JSONResponse( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + content={ + "session_id": request.session_id, + "response": f"I encountered an error while processing your request: {str(e)}. Please try again.", + "stage": "error", + "session_state": "{}", + "error": str(e), + "is_processing": False, + }, + ) + + def _browser_recording(self, session_id): + try: + recordings_dir = os.path.join("podcasts/recordings", session_id) + webm_files = glob.glob(os.path.join(recordings_dir, "*.webm")) + if webm_files: + browser_recording_path = webm_files[0] + if (os.path.exists(browser_recording_path) and + os.path.getsize(browser_recording_path) > 8192 and + os.access(browser_recording_path, os.R_OK)): + return browser_recording_path + return None + except Exception as _: + return None + + async def check_result_status(self, request): + try: + if not request.session_id: + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content={"error": "Session ID is required"}, + ) + + browser_recording_path = self._browser_recording(request.session_id) + + task_id = getattr(request, "task_id", None) + if task_id: + task = agent_chat.AsyncResult(task_id) + if task.state == "PENDING" or task.state == "STARTED": + return { + "session_id": request.session_id, + "response": "Your request is still being processed.", + "stage": "processing", + "session_state": "{}", + "is_processing": True, + "process_type": "chat", + "task_id": task_id, + "browser_recording_path": browser_recording_path, + } + elif task.state == "SUCCESS": + result = task.result + if result and isinstance(result, dict): + if result.get("session_id") != request.session_id: + return { + "session_id": request.session_id, + "response": "Error: Received result for wrong session.", + "stage": "error", + "session_state": "{}", + "is_processing": False, + "browser_recording_path": browser_recording_path, + } + return result + else: + error_info = str(task.result) if task.result else f"Task failed with state: {task.state}" + return { + "session_id": request.session_id, + "response": f"Error processing request: {error_info}", + "stage": "error", + "session_state": "{}", + "is_processing": False, + "browser_recording_path": browser_recording_path, + } + return await self.get_session_state(request.session_id) + except Exception as e: + return JSONResponse( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + content={ + "error": f"Error checking result status: {str(e)}", + "session_id": request.session_id, + "response": f"Error checking result status: {str(e)}", + "stage": "error", + "session_state": "{}", + "is_processing": False, + "browser_recording_path": browser_recording_path, + }, + ) + + async def get_session_state(self, session_id): + try: + db_path = get_agent_session_db_path() + async with aiosqlite.connect(db_path) as conn: + conn.row_factory = lambda cursor, row: {col[0]: row[idx] for idx, col in enumerate(cursor.description)} + async with conn.execute("SELECT session_data FROM podcast_sessions WHERE session_id = ?", (session_id,)) as cursor: + row = await cursor.fetchone() + if not row: + return { + "session_id": session_id, + "response": "No session data found.", + "stage": "idle", + "session_state": "{}", + "is_processing": False, + } + + session = SessionService.get_session(session_id) + session_state = session.get("state", {}) + return { + "session_id": session_id, + "response": "", + "stage": session_state.get("stage", "idle"), + "session_state": json.dumps(session_state), + "is_processing": False, + } + except Exception as e: + return { + "session_id": session_id, + "response": f"Error retrieving session state: {str(e)}", + "stage": "error", + "session_state": "{}", + "is_processing": False, + } + + async def list_sessions(self, page=1, per_page=10): + try: + db_path = get_agent_session_db_path() + async with aiosqlite.connect(db_path) as conn: + conn.row_factory = lambda cursor, row: {col[0]: row[idx] for idx, col in enumerate(cursor.description)} + async with conn.execute( + """ + select name from sqlite_master + where type='table' and name='podcast_sessions' + """ + ) as cursor: + table = await cursor.fetchone() + if not table: + return { + "sessions": [], + "pagination": { + "total": 0, + "page": page, + "per_page": per_page, + "total_pages": 0, + }, + } + async with conn.execute("SELECT COUNT(*) as count FROM podcast_sessions") as cursor: + row = await cursor.fetchone() + total_sessions = row["count"] if row else 0 + offset = (page - 1) * per_page + async with conn.execute( + "SELECT session_id, session_data, updated_at FROM podcast_sessions ORDER BY updated_at DESC LIMIT ? OFFSET ?", (per_page, offset) + ) as cursor: + rows = await cursor.fetchall() + sessions = [] + for row in rows: + try: + session = SessionService.get_session(row["session_id"]) + session_state = session.get("state", {}) + title = session_state.get("title", "Untitled Podcast") + stage = session_state.get("stage", "welcome") + updated_at = row["updated_at"] + sessions.append({"session_id": row["session_id"], "topic": title, "stage": stage, "updated_at": updated_at}) + except Exception as e: + print(f"Error parsing session data: {e}") + return { + "sessions": sessions, + "pagination": { + "total": total_sessions, + "page": page, + "per_page": per_page, + "total_pages": (total_sessions + per_page - 1) // per_page, + }, + } + except Exception as e: + print(f"Error listing sessions: {e}") + return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"error": f"Failed to list sessions: {str(e)}"}) + + async def delete_session(self, session_id: str): + try: + db_path = get_agent_session_db_path() + async with aiosqlite.connect(db_path) as conn: + conn.row_factory = lambda cursor, row: {col[0]: row[idx] for idx, col in enumerate(cursor.description)} + async with conn.execute("SELECT session_data FROM podcast_sessions WHERE session_id = ?", (session_id,)) as cursor: + row = await cursor.fetchone() + if not row: + return JSONResponse(status_code=status.HTTP_404_NOT_FOUND, content={"error": f"Session with ID {session_id} not found"}) + try: + session = SessionService.get_session(session_id) + session_state = session.get("state", {}) + stage = session_state.get("stage") + is_completed = stage == "complete" or session_state.get("podcast_generated", False) + banner_url = session_state.get("banner_url") + audio_url = session_state.get("audio_url") + web_search_recording = session_state.get("web_search_recording") + await conn.execute("DELETE FROM podcast_sessions WHERE session_id = ?", (session_id,)) + await conn.commit() + if is_completed: + print(f"Session {session_id} is in 'complete' stage, keeping assets but removing session record") + else: + if banner_url: + banner_path = os.path.join(PODCAST_IMG_DIR, banner_url) + if os.path.exists(banner_path): + try: + os.remove(banner_path) + print(f"Deleted banner image: {banner_path}") + except Exception as e: + print(f"Error deleting banner image: {e}") + if audio_url: + audio_path = os.path.join(PODCAST_AUIDO_DIR, audio_url) + if os.path.exists(audio_path): + try: + os.remove(audio_path) + print(f"Deleted audio file: {audio_path}") + except Exception as e: + print(f"Error deleting audio file: {e}") + if web_search_recording: + recording_dir = os.path.join(PODCAST_RECORDINGS_DIR, session_id) + if os.path.exists(recording_dir): + try: + import shutil + + shutil.rmtree(recording_dir) + print(f"Deleted recordings directory: {recording_dir}") + except Exception as e: + print(f"Error deleting recordings directory: {e}") + if is_completed: + return {"success": True, "message": f"Session {session_id} deleted, but assets preserved"} + else: + return {"success": True, "message": f"Session {session_id} and its associated data deleted successfully"} + except Exception as e: + print(f"Error parsing session data for deletion: {e}") + return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"error": f"Error deleting session: {str(e)}"}) + except Exception as e: + print(f"Error deleting session: {e}") + return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"error": f"Failed to delete session: {str(e)}"}) + + async def _get_chat_messages(self, row, session_id): + formatted_messages = [] + session_state = {} + if row["session_data"]: + try: + session = SessionService.get_session(session_id) + session_state = session.get("state", {}) + except Exception as e: + print(f"Error parsing session_data: {e}") + + if row["memory"]: + try: + memory_data = json.loads(row["memory"]) if isinstance(row["memory"], str) else row["memory"] + if "runs" in memory_data and isinstance(memory_data["runs"], list): + for run in memory_data["runs"]: + if "messages" in run and isinstance(run["messages"], list): + for msg in run["messages"]: + if msg.get("role") in ["user", "assistant"] and "content" in msg: + if msg.get("role") == "assistant" and "tool_calls" in msg: + if not msg.get("content"): + continue + if msg.get("content"): + formatted_messages.append({"role": msg["role"], "content": msg["content"]}) + except json.JSONDecodeError as e: + print(f"Error parsing memory data: {e}") + + return formatted_messages, session_state + + async def get_session_history(self, session_id: str): + try: + db_path = get_agent_session_db_path() + async with aiosqlite.connect(db_path) as conn: + conn.row_factory = lambda cursor, row: {col[0]: row[idx] for idx, col in enumerate(cursor.description)} + async with conn.execute( + """ + select name from sqlite_master + where type='table' and name='podcast_sessions' + """ + ) as cursor: + table = await cursor.fetchone() + if not table: + return {"session_id": session_id, "messages": [], "state": "{}", "is_processing": False, "process_type": None} + async with conn.execute("SELECT memory, session_data FROM podcast_sessions WHERE session_id = ?", (session_id,)) as cursor: + row = await cursor.fetchone() + if not row: + return {"session_id": session_id, "messages": [], "state": "{}", "is_processing": False, "process_type": None} + formatted_messages, session_state = await self._get_chat_messages(row, session_id) + + task_id = await self.get_active_task(session_id) + is_processing = bool(task_id) + process_type = "chat" if is_processing else None + browser_recording_path = self._browser_recording(session_id) + + return { + "session_id": session_id, + "messages": formatted_messages, + "state": json.dumps(session_state), + "is_processing": is_processing, + "process_type": process_type, + "task_id": task_id if task_id and is_processing else None, + "browser_recording_path": browser_recording_path, + } + except Exception as e: + return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"error": f"Error retrieving session history: {str(e)}"}) + + async def get_supported_languages(self): + return {"languages": AVAILABLE_LANGS} + + +podcast_agent_service = PodcastAgentService() \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/celery_app.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/celery_app.py new file mode 100644 index 0000000..4880c05 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/celery_app.py @@ -0,0 +1,70 @@ +from celery import Celery, Task +import redis +import os +import time +import json +from dotenv import load_dotenv + + +load_dotenv() + +REDIS_HOST = os.environ.get("REDIS_HOST", "localhost") +REDIS_PORT = int(os.environ.get("REDIS_PORT", 6379)) +REDIS_DB = int(os.environ.get("REDIS_DB", 0)) +REDIS_LOCK_EXP_TIME_SEC = 60 * 10 +REDIS_LOCK_INFO_EXP_TIME_SEC = 60 * 15 +STALE_LOCK_THRESHOLD_SEC = 60 * 15 + +redis_client = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB + 1) + +app = Celery("beifong_tasks", broker=f"redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}", backend=f"redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}") + +app.conf.update( + result_expires=60 * 2, + task_track_started=True, + worker_concurrency=2, + task_acks_late=True, + task_time_limit=600, + task_soft_time_limit=540, +) + + +class SessionLockedTask(Task): + def __call__(self, *args, **kwargs): + session_id = args[0] if args else kwargs.get("session_id") + if not session_id: + return super().__call__(*args, **kwargs) + + lock_key = f"lock:{session_id}" + lock_info = redis_client.get(f"lock_info:{session_id}") + if lock_info: + try: + lock_data = json.loads(lock_info.decode("utf-8")) + lock_time = lock_data.get("timestamp", 0) + if time.time() - lock_time > STALE_LOCK_THRESHOLD_SEC: + redis_client.delete(lock_key) + redis_client.delete(f"lock_info:{session_id}") + except (ValueError, TypeError) as e: + print(f"Error checking lock time: {e}") + + acquired = redis_client.set(lock_key, "1", nx=True, ex=REDIS_LOCK_EXP_TIME_SEC) + if acquired: + lock_data = {"timestamp": time.time(), "task_id": self.request.id if hasattr(self, "request") else None} + redis_client.set(f"lock_info:{session_id}", json.dumps(lock_data), ex=REDIS_LOCK_INFO_EXP_TIME_SEC) + + if not acquired: + return { + "error": "Session busy", + "response": "This session is already processing a message. Please wait.", + "session_id": session_id, + "stage": "busy", + "session_state": "{}", + "is_processing": True, + "process_type": "chat", + } + + try: + return super().__call__(*args, **kwargs) + finally: + redis_client.delete(lock_key) + redis_client.delete(f"lock_info:{session_id}") diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/celery_tasks.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/celery_tasks.py new file mode 100644 index 0000000..ff5735b --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/celery_tasks.py @@ -0,0 +1,85 @@ +from agno.agent import Agent +from agno.models.openai import OpenAIChat +from agno.storage.sqlite import SqliteStorage +import os +from dotenv import load_dotenv +from services.celery_app import app, SessionLockedTask +from db.config import get_agent_session_db_path +from db.agent_config_v2 import ( + AGENT_DESCRIPTION, + AGENT_INSTRUCTIONS, + AGENT_MODEL, + INITIAL_SESSION_STATE, +) +from agents.search_agent import search_agent_run +from agents.scrape_agent import scrape_agent_run +from agents.script_agent import podcast_script_agent_run +from tools.ui_manager import ui_manager_run +from tools.user_source_selection import user_source_selection_run +from tools.session_state_manager import update_language, update_chat_title, mark_session_finished +from agents.image_generate_agent import image_generation_agent_run +from agents.audio_generate_agent import audio_generate_agent_run +import json + +load_dotenv() + +db_file = get_agent_session_db_path() + + +@app.task(bind=True, max_retries=0, base=SessionLockedTask) +def agent_chat(self, session_id, message): + try: + print(f"Processing message for session {session_id}: {message[:50]}...") + db_file = get_agent_session_db_path() + os.makedirs(os.path.dirname(db_file), exist_ok=True) + from services.internal_session_service import SessionService + + session_state = SessionService.get_session(session_id).get("state", INITIAL_SESSION_STATE) + + _agent = Agent( + model=OpenAIChat(id=AGENT_MODEL, api_key=os.getenv("OPENAI_API_KEY")), + storage=SqliteStorage(table_name="podcast_sessions", db_file=db_file), + add_history_to_messages=True, + read_chat_history=True, + add_state_in_messages=True, + num_history_runs=30, + instructions=AGENT_INSTRUCTIONS, + description=AGENT_DESCRIPTION, + session_state=session_state, + session_id=session_id, + tools=[ + search_agent_run, + scrape_agent_run, + ui_manager_run, + user_source_selection_run, + update_language, + podcast_script_agent_run, + image_generation_agent_run, + audio_generate_agent_run, + update_chat_title, + mark_session_finished, + ], + markdown=True, + ) + response = _agent.run(message, session_id=session_id) + print(f"Response generated for session {session_id}") + _agent.write_to_storage(session_id=session_id) + session_state = SessionService.get_session(session_id).get("state", INITIAL_SESSION_STATE) + return { + "session_id": session_id, + "response": response.content, + "stage": _agent.session_state.get("stage", "unknown"), + "session_state": json.dumps(session_state), + "is_processing": False, + "process_type": None, + } + except Exception as e: + print(f"Error in agent_chat for session {session_id}: {str(e)}") + return { + "session_id": session_id, + "response": f"I'm sorry, I encountered an error: {str(e)}. Please try again.", + "stage": "error", + "session_state": "{}", + "is_processing": False, + "process_type": None, + } diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/db_init.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/db_init.py new file mode 100644 index 0000000..82fcf33 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/db_init.py @@ -0,0 +1,369 @@ +import os +import sqlite3 +import asyncio +import time +from contextlib import contextmanager +from concurrent.futures import ThreadPoolExecutor +from services.db_service import get_db_path + + +@contextmanager +def db_connection(db_path): + conn = sqlite3.connect(db_path) + conn.row_factory = sqlite3.Row + conn.execute("PRAGMA journal_mode=WAL") + conn.execute("PRAGMA synchronous=NORMAL") + conn.execute("PRAGMA cache_size=10000") + conn.execute("PRAGMA temp_store=MEMORY") + try: + yield conn + finally: + conn.close() + + +def init_sources_db(): + start_time = time.time() + db_path = get_db_path("sources_db") + with db_connection(db_path) as conn: + cursor = conn.cursor() + cursor.execute("BEGIN TRANSACTION") + cursor.execute(""" + CREATE TABLE IF NOT EXISTS sources ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + description TEXT, + url TEXT, + is_active BOOLEAN DEFAULT 1, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + cursor.execute(""" + CREATE TABLE IF NOT EXISTS categories ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT UNIQUE NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + cursor.execute(""" + CREATE TABLE IF NOT EXISTS source_categories ( + source_id INTEGER, + category_id INTEGER, + PRIMARY KEY (source_id, category_id), + FOREIGN KEY (source_id) REFERENCES sources(id), + FOREIGN KEY (category_id) REFERENCES categories(id) + ) + """) + cursor.execute(""" + CREATE TABLE IF NOT EXISTS source_feeds ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + source_id INTEGER, + feed_url TEXT UNIQUE, + feed_type TEXT, + is_active BOOLEAN DEFAULT 1, + last_crawled TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (source_id) REFERENCES sources(id) + ) + """) + cursor.execute("CREATE INDEX IF NOT EXISTS idx_source_feeds_source_id ON source_feeds(source_id)") + cursor.execute("CREATE INDEX IF NOT EXISTS idx_sources_is_active ON sources(is_active)") + cursor.execute("CREATE INDEX IF NOT EXISTS idx_source_feeds_is_active ON source_feeds(is_active)") + cursor.execute("CREATE INDEX IF NOT EXISTS idx_source_categories_source_id ON source_categories(source_id)") + cursor.execute("CREATE INDEX IF NOT EXISTS idx_source_categories_category_id ON source_categories(category_id)") + conn.commit() + + elapsed = time.time() - start_time + print(f"Sources database initialized in {elapsed:.3f}s") + + +def init_tracking_db(): + start_time = time.time() + db_path = get_db_path("tracking_db") + with db_connection(db_path) as conn: + cursor = conn.cursor() + cursor.execute("BEGIN TRANSACTION") + cursor.execute(""" + CREATE TABLE IF NOT EXISTS feed_tracking ( + feed_id INTEGER PRIMARY KEY, + source_id INTEGER, + feed_url TEXT, + last_processed TIMESTAMP, + last_etag TEXT, + last_modified TEXT, + entry_hash TEXT + ) + """) + cursor.execute(""" + CREATE TABLE IF NOT EXISTS feed_entries ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + feed_id INTEGER, + source_id INTEGER, + entry_id TEXT, + title TEXT, + link TEXT UNIQUE, + published_date TIMESTAMP, + content TEXT, + summary TEXT, + crawl_status TEXT DEFAULT 'pending', + crawl_attempts INTEGER DEFAULT 0, + processed_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(feed_id, entry_id) + ) + """) + cursor.execute(""" + CREATE TABLE IF NOT EXISTS crawled_articles ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + entry_id INTEGER, + source_id INTEGER, + feed_id INTEGER, + title TEXT, + url TEXT UNIQUE, + published_date TIMESTAMP, + raw_content TEXT, + content TEXT, + summary TEXT, + metadata TEXT, + ai_status TEXT DEFAULT 'pending', + ai_error TEXT DEFAULT NULL, + ai_attempts INTEGER DEFAULT 0, + crawled_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + processed BOOLEAN DEFAULT 0, + embedding_status TEXT DEFAULT NULL, + FOREIGN KEY (entry_id) REFERENCES feed_entries(id) + ) + """) + cursor.execute(""" + CREATE TABLE IF NOT EXISTS article_categories ( + article_id INTEGER, + category_name TEXT NOT NULL, + PRIMARY KEY (article_id, category_name), + FOREIGN KEY (article_id) REFERENCES crawled_articles(id) + ) + """) + cursor.execute(""" + CREATE TABLE IF NOT EXISTS article_embeddings ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + article_id INTEGER NOT NULL, + embedding BLOB NOT NULL, + embedding_model TEXT NOT NULL, + created_at TEXT NOT NULL, + in_faiss_index INTEGER DEFAULT 0, + FOREIGN KEY (article_id) REFERENCES crawled_articles(id) + ) + """) + indexes = [ + "CREATE INDEX IF NOT EXISTS idx_feed_entries_feed_id ON feed_entries(feed_id)", + "CREATE INDEX IF NOT EXISTS idx_feed_entries_link ON feed_entries(link)", + "CREATE INDEX IF NOT EXISTS idx_crawled_articles_url ON crawled_articles(url)", + "CREATE INDEX IF NOT EXISTS idx_crawled_articles_entry_id ON crawled_articles(entry_id)", + "CREATE INDEX IF NOT EXISTS idx_feed_entries_crawl_status ON feed_entries(crawl_status)", + "CREATE INDEX IF NOT EXISTS idx_crawled_articles_processed ON crawled_articles(processed)", + "CREATE INDEX IF NOT EXISTS idx_crawled_articles_ai_status ON crawled_articles(ai_status)", + "CREATE INDEX IF NOT EXISTS idx_article_categories_article_id ON article_categories(article_id)", + "CREATE INDEX IF NOT EXISTS idx_article_categories_category_name ON article_categories(category_name)", + "CREATE INDEX IF NOT EXISTS idx_article_embeddings_article_id ON article_embeddings(article_id)", + "CREATE INDEX IF NOT EXISTS idx_article_embeddings_in_faiss ON article_embeddings(in_faiss_index)", + "CREATE INDEX IF NOT EXISTS idx_crawled_articles_embedding_status ON crawled_articles(embedding_status)", + ] + for index_sql in indexes: + cursor.execute(index_sql) + conn.commit() + elapsed = time.time() - start_time + print(f"Tracking database initialized in {elapsed:.3f}s") + + +def init_podcasts_db(): + start_time = time.time() + db_path = get_db_path("podcasts_db") + with db_connection(db_path) as conn: + cursor = conn.cursor() + cursor.execute("BEGIN TRANSACTION") + cursor.execute(""" + CREATE TABLE IF NOT EXISTS podcasts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT, + date TEXT, + content_json TEXT, + audio_generated BOOLEAN DEFAULT 0, + audio_path TEXT, + banner_img_path TEXT, + tts_engine TEXT DEFAULT 'elevenlabs', + language_code TEXT DEFAULT 'en', + sources_json TEXT, + banner_images TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + indexes = [ + "CREATE INDEX IF NOT EXISTS idx_podcasts_date ON podcasts(date)", + "CREATE INDEX IF NOT EXISTS idx_podcasts_audio_generated ON podcasts(audio_generated)", + "CREATE INDEX IF NOT EXISTS idx_podcasts_tts_engine ON podcasts(tts_engine)", + "CREATE INDEX IF NOT EXISTS idx_podcasts_language_code ON podcasts(language_code)", + ] + for index_sql in indexes: + cursor.execute(index_sql) + conn.commit() + elapsed = time.time() - start_time + print(f"Podcasts database initialized in {elapsed:.3f}s") + + +def init_tasks_db(): + start_time = time.time() + db_path = get_db_path("tasks_db") + with db_connection(db_path) as conn: + cursor = conn.cursor() + cursor.execute("BEGIN TRANSACTION") + cursor.execute(""" + CREATE TABLE IF NOT EXISTS tasks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + task_type TEXT, + description TEXT, + command TEXT NOT NULL, + frequency INTEGER NOT NULL, + frequency_unit TEXT NOT NULL, + enabled BOOLEAN DEFAULT 1, + last_run TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + cursor.execute(""" + CREATE TABLE IF NOT EXISTS task_executions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + task_id INTEGER NOT NULL, + start_time TIMESTAMP NOT NULL, + end_time TIMESTAMP, + status TEXT NOT NULL, + error_message TEXT, + output TEXT, + FOREIGN KEY (task_id) REFERENCES tasks(id) + ) + """) + cursor.execute(""" + CREATE TABLE IF NOT EXISTS podcast_configs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + description TEXT, + prompt TEXT NOT NULL, + time_range_hours INTEGER DEFAULT 24, + limit_articles INTEGER DEFAULT 20, + is_active BOOLEAN DEFAULT 1, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + tts_engine TEXT DEFAULT 'elevenlabs', + language_code TEXT DEFAULT 'en', + podcast_script_prompt TEXT, + image_prompt TEXT + ) + """) + indexes = [ + "CREATE INDEX IF NOT EXISTS idx_tasks_enabled ON tasks(enabled)", + "CREATE INDEX IF NOT EXISTS idx_tasks_frequency ON tasks(frequency, frequency_unit)", + "CREATE INDEX IF NOT EXISTS idx_tasks_last_run ON tasks(last_run)", + "CREATE INDEX IF NOT EXISTS idx_task_executions_task_id ON task_executions(task_id)", + "CREATE INDEX IF NOT EXISTS idx_task_executions_status ON task_executions(status)", + "CREATE INDEX IF NOT EXISTS idx_task_executions_start_time ON task_executions(start_time)", + "CREATE INDEX IF NOT EXISTS idx_podcast_configs_is_active ON podcast_configs(is_active)", + "CREATE INDEX IF NOT EXISTS idx_podcast_configs_name ON podcast_configs(name)", + ] + for index_sql in indexes: + cursor.execute(index_sql) + conn.commit() + elapsed = time.time() - start_time + print(f"Tasks database initialized in {elapsed:.3f}s") + + +async def init_agent_session_db(): + """Initialize the agent session database. auto generated""" + pass + + +def init_internal_sessions_db(): + start_time = time.time() + db_path = get_db_path("internal_sessions_db") + with db_connection(db_path) as conn: + cursor = conn.cursor() + cursor.execute("BEGIN TRANSACTION") + cursor.execute(""" + CREATE TABLE IF NOT EXISTS session_state ( + session_id TEXT PRIMARY KEY, + state JSON, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + cursor.execute("CREATE INDEX IF NOT EXISTS idx_session_state_session_id ON session_state(session_id)") + conn.commit() + elapsed = time.time() - start_time + print(f"Internal sessions database initialized in {elapsed:.3f}s") + + +def init_social_media_db(): + start_time = time.time() + db_path = get_db_path("social_media_db") + with db_connection(db_path) as conn: + cursor = conn.cursor() + cursor.execute("BEGIN TRANSACTION") + cursor.execute(""" + CREATE TABLE IF NOT EXISTS posts ( + post_id TEXT PRIMARY KEY, + platform TEXT, + user_display_name TEXT, + user_handle TEXT, + user_profile_pic_url TEXT, + post_timestamp TEXT, + post_display_time TEXT, + post_url TEXT, + post_text TEXT, + post_mentions TEXT, + engagement_reply_count INTEGER, + engagement_retweet_count INTEGER, + engagement_like_count INTEGER, + engagement_bookmark_count INTEGER, + engagement_view_count INTEGER, + media TEXT, + media_count INTEGER, + is_ad BOOLEAN, + sentiment TEXT, + categories TEXT, + tags TEXT, + analysis_reasoning TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + indexes = [ + "CREATE INDEX IF NOT EXISTS idx_posts_platform ON posts(platform)", + "CREATE INDEX IF NOT EXISTS idx_posts_user_handle ON posts(user_handle)", + "CREATE INDEX IF NOT EXISTS idx_posts_post_timestamp ON posts(post_timestamp)", + "CREATE INDEX IF NOT EXISTS idx_posts_sentiment ON posts(sentiment)", + ] + for index_sql in indexes: + cursor.execute(index_sql) + conn.commit() + elapsed = time.time() - start_time + print(f"Social media database initialized in {elapsed:.3f}s") + + +async def init_databases(): + total_start = time.time() + print("Initializing all databases...") + for db_name in ["sources_db", "tracking_db", "podcasts_db", "tasks_db"]: + db_path = get_db_path(db_name) + os.makedirs(os.path.dirname(db_path), exist_ok=True) + loop = asyncio.get_event_loop() + with ThreadPoolExecutor(max_workers=4) as executor: + tasks = [ + loop.run_in_executor(executor, init_sources_db), + loop.run_in_executor(executor, init_tracking_db), + loop.run_in_executor(executor, init_podcasts_db), + loop.run_in_executor(executor, init_tasks_db), + loop.run_in_executor(executor, init_internal_sessions_db), + loop.run_in_executor(executor, init_social_media_db), + ] + await asyncio.gather(*tasks) + total_elapsed = time.time() - total_start + print(f"All databases initialized in {total_elapsed:.3f}s") + + +def init_all_databases(): + asyncio.run(init_databases()) \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/db_service.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/db_service.py new file mode 100644 index 0000000..2a367b3 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/db_service.py @@ -0,0 +1,75 @@ +import os +from typing import Dict, List, Any, Tuple, Union +from fastapi import HTTPException +from contextlib import contextmanager +import sqlite3 +from db.config import get_db_path + + +@contextmanager +def db_connection(db_path: str): + """Context manager for database connections.""" + if not os.path.exists(db_path): + raise HTTPException(status_code=404, detail=f"Database {db_path} not found. Initialize the database first.") + conn = sqlite3.connect(db_path) + conn.row_factory = sqlite3.Row + try: + yield conn + finally: + conn.close() + + +class DatabaseService: + """Service for managing database connections and operations.""" + + def __init__(self, db_name: str): + """ + Initialize the database service. + + Args: + db_name: Name of the database (sources_db, tracking_db, etc.) + """ + self.db_path = get_db_path(db_name) + + async def execute_query( + self, query: str, params: Tuple = (), fetch: bool = False, fetch_one: bool = False + ) -> Union[List[Dict[str, Any]], Dict[str, Any], int]: + """Execute a query with error handling for FastAPI.""" + try: + with db_connection(self.db_path) as conn: + cursor = conn.cursor() + cursor.execute(query, params) + + if fetch_one: + result = cursor.fetchone() + return dict(result) if result else None + elif fetch: + return [dict(row) for row in cursor.fetchall()] + else: + conn.commit() + return cursor.lastrowid + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Database error: {str(e)}") + + async def execute_write_many(self, query: str, params_list: List[Tuple]) -> int: + """Execute multiple write operations in a single transaction.""" + try: + with db_connection(self.db_path) as conn: + cursor = conn.cursor() + cursor.executemany(query, params_list) + conn.commit() + return cursor.rowcount + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Database error: {str(e)}") + + + +sources_db = DatabaseService(db_name="sources_db") +tracking_db = DatabaseService(db_name="tracking_db") +podcasts_db = DatabaseService(db_name="podcasts_db") +tasks_db = DatabaseService(db_name="tasks_db") +social_media_db = DatabaseService(db_name="social_media_db") diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/internal_session_service.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/internal_session_service.py new file mode 100644 index 0000000..92c6a5d --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/internal_session_service.py @@ -0,0 +1,155 @@ +from typing import Optional, Dict, Any +from fastapi import HTTPException +import json +from datetime import datetime +from db.config import get_db_path +from db.agent_config_v2 import INITIAL_SESSION_STATE +import sqlite3 +from contextlib import contextmanager + + +@contextmanager +def get_db_connection(db_name: str): + """Get a fresh database connection each time.""" + db_path = get_db_path(db_name) + conn = sqlite3.connect(db_path) + conn.row_factory = sqlite3.Row + try: + yield conn + finally: + conn.close() + + +class SessionService: + """Service for managing internal session operations.""" + + @staticmethod + def get_session(session_id: str) -> Dict[str, Any]: + try: + with get_db_connection("internal_sessions_db") as conn: + cursor = conn.cursor() + query = """ + SELECT session_id, state, created_at + FROM session_state + WHERE session_id = ? + """ + cursor.execute(query, (session_id,)) + session = cursor.fetchone() + if not session: + return SessionService._initialize_session(session_id) + session_dict = dict(session) + if session_dict.get("state"): + try: + session_dict["state"] = json.loads(session_dict["state"]) + except json.JSONDecodeError: + session_dict["state"] = {} + return session_dict + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching session: {str(e)}") + + @staticmethod + def _initialize_session(session_id: str) -> Dict[str, Any]: + try: + with get_db_connection("internal_sessions_db") as conn: + cursor = conn.cursor() + state_json = json.dumps(INITIAL_SESSION_STATE) + insert_query = """ + INSERT INTO session_state (session_id, state, created_at) + VALUES (?, ?, ?) + """ + current_time = datetime.now().isoformat() + cursor.execute(insert_query, (session_id, state_json, current_time)) + conn.commit() + return {"session_id": session_id, "state": INITIAL_SESSION_STATE, "created_at": current_time} + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error initializing session: {str(e)}") + + @staticmethod + def save_session(session_id: str, state: Dict[str, Any]) -> Dict[str, Any]: + try: + state_json = json.dumps(state) + with get_db_connection("internal_sessions_db") as conn: + cursor = conn.cursor() + conn.execute("BEGIN IMMEDIATE") + existing_query = "SELECT session_id FROM session_state WHERE session_id = ?" + cursor.execute(existing_query, (session_id,)) + existing_session = cursor.fetchone() + if existing_session: + update_query = "UPDATE session_state SET state = ? WHERE session_id = ?" + cursor.execute(update_query, (state_json, session_id)) + else: + insert_query = "INSERT INTO session_state (session_id, state, created_at) VALUES (?, ?, ?)" + current_time = datetime.now().isoformat() + cursor.execute(insert_query, (session_id, state_json, current_time)) + conn.commit() + return SessionService.get_session(session_id) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error saving session: {str(e)}") + + @staticmethod + def delete_session(session_id: str) -> Dict[str, str]: + try: + with get_db_connection("internal_sessions_db") as conn: + cursor = conn.cursor() + existing_query = "SELECT session_id FROM session_state WHERE session_id = ?" + cursor.execute(existing_query, (session_id,)) + existing_session = cursor.fetchone() + if not existing_session: + raise HTTPException(status_code=404, detail="Session not found") + delete_query = "DELETE FROM session_state WHERE session_id = ?" + cursor.execute(delete_query, (session_id,)) + conn.commit() + return {"message": f"Session with ID {session_id} successfully deleted"} + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error deleting session: {str(e)}") + + @staticmethod + def list_sessions(page: int = 1, per_page: int = 10, search: Optional[str] = None) -> Dict[str, Any]: + try: + with get_db_connection("internal_sessions_db") as conn: + cursor = conn.cursor() + offset = (page - 1) * per_page + query_parts = [ + "SELECT session_id, created_at", + "FROM session_state", + ] + query_params = [] + if search: + query_parts.append("WHERE session_id LIKE ?") + search_param = f"%{search}%" + query_params.append(search_param) + + count_query = " ".join(query_parts).replace( + "SELECT session_id, created_at", + "SELECT COUNT(*)", + ) + cursor.execute(count_query, tuple(query_params)) + total_count = cursor.fetchone()[0] + query_parts.append("ORDER BY created_at DESC") + query_parts.append("LIMIT ? OFFSET ?") + query_params.extend([per_page, offset]) + sessions_query = " ".join(query_parts) + cursor.execute(sessions_query, tuple(query_params)) + sessions = [dict(row) for row in cursor.fetchall()] + total_pages = (total_count + per_page - 1) // per_page if total_count > 0 else 0 + has_next = page < total_pages + has_prev = page > 1 + return { + "items": sessions, + "total": total_count, + "page": page, + "per_page": per_page, + "total_pages": total_pages, + "has_next": has_next, + "has_prev": has_prev, + } + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error listing sessions: {str(e)}") \ No newline at end of file diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/podcast_config_service.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/podcast_config_service.py new file mode 100644 index 0000000..e0c4e83 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/podcast_config_service.py @@ -0,0 +1,177 @@ +from typing import List, Optional, Dict, Any +from datetime import datetime +from fastapi import HTTPException +from services.db_service import tasks_db + + +class PodcastConfigService: + """Service for managing podcast configurations.""" + + async def get_all_configs(self, active_only: bool = False) -> List[Dict[str, Any]]: + """Get all podcast configurations with optional filtering.""" + try: + if active_only: + query = """ + SELECT id, name, description, prompt, time_range_hours, limit_articles, + is_active, tts_engine, language_code, podcast_script_prompt, + image_prompt, created_at, updated_at + FROM podcast_configs + WHERE is_active = 1 + ORDER BY name + """ + params = () + else: + query = """ + SELECT id, name, description, prompt, time_range_hours, limit_articles, + is_active, tts_engine, language_code, podcast_script_prompt, + image_prompt, created_at, updated_at + FROM podcast_configs + ORDER BY name + """ + params = () + configs = await tasks_db.execute_query(query, params, fetch=True) + for config in configs: + config["is_active"] = bool(config.get("is_active", 0)) + return configs + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching podcast configurations: {str(e)}") + + async def get_config(self, config_id: int) -> Dict[str, Any]: + """Get a specific podcast configuration by ID.""" + try: + query = """ + SELECT id, name, description, prompt, time_range_hours, limit_articles, + is_active, tts_engine, language_code, podcast_script_prompt, + image_prompt, created_at, updated_at + FROM podcast_configs + WHERE id = ? + """ + config = await tasks_db.execute_query(query, (config_id,), fetch=True, fetch_one=True) + if not config: + raise HTTPException(status_code=404, detail="Podcast configuration not found") + config["is_active"] = bool(config.get("is_active", 0)) + return config + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching podcast configuration: {str(e)}") + + async def create_config( + self, + name: str, + prompt: str, + description: Optional[str] = None, + time_range_hours: int = 24, + limit_articles: int = 20, + is_active: bool = True, + tts_engine: str = "kokoro", + language_code: str = "en", + podcast_script_prompt: Optional[str] = None, + image_prompt: Optional[str] = None, + ) -> Dict[str, Any]: + """Create a new podcast configuration.""" + try: + current_time = datetime.now().isoformat() + query = """ + INSERT INTO podcast_configs + (name, description, prompt, time_range_hours, limit_articles, + is_active, tts_engine, language_code, podcast_script_prompt, + image_prompt, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """ + params = ( + name, + description, + prompt, + time_range_hours, + limit_articles, + 1 if is_active else 0, + tts_engine, + language_code, + podcast_script_prompt, + image_prompt, + current_time, + current_time, + ) + config_id = await tasks_db.execute_query(query, params) + return await self.get_config(config_id) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error creating podcast configuration: {str(e)}") + + async def update_config(self, config_id: int, updates: Dict[str, Any]) -> Dict[str, Any]: + """Update an existing podcast configuration.""" + try: + allowed_fields = [ + "name", + "description", + "prompt", + "time_range_hours", + "limit_articles", + "is_active", + "tts_engine", + "language_code", + "podcast_script_prompt", + "image_prompt", + ] + set_clauses = [] + params = [] + set_clauses.append("updated_at = ?") + params.append(datetime.now().isoformat()) + for field, value in updates.items(): + if field in allowed_fields: + if field == "is_active": + value = 1 if value else 0 + set_clauses.append(f"{field} = ?") + params.append(value) + if not set_clauses: + return await self.get_config(config_id) + params.append(config_id) + update_query = f""" + UPDATE podcast_configs + SET {", ".join(set_clauses)} + WHERE id = ? + """ + await tasks_db.execute_query(update_query, tuple(params)) + return await self.get_config(config_id) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error updating podcast configuration: {str(e)}") + + async def delete_config(self, config_id: int) -> Dict[str, str]: + """Delete a podcast configuration.""" + try: + config = await self.get_config(config_id) + query = """ + DELETE FROM podcast_configs + WHERE id = ? + """ + await tasks_db.execute_query(query, (config_id,)) + return {"message": f"Podcast configuration '{config['name']}' has been deleted"} + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error deleting podcast configuration: {str(e)}") + + async def toggle_config(self, config_id: int, enable: bool) -> Dict[str, Any]: + """Enable or disable a podcast configuration.""" + try: + query = """ + UPDATE podcast_configs + SET is_active = ?, updated_at = ? + WHERE id = ? + """ + current_time = datetime.now().isoformat() + await tasks_db.execute_query(query, (1 if enable else 0, current_time, config_id)) + return await self.get_config(config_id) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error updating podcast configuration: {str(e)}") + + +podcast_config_service = PodcastConfigService() diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/podcast_service.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/podcast_service.py new file mode 100644 index 0000000..2964c94 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/podcast_service.py @@ -0,0 +1,349 @@ +import os +import json +from typing import List, Dict, Optional, Any +from datetime import datetime +from fastapi import HTTPException, UploadFile +from services.db_service import podcasts_db +import math + +AUDIO_DIR = "podcasts/audio" +IMAGE_DIR = "podcasts/images" + + +class PodcastService: + """Service for managing podcast operations with the new database structure.""" + + def __init__(self): + """Initialize the podcast service with directories.""" + os.makedirs(AUDIO_DIR, exist_ok=True) + os.makedirs(IMAGE_DIR, exist_ok=True) + + async def get_podcasts( + self, + page: int = 1, + per_page: int = 10, + search: str = None, + date_from: str = None, + date_to: str = None, + language_code: str = None, + tts_engine: str = None, + has_audio: bool = None, + ) -> Dict[str, Any]: + """ + Get a paginated list of podcasts with optional filtering. + """ + try: + offset = (page - 1) * per_page + count_query = "SELECT COUNT(*) as count FROM podcasts" + query = """ + SELECT id, title, date, audio_generated, audio_path, banner_img_path, + language_code, tts_engine, created_at + FROM podcasts + """ + where_conditions = [] + params = [] + if search: + where_conditions.append("(title LIKE ?)") + search_param = f"%{search}%" + params.append(search_param) + if date_from: + where_conditions.append("date >= ?") + params.append(date_from) + if date_to: + where_conditions.append("date <= ?") + params.append(date_to) + if language_code: + where_conditions.append("language_code = ?") + params.append(language_code) + if tts_engine: + where_conditions.append("tts_engine = ?") + params.append(tts_engine) + if has_audio is not None: + where_conditions.append("audio_generated = ?") + params.append(1 if has_audio else 0) + if where_conditions: + where_clause = " WHERE " + " AND ".join(where_conditions) + query += where_clause + count_query += where_clause + query += " ORDER BY date DESC, created_at DESC" + query += " LIMIT ? OFFSET ?" + params.extend([per_page, offset]) + total_result = await podcasts_db.execute_query(count_query, tuple(params[:-2] if params else ()), fetch=True, fetch_one=True) + total_items = total_result.get("count", 0) if total_result else 0 + total_pages = math.ceil(total_items / per_page) if total_items > 0 else 0 + podcasts = await podcasts_db.execute_query(query, tuple(params), fetch=True) + for podcast in podcasts: + podcast["audio_generated"] = bool(podcast.get("audio_generated", 0)) + if podcast.get("banner_img_path"): + podcast["banner_img"] = podcast.get("banner_img_path") + else: + podcast["banner_img"] = None + podcast.pop("banner_img_path", None) + podcast["identifier"] = str(podcast.get("id", "")) + has_next = page < total_pages + has_prev = page > 1 + return { + "items": podcasts, + "total": total_items, + "page": page, + "per_page": per_page, + "total_pages": total_pages, + "has_next": has_next, + "has_prev": has_prev, + } + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error loading podcasts: {str(e)}") + + async def get_podcast(self, podcast_id: int) -> Optional[Dict[str, Any]]: + """Get a specific podcast by ID without content.""" + try: + query = """ + SELECT id, title, date, audio_generated, audio_path, banner_img_path, + language_code, tts_engine, created_at, banner_images + FROM podcasts + WHERE id = ? + """ + podcast = await podcasts_db.execute_query(query, (podcast_id,), fetch=True, fetch_one=True) + if not podcast: + raise HTTPException(status_code=404, detail="Podcast not found") + podcast["audio_generated"] = bool(podcast.get("audio_generated", 0)) + if podcast.get("banner_img_path"): + podcast["banner_img"] = podcast.get("banner_img_path") + else: + podcast["banner_img"] = None + podcast.pop("banner_img_path", None) + podcast["identifier"] = str(podcast.get("id", "")) + sources_query = "SELECT sources_json FROM podcasts WHERE id = ?" + sources_result = await podcasts_db.execute_query(sources_query, (podcast_id,), fetch=True, fetch_one=True) + sources = [] + if sources_result and sources_result.get("sources_json"): + try: + parsed_sources = json.loads(sources_result["sources_json"]) + if isinstance(parsed_sources, list): + sources = parsed_sources + else: + sources = [parsed_sources] + except json.JSONDecodeError: + sources = [] + podcast["sources"] = sources + + try: + banner_images = json.loads(podcast.get("banner_images", "[]")) + except json.JSONDecodeError: + banner_images = [] + podcast["banner_images"] = banner_images + + return podcast + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error loading podcast: {str(e)}") + + async def get_podcast_by_identifier(self, identifier: str) -> Optional[Dict[str, Any]]: + """Get a specific podcast by string identifier (which is actually the ID).""" + try: + try: + podcast_id = int(identifier) + except ValueError: + raise HTTPException(status_code=404, detail="Invalid podcast identifier") + return await self.get_podcast(podcast_id) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error loading podcast: {str(e)}") + + async def get_podcast_content(self, podcast_id: int) -> Dict[str, Any]: + """Get the content of a specific podcast.""" + try: + query = """ + SELECT content_json FROM podcasts WHERE id = ? + """ + result = await podcasts_db.execute_query(query, (podcast_id,), fetch=True, fetch_one=True) + if not result or not result.get("content_json"): + raise HTTPException(status_code=404, detail="Podcast content not found") + try: + content = json.loads(result["content_json"]) + return content + except json.JSONDecodeError: + raise HTTPException(status_code=500, detail="Invalid podcast content format") + + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error loading podcast content: {str(e)}") + + async def get_podcast_audio_url(self, podcast: Dict[str, Any]) -> Optional[str]: + """Get the URL for the podcast audio file if available.""" + if podcast.get("audio_generated") and podcast.get("audio_path"): + return f"/audio/{podcast.get('audio_path')}" + return None + + async def get_podcast_formats(self) -> List[str]: + """Get list of available podcast formats for filtering.""" + # Note: This may need to be adapted if format field is added + return ["daily", "weekly", "tech", "news"] + + async def get_language_codes(self) -> List[str]: + try: + query = """ + SELECT DISTINCT language_code FROM podcasts WHERE language_code IS NOT NULL + """ + results = await podcasts_db.execute_query(query, (), fetch=True) + language_codes = [result["language_code"] for result in results if result["language_code"]] + if "en" not in language_codes: + language_codes.append("en") + return sorted(language_codes) + except Exception as _: + return ["en"] + + async def get_tts_engines(self) -> List[str]: + """Get list of available TTS engines for filtering.""" + try: + query = """ + SELECT DISTINCT tts_engine FROM podcasts WHERE tts_engine IS NOT NULL + """ + results = await podcasts_db.execute_query(query, (), fetch=True) + tts_engines = [result["tts_engine"] for result in results if result["tts_engine"]] + default_engines = ["elevenlabs", "openai", "kokoro"] + for engine in default_engines: + if engine not in tts_engines: + tts_engines.append(engine) + return sorted(tts_engines) + except Exception as e: + return ["elevenlabs", "openai", "kokoro"] + + async def create_podcast( + self, title: str, date: str, content: Dict[str, Any], sources: List[str] = None, language_code: str = "en", tts_engine: str = "kokoro" + ) -> Dict[str, Any]: + """Create a new podcast in the database.""" + try: + content_json = json.dumps(content) + sources_json = json.dumps(sources) if sources else None + current_time = datetime.now().isoformat() + query = """ + INSERT INTO podcasts + (title, date, content_json, audio_generated, sources_json, language_code, tts_engine, created_at) + VALUES (?, ?, ?, 0, ?, ?, ?, ?) + """ + params = (title, date, content_json, sources_json, language_code, tts_engine, current_time) + podcast_id = await podcasts_db.execute_query(query, params) + return await self.get_podcast(podcast_id) + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error creating podcast: {str(e)}") + + async def update_podcast(self, podcast_id: int, podcast_data: Dict[str, Any]) -> Dict[str, Any]: + """Update podcast metadata and content.""" + try: + existing = await self.get_podcast(podcast_id) + if not existing: + raise HTTPException(status_code=404, detail="Podcast not found") + fields = [] + params = [] + if "title" in podcast_data: + fields.append("title = ?") + params.append(podcast_data["title"]) + if "date" in podcast_data: + fields.append("date = ?") + params.append(podcast_data["date"]) + if "content" in podcast_data and isinstance(podcast_data["content"], dict): + fields.append("content_json = ?") + params.append(json.dumps(podcast_data["content"])) + if "audio_generated" in podcast_data: + fields.append("audio_generated = ?") + params.append(1 if podcast_data["audio_generated"] else 0) + if "audio_path" in podcast_data: + fields.append("audio_path = ?") + params.append(podcast_data["audio_path"]) + if "banner_img_path" in podcast_data: + fields.append("banner_img_path = ?") + params.append(podcast_data["banner_img_path"]) + if "sources" in podcast_data: + fields.append("sources_json = ?") + params.append(json.dumps(podcast_data["sources"])) + if "language_code" in podcast_data: + fields.append("language_code = ?") + params.append(podcast_data["language_code"]) + if "tts_engine" in podcast_data: + fields.append("tts_engine = ?") + params.append(podcast_data["tts_engine"]) + if not fields: + return existing + params.append(podcast_id) + query = f""" + UPDATE podcasts SET {", ".join(fields)} + WHERE id = ? + """ + await podcasts_db.execute_query(query, tuple(params)) + return await self.get_podcast(podcast_id) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error updating podcast: {str(e)}") + + async def delete_podcast(self, podcast_id: int, delete_assets: bool = False) -> bool: + """Delete a podcast from the database.""" + try: + existing = await self.get_podcast(podcast_id) + if not existing: + raise HTTPException(status_code=404, detail="Podcast not found") + query = "DELETE FROM podcasts WHERE id = ?" + result = await podcasts_db.execute_query(query, (podcast_id,)) + if delete_assets: + if existing.get("audio_path"): + audio_path = os.path.join(AUDIO_DIR, existing["audio_path"]) + if os.path.exists(audio_path): + os.remove(audio_path) + if existing.get("banner_img_path"): + img_path = os.path.join(IMAGE_DIR, existing["banner_img_path"]) + if os.path.exists(img_path): + os.remove(img_path) + return result > 0 + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error deleting podcast: {str(e)}") + + async def upload_podcast_audio(self, podcast_id: int, file: UploadFile) -> Dict[str, Any]: + """Upload an audio file for a podcast.""" + try: + await self.get_podcast(podcast_id) + filename = f"podcast_{podcast_id}_{datetime.now().strftime('%Y%m%d%H%M%S')}" + if file.filename: + ext = os.path.splitext(file.filename)[1] + filename = f"{filename}{ext}" + else: + filename = f"{filename}.mp3" + file_path = os.path.join(AUDIO_DIR, filename) + contents = await file.read() + with open(file_path, "wb") as f: + f.write(contents) + update_data = {"audio_generated": True, "audio_path": filename} + return await self.update_podcast(podcast_id, update_data) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error uploading audio: {str(e)}") + + async def upload_podcast_banner(self, podcast_id: int, file: UploadFile) -> Dict[str, Any]: + """Upload a banner image for a podcast.""" + try: + await self.get_podcast(podcast_id) + filename = f"banner_{podcast_id}_{datetime.now().strftime('%Y%m%d%H%M%S')}" + if file.filename: + ext = os.path.splitext(file.filename)[1] + filename = f"{filename}{ext}" + else: + filename = f"{filename}.jpg" + file_path = os.path.join(IMAGE_DIR, filename) + contents = await file.read() + with open(file_path, "wb") as f: + f.write(contents) + update_data = {"banner_img_path": filename} + return await self.update_podcast(podcast_id, update_data) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error uploading banner: {str(e)}") + + +podcast_service = PodcastService() diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/social_media_service.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/social_media_service.py new file mode 100644 index 0000000..4c48242 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/social_media_service.py @@ -0,0 +1,603 @@ +import json +from typing import List, Optional, Dict, Any +from fastapi import HTTPException +from services.db_service import social_media_db +from models.social_media_schemas import PaginatedPosts, Post +from datetime import datetime, timedelta + + +class SocialMediaService: + """Service for managing social media posts.""" + + async def get_posts( + self, + page: int = 1, + per_page: int = 10, + platform: Optional[str] = None, + user_handle: Optional[str] = None, + sentiment: Optional[str] = None, + category: Optional[str] = None, + date_from: Optional[str] = None, + date_to: Optional[str] = None, + search: Optional[str] = None, + ) -> PaginatedPosts: + """Get social media posts with pagination and filtering.""" + try: + offset = (page - 1) * per_page + query_parts = [ + "SELECT * FROM posts", + "WHERE 1=1", + ] + query_params = [] + if platform: + query_parts.append("AND platform = ?") + query_params.append(platform) + if user_handle: + query_parts.append("AND user_handle = ?") + query_params.append(user_handle) + if sentiment: + query_parts.append("AND sentiment = ?") + query_params.append(sentiment) + if category: + query_parts.append("AND categories LIKE ?") + query_params.append(f'%"{category}"%') + if date_from: + query_parts.append("AND datetime(post_timestamp) >= datetime(?)") + query_params.append(date_from) + if date_to: + query_parts.append("AND datetime(post_timestamp) <= datetime(?)") + query_params.append(date_to) + if search: + query_parts.append("AND (post_text LIKE ? OR user_display_name LIKE ? OR user_handle LIKE ?)") + search_param = f"%{search}%" + query_params.extend([search_param, search_param, search_param]) + count_query = " ".join(query_parts).replace("SELECT *", "SELECT COUNT(*)") + total_posts = await social_media_db.execute_query(count_query, tuple(query_params), fetch=True, fetch_one=True) + total_count = total_posts.get("COUNT(*)", 0) if total_posts else 0 + query_parts.append("ORDER BY datetime(post_timestamp) DESC, post_id DESC") + query_parts.append("LIMIT ? OFFSET ?") + query_params.extend([per_page, offset]) + posts_query = " ".join(query_parts) + posts_data = await social_media_db.execute_query(posts_query, tuple(query_params), fetch=True) + posts = [] + for post in posts_data: + post_dict = dict(post) + if post_dict.get("media"): + try: + post_dict["media"] = json.loads(post_dict["media"]) + except json.JSONDecodeError: + post_dict["media"] = [] + if post_dict.get("categories"): + try: + post_dict["categories"] = json.loads(post_dict["categories"]) + except json.JSONDecodeError: + post_dict["categories"] = [] + if post_dict.get("tags"): + try: + post_dict["tags"] = json.loads(post_dict["tags"]) + except json.JSONDecodeError: + post_dict["tags"] = [] + post_dict["engagement"] = { + "replies": post_dict.pop("engagement_reply_count", 0), + "retweets": post_dict.pop("engagement_retweet_count", 0), + "likes": post_dict.pop("engagement_like_count", 0), + "bookmarks": post_dict.pop("engagement_bookmark_count", 0), + "views": post_dict.pop("engagement_view_count", 0), + } + posts.append(post_dict) + total_pages = (total_count + per_page - 1) // per_page if total_count > 0 else 0 + has_next = page < total_pages + has_prev = page > 1 + return PaginatedPosts( + items=posts, + total=total_count, + page=page, + per_page=per_page, + total_pages=total_pages, + has_next=has_next, + has_prev=has_prev, + ) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching social media posts: {str(e)}") + + async def get_post(self, post_id: str) -> Dict[str, Any]: + """Get a specific post by ID.""" + try: + query = "SELECT * FROM posts WHERE post_id = ?" + post = await social_media_db.execute_query(query, (post_id,), fetch=True, fetch_one=True) + if not post: + raise HTTPException(status_code=404, detail="Post not found") + post_dict = dict(post) + if post_dict.get("media"): + try: + post_dict["media"] = json.loads(post_dict["media"]) + except json.JSONDecodeError: + post_dict["media"] = [] + if post_dict.get("categories"): + try: + post_dict["categories"] = json.loads(post_dict["categories"]) + except json.JSONDecodeError: + post_dict["categories"] = [] + if post_dict.get("tags"): + try: + post_dict["tags"] = json.loads(post_dict["tags"]) + except json.JSONDecodeError: + post_dict["tags"] = [] + post_dict["engagement"] = { + "replies": post_dict.pop("engagement_reply_count", 0), + "retweets": post_dict.pop("engagement_retweet_count", 0), + "likes": post_dict.pop("engagement_like_count", 0), + "bookmarks": post_dict.pop("engagement_bookmark_count", 0), + "views": post_dict.pop("engagement_view_count", 0), + } + return post_dict + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching social media post: {str(e)}") + + async def get_platforms(self) -> List[str]: + """Get all platforms that have posts.""" + query = "SELECT DISTINCT platform FROM posts ORDER BY platform" + result = await social_media_db.execute_query(query, fetch=True) + return [row.get("platform", "") for row in result if row.get("platform")] + + async def get_sentiments(self, date_from: Optional[str] = None, date_to: Optional[str] = None) -> List[Dict[str, Any]]: + """Get sentiment distribution with post counts.""" + try: + query_parts = [ + """ + SELECT + sentiment, COUNT(*) as post_count + FROM posts + WHERE sentiment IS NOT NULL + """ + ] + params = [] + if date_from: + query_parts.append("AND datetime(post_timestamp) >= datetime(?)") + params.append(date_from) + if date_to: + query_parts.append("AND datetime(post_timestamp) <= datetime(?)") + params.append(date_to) + query_parts.append("GROUP BY sentiment ORDER BY post_count DESC") + query = " ".join(query_parts) + return await social_media_db.execute_query(query, tuple(params), fetch=True) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching sentiments: {str(e)}") + + async def get_top_users( + self, + platform: Optional[str] = None, + limit: int = 10, + date_from: Optional[str] = None, + date_to: Optional[str] = None, + ) -> List[Dict[str, Any]]: + """Get top users by post count.""" + query_parts = ["SELECT user_handle, user_display_name, COUNT(*) as post_count", "FROM posts", "WHERE user_handle IS NOT NULL"] + params = [] + if platform: + query_parts.append("AND platform = ?") + params.append(platform) + if date_from: + query_parts.append("AND datetime(post_timestamp) >= datetime(?)") + params.append(date_from) + if date_to: + query_parts.append("AND datetime(post_timestamp) <= datetime(?)") + params.append(date_to) + query_parts.extend(["GROUP BY user_handle", "ORDER BY post_count DESC", "LIMIT ?"]) + params.append(limit) + query = " ".join(query_parts) + return await social_media_db.execute_query(query, tuple(params), fetch=True) + + async def get_categories(self, date_from: Optional[str] = None, date_to: Optional[str] = None) -> List[Dict[str, Any]]: + """Get all categories with post counts.""" + try: + query_parts = ["SELECT categories FROM posts WHERE categories IS NOT NULL"] + params = [] + if date_from: + query_parts.append("AND datetime(post_timestamp) >= datetime(?)") + params.append(date_from) + if date_to: + query_parts.append("AND datetime(post_timestamp) <= datetime(?)") + params.append(date_to) + query = " ".join(query_parts) + result = await social_media_db.execute_query(query, tuple(params), fetch=True) + category_counts = {} + for row in result: + if row.get("categories"): + try: + categories = json.loads(row["categories"]) + for category in categories: + if category in category_counts: + category_counts[category] += 1 + else: + category_counts[category] = 1 + except json.JSONDecodeError: + pass + return [{"category": category, "post_count": count} for category, count in sorted(category_counts.items(), key=lambda x: x[1], reverse=True)] + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching categories: {str(e)}") + + async def get_user_sentiment( + self, + limit: int = 10, + platform: Optional[str] = None, + date_from: Optional[str] = None, + date_to: Optional[str] = None, + ) -> List[Dict[str, Any]]: + """Get users with their sentiment breakdown.""" + try: + query_parts = [ + """ + SELECT + user_handle, + user_display_name, + COUNT(*) as total_posts, + SUM(CASE WHEN sentiment = 'positive' THEN 1 ELSE 0 END) as positive_count, + SUM(CASE WHEN sentiment = 'negative' THEN 1 ELSE 0 END) as negative_count, + SUM(CASE WHEN sentiment = 'neutral' THEN 1 ELSE 0 END) as neutral_count, + SUM(CASE WHEN sentiment = 'critical' THEN 1 ELSE 0 END) as critical_count + FROM posts + WHERE user_handle IS NOT NULL + """ + ] + params = [] + if platform: + query_parts.append("AND platform = ?") + params.append(platform) + if date_from: + query_parts.append("AND datetime(post_timestamp) >= datetime(?)") + params.append(date_from) + if date_to: + query_parts.append("AND datetime(post_timestamp) <= datetime(?)") + params.append(date_to) + query_parts.extend(["GROUP BY user_handle, user_display_name", "ORDER BY total_posts DESC", "LIMIT ?"]) + params.append(limit) + query = " ".join(query_parts) + result = await social_media_db.execute_query(query, tuple(params), fetch=True) + for user in result: + total = user["total_posts"] + user["positive_percent"] = (user["positive_count"] / total) * 100 if total > 0 else 0 + user["negative_percent"] = (user["negative_count"] / total) * 100 if total > 0 else 0 + user["neutral_percent"] = (user["neutral_count"] / total) * 100 if total > 0 else 0 + user["critical_percent"] = (user["critical_count"] / total) * 100 if total > 0 else 0 + return result + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching user sentiment: {str(e)}") + + async def get_category_sentiment(self, date_from: Optional[str] = None, date_to: Optional[str] = None) -> List[Dict[str, Any]]: + """Get sentiment distribution by category.""" + try: + date_filter = "" + params = [] + if date_from or date_to: + date_filter = "WHERE " + if date_from: + date_filter += "datetime(p.post_timestamp) >= datetime(?)" + params.append(date_from) + if date_to: + date_filter += " AND " + if date_to: + date_filter += "datetime(p.post_timestamp) <= datetime(?)" + params.append(date_to) + query = f""" + WITH category_data AS ( + SELECT + json_each.value as category, + sentiment, + COUNT(*) as count + FROM + posts p, + json_each(p.categories) + {date_filter} + GROUP BY + json_each.value, sentiment + ) + SELECT + category, + SUM(count) as total_count, + SUM(CASE WHEN sentiment = 'positive' THEN count ELSE 0 END) as positive_count, + SUM(CASE WHEN sentiment = 'negative' THEN count ELSE 0 END) as negative_count, + SUM(CASE WHEN sentiment = 'neutral' THEN count ELSE 0 END) as neutral_count, + SUM(CASE WHEN sentiment = 'critical' THEN count ELSE 0 END) as critical_count + FROM + category_data + GROUP BY + category + ORDER BY + total_count DESC + """ + result = await social_media_db.execute_query(query, tuple(params), fetch=True) + for category in result: + total = category["total_count"] + category["positive_percent"] = (category["positive_count"] / total) * 100 if total > 0 else 0 + category["negative_percent"] = (category["negative_count"] / total) * 100 if total > 0 else 0 + category["neutral_percent"] = (category["neutral_count"] / total) * 100 if total > 0 else 0 + category["critical_percent"] = (category["critical_count"] / total) * 100 if total > 0 else 0 + return result + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching category sentiment: {str(e)}") + + async def get_trending_topics( + self, + date_from: Optional[str] = None, + date_to: Optional[str] = None, + limit: int = 10 + ) -> List[Dict[str, Any]]: + """Get trending topics with sentiment breakdown.""" + try: + query_parts = [ + """ + WITH topic_data AS ( + SELECT + json_each.value as topic, + sentiment, + COUNT(*) as count + FROM + posts, + json_each(posts.tags) + WHERE tags IS NOT NULL + """ + ] + params = [] + if date_from: + query_parts.append("AND datetime(post_timestamp) >= datetime(?)") + params.append(date_from) + if date_to: + query_parts.append("AND datetime(post_timestamp) <= datetime(?)") + params.append(date_to) + query_parts.append( + """ + GROUP BY + json_each.value, sentiment + ) + SELECT + topic, + SUM(count) as total_count, + SUM(CASE WHEN sentiment = 'positive' THEN count ELSE 0 END) as positive_count, + SUM(CASE WHEN sentiment = 'negative' THEN count ELSE 0 END) as negative_count, + SUM(CASE WHEN sentiment = 'neutral' THEN count ELSE 0 END) as neutral_count, + SUM(CASE WHEN sentiment = 'critical' THEN count ELSE 0 END) as critical_count + FROM + topic_data + GROUP BY + topic + ORDER BY + total_count DESC + LIMIT ? + """ + ) + params.append(limit) + query = " ".join(query_parts) + result = await social_media_db.execute_query(query, tuple(params), fetch=True) + for topic in result: + total = topic["total_count"] + topic["positive_percent"] = (topic["positive_count"] / total) * 100 if total > 0 else 0 + topic["negative_percent"] = (topic["negative_count"] / total) * 100 if total > 0 else 0 + topic["neutral_percent"] = (topic["neutral_count"] / total) * 100 if total > 0 else 0 + topic["critical_percent"] = (topic["critical_count"] / total) * 100 if total > 0 else 0 + return result + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching trending topics: {str(e)}") + + async def get_sentiment_over_time( + self, + date_from: Optional[str] = None, + date_to: Optional[str] = None, + platform: Optional[str] = None + ) -> List[Dict[str, Any]]: + """Get sentiment trends over time.""" + try: + date_range_query = "" + if date_from and date_to: + date_range_query = f""" + WITH RECURSIVE date_range(date) AS ( + SELECT date('{date_from}') + UNION ALL + SELECT date(date, '+1 day') + FROM date_range + WHERE date < date('{date_to}') + ) + SELECT date as post_date FROM date_range + """ + else: + days_ago = (datetime.now() - timedelta(days=30)).isoformat() + date_range_query = f""" + WITH RECURSIVE date_range(date) AS ( + SELECT date('{days_ago}') + UNION ALL + SELECT date(date, '+1 day') + FROM date_range + WHERE date < date('now') + ) + SELECT date as post_date FROM date_range + """ + query_parts = [ + f""" + WITH dates AS ( + {date_range_query} + ) + SELECT + dates.post_date, + COALESCE(SUM(CASE WHEN sentiment = 'positive' THEN 1 ELSE 0 END), 0) as positive_count, + COALESCE(SUM(CASE WHEN sentiment = 'negative' THEN 1 ELSE 0 END), 0) as negative_count, + COALESCE(SUM(CASE WHEN sentiment = 'neutral' THEN 1 ELSE 0 END), 0) as neutral_count, + COALESCE(SUM(CASE WHEN sentiment = 'critical' THEN 1 ELSE 0 END), 0) as critical_count, + COUNT(posts.post_id) as total_count + FROM + dates + LEFT JOIN + posts ON date(posts.post_timestamp) = dates.post_date + """ + ] + params = [] + if platform: + query_parts.append("AND posts.platform = ?") + params.append(platform) + query_parts.append("GROUP BY dates.post_date ORDER BY dates.post_date") + query = " ".join(query_parts) + result = await social_media_db.execute_query(query, tuple(params), fetch=True) + for day in result: + total = day["total_count"] + day["positive_percent"] = (day["positive_count"] / total) * 100 if total > 0 else 0 + day["negative_percent"] = (day["negative_count"] / total) * 100 if total > 0 else 0 + day["neutral_percent"] = (day["neutral_count"] / total) * 100 if total > 0 else 0 + day["critical_percent"] = (day["critical_count"] / total) * 100 if total > 0 else 0 + return result + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching sentiment over time: {str(e)}") + + async def get_influential_posts( + self, + sentiment: Optional[str] = None, + limit: int = 5, + date_from: Optional[str] = None, + date_to: Optional[str] = None + ) -> List[Dict[str, Any]]: + """Get most influential posts by engagement, optionally filtered by sentiment.""" + try: + query_parts = [ + """ + SELECT *, + (COALESCE(engagement_reply_count, 0) + + COALESCE(engagement_retweet_count, 0) + + COALESCE(engagement_like_count, 0) + + COALESCE(engagement_bookmark_count, 0)) as total_engagement + FROM posts + WHERE 1=1 + """ + ] + params = [] + if sentiment: + query_parts.append("AND sentiment = ?") + params.append(sentiment) + if date_from: + query_parts.append("AND datetime(post_timestamp) >= datetime(?)") + params.append(date_from) + if date_to: + query_parts.append("AND datetime(post_timestamp) <= datetime(?)") + params.append(date_to) + query_parts.extend(["ORDER BY total_engagement DESC", "LIMIT ?"]) + params.append(limit) + query = " ".join(query_parts) + result = await social_media_db.execute_query(query, tuple(params), fetch=True) + processed_posts = [] + for post in result: + post_dict = dict(post) + if post_dict.get("media"): + try: + post_dict["media"] = json.loads(post_dict["media"]) + except json.JSONDecodeError: + post_dict["media"] = [] + if post_dict.get("categories"): + try: + post_dict["categories"] = json.loads(post_dict["categories"]) + except json.JSONDecodeError: + post_dict["categories"] = [] + if post_dict.get("tags"): + try: + post_dict["tags"] = json.loads(post_dict["tags"]) + except json.JSONDecodeError: + post_dict["tags"] = [] + post_dict["engagement"] = { + "replies": post_dict.pop("engagement_reply_count", 0), + "retweets": post_dict.pop("engagement_retweet_count", 0), + "likes": post_dict.pop("engagement_like_count", 0), + "bookmarks": post_dict.pop("engagement_bookmark_count", 0), + "views": post_dict.pop("engagement_view_count", 0), + } + processed_posts.append(post_dict) + return processed_posts + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching influential posts: {str(e)}") + + async def get_engagement_stats( + self, + date_from: Optional[str] = None, + date_to: Optional[str] = None + ) -> Dict[str, Any]: + """Get overall engagement statistics.""" + try: + query_parts = [ + """ + SELECT + AVG(COALESCE(engagement_reply_count, 0)) as avg_replies, + AVG(COALESCE(engagement_retweet_count, 0)) as avg_retweets, + AVG(COALESCE(engagement_like_count, 0)) as avg_likes, + AVG(COALESCE(engagement_bookmark_count, 0)) as avg_bookmarks, + AVG(COALESCE(engagement_view_count, 0)) as avg_views, + MAX(COALESCE(engagement_reply_count, 0)) as max_replies, + MAX(COALESCE(engagement_retweet_count, 0)) as max_retweets, + MAX(COALESCE(engagement_like_count, 0)) as max_likes, + MAX(COALESCE(engagement_bookmark_count, 0)) as max_bookmarks, + MAX(COALESCE(engagement_view_count, 0)) as max_views, + COUNT(*) as total_posts, + COUNT(DISTINCT user_handle) as unique_authors + FROM posts + WHERE 1=1 + """ + ] + params = [] + if date_from: + query_parts.append("AND datetime(post_timestamp) >= datetime(?)") + params.append(date_from) + if date_to: + query_parts.append("AND datetime(post_timestamp) <= datetime(?)") + params.append(date_to) + query = " ".join(query_parts) + result = await social_media_db.execute_query(query, tuple(params), fetch=True, fetch_one=True) + if not result: + return {"avg_engagement": 0, "total_posts": 0, "unique_authors": 0} + result_dict = dict(result) + result_dict["avg_engagement"] = ( + result_dict["avg_replies"] + result_dict["avg_retweets"] + result_dict["avg_likes"] + result_dict["avg_bookmarks"] + ) + platform_query_parts = [ + """ + SELECT + platform, + COUNT(*) as post_count + FROM posts + WHERE 1=1 + """ + ] + if date_from: + platform_query_parts.append("AND datetime(post_timestamp) >= datetime(?)") + if date_to: + platform_query_parts.append("AND datetime(post_timestamp) <= datetime(?)") + platform_query_parts.extend([ + "GROUP BY platform", + "ORDER BY post_count DESC", + "LIMIT 10" + ]) + platforms = await social_media_db.execute_query( + " ".join(platform_query_parts), + tuple(params), + fetch=True + ) + result_dict["platforms"] = platforms + return result_dict + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching engagement stats: {str(e)}") + + +social_media_service = SocialMediaService() diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/source_service.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/source_service.py new file mode 100644 index 0000000..e199d91 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/source_service.py @@ -0,0 +1,375 @@ +from typing import List, Optional, Dict, Any +from fastapi import HTTPException +from datetime import datetime +from services.db_service import sources_db, tracking_db +from models.source_schemas import SourceCreate, SourceUpdate, SourceFeedCreate, PaginatedSources + + +class SourceService: + """Service for managing source operations with the new database structure.""" + + async def get_sources( + self, page: int = 1, per_page: int = 10, category: Optional[str] = None, search: Optional[str] = None, include_inactive: bool = False + ) -> PaginatedSources: + """Get sources with pagination and filtering.""" + try: + query_parts = ["SELECT s.id, s.name, s.url, s.description, s.is_active, s.created_at", "FROM sources s", "WHERE 1=1"] + query_params = [] + if not include_inactive: + query_parts.append("AND s.is_active = 1") + if category: + query_parts.append(""" + AND EXISTS ( + SELECT 1 FROM source_categories sc + JOIN categories c ON sc.category_id = c.id + WHERE sc.source_id = s.id AND c.name = ? + ) + """) + query_params.append(category) + if search: + query_parts.append("AND (s.name LIKE ? OR s.description LIKE ?)") + search_param = f"%{search}%" + query_params.extend([search_param, search_param]) + count_query = " ".join(query_parts).replace("SELECT s.id, s.name, s.url, s.description, s.is_active, s.created_at", "SELECT COUNT(*)") + total_sources = await sources_db.execute_query(count_query, tuple(query_params), fetch=True, fetch_one=True) + total_count = total_sources.get("COUNT(*)", 0) if total_sources else 0 + query_parts.append("ORDER BY s.name") + offset = (page - 1) * per_page + query_parts.append("LIMIT ? OFFSET ?") + query_params.extend([per_page, offset]) + final_query = " ".join(query_parts) + sources = await sources_db.execute_query(final_query, tuple(query_params), fetch=True) + for source in sources: + source["categories"] = await self.get_source_categories(source["id"]) + source["last_crawled"] = await self.get_source_last_crawled(source["id"]) + source["website"] = source["url"] + if source["categories"] and isinstance(source["categories"], list): + source["category"] = source["categories"][0] if source["categories"] else "" + total_pages = (total_count + per_page - 1) // per_page if total_count > 0 else 0 + has_next = page < total_pages + has_prev = page > 1 + return PaginatedSources( + items=sources, total=total_count, page=page, per_page=per_page, total_pages=total_pages, has_next=has_next, has_prev=has_prev + ) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching sources: {str(e)}") + + async def get_source(self, source_id: int) -> Dict[str, Any]: + """Get a specific source by ID.""" + query = """ + SELECT id, name, url, description, is_active, created_at + FROM sources + WHERE id = ? + """ + source = await sources_db.execute_query(query, (source_id,), fetch=True, fetch_one=True) + if not source: + raise HTTPException(status_code=404, detail="Source not found") + source["categories"] = await self.get_source_categories(source["id"]) + source["last_crawled"] = await self.get_source_last_crawled(source["id"]) + source["website"] = source["url"] + if source["categories"] and isinstance(source["categories"], list): + source["category"] = source["categories"][0] if source["categories"] else "" + return source + + async def get_source_categories(self, source_id: int) -> List[str]: + """Get all categories for a specific source.""" + query = """ + SELECT c.name + FROM source_categories sc + JOIN categories c ON sc.category_id = c.id + WHERE sc.source_id = ? + ORDER BY c.name + """ + categories = await sources_db.execute_query(query, (source_id,), fetch=True) + return [category.get("name", "") for category in categories if category.get("name")] + + async def get_source_last_crawled(self, source_id: int) -> Optional[str]: + """Get the last crawl time for a source's feeds.""" + query = """ + SELECT MAX(ft.last_processed) as last_crawled + FROM feed_tracking ft + WHERE ft.source_id = ? + """ + result = await tracking_db.execute_query(query, (source_id,), fetch=True, fetch_one=True) + return result.get("last_crawled") if result else None + + async def get_source_by_name(self, name: str) -> Dict[str, Any]: + """Get a specific source by name.""" + query = """ + SELECT id, name, url, description, is_active, created_at + FROM sources + WHERE name = ? + """ + source = await sources_db.execute_query(query, (name,), fetch=True, fetch_one=True) + if not source: + raise HTTPException(status_code=404, detail="Source not found") + source["categories"] = await self.get_source_categories(source["id"]) + source["last_crawled"] = await self.get_source_last_crawled(source["id"]) + source["website"] = source["url"] + if source["categories"] and isinstance(source["categories"], list): + source["category"] = source["categories"][0] if source["categories"] else "" + return source + + async def get_source_feeds(self, source_id: int) -> List[Dict[str, Any]]: + """Get all feeds for a specific source.""" + query = """ + SELECT id, feed_url, feed_type, is_active, created_at, last_crawled + FROM source_feeds + WHERE source_id = ? + ORDER BY feed_type + """ + feeds = await sources_db.execute_query(query, (source_id,), fetch=True) + for feed in feeds: + feed["description"] = feed.get("feed_type", "Main feed").capitalize() + return feeds + + async def get_categories(self) -> List[Dict[str, Any]]: + """Get all source categories.""" + query = """ + SELECT id, name + FROM categories + ORDER BY name + """ + categories = await sources_db.execute_query(query, fetch=True) + for category in categories: + category["description"] = f"Articles about {category.get('name', '')}" + return categories + + async def get_source_by_category(self, category_name: str) -> List[Dict[str, Any]]: + """Get sources by category using the junction table.""" + query = """ + SELECT s.id, s.name, s.url, s.description, s.is_active + FROM sources s + JOIN source_categories sc ON s.id = sc.source_id + JOIN categories c ON sc.category_id = c.id + WHERE c.name = ? AND s.is_active = 1 + ORDER BY s.name + """ + sources = await sources_db.execute_query(query, (category_name,), fetch=True) + for source in sources: + source["categories"] = await self.get_source_categories(source["id"]) + source["website"] = source["url"] + if source["categories"] and isinstance(source["categories"], list): + source["category"] = source["categories"][0] if source["categories"] else "" + return sources + + async def create_source(self, source_data: SourceCreate) -> Dict[str, Any]: + """Create a new source.""" + try: + source_query = """ + INSERT INTO sources (name, url, description, is_active, created_at) + VALUES (?, ?, ?, ?, ?) + """ + source_params = (source_data.name, source_data.url, source_data.description, source_data.is_active, datetime.now().isoformat()) + source_id = await sources_db.execute_query(source_query, source_params) + if source_data.categories: + for category_name in source_data.categories: + await self.add_source_category(source_id, category_name) + elif hasattr(source_data, "category") and source_data.category: + await self.add_source_category(source_id, source_data.category) + if source_data.feeds: + for feed in source_data.feeds: + await self.add_feed_to_source(source_id, feed) + return await self.get_source(source_id) + except Exception as e: + if isinstance(e, HTTPException): + raise e + if "UNIQUE constraint failed" in str(e) and "name" in str(e): + raise HTTPException(status_code=409, detail="Source with this name already exists") + raise HTTPException(status_code=500, detail=f"Error creating source: {str(e)}") + + async def add_source_category(self, source_id: int, category_name: str) -> None: + """Add a category to a source, creating the category if it doesn't exist.""" + category_query = """ + INSERT OR IGNORE INTO categories (name, created_at) + VALUES (?, ?) + """ + await sources_db.execute_query(category_query, (category_name, datetime.now().isoformat())) + get_category_id_query = "SELECT id FROM categories WHERE name = ?" + category = await sources_db.execute_query(get_category_id_query, (category_name,), fetch=True, fetch_one=True) + if not category: + raise HTTPException(status_code=500, detail=f"Failed to find or create category: {category_name}") + link_query = """ + INSERT OR IGNORE INTO source_categories (source_id, category_id) + VALUES (?, ?) + """ + await sources_db.execute_query(link_query, (source_id, category["id"])) + + async def update_source(self, source_id: int, source_data: SourceUpdate) -> Dict[str, Any]: + """Update an existing source.""" + try: + await self.get_source(source_id) + update_fields = [] + update_params = [] + if source_data.name is not None: + update_fields.append("name = ?") + update_params.append(source_data.name) + if source_data.url is not None: + update_fields.append("url = ?") + update_params.append(source_data.url) + if source_data.description is not None: + update_fields.append("description = ?") + update_params.append(source_data.description) + if source_data.is_active is not None: + update_fields.append("is_active = ?") + update_params.append(source_data.is_active) + if update_fields: + update_params.append(source_id) + update_query = f""" + UPDATE sources + SET {", ".join(update_fields)} + WHERE id = ? + """ + await sources_db.execute_query(update_query, tuple(update_params)) + if source_data.categories is not None: + delete_categories_query = "DELETE FROM source_categories WHERE source_id = ?" + await sources_db.execute_query(delete_categories_query, (source_id,)) + if source_data.categories: + for category_name in source_data.categories: + await self.add_source_category(source_id, category_name) + elif hasattr(source_data, "category") and source_data.category is not None: + delete_categories_query = "DELETE FROM source_categories WHERE source_id = ?" + await sources_db.execute_query(delete_categories_query, (source_id,)) + if source_data.category: + await self.add_source_category(source_id, source_data.category) + return await self.get_source(source_id) + except Exception as e: + if isinstance(e, HTTPException): + raise e + if "UNIQUE constraint failed" in str(e) and "name" in str(e): + raise HTTPException(status_code=409, detail="Source with this name already exists") + raise HTTPException(status_code=500, detail=f"Error updating source: {str(e)}") + + async def delete_source(self, source_id: int) -> Dict[str, Any]: + """Delete a source (soft delete by setting is_active to false).""" + try: + source = await self.get_source(source_id) + update_query = """ + UPDATE sources + SET is_active = 0 + WHERE id = ? + """ + await sources_db.execute_query(update_query, (source_id,)) + feeds_query = """ + UPDATE source_feeds + SET is_active = 0 + WHERE source_id = ? + """ + await sources_db.execute_query(feeds_query, (source_id,)) + return {**source, "is_active": False} + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error deleting source: {str(e)}") + + async def hard_delete_source(self, source_id: int) -> Dict[str, str]: + """Permanently delete a source and its feeds.""" + try: + source = await self.get_source(source_id) + delete_feeds_query = """ + DELETE FROM source_feeds + WHERE source_id = ? + """ + await sources_db.execute_query(delete_feeds_query, (source_id,)) + delete_categories_query = """ + DELETE FROM source_categories + WHERE source_id = ? + """ + await sources_db.execute_query(delete_categories_query, (source_id,)) + delete_source_query = """ + DELETE FROM sources + WHERE id = ? + """ + await sources_db.execute_query(delete_source_query, (source_id,)) + return {"message": f"Source '{source['name']}' has been permanently deleted"} + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error deleting source: {str(e)}") + + async def add_feed_to_source(self, source_id: int, feed_data: SourceFeedCreate) -> Dict[str, Any]: + """Add a new feed to an existing source.""" + try: + await self.get_source(source_id) + check_query = """ + SELECT id, source_id FROM source_feeds WHERE feed_url = ? + """ + existing_feed = await sources_db.execute_query(check_query, (feed_data.feed_url,), fetch=True, fetch_one=True) + if existing_feed: + source_query = """ + SELECT name FROM sources WHERE id = ? + """ + source = await sources_db.execute_query(source_query, (existing_feed["source_id"],), fetch=True, fetch_one=True) + source_name = source["name"] if source else "another source" + raise HTTPException( + status_code=409, + detail=f"A feed with this URL already exists for {source_name}. Please edit the existing feed (ID: {existing_feed['id']}) instead.", + ) + feed_query = """ + INSERT INTO source_feeds (source_id, feed_url, feed_type, is_active, created_at) + VALUES (?, ?, ?, ?, ?) + """ + feed_params = (source_id, feed_data.feed_url, feed_data.feed_type, feed_data.is_active, datetime.now().isoformat()) + await sources_db.execute_query(feed_query, feed_params) + return await self.get_source_feeds(source_id) + except Exception as e: + if isinstance(e, HTTPException): + raise e + if "UNIQUE constraint failed" in str(e) and "feed_url" in str(e): + raise HTTPException(status_code=409, detail="This feed URL already exists. Please check your existing feeds or try a different URL.") + raise HTTPException(status_code=500, detail=f"Error adding feed: {str(e)}") + + async def update_feed(self, feed_id: int, feed_data: Dict[str, Any]) -> Dict[str, Any]: + """Update an existing feed.""" + try: + feed_query = "SELECT id, source_id FROM source_feeds WHERE id = ?" + feed = await sources_db.execute_query(feed_query, (feed_id,), fetch=True, fetch_one=True) + if not feed: + raise HTTPException(status_code=404, detail="Feed not found") + update_fields = [] + update_params = [] + if "feed_url" in feed_data: + update_fields.append("feed_url = ?") + update_params.append(feed_data["feed_url"]) + if "feed_type" in feed_data: + update_fields.append("feed_type = ?") + update_params.append(feed_data["feed_type"]) + if "is_active" in feed_data: + update_fields.append("is_active = ?") + update_params.append(feed_data["is_active"]) + if not update_fields: + return await self.get_source_feeds(feed["source_id"]) + update_params.append(feed_id) + update_query = f""" + UPDATE source_feeds + SET {", ".join(update_fields)} + WHERE id = ? + """ + await sources_db.execute_query(update_query, tuple(update_params)) + return await self.get_source_feeds(feed["source_id"]) + except Exception as e: + if isinstance(e, HTTPException): + raise e + if "UNIQUE constraint failed" in str(e) and "feed_url" in str(e): + raise HTTPException(status_code=409, detail="Feed URL already exists") + raise HTTPException(status_code=500, detail=f"Error updating feed: {str(e)}") + + async def delete_feed(self, feed_id: int) -> Dict[str, str]: + """Delete a feed.""" + try: + feed_query = "SELECT * FROM source_feeds WHERE id = ?" + feed = await sources_db.execute_query(feed_query, (feed_id,), fetch=True, fetch_one=True) + if not feed: + raise HTTPException(status_code=404, detail="Feed not found") + delete_query = "DELETE FROM source_feeds WHERE id = ?" + await sources_db.execute_query(delete_query, (feed_id,)) + return {"message": "Feed has been deleted"} + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error deleting feed: {str(e)}") + + +source_service = SourceService() diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/task_service.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/task_service.py new file mode 100644 index 0000000..471ac43 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/services/task_service.py @@ -0,0 +1,315 @@ +from typing import List, Optional, Dict, Any +from datetime import datetime, timedelta +from fastapi import HTTPException +from services.db_service import tasks_db +from models.tasks_schemas import TASK_TYPES + + +class TaskService: + """Service for managing scheduled tasks.""" + + async def get_tasks(self, include_disabled: bool = False) -> List[Dict[str, Any]]: + """Get all tasks with optional filtering.""" + try: + if include_disabled: + query = """ + SELECT id, name, description, command, task_type, frequency, frequency_unit, + enabled, last_run, created_at + FROM tasks + ORDER BY name + """ + params = () + else: + query = """ + SELECT id, name, description, command, task_type, frequency, frequency_unit, + enabled, last_run, created_at + FROM tasks + WHERE enabled = 1 + ORDER BY name + """ + params = () + tasks = await tasks_db.execute_query(query, params, fetch=True) + for task in tasks: + task["enabled"] = bool(task.get("enabled", 0)) + return tasks + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching tasks: {str(e)}") + + async def get_task(self, task_id: int) -> Dict[str, Any]: + """Get a specific task by ID.""" + try: + query = """ + SELECT id, name, description, command, task_type, frequency, frequency_unit, + enabled, last_run, created_at + FROM tasks + WHERE id = ? + """ + task = await tasks_db.execute_query(query, (task_id,), fetch=True, fetch_one=True) + if not task: + raise HTTPException(status_code=404, detail="Task not found") + task["enabled"] = bool(task.get("enabled", 0)) + return task + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching task: {str(e)}") + + async def check_task_exists(self, task_type: str) -> Optional[Dict[str, Any]]: + """Check if a task with the given type already exists.""" + try: + query = """ + SELECT id, name, task_type + FROM tasks + WHERE task_type = ? + LIMIT 1 + """ + task = await tasks_db.execute_query(query, (task_type,), fetch=True, fetch_one=True) + return task + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error checking task existence: {str(e)}") + + async def create_task( + self, + name: str, + task_type: str, + frequency: int, + frequency_unit: str, + description: Optional[str] = None, + enabled: bool = True, + ) -> Dict[str, Any]: + """Create a new task.""" + try: + existing_task = await self.check_task_exists(task_type) + if existing_task: + raise HTTPException( + status_code=409, + detail=f"A task with type '{task_type}' already exists (Task: '{existing_task['name']}', ID: {existing_task['id']}). Please edit the existing task instead of creating a duplicate.", + ) + if task_type not in TASK_TYPES: + raise HTTPException( + status_code=400, detail=f"Invalid task type: '{task_type}'. Please select a valid task type from the available options." + ) + command = TASK_TYPES[task_type]["command"] + current_time = datetime.now().isoformat() + query = """ + INSERT INTO tasks + (name, description, command, task_type, frequency, frequency_unit, enabled, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + """ + params = ( + name, + description, + command, + task_type, + frequency, + frequency_unit, + 1 if enabled else 0, + current_time, + ) + task_id = await tasks_db.execute_query(query, params) + return await self.get_task(task_id) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error creating task: {str(e)}") + + async def update_task(self, task_id: int, updates: Dict[str, Any]) -> Dict[str, Any]: + """Update an existing task.""" + try: + current_task = await self.get_task(task_id) + if "task_type" in updates and updates["task_type"] != current_task["task_type"]: + existing_task = await self.check_task_exists(updates["task_type"]) + if existing_task and existing_task["id"] != task_id: + raise HTTPException( + status_code=409, + detail=f"A task with type '{updates['task_type']}' already exists (Task: '{existing_task['name']}', ID: {existing_task['id']}). You cannot have duplicate task types in the system.", + ) + if updates["task_type"] in TASK_TYPES: + updates["command"] = TASK_TYPES[updates["task_type"]]["command"] + allowed_fields = [ + "name", + "description", + "command", + "task_type", + "frequency", + "frequency_unit", + "enabled", + ] + set_clauses = [] + params = [] + for field, value in updates.items(): + if field in allowed_fields: + if field == "enabled": + value = 1 if value else 0 + set_clauses.append(f"{field} = ?") + params.append(value) + if not set_clauses: + return await self.get_task(task_id) + params.append(task_id) + update_query = f""" + UPDATE tasks + SET {", ".join(set_clauses)} + WHERE id = ? + """ + await tasks_db.execute_query(update_query, tuple(params)) + return await self.get_task(task_id) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error updating task: {str(e)}") + + async def delete_task(self, task_id: int) -> Dict[str, str]: + """Delete a task.""" + try: + task = await self.get_task(task_id) + query = """ + DELETE FROM tasks + WHERE id = ? + """ + await tasks_db.execute_query(query, (task_id,)) + return {"message": f"Task '{task['name']}' has been deleted"} + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error deleting task: {str(e)}") + + async def toggle_task(self, task_id: int, enable: bool) -> Dict[str, Any]: + """Enable or disable a task.""" + try: + query = """ + UPDATE tasks + SET enabled = ? + WHERE id = ? + """ + await tasks_db.execute_query(query, (1 if enable else 0, task_id)) + return await self.get_task(task_id) + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error updating task: {str(e)}") + + async def get_task_executions(self, task_id: Optional[int] = None, page: int = 1, per_page: int = 10) -> Dict[str, Any]: + """Get paginated task executions.""" + try: + offset = (page - 1) * per_page + if task_id: + count_query = """ + SELECT COUNT(*) as count + FROM task_executions + WHERE task_id = ? + """ + count_params = (task_id,) + query = """ + SELECT id, task_id, start_time, end_time, status, error_message, output + FROM task_executions + WHERE task_id = ? + ORDER BY start_time DESC + LIMIT ? OFFSET ? + """ + params = (task_id, per_page, offset) + else: + count_query = """ + SELECT COUNT(*) as count + FROM task_executions + """ + count_params = () + query = """ + SELECT id, task_id, start_time, end_time, status, error_message, output + FROM task_executions + ORDER BY start_time DESC + LIMIT ? OFFSET ? + """ + params = (per_page, offset) + count_result = await tasks_db.execute_query(count_query, count_params, fetch=True, fetch_one=True) + total_items = count_result.get("count", 0) if count_result else 0 + executions = await tasks_db.execute_query(query, params, fetch=True) + for execution in executions: + if execution.get("task_id"): + try: + task = await self.get_task(execution["task_id"]) + execution["task_name"] = task.get("name", "Unknown Task") + except Exception as _: + execution["task_name"] = "Unknown Task" + total_pages = (total_items + per_page - 1) // per_page if total_items > 0 else 0 + has_next = page < total_pages + has_prev = page > 1 + return { + "items": executions, + "total": total_items, + "page": page, + "per_page": per_page, + "total_pages": total_pages, + "has_next": has_next, + "has_prev": has_prev, + } + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching task executions: {str(e)}") + + async def get_pending_tasks(self) -> List[Dict[str, Any]]: + """Get tasks that are due to run.""" + try: + query = """ + SELECT id, name, description, command, task_type, frequency, frequency_unit, enabled, last_run + FROM tasks + WHERE enabled = 1 + AND ( + last_run IS NULL + OR + CASE frequency_unit + WHEN 'minutes' THEN datetime(last_run, '+' || frequency || ' minutes') <= datetime('now', 'localtime') + WHEN 'hours' THEN datetime(last_run, '+' || frequency || ' hours') <= datetime('now', 'localtime') + WHEN 'days' THEN datetime(last_run, '+' || frequency || ' days') <= datetime('now', 'localtime') + ELSE datetime(last_run, '+' || frequency || ' seconds') <= datetime('now', 'localtime') + END + ) + ORDER BY last_run + """ + tasks = await tasks_db.execute_query(query, fetch=True) + for task in tasks: + task["enabled"] = bool(task.get("enabled", 0)) + return tasks + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching pending tasks: {str(e)}") + + async def get_stats(self) -> Dict[str, Any]: + """Get task statistics.""" + try: + task_query = """ + SELECT + COUNT(*) as total_tasks, + SUM(CASE WHEN enabled = 1 THEN 1 ELSE 0 END) as active_tasks, + SUM(CASE WHEN enabled = 0 THEN 1 ELSE 0 END) as disabled_tasks, + SUM(CASE WHEN last_run IS NULL THEN 1 ELSE 0 END) as never_run_tasks + FROM tasks + """ + task_stats = await tasks_db.execute_query(task_query, fetch=True, fetch_one=True) + cutoff_date = (datetime.now() - timedelta(days=7)).isoformat() + exec_query = """ + SELECT + COUNT(*) as total_executions, + COALESCE(SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END), 0) as successful_executions, + COALESCE(SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END), 0) as failed_executions, + COALESCE(SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END), 0) as running_executions, + COALESCE(AVG(CASE WHEN end_time IS NOT NULL + THEN (julianday(end_time) - julianday(start_time)) * 86400.0 + ELSE NULL END), 0) as avg_execution_time_seconds + FROM task_executions + WHERE start_time >= ? + """ + exec_stats = await tasks_db.execute_query(exec_query, (cutoff_date,), fetch=True, fetch_one=True) + return {"tasks": task_stats or {}, "executions": exec_stats or {}} + except Exception as e: + if isinstance(e, HTTPException): + raise e + raise HTTPException(status_code=500, detail=f"Error fetching task statistics: {str(e)}") + + +task_service = TaskService() diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/static/images/ponyo.png b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/static/images/ponyo.png new file mode 100644 index 0000000000000000000000000000000000000000..f2f2d51ac0d8b777705c07d5aa3266631b93aae2 GIT binary patch literal 317720 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

B)?$dze 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�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[ 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

uVh5X2*2N4Vb(|4VUy4ny>Od_A-9xw?N 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�ZwkrqRqKMbLBT&>`IM-S25+ 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 literal 0 HcmV?d00001 diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/static/musics/intro_audio.mp3 b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/static/musics/intro_audio.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..42be2a223b8dd1938496a595682fa0aa511ea666 GIT binary patch literal 244892 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

  • > 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
    }E@3XvuNXLOq^2K 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­W?B4P}t84G$hOqys79h~0DW%BzI;#e?SY{w_ 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 literal 0 HcmV?d00001 diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/__init__.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/agent_agno_test.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/agent_agno_test.py new file mode 100644 index 0000000..bd2f694 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/agent_agno_test.py @@ -0,0 +1,12 @@ +from typing import Iterator +from agno.agent import Agent, RunResponse +from agno.models.openai import OpenAIChat +from agno.utils.pprint import pprint_run_response +from dotenv import load_dotenv + +load_dotenv() +agent = Agent(model=OpenAIChat(id="gpt-4o-mini")) +response: RunResponse = agent.run("Tell me a 5 second short story about a robot") +response_stream: Iterator[RunResponse] = agent.run("Tell me a 5 second short story about a lion", stream=True) +pprint_run_response(response, markdown=True) +pprint_run_response(response_stream, markdown=True) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/embedding_search_test.py b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/embedding_search_test.py new file mode 100644 index 0000000..cbe51a9 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/embedding_search_test.py @@ -0,0 +1,194 @@ +import argparse +import os +import numpy as np +import faiss +from openai import OpenAI +from db.config import get_tracking_db_path +from db.connection import execute_query +from utils.load_api_keys import load_api_key +from db.config import get_faiss_db_path + +EMBEDDING_MODEL = "text-embedding-3-small" +FAISS_INDEX_PATH, FAIS_MAPPING_PATH = get_faiss_db_path() + + +def generate_query_embedding(client, query_text, model=EMBEDDING_MODEL): + try: + response = client.embeddings.create(input=query_text, model=model) + return response.data[0].embedding, model + except Exception as e: + print(f"Error generating query embedding: {str(e)}") + return None, None + + +def load_faiss_index(index_path=FAISS_INDEX_PATH): + if not os.path.exists(index_path): + raise FileNotFoundError(f"FAISS index not found at {index_path}") + return faiss.read_index(index_path) + + +def load_id_mapping(mapping_path=FAIS_MAPPING_PATH): + if not os.path.exists(mapping_path): + raise FileNotFoundError(f"ID mapping not found at {mapping_path}") + return np.load(mapping_path).tolist() + + +def get_article_details(tracking_db_path, article_ids): + if not article_ids: + return [] + placeholders = ",".join(["?"] * len(article_ids)) + query = f""" + SELECT id, title, url, published_date, summary + FROM crawled_articles + WHERE id IN ({placeholders}) + """ + return execute_query(tracking_db_path, query, article_ids, fetch=True) + + +def search_articles( + query_text, + tracking_db_path=None, + openai_api_key=None, + index_path="databases/faiss/article_index.faiss", + mapping_path="databases/faiss/article_id_map.npy", + top_k=5, + search_params=None, +): + if tracking_db_path is None: + tracking_db_path = get_tracking_db_path() + if openai_api_key is None: + openai_api_key = load_api_key() + if not openai_api_key: + raise ValueError("OpenAI API key is required") + client = OpenAI(api_key=openai_api_key) + query_embedding, _ = generate_query_embedding(client, query_text) + if not query_embedding: + raise ValueError("Failed to generate query embedding") + query_vector = np.array([query_embedding]).astype(np.float32) + try: + faiss_index = load_faiss_index(index_path) + id_map = load_id_mapping(mapping_path) + if search_params: + if isinstance(faiss_index, faiss.IndexIVF) and "nprobe" in search_params: + faiss_index.nprobe = search_params["nprobe"] + print(f"Set nprobe to {faiss_index.nprobe}") + if hasattr(faiss_index, "hnsw") and "ef" in search_params: + faiss_index.hnsw.efSearch = search_params["ef"] + print(f"Set efSearch to {faiss_index.hnsw.efSearch}") + index_type = "unknown" + if isinstance(faiss_index, faiss.IndexFlatL2): + index_type = "flat" + elif isinstance(faiss_index, faiss.IndexIVFFlat): + index_type = "ivfflat" + print(f"Using IVF index with nprobe = {faiss_index.nprobe}") + elif isinstance(faiss_index, faiss.IndexIVFPQ): + index_type = "ivfpq" + print(f"Using IVF-PQ index with nprobe = {faiss_index.nprobe}") + elif hasattr(faiss_index, "hnsw"): + index_type = "hnsw" + print(f"Using HNSW index with efSearch = {faiss_index.hnsw.efSearch}") + print(f"Searching {index_type} FAISS index with {len(id_map)} articles...") + distances, indices = faiss_index.search(query_vector, top_k) + result_article_ids = [id_map[idx] for idx in indices[0] if idx < len(id_map)] + results = get_article_details(tracking_db_path, result_article_ids) + for i, result in enumerate(results): + distance = float(distances[0][i]) + similarity = float(np.exp(-distance)) + result["distance"] = distance + result["similarity"] = similarity + result["score"] = similarity + return results + except Exception as e: + print(f"Error during search: {str(e)}") + import traceback + + traceback.print_exc() + return [] + + +def print_search_results(results): + if not results: + print("No results found.") + return + print(f"\nFound {len(results)} results:\n") + for i, result in enumerate(results): + similarity_pct = result.get("similarity", 0) * 100 + print(f"{i + 1}. {result['title']}") + print(f" Relevance: {similarity_pct:.1f}%") + if "distance" in result: + print(f" Vector distance: {result['distance']:.4f}") + print(f" Published: {result['published_date']}") + print(f" URL: {result['url']}") + if len(result["summary"]) > 150: + print(f" Summary: {result['summary'][:150]}...") + else: + print(f" Summary: {result['summary']}") + print() + + +def parse_arguments(): + parser = argparse.ArgumentParser(description="Search for articles using FAISS") + parser.add_argument( + "query", + help="Search query text", + ) + parser.add_argument( + "--api_key", + help="OpenAI API Key (overrides environment variables)", + ) + parser.add_argument( + "--top_k", + type=int, + default=5, + help="Number of results to return", + ) + parser.add_argument( + "--nprobe", + type=int, + help="Number of clusters to search (for IVF indexes)", + ) + parser.add_argument( + "--ef", + type=int, + help="Search depth (for HNSW indexes)", + ) + parser.add_argument( + "--index_path", + default=FAISS_INDEX_PATH, + help="Path to the FAISS index file", + ) + parser.add_argument( + "--mapping_path", + default=FAIS_MAPPING_PATH, + help="Path to the ID mapping file", + ) + return parser.parse_args() + + +if __name__ == "__main__": + args = parse_arguments() + api_key = args.api_key or load_api_key() + if not api_key: + print("Error: No OpenAI API key provided. Please provide via --api_key or set OPENAI_API_KEY in .env file") + exit(1) + search_params = {} + if args.nprobe: + search_params["nprobe"] = args.nprobe + if args.ef: + search_params["ef"] = args.ef + try: + results = search_articles( + query_text=args.query, + openai_api_key=api_key, + top_k=args.top_k, + index_path=args.index_path, + mapping_path=args.mapping_path, + search_params=search_params, + ) + print_search_results(results) + except Exception as e: + print(f"Error: {str(e)}") + import traceback + + traceback.print_exc() + exit(1) diff --git a/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/english_sample_0.wav b/advanced_ai_agents/multi_agent_apps/ai_news_and_podcast_agents/beifong/tests/english_sample_0.wav new file mode 100644 index 0000000000000000000000000000000000000000..1fab1956b0d88d752c48d0356045ad33b2fd50ba GIT binary patch literal 200444 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