diff --git a/memory_agents/youtube_trend_agent/.env.example b/memory_agents/youtube_trend_agent/.env.example new file mode 100644 index 0000000..f0b220f --- /dev/null +++ b/memory_agents/youtube_trend_agent/.env.example @@ -0,0 +1,3 @@ +OPENAI_API_KEY=Your Minimax api key here +EXA_API_KEY=Your exaAI api key here +MEMORI_API_KEY=Your Memori API key here \ No newline at end of file diff --git a/memory_agents/youtube_trend_agent/README.md b/memory_agents/youtube_trend_agent/README.md index 45df21b..78cc074 100644 --- a/memory_agents/youtube_trend_agent/README.md +++ b/memory_agents/youtube_trend_agent/README.md @@ -1,14 +1,39 @@ -## YouTube Trend Analysis Agent +## YouTube Trend Analysis Agent with Memori & MiniMax -YouTube channel analysis agent powered by **Memori**, **Agno (Nebius)**, **Exa**, and **yt-dlp**. -Paste your YouTube channel URL, ingest recent videos into Memori, then chat with an agent that surfaces trends and concrete new video ideas grounded in your own content. +An AI-powered **YouTube Trend Coach** that uses **Memori v3** as long‑term memory and **MiniMax (OpenAI‑compatible)** for reasoning. + +- **Scrapes your channel** with `yt-dlp` and stores video metadata in Memori. +- Uses **MiniMax** to analyze your channel history plus **Exa** web trends. +- Provides a **Streamlit chat UI** to ask for trends and concrete new video ideas grounded in your own content. + +--- ### Features -- **Direct YouTube scraping**: Uses `yt-dlp` to scrape your channel or playlist from YouTube and collect titles, tags, dates, views, and descriptions. -- **Memori memory store**: Stores each video as a Memori memory (via OpenAI) for fast semantic search and reuse across chats. -- **Web trend context with Exa**: Calls Exa to pull recent articles and topics for your niche and blends them with your own channel history. -- **Streamlit UI**: Sidebar for API keys + channel URL and a chat area for asking about trends and ideas. +- **Direct YouTube scraping** + - Uses `yt-dlp` to scrape a channel or playlist URL (titles, tags, dates, views, descriptions). + - Stores each video as a Memori document for later semantic search. + +- **Memori memory store** + - Uses `Memori` + a MiniMax/OpenAI‑compatible client to persist “memories” of your videos. + - Ingestion happens via `ingest_channel_into_memori` in `core.py`, which calls `client.chat.completions.create(...)` so Memori can automatically capture documents. + +- **Web trend context with Exa (optional)** + - If `EXA_API_KEY` is set, fetches web articles and topics for your niche via `Exa`. + - Blends Exa trends with your channel history when generating ideas. + +- **Streamlit UI** + - Sidebar for API keys, MiniMax base URL, and channel URL. + - Main area provides a chat interface for asking about trends and ideas. + +--- + +### Prerequisites + +- Python 3.11+ +- [`uv`](https://github.com/astral-sh/uv) (recommended) or `pip` +- MiniMax account + API key (used via the OpenAI SDK) +- Optional: Exa and Memori API keys --- @@ -29,12 +54,12 @@ uv sync This will create a virtual environment (if needed) and install all dependencies declared in `pyproject.toml`. -3. **Environment variables** (set in your shell or a local `.env` file in this folder): +3. **Environment variables** -- `NEBIUS_API_KEY` – required (used both for Memori ingestion and the Agno-powered advisor). -- `EXA_API_KEY` – optional but recommended (for external trend context via Exa). -- `MEMORI_API_KEY` – optional, for Memori Advanced Augmentation / higher quotas. -- `SQLITE_DB_PATH` – optional, defaults to `./memori.sqlite` if unset. +You can either: + +- Set these in your `.env`, (see .env.example) **or** +- Enter them in the Streamlit **sidebar** (the app writes them into `os.environ` for the current process). --- @@ -46,13 +71,28 @@ From the `youtube_trend_agent` directory: uv run streamlit run app.py ``` +--- + +### Using the App + In the **sidebar**: -1. Enter your **Nebius**, optional **Exa**, and optional **Memori** API keys. -2. Paste your **YouTube channel (or playlist) URL**. -3. Click **“Ingest channel into Memori”** to scrape and store recent videos. +1. Enter your **MiniMax API Key** and (optionally) **MiniMax Base URL**. +2. Optionally enter **Exa** and **Memori** API keys. +3. Paste your **YouTube channel (or playlist) URL**. +4. Click **“Save Settings”** to store the keys for this session. +5. Click **“Ingest channel into Memori”** to scrape and store recent videos. + +Then, in the main chat: + +- Ask things like: + - “Suggest 5 new video ideas that build on my existing content and current trends.” + - “What trends am I missing in my current uploads?” + - “Which topics seem to perform best on my channel?” -Then use the main chat box to ask things like: +The agent will: -- “Suggest 5 new video ideas that build on my existing content and current trends.” -- “What trends am I missing in my current uploads?” +- Pull context from **Memori** (your stored video history), +- Use **MiniMax** (`MiniMax-M2.1` by default, configurable), +- Optionally incorporate **Exa** web trends, +- And respond with specific, actionable ideas and analysis. diff --git a/memory_agents/youtube_trend_agent/app.py b/memory_agents/youtube_trend_agent/app.py index 7067546..c3df7d1 100644 --- a/memory_agents/youtube_trend_agent/app.py +++ b/memory_agents/youtube_trend_agent/app.py @@ -1,12 +1,12 @@ """ -YouTube Trend Analysis Agent with Memori, Agno (Nebius), and YouTube scraping. +YouTube Trend Analysis Agent with Memori, MiniMax (OpenAI-compatible), and YouTube scraping. Streamlit app: - Sidebar: API keys + YouTube channel URL + "Ingest channel into Memori" button. - Main: Chat interface to ask about trends and get new video ideas. This app uses: -- Nebius (via both the OpenAI SDK and Agno's Nebius model) for LLM reasoning. +- MiniMax (via the OpenAI SDK) for LLM reasoning. - yt-dlp to scrape YouTube channel/playlist videos. - Memori to store and search your channel's video history. """ @@ -15,9 +15,8 @@ import os import streamlit as st -from agno.agent import Agent -from agno.models.nebius import Nebius from dotenv import load_dotenv +from openai import OpenAI from core import fetch_exa_trends, ingest_channel_into_memori @@ -69,18 +68,17 @@ def main(): with st.sidebar: st.subheader("🔑 API Keys & Channel") - # Nebius logo above the Nebius API key field - try: - st.image("assets/Nebius_Logo.png", width=120) - except Exception: - # Non-fatal if the logo is missing - pass - - nebius_api_key_input = st.text_input( - "Nebius API Key", - value=os.getenv("NEBIUS_API_KEY", ""), + minimax_api_key_input = st.text_input( + "MiniMax API Key", + value=os.getenv("OPENAI_API_KEY", ""), type="password", - help="Your Nebius API key (used for both Memori and Agno).", + help="Your MiniMax API key (used via the OpenAI-compatible SDK).", + ) + + minimax_base_url_input = st.text_input( + "MiniMax Base URL", + value=os.getenv("OPENAI_BASE_URL", "https://api.minimax.io/v1"), + help="Base URL for MiniMax's OpenAI-compatible API.", ) exa_api_key_input = st.text_input( @@ -103,8 +101,10 @@ def main(): ) if st.button("Save Settings"): - if nebius_api_key_input: - os.environ["NEBIUS_API_KEY"] = nebius_api_key_input + if minimax_api_key_input: + os.environ["OPENAI_API_KEY"] = minimax_api_key_input + if minimax_base_url_input: + os.environ["OPENAI_BASE_URL"] = minimax_base_url_input if exa_api_key_input: os.environ["EXA_API_KEY"] = exa_api_key_input if memori_api_key_input: @@ -115,8 +115,8 @@ def main(): st.markdown("---") if st.button("Ingest channel into Memori"): - if not os.getenv("NEBIUS_API_KEY"): - st.warning("NEBIUS_API_KEY is required before ingestion.") + if not os.getenv("OPENAI_API_KEY"): + st.warning("OPENAI_API_KEY (MiniMax) is required before ingestion.") elif not channel_url_input.strip(): st.warning("Please enter a YouTube channel or playlist URL.") else: @@ -139,25 +139,23 @@ def main(): ) # Get keys for main app logic - nebius_key = os.getenv("NEBIUS_API_KEY", "") - if not nebius_key: + api_key = os.getenv("OPENAI_API_KEY", "") + base_url = os.getenv("OPENAI_BASE_URL", "https://api.minimax.io/v1") + if not api_key: st.warning( - "⚠️ Please enter your Nebius API key in the sidebar to start chatting!" + "⚠️ Please enter your MiniMax API key in the sidebar to start chatting!" ) st.stop() - # Initialize Nebius model for the advisor (once) - if "nebius_model" not in st.session_state: + # Initialize MiniMax/OpenAI client for the advisor (once) + if "openai_client" not in st.session_state: try: - st.session_state.nebius_model = Nebius( - id=os.getenv( - "YOUTUBE_TREND_MODEL", - "moonshotai/Kimi-K2-Instruct", - ), - api_key=nebius_key, + st.session_state.openai_client = OpenAI( + base_url=base_url, + api_key=api_key, ) except Exception as e: - st.error(f"Failed to initialize Nebius model: {e}") + st.error(f"Failed to initialize MiniMax client: {e}") st.stop() # Display chat history @@ -252,18 +250,30 @@ def main(): {exa_trends} """ - advisor = Agent( - name="YouTube Trend Advisor", - model=st.session_state.nebius_model, - markdown=True, + client = st.session_state.openai_client + completion = client.chat.completions.create( + model=os.getenv( + "YOUTUBE_TREND_MODEL", + "MiniMax-M2.1", + ), + messages=[ + { + "role": "system", + "content": ( + "You are a YouTube strategy assistant that analyzes a creator's " + "channel and suggests specific, actionable video ideas." + ), + }, + { + "role": "user", + "content": full_prompt, + }, + ], + extra_body={"reasoning_split": True}, ) - result = advisor.run(full_prompt) - response_text = ( - str(result.content) - if hasattr(result, "content") - else str(result) - ) + message = completion.choices[0].message + response_text = getattr(message, "content", "") or str(message) st.session_state.messages.append( {"role": "assistant", "content": response_text} diff --git a/memory_agents/youtube_trend_agent/core.py b/memory_agents/youtube_trend_agent/core.py index 42f4f47..8883e2a 100644 --- a/memory_agents/youtube_trend_agent/core.py +++ b/memory_agents/youtube_trend_agent/core.py @@ -2,7 +2,7 @@ Core logic for the YouTube Trend Analysis Agent. This module contains: -- Memori + Nebius initialization helpers. +- Memori initialization helpers (using an OpenAI-compatible client, e.g. MiniMax). - YouTube scraping utilities. - Exa-based trend fetching. - Channel ingestion into Memori. @@ -43,13 +43,20 @@ def init_memori_with_nebius() -> Memori | None: Initialize Memori v3 + Nebius client (via the OpenAI SDK). This is used so Memori can automatically persist "memories" when we send - documents through the registered Nebius-backed client. Agno + Nebius power all - YouTube analysis and idea generation. + documents through the registered OpenAI-compatible client. + + NOTE: + - To use MiniMax, set: + OPENAI_BASE_URL = "https://api.minimax.io/v1" + OPENAI_API_KEY = "" """ - nebius_key = os.getenv("NEBIUS_API_KEY", "") - if not nebius_key: + # MiniMax (or other OpenAI-compatible) configuration via standard OpenAI env vars + base_url = os.getenv("OPENAI_BASE_URL", "https://api.minimax.io/v1") + api_key = os.getenv("OPENAI_API_KEY", "") + + if not api_key: st.warning( - "NEBIUS_API_KEY is not set – Memori v3 ingestion will not be active." + "OPENAI_API_KEY is not set – Memori v3 ingestion will not be active." ) return None @@ -69,10 +76,10 @@ def init_memori_with_nebius() -> Memori | None: SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) client = OpenAI( - base_url="https://api.studio.nebius.com/v1/", - api_key=nebius_key, + base_url=base_url, + api_key=api_key, ) - # Use the OpenAI-compatible registration API; the client itself points to Nebius. + # Use the OpenAI-compatible registration API; the client itself points to MiniMax (or any compatible provider). mem = Memori(conn=SessionLocal).openai.register(client) # Attribution so Memori can attach memories to this process/entity. mem.attribution(entity_id="youtube-channel", process_id="youtube-trend-agent") @@ -297,7 +304,7 @@ def ingest_channel_into_memori(channel_url: str) -> int: _ = client.chat.completions.create( model=os.getenv( "YOUTUBE_TREND_INGEST_MODEL", - "moonshotai/Kimi-K2-Instruct", + "MiniMax-M2.1", ), messages=[ {