TradingAgents/tradingagents/agents/utils/agent_utils.py

99 lines
3.0 KiB
Python

from langchain_core.messages import HumanMessage, RemoveMessage
# Import tools from separate utility files
from tradingagents.agents.utils.core_stock_tools import (
get_stock_data
)
from tradingagents.agents.utils.technical_indicators_tools import (
get_indicators
)
from tradingagents.agents.utils.fundamental_data_tools import (
get_fundamentals,
get_balance_sheet,
get_cashflow,
get_income_statement
)
from tradingagents.agents.utils.news_data_tools import (
get_news,
get_insider_sentiment,
get_insider_transactions,
get_global_news
)
def create_msg_delete():
def delete_messages(state):
"""Clear messages and add placeholder for Anthropic compatibility"""
messages = state["messages"]
# Remove all messages
removal_operations = [RemoveMessage(id=m.id) for m in messages]
# Add a minimal placeholder message
placeholder = HumanMessage(content="Continue")
return {"messages": removal_operations + [placeholder]}
return delete_messages
import json
import os
import tempfile
from typing import Dict, Any, Union, List
def write_json_atomic(path: str, data: Dict[str, Any]):
"""
Atomically write JSON data to a file.
1. Writes to a temporary file in the same directory.
2. Renames the temp file to the target path (atomic operation).
"""
directory = os.path.dirname(path)
if not os.path.exists(directory):
os.makedirs(directory, exist_ok=True)
try:
# Create temp file in the same directory to ensure atomic rename works
with tempfile.NamedTemporaryFile(mode='w', dir=directory, delete=False) as tf:
json.dump(data, tf, indent=4)
temp_path = tf.name
# Atomic rename
os.replace(temp_path, path)
except Exception as e:
# Cleanup if something failed before rename
if 'temp_path' in locals() and os.path.exists(temp_path):
os.remove(temp_path)
raise e
def normalize_agent_output(content: Union[str, List, Any]) -> str:
"""
Normalize LLM output into a clean string.
Handlers:
- String: Returns as-is
- List (Anthropic/Gemini): Extracts 'text' fields or joins items
- Other: Converts to string via str()
This ensures AgentState always contains normalized strings,
preventing downstream crashes in CLI/UI.
"""
if not content:
return ""
if isinstance(content, str):
return content
elif isinstance(content, list):
# Handle Anthropic/Gemini list format
text_parts = []
for item in content:
if isinstance(item, dict):
if item.get('type') == 'text':
text_parts.append(item.get('text', ''))
# Skip 'tool_use' blocks in the final report string
else:
text_parts.append(str(item))
return ' '.join(text_parts)
return str(content)