From b190aec975017d32582e92fc762714997a98c994 Mon Sep 17 00:00:00 2001 From: paisley Date: Thu, 30 Oct 2025 14:58:05 +0800 Subject: [PATCH 1/2] feat: news agent --- python/configs/agent_cards/news_agent.json | 52 ++++++ python/configs/agents/news_agent.yaml | 109 +++++++++++ .../valuecell/agents/news_agent/__init__.py | 5 + .../valuecell/agents/news_agent/__main__.py | 62 +++++++ python/valuecell/agents/news_agent/core.py | 171 ++++++++++++++++++ python/valuecell/agents/news_agent/prompts.py | 96 ++++++++++ python/valuecell/agents/news_agent/tools.py | 115 ++++++++++++ 7 files changed, 610 insertions(+) create mode 100644 python/configs/agent_cards/news_agent.json create mode 100644 python/configs/agents/news_agent.yaml create mode 100644 python/valuecell/agents/news_agent/__init__.py create mode 100644 python/valuecell/agents/news_agent/__main__.py create mode 100644 python/valuecell/agents/news_agent/core.py create mode 100644 python/valuecell/agents/news_agent/prompts.py create mode 100644 python/valuecell/agents/news_agent/tools.py diff --git a/python/configs/agent_cards/news_agent.json b/python/configs/agent_cards/news_agent.json new file mode 100644 index 000000000..815a271bf --- /dev/null +++ b/python/configs/agent_cards/news_agent.json @@ -0,0 +1,52 @@ +{ + "name": "NewsAgent", + "display_name": "News Agent", + "url": "http://localhost:10005/", + "description": "Professional news agent specialized in gathering, analyzing, and presenting current events, breaking news, and financial market information from reliable sources with real-time web search capabilities.", + "skills": [ + { + "id": "web_search", + "name": "Real-time news search", + "description": "Search for current news articles and information across the web using advanced search capabilities.", + "examples": [ + "What's the latest news about artificial intelligence?", + "Search for recent developments in renewable energy", + "Find news about the latest tech IPOs" + ], + "tags": ["web search", "real-time", "current events", "research"] + }, + { + "id": "get_breaking_news", + "name": "Breaking news monitoring", + "description": "Monitor and retrieve urgent breaking news updates and alerts from multiple sources.", + "examples": [ + "Any breaking news right now?", + "What are the latest urgent news updates?", + "Show me breaking news from the past hour" + ], + "tags": ["breaking news", "urgent", "alerts", "monitoring"] + }, + { + "id": "get_financial_news", + "name": "Financial market news", + "description": "Provide comprehensive financial market news, business updates, and economic developments.", + "examples": [ + "What's happening in the stock market today?", + "Latest financial news and market updates", + "Show me news about major economic indicators" + ], + "tags": ["finance", "markets", "business", "stocks", "economics"] + } + ], + "enabled": true, + "metadata": { + "version": "1.0.0", + "author": "ValueCell Team", + "tags": [ + "news", + "current events", + "financial markets", + "real-time search" + ] + } +} \ No newline at end of file diff --git a/python/configs/agents/news_agent.yaml b/python/configs/agents/news_agent.yaml new file mode 100644 index 000000000..626d1cdfa --- /dev/null +++ b/python/configs/agents/news_agent.yaml @@ -0,0 +1,109 @@ +# ============================================ +# News Agent Configuration +# ============================================ +# The News Agent specializes in gathering, analyzing, and presenting news information. +# It provides real-time news search, categorized news retrieval, and comprehensive analysis. +# ============================================ +# Configuration Priority (highest to lowest): +# 1. Environment Variables (highest priority) +# 2. .env file +# 3. YAML files (lowest priority, system defaults) +# ============================================ + +name: "News Agent" +enabled: true + +# Model Configuration +# News Agent needs a capable model for news analysis and summarization +models: + # Primary model for news analysis, summarization, and query processing + primary: + model_id: "anthropic/claude-haiku-4.5" + provider: "openrouter" # Must explicitly specify provider (not null) + + # Provider-specific model mappings for fallback + # Used when primary provider fails and fallback is attempted + provider_models: + siliconflow: "Qwen/Qwen3-235B-A22B-Thinking-2507" # Strong reasoning for news analysis + google: "gemini-2.5-flash" # Fast and efficient for news processing + + parameters: + # temperature: 0.3 # Lower temperature for more factual news reporting + # max_tokens: 4096 # Sufficient for comprehensive news analysis + + # Web search model for real-time information gathering + # Uses specialized models optimized for web search and real-time data + search: + model_id: "perplexity/sonar" + provider: "openrouter" + + # Provider-specific search model mappings + provider_models: + google: "gemini-2.5-flash" # With search grounding enabled + + parameters: + # max_tokens: 2048 # Optimized for search result processing + +# Environment Variable Overrides +# Format: ENV_VAR_NAME -> config.path +# These allow runtime configuration through environment variables +env_overrides: + # Primary model overrides + NEWS_AGENT_MODEL_ID: "models.primary.model_id" + NEWS_AGENT_PROVIDER: "models.primary.provider" + NEWS_AGENT_TEMPERATURE: "models.primary.parameters.temperature" + NEWS_AGENT_MAX_TOKENS: "models.primary.parameters.max_tokens" + + # Search model overrides + NEWS_SEARCH_MODEL_ID: "models.search.model_id" + NEWS_SEARCH_PROVIDER: "models.search.provider" + + # Web search provider configuration + WEB_SEARCH_PROVIDER: "capabilities.web_search.provider" + +# News Agent Capabilities +capabilities: + # Web search configuration + web_search: + enabled: true + provider: "perplexity" # Default: perplexity, alternative: google + max_results: 10 + + # Breaking news monitoring + breaking_news: + enabled: true # Enable breaking news monitoring + refresh_interval: 300 # seconds + + # Financial news specialization + financial_news: + enabled: true + supported_markets: ["US", "Global"] + include_sectors: true + include_individual_stocks: true + + + + # Multi-language support + languages: + enabled: true + supported: ["en", "zh", "ja", "ko", "es", "fr", "de"] + default: "en" + + # News analysis features + analysis: + sentiment_analysis: true + topic_extraction: true + entity_recognition: true + summarization: true + fact_checking: false # Requires additional verification tools + +# Tool Configuration +tools: + web_search: + enabled: true + + get_breaking_news: + enabled: true + + get_financial_news: + enabled: true \ No newline at end of file diff --git a/python/valuecell/agents/news_agent/__init__.py b/python/valuecell/agents/news_agent/__init__.py new file mode 100644 index 000000000..bb0036a6c --- /dev/null +++ b/python/valuecell/agents/news_agent/__init__.py @@ -0,0 +1,5 @@ +"""News Agent for fetching and analyzing news content.""" + +from .core import NewsAgent + +__all__ = ["NewsAgent"] diff --git a/python/valuecell/agents/news_agent/__main__.py b/python/valuecell/agents/news_agent/__main__.py new file mode 100644 index 000000000..21d674034 --- /dev/null +++ b/python/valuecell/agents/news_agent/__main__.py @@ -0,0 +1,62 @@ +"""Main entry point for the News Agent.""" + +import asyncio + +from valuecell.core.agent.decorator import create_wrapped_agent + +from .core import NewsAgent + + +async def main(): + """Main function to run the News Agent.""" + agent = create_wrapped_agent(NewsAgent) + + # Example queries for testing + test_queries = [ + "What's the latest news today?", + "Any breaking news right now?", + "What's happening in the financial markets?", + "Latest technology news and AI developments", + "News about climate change in the past week", + ] + + print("News Agent is ready! Try these example queries:") + for i, query in enumerate(test_queries, 1): + print(f"{i}. {query}") + + print("\nEnter your news query (or 'quit' to exit):") + + while True: + try: + user_input = input("\n> ").strip() + + if user_input.lower() in ["quit", "exit", "q"]: + print("Goodbye!") + break + + if not user_input: + continue + + print("\nFetching news...") + + # Stream the response + async for chunk in agent.stream(user_input): + if chunk.get("type") == "message": + print(chunk.get("content", ""), end="", flush=True) + elif chunk.get("type") == "tool_call_start": + tool_name = chunk.get("tool_name", "") + print(f"\n[Using tool: {tool_name}]") + elif chunk.get("type") == "error": + print(f"\nError: {chunk.get('content', '')}") + + print("\n" + "=" * 50) + + except KeyboardInterrupt: + print("\nGoodbye!") + break + except Exception as e: + print(f"Error: {e}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/valuecell/agents/news_agent/core.py b/python/valuecell/agents/news_agent/core.py new file mode 100644 index 000000000..2f77630f1 --- /dev/null +++ b/python/valuecell/agents/news_agent/core.py @@ -0,0 +1,171 @@ +"""News Agent core implementation.""" + +from typing import AsyncGenerator, Dict, Optional + +from agno.agent import Agent +from loguru import logger + +from valuecell.adapters.models import create_model_for_agent +from valuecell.config.manager import get_config_manager +from valuecell.core.agent.responses import streaming +from valuecell.core.types import BaseAgent, StreamResponse + +from .prompts import NEWS_AGENT_INSTRUCTIONS +from .tools import get_breaking_news, get_financial_news, web_search + + +class NewsAgent(BaseAgent): + """News Agent for fetching and analyzing news content.""" + + def __init__(self): + """Initialize the News Agent with news-related tools.""" + super().__init__() + + # Load agent configuration + self.config_manager = get_config_manager() + self.agent_config = self.config_manager.get_agent_config("news_agent") + + # Load tools based on configuration + available_tools = [] + + available_tools.extend([web_search, get_breaking_news, get_financial_news]) + + # Create the knowledge news agent with configured model and tools + # Use create_model_for_agent to load agent-specific configuration + self.knowledge_news_agent = Agent( + model=create_model_for_agent("news_agent"), + tools=available_tools, + instructions=NEWS_AGENT_INSTRUCTIONS, + ) + + logger.info("NewsAgent initialized with news tools") + + async def stream( + self, + query: str, + conversation_id: str, + task_id: str, + dependencies: Optional[Dict] = None, + ) -> AsyncGenerator[StreamResponse, None]: + """ + Stream news information based on the user query. + + Args: + query: The user's news query + conversation_id: Conversation ID for context + task_id: Task ID + dependencies: Optional dependencies + + Yields: + StreamResponse: Streaming responses with proper event types + """ + response_stream = self.knowledge_news_agent.arun( + query, + stream=True, + stream_intermediate_steps=True, + session_id=conversation_id, + ) + async for event in response_stream: + if event.event == "RunContent": + yield streaming.message_chunk(event.content) + elif event.event == "ToolCallStarted": + yield streaming.tool_call_started( + event.tool.tool_call_id, event.tool.tool_name + ) + elif event.event == "ToolCallCompleted": + yield streaming.tool_call_completed( + event.tool.result, event.tool.tool_call_id, event.tool.tool_name + ) + logger.info("News query processing completed") + + yield streaming.done() + + async def run( + self, query: str, conversation_id: Optional[str] = None, **kwargs + ) -> str: + """ + Run news query and return the complete response. + + Args: + query: The user's news query + conversation_id: Optional conversation ID for context + **kwargs: Additional parameters + + Returns: + Complete news response as string + """ + try: + logger.info(f"Running news query: {query}") + + # Get the complete response from the knowledge news agent + response = await self.knowledge_news_agent.arun(query) + + return response.content + + except Exception as e: + logger.error(f"Error in NewsAgent run: {e}") + return f"Error processing news query: {str(e)}" + + def get_capabilities(self) -> dict: + """Get the capabilities and available tools of the News Agent.""" + # Get capabilities from configuration if available + if self.agent_config: + config_capabilities = self.agent_config.capabilities + + # Build tools list based on enabled capabilities + enabled_tools = [] + if config_capabilities.get("web_search", {}).get("enabled", True): + enabled_tools.append("web_search") + if config_capabilities.get("breaking_news", {}).get("enabled", True): + enabled_tools.append("get_breaking_news") + if config_capabilities.get("financial_news", {}).get("enabled", True): + enabled_tools.append("get_financial_news") + + # Build capabilities list based on configuration + capabilities_list = [] + if config_capabilities.get("web_search", {}).get("enabled", True): + capabilities_list.extend( + ["Real-time news search", "Topic-based news analysis"] + ) + if config_capabilities.get("breaking_news", {}).get("enabled", True): + capabilities_list.append("Categorized news retrieval") + if config_capabilities.get("financial_news", {}).get("enabled", True): + capabilities_list.extend( + [ + "Comprehensive stock news analysis", + "Macroeconomic news", + "Industry sector news", + "Individual stock news analysis", + ] + ) + if config_capabilities.get("languages", {}).get("enabled", True): + capabilities_list.append("Multi-language support") + if config_capabilities.get("analysis", {}).get("sentiment_analysis", True): + capabilities_list.append("Intelligent news summarization") + + return { + "name": "news_agent", + "description": "Professional news query and analysis agent supporting multiple news sources and query types", + "tools": enabled_tools, + "capabilities": capabilities_list, + "enabled": self.agent_config.enabled, + } + + # Fallback to default capabilities if no config found + return { + "name": "news_agent", + "description": "Professional news query and analysis agent supporting multiple news sources and query types", + "tools": ["web_search", "get_breaking_news", "get_financial_news"], + "capabilities": [ + "Real-time news search", + "Categorized news retrieval", + "Topic-based news analysis", + "Comprehensive stock news analysis", + "Macroeconomic news", + "Industry sector news", + "Individual stock news analysis", + "Multi-language support", + "Intelligent news summarization", + ], + "enabled": True, + } diff --git a/python/valuecell/agents/news_agent/prompts.py b/python/valuecell/agents/news_agent/prompts.py new file mode 100644 index 000000000..8e5d02a96 --- /dev/null +++ b/python/valuecell/agents/news_agent/prompts.py @@ -0,0 +1,96 @@ +"""Prompts for the News Agent.""" + +NEWS_AGENT_INSTRUCTIONS = """ +You are a professional News Agent specialized in gathering, analyzing, and presenting news information. Your primary role is to help users stay informed about current events, breaking news, and specific topics of interest. + +## Core Capabilities + +1. **News Retrieval**: Fetch the latest news from various sources and categories +2. **Topic Analysis**: Provide in-depth analysis of specific news topics +3. **Breaking News**: Monitor and report urgent news updates +4. **Financial News**: Specialized coverage of market and financial news +5. **Technology News**: Focus on tech industry developments and innovations +6. **Custom Search**: Search for news on specific topics with date ranges + +## Tool Usage Guidelines +- Use `get_breaking_news()` for urgent updates +- Use `get_financial_news()` for market and business news +- Use `web_search()` for comprehensive information gathering + +## Date Awareness + +Always be aware of the current date and time context. When discussing news: +- Clearly indicate when events occurred +- Distinguish between breaking news and historical events +- Provide temporal context for better understanding + +## Response Planning + +Before responding to any query: +1. Identify the type of news request (general, breaking, financial, tech, specific topic) +2. Determine the appropriate tools to use +3. Consider the time sensitivity of the information +4. Plan how to structure the response for maximum clarity + +## Output Format + +Structure your responses as follows: + +### News Summary +- Provide a clear, concise headline +- Include key details (who, what, when, where, why) +- Mention the source and credibility when possible + +### Analysis (when appropriate) +- Explain the significance of the news +- Provide context and background information +- Discuss potential implications or consequences + +### Stock News Special Format + +For stock news queries, organize information according to the following structure: +1. **Macroeconomic Level**: Macroeconomic policies, market environment changes affecting the stock +2. **Industry Level**: Development trends, policy changes, and competitive landscape of the industry +3. **Individual Stock Level**: Specific business dynamics, financial reports, and major events of the company + +### Additional Information +- Related news or developments +- Links to further reading (when available) +- Recommendations for follow-up topics + +## Tone and Style + +- **Professional**: Maintain journalistic integrity and objectivity +- **Clear**: Use accessible language while being informative +- **Timely**: Emphasize the currency and relevance of information +- **Balanced**: Present multiple perspectives when covering controversial topics +- **Factual**: Stick to verified information and clearly distinguish between facts and opinions + +## Constraints + +- Always verify information through multiple sources when possible +- Clearly indicate when information is preliminary or unconfirmed +- Avoid spreading misinformation or unverified claims +- Respect privacy and ethical journalism standards +- Be transparent about limitations in real-time information access + +## Follow-up Strategy + +After providing news information: +- Ask if the user wants more details on specific aspects +- Suggest related topics that might be of interest +- Offer to set up monitoring for ongoing stories +- Provide options for different types of news coverage + +## Examples + +### Example 1: Breaking News +User: "Any breaking news right now?" +Response: Use `get_breaking_news()` to get urgent updates, then present them with appropriate urgency indicators and context. + +### Example 2: Financial News +User: "What's happening in the stock market?" +Response: Use `get_financial_news()` to get market updates, then provide analysis of market movements, key factors, and implications. + +Remember: Your goal is to keep users well-informed with accurate, timely, and relevant news information while maintaining the highest standards of journalistic integrity. +""" diff --git a/python/valuecell/agents/news_agent/tools.py b/python/valuecell/agents/news_agent/tools.py new file mode 100644 index 000000000..038dcd84d --- /dev/null +++ b/python/valuecell/agents/news_agent/tools.py @@ -0,0 +1,115 @@ +"""News-related tools for the News Agent.""" + +import os +from datetime import datetime +from typing import Optional + +from agno.agent import Agent +from loguru import logger + +from valuecell.adapters.models import create_model + + +async def web_search(query: str) -> str: + """Search web for the given query and return a summary of the top results. + + This function uses the centralized configuration system to create model instances. + It supports multiple search providers: + - Google (Gemini with search enabled) - when WEB_SEARCH_PROVIDER=google and GOOGLE_API_KEY is set + - Perplexity (via OpenRouter) - default fallback + + Args: + query: The search query string. + + Returns: + A summary of the top search results. + """ + # Check which provider to use based on environment configuration + if os.getenv("WEB_SEARCH_PROVIDER", "google").lower() == "google" and os.getenv( + "GOOGLE_API_KEY" + ): + return await _web_search_google(query) + + # Use Perplexity Sonar via OpenRouter for web search + # Perplexity models are optimized for web search and real-time information + model = create_model( + provider="openrouter", + model_id="perplexity/sonar", + max_tokens=None, + ) + response = await Agent(model=model).arun(query) + return response.content + + +async def _web_search_google(query: str) -> str: + """Search Google for the given query and return a summary of the top results. + + Uses Google Gemini with search grounding enabled for real-time web information. + + Args: + query: The search query string. + + Returns: + A summary of the top search results. + """ + # Use Google Gemini with search enabled + # The search=True parameter enables Google Search grounding for real-time information + model = create_model( + provider="google", + model_id="gemini-2.5-flash", + search=True, # Enable Google Search grounding + ) + response = await Agent(model=model).arun(query) + return response.content + + +async def get_breaking_news() -> str: + """Get breaking news and urgent updates. + + Returns: + Formatted string containing breaking news + """ + try: + search_query = "breaking news urgent updates today" + logger.info("Fetching breaking news") + + news_content = await web_search(search_query) + return news_content + + except Exception as e: + logger.error(f"Error fetching breaking news: {e}") + return f"Error fetching breaking news: {str(e)}" + + +async def get_financial_news( + ticker: Optional[str] = None, sector: Optional[str] = None +) -> str: + """Get financial and market news. + + Args: + ticker: Stock ticker symbol for company-specific news + sector: Industry sector for sector-specific news + + Returns: + Formatted string containing financial news + """ + try: + search_query = "financial market news" + + if ticker: + search_query = f"{ticker} stock news financial market" + elif sector: + search_query = f"{sector} sector financial news market" + + # Add time constraint for recent news + today = datetime.now().strftime("%Y-%m-%d") + search_query += f" {today}" + + logger.info(f"Searching for financial news with query: {search_query}") + + news_content = await web_search(search_query) + return news_content + + except Exception as e: + logger.error(f"Error fetching financial news: {e}") + return f"Error fetching financial news: {str(e)}" From e8ffa394a10fc56dc6bc9b5f43ee3dc0d9e5bd3a Mon Sep 17 00:00:00 2001 From: paisley Date: Thu, 30 Oct 2025 16:49:24 +0800 Subject: [PATCH 2/2] feat:news agent --- python/configs/agent_cards/news_agent.json | 3 + python/scripts/launch.py | 9 +- .../valuecell/agents/news_agent/__main__.py | 62 +----- python/valuecell/agents/news_agent/core.py | 194 +++++++----------- 4 files changed, 90 insertions(+), 178 deletions(-) diff --git a/python/configs/agent_cards/news_agent.json b/python/configs/agent_cards/news_agent.json index 815a271bf..2f70c77d0 100644 --- a/python/configs/agent_cards/news_agent.json +++ b/python/configs/agent_cards/news_agent.json @@ -3,6 +3,9 @@ "display_name": "News Agent", "url": "http://localhost:10005/", "description": "Professional news agent specialized in gathering, analyzing, and presenting current events, breaking news, and financial market information from reliable sources with real-time web search capabilities.", + "capabilities": { + "streaming": true + }, "skills": [ { "id": "web_search", diff --git a/python/scripts/launch.py b/python/scripts/launch.py index 464c784a4..0ae1c3b3f 100644 --- a/python/scripts/launch.py +++ b/python/scripts/launch.py @@ -31,15 +31,13 @@ TRADING_AGENTS_NAME = "TradingAgents" RESEARCH_AGENT_NAME = "ResearchAgent" AUTO_TRADING_AGENT_NAME = "AutoTradingAgent" +NEWS_AGENT_NAME = "NewsAgent" # AGENTS = list(MAP_NAME_ANALYST.keys()) + [ # TRADING_AGENTS_NAME, # RESEARCH_AGENT_NAME, # AUTO_TRADING_AGENT_NAME, # ] -AGENTS = [ - RESEARCH_AGENT_NAME, - AUTO_TRADING_AGENT_NAME, -] +AGENTS = [RESEARCH_AGENT_NAME, AUTO_TRADING_AGENT_NAME, NEWS_AGENT_NAME] PROJECT_DIR = Path(__file__).resolve().parent.parent.parent PYTHON_DIR = PROJECT_DIR / "python" @@ -66,6 +64,9 @@ MAP_NAME_COMMAND[AUTO_TRADING_AGENT_NAME] = ( f"uv run --env-file {ENV_PATH_STR} -m valuecell.agents.auto_trading_agent" ) +MAP_NAME_COMMAND[NEWS_AGENT_NAME] = ( + f"uv run --env-file {ENV_PATH_STR} -m valuecell.agents.news_agent" +) BACKEND_COMMAND = ( f"cd {PYTHON_DIR_STR} && uv run --env-file {ENV_PATH_STR} -m valuecell.server.main" ) diff --git a/python/valuecell/agents/news_agent/__main__.py b/python/valuecell/agents/news_agent/__main__.py index 21d674034..beeb0d24d 100644 --- a/python/valuecell/agents/news_agent/__main__.py +++ b/python/valuecell/agents/news_agent/__main__.py @@ -1,62 +1,8 @@ -"""Main entry point for the News Agent.""" - import asyncio -from valuecell.core.agent.decorator import create_wrapped_agent - -from .core import NewsAgent - - -async def main(): - """Main function to run the News Agent.""" - agent = create_wrapped_agent(NewsAgent) - - # Example queries for testing - test_queries = [ - "What's the latest news today?", - "Any breaking news right now?", - "What's happening in the financial markets?", - "Latest technology news and AI developments", - "News about climate change in the past week", - ] - - print("News Agent is ready! Try these example queries:") - for i, query in enumerate(test_queries, 1): - print(f"{i}. {query}") - - print("\nEnter your news query (or 'quit' to exit):") - - while True: - try: - user_input = input("\n> ").strip() - - if user_input.lower() in ["quit", "exit", "q"]: - print("Goodbye!") - break - - if not user_input: - continue - - print("\nFetching news...") - - # Stream the response - async for chunk in agent.stream(user_input): - if chunk.get("type") == "message": - print(chunk.get("content", ""), end="", flush=True) - elif chunk.get("type") == "tool_call_start": - tool_name = chunk.get("tool_name", "") - print(f"\n[Using tool: {tool_name}]") - elif chunk.get("type") == "error": - print(f"\nError: {chunk.get('content', '')}") - - print("\n" + "=" * 50) - - except KeyboardInterrupt: - print("\nGoodbye!") - break - except Exception as e: - print(f"Error: {e}") - +from valuecell.agents.news_agent import NewsAgent +from valuecell.core import create_wrapped_agent if __name__ == "__main__": - asyncio.run(main()) + agent = create_wrapped_agent(NewsAgent) + asyncio.run(agent.serve()) diff --git a/python/valuecell/agents/news_agent/core.py b/python/valuecell/agents/news_agent/core.py index 2f77630f1..8bd6f992c 100644 --- a/python/valuecell/agents/news_agent/core.py +++ b/python/valuecell/agents/news_agent/core.py @@ -1,6 +1,6 @@ -"""News Agent core implementation.""" +"""News Agent Core Implementation.""" -from typing import AsyncGenerator, Dict, Optional +from typing import Any, AsyncGenerator, Dict, Optional from agno.agent import Agent from loguru import logger @@ -15,12 +15,11 @@ class NewsAgent(BaseAgent): - """News Agent for fetching and analyzing news content.""" - - def __init__(self): - """Initialize the News Agent with news-related tools.""" - super().__init__() + """News Agent for fetching and analyzing news.""" + def __init__(self, **kwargs): + """Initialize the News Agent.""" + super().__init__(**kwargs) # Load agent configuration self.config_manager = get_config_manager() self.agent_config = self.config_manager.get_agent_config("news_agent") @@ -30,7 +29,6 @@ def __init__(self): available_tools.extend([web_search, get_breaking_news, get_financial_news]) - # Create the knowledge news agent with configured model and tools # Use create_model_for_agent to load agent-specific configuration self.knowledge_news_agent = Agent( model=create_model_for_agent("news_agent"), @@ -47,125 +45,89 @@ async def stream( task_id: str, dependencies: Optional[Dict] = None, ) -> AsyncGenerator[StreamResponse, None]: - """ - Stream news information based on the user query. - - Args: - query: The user's news query - conversation_id: Conversation ID for context - task_id: Task ID - dependencies: Optional dependencies - - Yields: - StreamResponse: Streaming responses with proper event types - """ - response_stream = self.knowledge_news_agent.arun( - query, - stream=True, - stream_intermediate_steps=True, - session_id=conversation_id, + """Stream news responses.""" + logger.info( + f"Processing news query: {query[:100]}{'...' if len(query) > 100 else ''}" + ) + + try: + response_stream = self.knowledge_news_agent.arun( + query, + stream=True, + stream_intermediate_steps=True, + session_id=conversation_id, + ) + async for event in response_stream: + if event.event == "RunContent": + yield streaming.message_chunk(event.content) + elif event.event == "ToolCallStarted": + yield streaming.tool_call_started( + event.tool.tool_call_id, event.tool.tool_name + ) + elif event.event == "ToolCallCompleted": + yield streaming.tool_call_completed( + event.tool.result, event.tool.tool_call_id, event.tool.tool_name + ) + + yield streaming.done() + logger.info("News query processed successfully") + + except Exception as e: + logger.error(f"Error processing news query: {str(e)}") + logger.exception("Full error details:") + yield {"type": "error", "content": f"Error processing news query: {str(e)}"} + + async def run(self, query: str, **kwargs) -> str: + """Run news agent and return response.""" + logger.info( + f"Running news agent with query: {query[:100]}{'...' if len(query) > 100 else ''}" ) - async for event in response_stream: - if event.event == "RunContent": - yield streaming.message_chunk(event.content) - elif event.event == "ToolCallStarted": - yield streaming.tool_call_started( - event.tool.tool_call_id, event.tool.tool_name - ) - elif event.event == "ToolCallCompleted": - yield streaming.tool_call_completed( - event.tool.result, event.tool.tool_call_id, event.tool.tool_name - ) - logger.info("News query processing completed") - - yield streaming.done() - - async def run( - self, query: str, conversation_id: Optional[str] = None, **kwargs - ) -> str: - """ - Run news query and return the complete response. - - Args: - query: The user's news query - conversation_id: Optional conversation ID for context - **kwargs: Additional parameters - - Returns: - Complete news response as string - """ + try: - logger.info(f"Running news query: {query}") + logger.debug("Starting news agent processing") # Get the complete response from the knowledge news agent response = await self.knowledge_news_agent.arun(query) + logger.info("News agent query completed successfully") + logger.debug(f"Response length: {len(str(response.content))} characters") + return response.content except Exception as e: logger.error(f"Error in NewsAgent run: {e}") + logger.exception("Full error details:") return f"Error processing news query: {str(e)}" - def get_capabilities(self) -> dict: - """Get the capabilities and available tools of the News Agent.""" - # Get capabilities from configuration if available - if self.agent_config: - config_capabilities = self.agent_config.capabilities - - # Build tools list based on enabled capabilities - enabled_tools = [] - if config_capabilities.get("web_search", {}).get("enabled", True): - enabled_tools.append("web_search") - if config_capabilities.get("breaking_news", {}).get("enabled", True): - enabled_tools.append("get_breaking_news") - if config_capabilities.get("financial_news", {}).get("enabled", True): - enabled_tools.append("get_financial_news") - - # Build capabilities list based on configuration - capabilities_list = [] - if config_capabilities.get("web_search", {}).get("enabled", True): - capabilities_list.extend( - ["Real-time news search", "Topic-based news analysis"] - ) - if config_capabilities.get("breaking_news", {}).get("enabled", True): - capabilities_list.append("Categorized news retrieval") - if config_capabilities.get("financial_news", {}).get("enabled", True): - capabilities_list.extend( - [ - "Comprehensive stock news analysis", - "Macroeconomic news", - "Industry sector news", - "Individual stock news analysis", - ] - ) - if config_capabilities.get("languages", {}).get("enabled", True): - capabilities_list.append("Multi-language support") - if config_capabilities.get("analysis", {}).get("sentiment_analysis", True): - capabilities_list.append("Intelligent news summarization") - - return { - "name": "news_agent", - "description": "Professional news query and analysis agent supporting multiple news sources and query types", - "tools": enabled_tools, - "capabilities": capabilities_list, - "enabled": self.agent_config.enabled, - } - - # Fallback to default capabilities if no config found - return { - "name": "news_agent", - "description": "Professional news query and analysis agent supporting multiple news sources and query types", - "tools": ["web_search", "get_breaking_news", "get_financial_news"], - "capabilities": [ - "Real-time news search", - "Categorized news retrieval", - "Topic-based news analysis", - "Comprehensive stock news analysis", - "Macroeconomic news", - "Industry sector news", - "Individual stock news analysis", - "Multi-language support", - "Intelligent news summarization", + def get_capabilities(self) -> Dict[str, Any]: + """Get agent capabilities.""" + logger.debug("Retrieving news agent capabilities") + + capabilities = { + "name": "News Agent", + "description": "Professional news agent for fetching and analyzing news", + "tools": [ + { + "name": "web_search", + "description": "Search for general news and information", + }, + { + "name": "get_breaking_news", + "description": "Get latest breaking news", + }, + { + "name": "get_financial_news", + "description": "Get financial and market news", + }, + ], + "supported_queries": [ + "Latest news", + "Breaking news", + "Financial news", + "Market updates", + "Topic-specific news search", ], - "enabled": True, } + + logger.debug("Capabilities retrieved successfully") + return capabilities