mirror of
https://github.com/Xe138/AI-Trader.git
synced 2026-04-12 13:37:24 -04:00
feat: add AI-powered summary generation to BaseAgent
This commit is contained in:
@@ -246,6 +246,71 @@ class BaseAgent:
|
|||||||
"""Clear conversation history (called at start of each trading day)."""
|
"""Clear conversation history (called at start of each trading day)."""
|
||||||
self.conversation_history = []
|
self.conversation_history = []
|
||||||
|
|
||||||
|
async def generate_summary(self, content: str, max_length: int = 200) -> str:
|
||||||
|
"""
|
||||||
|
Generate a concise summary of reasoning content.
|
||||||
|
|
||||||
|
Uses the same AI model to summarize its own reasoning.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
content: Full reasoning content to summarize
|
||||||
|
max_length: Approximate character limit for summary
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
1-2 sentence summary of key decisions and reasoning
|
||||||
|
"""
|
||||||
|
# Truncate content to avoid token limits (keep first 2000 chars)
|
||||||
|
truncated = content[:2000] if len(content) > 2000 else content
|
||||||
|
|
||||||
|
prompt = f"""Summarize the following trading decision in 1-2 sentences (max {max_length} characters), focusing on the key reasoning and actions taken:
|
||||||
|
|
||||||
|
{truncated}
|
||||||
|
|
||||||
|
Summary:"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Use ainvoke for async call
|
||||||
|
response = await self.model.ainvoke(prompt)
|
||||||
|
|
||||||
|
# Extract content from response
|
||||||
|
if hasattr(response, 'content'):
|
||||||
|
summary = response.content.strip()
|
||||||
|
elif isinstance(response, dict) and 'content' in response:
|
||||||
|
summary = response['content'].strip()
|
||||||
|
else:
|
||||||
|
summary = str(response).strip()
|
||||||
|
|
||||||
|
# Truncate if too long
|
||||||
|
if len(summary) > max_length:
|
||||||
|
summary = summary[:max_length-3] + "..."
|
||||||
|
|
||||||
|
return summary
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# If summary generation fails, return truncated original
|
||||||
|
return truncated[:max_length-3] + "..."
|
||||||
|
|
||||||
|
def generate_summary_sync(self, content: str, max_length: int = 200) -> str:
|
||||||
|
"""
|
||||||
|
Synchronous wrapper for generate_summary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
content: Full reasoning content to summarize
|
||||||
|
max_length: Approximate character limit for summary
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Summary string
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
try:
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
except RuntimeError:
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
|
||||||
|
return loop.run_until_complete(self.generate_summary(content, max_length))
|
||||||
|
|
||||||
def _setup_logging(self, today_date: str) -> str:
|
def _setup_logging(self, today_date: str) -> str:
|
||||||
"""Set up log file path"""
|
"""Set up log file path"""
|
||||||
log_path = os.path.join(self.base_log_path, self.signature, 'log', today_date)
|
log_path = os.path.join(self.base_log_path, self.signature, 'log', today_date)
|
||||||
|
|||||||
84
tests/unit/test_base_agent_summary.py
Normal file
84
tests/unit/test_base_agent_summary.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
"""Tests for BaseAgent summary generation."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from agent.base_agent.base_agent import BaseAgent
|
||||||
|
from agent.mock_provider.mock_langchain_model import MockChatModel
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_generate_summary_basic():
|
||||||
|
"""Should generate summary from content."""
|
||||||
|
agent = BaseAgent(
|
||||||
|
signature="test-agent",
|
||||||
|
basemodel="test-model"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Use mock model for testing
|
||||||
|
agent.model = MockChatModel(model="test", signature="test")
|
||||||
|
|
||||||
|
content = """Key intermediate steps
|
||||||
|
|
||||||
|
- Read yesterday's positions: all zeros, $10,000 cash
|
||||||
|
- Analyzed NVDA strong Q2 results, bought 10 shares
|
||||||
|
- Analyzed AMD AI momentum, bought 6 shares
|
||||||
|
- Portfolio now 51% cash reserve for volatility management
|
||||||
|
|
||||||
|
<FINISH_SIGNAL>"""
|
||||||
|
|
||||||
|
summary = await agent.generate_summary(content)
|
||||||
|
|
||||||
|
assert isinstance(summary, str)
|
||||||
|
assert len(summary) > 0
|
||||||
|
assert len(summary) <= 203 # 200 + "..."
|
||||||
|
|
||||||
|
|
||||||
|
def test_generate_summary_sync():
|
||||||
|
"""Synchronous summary generation should work."""
|
||||||
|
agent = BaseAgent(
|
||||||
|
signature="test-agent",
|
||||||
|
basemodel="test-model"
|
||||||
|
)
|
||||||
|
agent.model = MockChatModel(model="test", signature="test")
|
||||||
|
|
||||||
|
content = "Bought AAPL 10 shares based on strong earnings."
|
||||||
|
summary = agent.generate_summary_sync(content)
|
||||||
|
|
||||||
|
assert isinstance(summary, str)
|
||||||
|
assert len(summary) > 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_generate_summary_truncates_long_content():
|
||||||
|
"""Should truncate very long content before summarizing."""
|
||||||
|
agent = BaseAgent(
|
||||||
|
signature="test-agent",
|
||||||
|
basemodel="test-model"
|
||||||
|
)
|
||||||
|
agent.model = MockChatModel(model="test", signature="test")
|
||||||
|
|
||||||
|
# Create content > 2000 chars
|
||||||
|
content = "Analysis: " + ("x" * 3000)
|
||||||
|
|
||||||
|
summary = await agent.generate_summary(content)
|
||||||
|
|
||||||
|
# Summary should be generated (not throw error)
|
||||||
|
assert isinstance(summary, str)
|
||||||
|
assert len(summary) <= 203
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_generate_summary_handles_errors():
|
||||||
|
"""Should handle errors gracefully."""
|
||||||
|
agent = BaseAgent(
|
||||||
|
signature="test-agent",
|
||||||
|
basemodel="test-model"
|
||||||
|
)
|
||||||
|
|
||||||
|
# No model set - will fail
|
||||||
|
agent.model = None
|
||||||
|
|
||||||
|
content = "Test content"
|
||||||
|
summary = await agent.generate_summary(content)
|
||||||
|
|
||||||
|
# Should return truncated original on error (with ... appended)
|
||||||
|
assert summary == "Test content..."
|
||||||
Reference in New Issue
Block a user