Skip to content

Instantly share code, notes, and snippets.

@colindotfun
Created September 24, 2025 10:51
Show Gist options
  • Select an option

  • Save colindotfun/17545898c904de3409160bdebb8d4fe4 to your computer and use it in GitHub Desktop.

Select an option

Save colindotfun/17545898c904de3409160bdebb8d4fe4 to your computer and use it in GitHub Desktop.
import json
import uuid
from typing import Optional, Dict, Any, List
from openai import OpenAI
import yfinance as yf
from dotenv import load_dotenv
import os
from helicone_helpers import HeliconeManualLogger
# Load environment variables
load_dotenv()
class StockInfoAgent:
def __init__(self):
# Initialize OpenAI client with Helicone
self.client = OpenAI(
api_key=os.getenv('OPENAI_API_KEY'),
base_url="https://oai.helicone.ai/v1",
default_headers={
"Helicone-Auth": f"Bearer {os.getenv('HELICONE_API_KEY')}"
}
)
# Initialize Helicone manual logger for tool calls
self.helicone_logger = HeliconeManualLogger(
api_key=os.getenv('HELICONE_API_KEY'),
headers={
"Helicone-Property-Type": "Stock-Info-Agent",
}
)
self.conversation_history = []
self.session_id = None
self.session_headers = {}
def start_new_session(self):
"""Initialize a new session for tracking."""
self.session_id = str(uuid.uuid4())
self.session_headers = {
"Helicone-Session-Id": self.session_id,
"Helicone-Session-Name": "Stock Information Chat",
"Helicone-Session-Path": "/stock-chat",
"Helicone-Property-Environment": "production"
}
print(f"Started new session: {self.session_id}")
def get_stock_price(self, ticker_symbol: str) -> Optional[str]:
"""Fetches the current stock price for the given ticker_symbol with Helicone logging."""
def price_operation(result_recorder):
try:
stock = yf.Ticker(ticker_symbol.upper())
info = stock.info
current_price = info.get('currentPrice') or info.get('regularMarketPrice')
if current_price:
result = f"{current_price:.2f} USD"
result_recorder.append_results({
"ticker": ticker_symbol.upper(),
"price": current_price,
"formatted_price": result,
"status": "success"
})
return result
else:
result_recorder.append_results({
"ticker": ticker_symbol.upper(),
"error": "Price not found",
"status": "error"
})
return None
except Exception as e:
result_recorder.append_results({
"ticker": ticker_symbol.upper(),
"error": str(e),
"status": "error"
})
print(f"Error fetching stock price: {e}")
return None
# Log the tool call with Helicone
return self.helicone_logger.log_request(
provider=None,
request={
"_type": "tool",
"toolName": "get_stock_price",
"input": {"ticker_symbol": ticker_symbol},
"metadata": {
"source": "yfinance",
"operation": "get_current_price"
}
},
operation=price_operation,
additional_headers={
**self.session_headers,
"Helicone-Session-Path": f"/stock-chat/price/{ticker_symbol.lower()}"
}
)
def get_company_ceo(self, ticker_symbol: str) -> Optional[str]:
"""Fetches the name of the CEO for the company with Helicone logging."""
def ceo_operation(result_recorder):
try:
stock = yf.Ticker(ticker_symbol.upper())
info = stock.info
# Look for CEO in various possible fields
ceo = None
for field in ['companyOfficers', 'officers']:
if field in info:
officers = info[field]
if isinstance(officers, list):
for officer in officers:
if isinstance(officer, dict):
title = officer.get('title', '').lower()
if 'ceo' in title or 'chief executive' in title:
ceo = officer.get('name')
break
result_recorder.append_results({
"ticker": ticker_symbol.upper(),
"ceo": ceo,
"status": "success" if ceo else "not_found"
})
return ceo
except Exception as e:
result_recorder.append_results({
"ticker": ticker_symbol.upper(),
"error": str(e),
"status": "error"
})
print(f"Error fetching CEO info: {e}")
return None
return self.helicone_logger.log_request(
provider=None,
request={
"_type": "tool",
"toolName": "get_company_ceo",
"input": {"ticker_symbol": ticker_symbol},
"metadata": {
"source": "yfinance",
"operation": "get_company_officers"
}
},
operation=ceo_operation,
additional_headers={
**self.session_headers,
"Helicone-Session-Path": f"/stock-chat/ceo/{ticker_symbol.lower()}"
}
)
def find_ticker_symbol(self, company_name: str) -> Optional[str]:
"""Tries to identify the stock ticker symbol with Helicone logging."""
def ticker_search_operation(result_recorder):
try:
# Use yfinance Lookup to search for the company
lookup = yf.Lookup(company_name)
stock_results = lookup.get_stock(count=5)
if not stock_results.empty:
ticker = stock_results.index[0]
result_recorder.append_results({
"company_name": company_name,
"ticker": ticker,
"search_type": "stock",
"results_count": len(stock_results),
"status": "success"
})
return ticker
# If no stocks found, try all instruments
all_results = lookup.get_all(count=5)
if not all_results.empty:
ticker = all_results.index[0]
result_recorder.append_results({
"company_name": company_name,
"ticker": ticker,
"search_type": "all_instruments",
"results_count": len(all_results),
"status": "success"
})
return ticker
result_recorder.append_results({
"company_name": company_name,
"error": "No ticker found",
"status": "not_found"
})
return None
except Exception as e:
result_recorder.append_results({
"company_name": company_name,
"error": str(e),
"status": "error"
})
print(f"Error searching for ticker: {e}")
return None
return self.helicone_logger.log_request(
provider=None,
request={
"_type": "tool",
"toolName": "find_ticker_symbol",
"input": {"company_name": company_name},
"metadata": {
"source": "yfinance_lookup",
"operation": "ticker_search"
}
},
operation=ticker_search_operation,
additional_headers={
**self.session_headers,
"Helicone-Session-Path": f"/stock-chat/search/{company_name.lower().replace(' ', '-')}"
}
)
def create_tool_definitions(self) -> List[Dict[str, Any]]:
"""Creates OpenAI function calling definitions for the tools."""
return [
{
"type": "function",
"function": {
"name": "get_stock_price",
"description": "Fetches the current stock price for the given ticker symbol",
"parameters": {
"type": "object",
"properties": {
"ticker_symbol": {
"type": "string",
"description": "The stock ticker symbol (e.g., 'AAPL', 'MSFT')"
}
},
"required": ["ticker_symbol"]
}
}
},
{
"type": "function",
"function": {
"name": "get_company_ceo",
"description": "Fetches the name of the CEO for the company associated with the ticker symbol",
"parameters": {
"type": "object",
"properties": {
"ticker_symbol": {
"type": "string",
"description": "The stock ticker symbol"
}
},
"required": ["ticker_symbol"]
}
}
},
{
"type": "function",
"function": {
"name": "find_ticker_symbol",
"description": "Tries to identify the stock ticker symbol for a given company name",
"parameters": {
"type": "object",
"properties": {
"company_name": {
"type": "string",
"description": "The name of the company"
}
},
"required": ["company_name"]
}
}
}
]
def execute_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any:
"""Executes the specified tool with given arguments."""
if tool_name == "get_stock_price":
return self.get_stock_price(arguments["ticker_symbol"])
elif tool_name == "get_company_ceo":
return self.get_company_ceo(arguments["ticker_symbol"])
elif tool_name == "find_ticker_symbol":
return self.find_ticker_symbol(arguments["company_name"])
else:
return None
def process_user_query(self, user_query: str) -> str:
"""Processes a user query using the OpenAI API with function calling and Helicone logging."""
# Add user message to conversation history
self.conversation_history.append({"role": "user", "content": user_query})
# System prompt to guide the agent's behavior
system_prompt = """You are a helpful stock information assistant. You have access to tools that can:
1. Get current stock prices
2. Find company CEOs
3. Find ticker symbols for company names
4. Ask users for clarification when needed
Use these tools one at a time to help answer user questions about stocks and companies. If information is ambiguous, ask for clarification."""
while True:
messages = [
{"role": "system", "content": system_prompt},
*self.conversation_history
]
def openai_operation(result_recorder):
# Call OpenAI API with function calling
response = self.client.chat.completions.create(
model="gpt-4o-mini-2024-07-18",
messages=messages,
tools=self.create_tool_definitions(),
tool_choice="auto"
)
# Log the response
result_recorder.append_results({
"model": "gpt-4o-mini-2024-07-18",
"response": response.choices[0].message.model_dump(),
"usage": response.usage.model_dump() if response.usage else None
})
return response
# Log the OpenAI call
response = self.helicone_logger.log_request(
provider="openai",
request={
"model": "gpt-4o-mini-2024-07-18",
"messages": messages,
"tools": self.create_tool_definitions(),
"tool_choice": "auto"
},
operation=openai_operation,
additional_headers={
**self.session_headers,
"Helicone-Prompt-Id": "stock-agent-reasoning"
}
)
response_message = response.choices[0].message
# If no tool calls, we're done
if not response_message.tool_calls:
self.conversation_history.append({"role": "assistant", "content": response_message.content})
return response_message.content
# Execute the first tool call
tool_call = response_message.tool_calls[0]
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
print(f"\nExecuting tool: {function_name} with args: {function_args}")
# Execute the tool (this will be logged separately by each tool method)
result = self.execute_tool(function_name, function_args)
# Add the assistant's message with tool calls to history
self.conversation_history.append({
"role": "assistant",
"content": None,
"tool_calls": [{
"id": tool_call.id,
"type": "function",
"function": {
"name": function_name,
"arguments": json.dumps(function_args)
}
}]
})
# Add tool result to history
self.conversation_history.append({
"tool_call_id": tool_call.id,
"role": "tool",
"name": function_name,
"content": str(result) if result is not None else "No result found"
})
def chat(self):
"""Interactive chat loop with session tracking."""
print("Stock Information Agent with Helicone Monitoring")
print("Ask me about stock prices, company CEOs, or any stock-related questions!")
print("Type 'quit' to exit.\n")
# Start a new session
self.start_new_session()
while True:
user_input = input("You: ")
if user_input.lower() in ['quit', 'exit', 'bye']:
print("Goodbye!")
break
try:
response = self.process_user_query(user_input)
print(f"\nAgent: {response}\n")
except Exception as e:
print(f"\nError: {e}\n")
if __name__ == "__main__":
agent = StockInfoAgent()
agent.chat()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment