diff --git a/README.md b/README.md index 24ac3e5..928c5a5 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ Are you ready to cook? 🚀 This is a collection of example code and guides for - [LiteLLM Proxy with Groq](/tutorials/litellm-proxy-groq): Call Groq through the LiteLLM proxy. - [Toolhouse with Groq](/tutorials/toolhouse-for-tool-use-with-groq-api): Use Toolhouse to create simple tool integrations with Groq. - [OpenTelemetry with Groq](/tutorials/opentelemetry-observability-groq): Integrate OpenTelemetry tracing and metrics for insights into Groq usage. +- [Maxim with Groq](/tutorials/maxim-groq-tracing): Integrate Maxim AI for observability and evaluation of Groq based agents. ### Agentic - [Mixture of Agents](/tutorials/mixture-of-agents): Create a mixture-of-agents system powered by Groq. diff --git a/tutorials/maxim-groq-tracing/README.md b/tutorials/maxim-groq-tracing/README.md new file mode 100644 index 0000000..ca165a1 --- /dev/null +++ b/tutorials/maxim-groq-tracing/README.md @@ -0,0 +1,430 @@ +--- +title: "Stock Market Analysis with Groq and Maxim" +description: "Learn how to add Maxim observability and tracing for Groq client" +--- + +In this cookbook, we'll build a stock market analysis tool that combines the power of Groq's LLM inference with function calling capabilities with observability powered by Maxim AI. Our tool will be able to understand natural language queries about stocks and automatically fetch data, perform analysis, and create beautiful visualizations. + +## Prerequisites +- Python 3.8+ +- [Groq](https://console.groq.com/home) API key (free at Groq Console) +- [Maxim](https://app.getmaxim.ai/) API key (for logging - Maxim Console) + +## Step 1: Setting Up Dependencies + +First, let's install all the required packages: + +```python +pip install groq yfinance pandas plotly maxim-py +``` + +**What each package does:** + +- `groq`: Fast LLM inference with function calling support +- `yfinance`: Yahoo Finance data retrieval +- `pandas`: Data manipulation and analysis +- `plotly`: Interactive data visualization +- `maxim-py`: AI observability and logging + +## Step 2: Environment Setup and API Configuration + +```env + +MAXIM_API_KEY = "your_maxim_api_key_here" +MAXIM_LOG_REPO_ID = "your_maxim_repo_id_here" +GROQ_API_KEY = "your_groq_api_key_here" +``` + +## Step 3: Initialize Maxim Logging and Groq Client + +```python {12} +import os +from maxim import Config, Maxim +from maxim.logger import LoggerConfig +from maxim.logger.groq import instrument_groq +from groq import Groq + +# Initialize Maxim for AI observability +maxim = Maxim(Config(api_key=os.getenv("MAXIM_API_KEY"))) +logger = maxim.logger(LoggerConfig(id=os.getenv("MAXIM_LOG_REPO_ID"))) + +# Set up Groq client with logging instrumentation +instrument_groq(logger) +client = Groq() +``` + +Why this matters: The Maxim integration gives us detailed insights into our AI model's performance, token usage, and function call patterns - essential for production applications. + +## Step 4: Building Core Data Retrieval Functions + +### Stock Information Function + +```python +import yfinance as yf + +def get_stock_info(symbol: str, key: str): + """Retrieve specific info about a stock""" + try: + data = yf.Ticker(symbol) + return data.info.get(key, f"Key '{key}' not found for symbol '{symbol}'") + except Exception as e: + return f"Error retrieving data for {symbol}: {str(e)}" + +``` + +This function fetches specific financial metrics like market cap, beta, P/E ratio, etc. + +### Date Parsing for Natural Language + +One of the biggest challenges is handling natural language date expressions. Here's our solution: + +```python +from datetime import datetime, timedelta +import re + +def parse_relative_date(date_str: str) -> str: + """Convert relative date strings to YYYY-MM-DD format""" + date_str = date_str.lower().strip() + today = datetime.now() + + if date_str in ['today', 'now']: + return today.strftime('%Y-%m-%d') + elif date_str == 'yesterday': + return (today - timedelta(days=1)).strftime('%Y-%m-%d') + elif 'month' in date_str: + # Extract number of months + numbers = re.findall(r'\d+', date_str) + months = int(numbers[0]) if numbers else 1 + # Approximate months as 30 days each + return (today - timedelta(days=months * 30)).strftime('%Y-%m-%d') + elif 'week' in date_str: + numbers = re.findall(r'\d+', date_str) + weeks = int(numbers[0]) if numbers else 1 + return (today - timedelta(weeks=weeks)).strftime('%Y-%m-%d') + elif 'day' in date_str: + numbers = re.findall(r'\d+', date_str) + days = int(numbers[0]) if numbers else 1 + return (today - timedelta(days=days)).strftime('%Y-%m-%d') + elif 'year' in date_str: + numbers = re.findall(r'\d+', date_str) + years = int(numbers[0]) if numbers else 1 + return (today - timedelta(days=years * 365)).strftime('%Y-%m-%d') + else: + # Try to parse as regular date, if it fails return as-is + try: + parsed_date = datetime.strptime(date_str, '%Y-%m-%d') + return date_str + except: + # If all else fails, assume it's today + return today.strftime('%Y-%m-%d') +``` + +Key insight: This function transforms user-friendly expressions like "6 months ago" into API-compatible date formats. + +### Historical Price Data Function + +```python +def get_historical_price(symbol: str, start_date: str, end_date: str): + """Retrieve historical stock price data""" + try: + # Parse relative dates + parsed_start = parse_relative_date(start_date) + parsed_end = parse_relative_date(end_date) + + print(f"Parsed dates: {start_date} -> {parsed_start}, {end_date} -> {parsed_end}") + + hist = yf.Ticker(symbol).history(start=parsed_start, end=parsed_end).reset_index() + hist[symbol] = hist['Close'] + return hist[['Date', symbol]].to_dict(orient='records') + except Exception as e: + return f"Error retrieving historical data: {str(e)}" + +``` + +## Step 5: Creating Stunning Visualizations + +```python +import pandas as pd +import plotly.graph_objects as go + +def plot_stock_price(data: list, symbol: str, title: str = None): + """Plot stock price data using plotly""" + if isinstance(data, str): # Error message + print(f"Cannot plot: {data}") + return None + + df = pd.DataFrame(data) + df['Date'] = pd.to_datetime(df['Date']) + + if title is None: + title = f"{symbol} Stock Price Over Time" + + fig = go.Figure() + fig.add_trace(go.Scatter( + x=df['Date'], + y=df[symbol], + mode='lines', + name=f'{symbol} Price', + line=dict(width=2) + )) + + fig.update_layout( + title=title, + xaxis_title="Date", + yaxis_title="Price ($)", + hovermode='x unified', + template='plotly_white' + ) + + fig.show() + return fig + +def compare_stocks(symbols: list, start_date: str, end_date: str): + """Compare multiple stocks on the same chart""" + fig = go.Figure() + + for symbol in symbols: + data = get_historical_price(symbol, start_date, end_date) + if isinstance(data, str): # Error message + print(f"Skipping {symbol}: {data}") + continue + + df = pd.DataFrame(data) + df['Date'] = pd.to_datetime(df['Date']) + + fig.add_trace(go.Scatter( + x=df['Date'], + y=df[symbol], + mode='lines', + name=f'{symbol}', + line=dict(width=2) + )) + + fig.update_layout( + title=f"Stock Price Comparison: {', '.join(symbols)}", + xaxis_title="Date", + yaxis_title="Price ($)", + hovermode='x unified', + template='plotly_white' + ) + + fig.show() + return fig +``` + +**Why Plotly:** Interactive charts that users can zoom, pan, and hover for details - much better than static matplotlib charts. + +## Step 6: Defining Function Schemas for Groq + +This is where the magic happens. We define our functions in a schema that Groq can understand: + +```python +functions = [ + { + "type": "function", + "function": { + "name": "get_stock_info", + "description": "Retrieve specific info about a stock", + "parameters": { + "type": "object", + "properties": { + "symbol": {"type": "string", "description": "Stock ticker like AAPL or GOOGL"}, + "key": {"type": "string", "description": "The financial attribute to retrieve (e.g., 'marketCap', 'beta', 'currentPrice')"} + }, + "required": ["symbol", "key"] + } + } + }, + { + "type": "function", + "function": { + "name": "get_historical_price", + "description": "Retrieve historical stock price data. Accepts both absolute dates (YYYY-MM-DD) and relative dates (like '6 months ago', 'today', '1 year ago', etc.)", + "parameters": { + "type": "object", + "properties": { + "symbol": {"type": "string", "description": "Stock ticker symbol"}, + "start_date": {"type": "string", "description": "Start date in YYYY-MM-DD format OR relative date like '6 months ago', '1 year ago'"}, + "end_date": {"type": "string", "description": "End date in YYYY-MM-DD format OR relative date like 'today', 'yesterday'"} + }, + "required": ["symbol", "start_date", "end_date"] + } + } + } +] + +``` + +**Critical detail:** Notice how we explicitly mention that relative dates are accepted. This guides the AI on how to use our functions. + +## Step 7: The Brain - Function Execution Handler + +```python +import json + +def execute_function_call(function_name: str, arguments: dict): + """Execute the appropriate function based on the function call""" + if function_name == "get_stock_info": + return get_stock_info(**arguments) + elif function_name == "get_historical_price": + return get_historical_price(**arguments) + else: + return f"Unknown function: {function_name}" + +``` + +This simple dispatcher routes function calls to the appropriate Python functions. + +## Step 8: The Complete Query Processing Engine + +Here's where everything comes together: + +```python +def process_stock_query(query: str, plot_chart: bool = True): + """Process a stock query and optionally plot results""" + + # Enhanced system message with date handling instructions + system_message = """You are a financial assistant. Use the available tools to get stock information and provide helpful analysis. + +For date parameters in get_historical_price: +- You can use relative dates like: "6 months ago", "1 year ago", "3 weeks ago", "today", "yesterday" +- Or absolute dates in YYYY-MM-DD format +- The function will automatically parse relative dates to the correct format + +Be helpful and provide insightful analysis of the stock data you retrieve.""" + + # Get initial response from Groq + response = client.chat.completions.create( + model="llama-3.3-70b-versatile", + messages=[ + {"role": "system", "content": system_message}, + {"role": "user", "content": query} + ], + tools=functions, + ) + + messages = [ + {"role": "system", "content": system_message}, + {"role": "user", "content": query} + ] + + # Process tool calls if any + if response.choices[0].message.tool_calls: + messages.append(response.choices[0].message) + + for tool_call in response.choices[0].message.tool_calls: + function_name = tool_call.function.name + arguments = json.loads(tool_call.function.arguments) + + print(f"Calling function: {function_name} with arguments: {arguments}") + + # Execute the function + function_result = execute_function_call(function_name, arguments) + + # Add function result to messages + messages.append({ + "role": "tool", + "tool_call_id": tool_call.id, + "content": str(function_result) + }) + + # If it's historical price data and plotting is requested, create a chart + if function_name == "get_historical_price" and plot_chart and not isinstance(function_result, str): + symbol = arguments.get('symbol', 'Unknown') + plot_stock_price(function_result, symbol) + + # Get final response with function results + final_response = client.chat.completions.create( + model="llama-3.3-70b-versatile", + messages=messages, + tools=functions, + ) + + return final_response.choices[0].message.content + else: + return response.choices[0].message.content + +``` + +**The flow:** + +1. Send user query to Groq +2. If Groq decides to call functions, execute them +3. Send results back to Groq for final analysis +4. Automatically create charts for historical data +5. Return comprehensive analysis + +## Step 9: Testing Our Creation + +Let's put our system through its paces: + +```python +# Test 1: Simple stock info query +print("=== Stock Info Query ===") +result1 = process_stock_query("What is the beta of Meta stock?", plot_chart=False) +print(result1) +print() + +# Test 2: Historical data with automatic chart +print("=== Historical Price with Chart ===") +result2 = process_stock_query("Show me Apple's stock price for the last 6 months", plot_chart=True) +print(result2) +print() + +# Test 3: Complex analysis +print("=== Complex Analysis ===") +result3 = process_stock_query( + "Get Tesla's stock price data for the last 3 months and tell me about its recent performance", + plot_chart=True +) +print(result3) +``` + +LLM Response for Apple's stock price: + +```text +Based on the historical price data for Apple's stock over the last 6 months, we can see that the stock price has been quite volatile. The price has fluctuated between a high of $245.51 and a low of $172.19. + +The stock price started the year at around $237.02 and initially trended downwards, reaching a low of $172.19 in mid-April. However, the price then rebounded and trended upwards, reaching a high of $245.51 in late May. + +Since then, the stock price has been trading in a range between $195 and $215. The current price is around $214.05, which is close to the upper end of this range. + +Overall, the historical price data suggests that Apple's stock has been quite volatile over the last 6 months, with significant fluctuations in price. However, the stock has shown a general trend of recovery and growth since the mid-April low. + +It's worth noting that the stock price can be affected by a wide range of factors, including company performance, industry trends, economic conditions, and market sentiment. As such, it's always important to do your own research and consult with a financial advisor before making any investment decisions. + +In terms of analysis, the stock's volatility can be measured using various metrics such as beta, which measures the stock's sensitivity to market movements. Apple's beta is around 1.2, which means that the stock tends to be more volatile than the overall market. + +The stock's valuation can also be analyzed using metrics such as price-to-earnings (P/E) ratio, which is around 25. This suggests that the stock is trading at a premium to its historical average, which could indicate that the market is expecting strong growth from the company. + +Overall, while the historical price data provides some insights into the stock's behavior, it's always important to consider a wide range of factors and do your own research before making any investment decisions. +``` +![](images/maxim_groq_appl.png) + +LLM Response for Tesla's stock price: + +```text +Based on the historical price data, Tesla's stock price has been quite volatile over the last 3 months. The stock price has fluctuated between a high of $362.89 and a low of $275.35. + +The stock started the 3-month period at around $282.16 and initially declined to $275.35. Then it started to rise, reaching a peak of $362.89. However, the stock price has been declining since then and is currently trading at around $325.59. + +The overall trend of the stock price over the last 3 months is slightly positive, with the stock gaining about 15%. However, the stock's volatility and recent decline suggest that investors should exercise caution and keep a close eye on the stock's performance. + +It's also important to consider other factors such as the company's financial health, industry trends, and overall market conditions when making investment decisions. +``` +![](images/maxim_groq_tesla.png) + +Stock Comparison Plot: + +![](images/maxim_groq_comparison.png) + +## Maxim Observability + +![](images/maxim_groq.gif) + +## Conclusion +We've built a powerful, AI-driven stock market analysis tool that demonstrates the incredible potential of combining fast LLM inference with function calling. The system understands natural language, fetches real-time data, creates beautiful visualizations, and provides intelligent analysis - all from simple English queries. + +## Resources +- Sign up on [Maxim AI](https://getmax.im/groq-repo) +- Cookbook for Maxim with Groq [here](https://getmax.im/groq-cookbook) diff --git a/tutorials/maxim-groq-tracing/images/maxim_groq.gif b/tutorials/maxim-groq-tracing/images/maxim_groq.gif new file mode 100644 index 0000000..f42116e Binary files /dev/null and b/tutorials/maxim-groq-tracing/images/maxim_groq.gif differ diff --git a/tutorials/maxim-groq-tracing/images/maxim_groq_appl.png b/tutorials/maxim-groq-tracing/images/maxim_groq_appl.png new file mode 100644 index 0000000..3878c56 Binary files /dev/null and b/tutorials/maxim-groq-tracing/images/maxim_groq_appl.png differ diff --git a/tutorials/maxim-groq-tracing/images/maxim_groq_comparison.png b/tutorials/maxim-groq-tracing/images/maxim_groq_comparison.png new file mode 100644 index 0000000..c72e3ed Binary files /dev/null and b/tutorials/maxim-groq-tracing/images/maxim_groq_comparison.png differ diff --git a/tutorials/maxim-groq-tracing/images/maxim_groq_tesla.png b/tutorials/maxim-groq-tracing/images/maxim_groq_tesla.png new file mode 100644 index 0000000..a22fb48 Binary files /dev/null and b/tutorials/maxim-groq-tracing/images/maxim_groq_tesla.png differ diff --git a/tutorials/maxim-groq-tracing/maxim_logging.ipynb b/tutorials/maxim-groq-tracing/maxim_logging.ipynb new file mode 100644 index 0000000..8e7b83b --- /dev/null +++ b/tutorials/maxim-groq-tracing/maxim_logging.ipynb @@ -0,0 +1,724 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tracing & Evaluation for Groq based Agents using Maxim AI\n", + "\n", + "Learn how to integrate Maxim observability with the Groq SDK. \n", + "\n", + "When you use Groq with Maxim instrumentation, the following information is automatically captured for each API call:\n", + "- Request Details: Model name, temperature, max tokens, and all other parameters\n", + "- Message History: Complete conversation context including system and user messages\n", + "- Response Content: Full assistant responses and metadata\n", + "- Usage Statistics: Input tokens, output tokens, total tokens consumed\n", + "- Cost Tracking: Estimated costs based on Groq’s pricing\n", + "- Error Handling: Any API errors or failures with detailed context\n", + "- Node Level Evaluations\n", + "- Get Real Time Alerts (Slack, PagerDuty, etc.)\n", + "\n", + "[Check Docs](https://getmax.im/HIF14Di)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Install dependencies & Set environment variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bRnCX2jMlVqm", + "outputId": "8e925524-ffe9-49c9-ee0c-4eb07df4aaed" + }, + "outputs": [], + "source": [ + "# Install dependencies\n", + "\n", + "'''\n", + "%pip install groq\n", + "%pip install maxim-py\n", + "'''\n", + "\n", + "# Set environment variables\n", + "import os\n", + "os.environ[\"MAXIM_API_KEY\"] = \"YOUR_MAXIM_API_KEY\"\n", + "os.environ[\"MAXIM_LOG_REPO_ID\"] = \"YOUR_MAXIM_LOG_REPO_ID\"\n", + "os.environ[\"GROQ_API_KEY\"] = \"YOUR_GROQ_API_KEY\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import the required dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "0zRefuKRmvau", + "outputId": "61742432-1311-49d6-e8be-21549b582e32" + }, + "outputs": [], + "source": [ + "from groq import Groq\n", + "\n", + "from maxim import Config, Maxim\n", + "from maxim.logger import LoggerConfig\n", + "\n", + "maxim = Maxim(Config(api_key=os.getenv(\"MAXIM_API_KEY\")))\n", + "logger = maxim.logger(LoggerConfig(id=os.getenv(\"MAXIM_LOG_REPO_ID\")))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Configure Groq with Maxim" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_ihKO7rfnGQO" + }, + "outputs": [], + "source": [ + "from maxim.logger.groq import instrument_groq\n", + "\n", + "instrument_groq(logger)\n", + "client = Groq()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simple Inference" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "THT6R0mWnIkb", + "outputId": "f27e81f6-867f-49aa-9ac6-2181ae4c8bfe" + }, + "outputs": [], + "source": [ + "chat_completion = client.chat.completions.create(\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n", + " {\"role\": \"user\", \"content\": \"What is a fast LLM useful for?\"}\n", + " ],\n", + " model=\"llama-3.3-70b-versatile\"\n", + ")\n", + "\n", + "print(chat_completion.choices[0].message.content)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Define the Tool Calls" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "zRifZVUbnKKG" + }, + "outputs": [], + "source": [ + "import yfinance as yf\n", + "\n", + "def get_stock_info(symbol: str, key: str):\n", + " data = yf.Ticker(symbol)\n", + " return data.info.get(key, f\"Key '{key}' not found for symbol '{symbol}'\")\n", + "\n", + "def get_historical_price(symbol: str, start_date: str, end_date: str):\n", + " hist = yf.Ticker(symbol).history(start=start_date, end=end_date).reset_index()\n", + " hist[symbol] = hist['Close']\n", + " return hist[['Date', symbol]].to_dict(orient='records')\n", + "\n", + "def plot_stock_price(data: list, symbol: str, title: str = None):\n", + " \"\"\"Plot stock price data using plotly\"\"\"\n", + " if isinstance(data, str): # Error message\n", + " print(f\"Cannot plot: {data}\")\n", + " return None\n", + "\n", + " df = pd.DataFrame(data)\n", + " df['Date'] = pd.to_datetime(df['Date'])\n", + "\n", + " if title is None:\n", + " title = f\"{symbol} Stock Price Over Time\"\n", + "\n", + " fig = go.Figure()\n", + " fig.add_trace(go.Scatter(\n", + " x=df['Date'],\n", + " y=df[symbol],\n", + " mode='lines',\n", + " name=f'{symbol} Price',\n", + " line=dict(width=2)\n", + " ))\n", + "\n", + " fig.update_layout(\n", + " title=title,\n", + " xaxis_title=\"Date\",\n", + " yaxis_title=\"Price ($)\",\n", + " hovermode='x unified',\n", + " template='plotly_white'\n", + " )\n", + "\n", + " fig.show()\n", + " return fig\n", + "\n", + "def compare_stocks(symbols: list, start_date: str, end_date: str):\n", + " \"\"\"Compare multiple stocks on the same chart\"\"\"\n", + " fig = go.Figure()\n", + "\n", + " for symbol in symbols:\n", + " data = get_historical_price(symbol, start_date, end_date)\n", + " if isinstance(data, str): # Error message\n", + " print(f\"Skipping {symbol}: {data}\")\n", + " continue\n", + "\n", + " df = pd.DataFrame(data)\n", + " df['Date'] = pd.to_datetime(df['Date'])\n", + "\n", + " fig.add_trace(go.Scatter(\n", + " x=df['Date'],\n", + " y=df[symbol],\n", + " mode='lines',\n", + " name=f'{symbol}',\n", + " line=dict(width=2)\n", + " ))\n", + "\n", + " fig.update_layout(\n", + " title=f\"Stock Price Comparison: {', '.join(symbols)}\",\n", + " xaxis_title=\"Date\",\n", + " yaxis_title=\"Price ($)\",\n", + " hovermode='x unified',\n", + " template='plotly_white'\n", + " )\n", + "\n", + " fig.show()\n", + " return fig\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "DlifoXlKnmuD" + }, + "outputs": [], + "source": [ + "functions = [\n", + " {\n", + " \"type\": \"function\",\n", + " \"function\": {\n", + " \"name\": \"get_stock_info\",\n", + " \"description\": \"Retrieve specific info about a stock\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"symbol\": {\"type\": \"string\", \"description\": \"Stock ticker like AAPL or GOOGL\"},\n", + " \"key\": {\"type\": \"string\", \"description\": \"The financial attribute to retrieve (e.g., 'marketCap', 'beta', 'currentPrice')\"}\n", + " },\n", + " \"required\": [\"symbol\", \"key\"]\n", + " }\n", + " }\n", + " },\n", + " {\n", + " \"type\": \"function\",\n", + " \"function\": {\n", + " \"name\": \"get_historical_price\",\n", + " \"description\": \"Retrieve historical stock price data\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"symbol\": {\"type\": \"string\"},\n", + " \"start_date\": {\"type\": \"string\", \"format\": \"date\"},\n", + " \"end_date\": {\"type\": \"string\", \"format\": \"date\"}\n", + " },\n", + " \"required\": [\"symbol\", \"start_date\", \"end_date\"]\n", + " }\n", + " }\n", + " }\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Making LLM Calls with Groq, with tool calling capabilities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "TYPKs3iLnqGf" + }, + "outputs": [], + "source": [ + "import json\n", + "\n", + "\n", + "def execute_function_call(function_name: str, arguments: dict):\n", + " \"\"\"Execute the appropriate function based on the function call\"\"\"\n", + " if function_name == \"get_stock_info\":\n", + " return get_stock_info(**arguments)\n", + " elif function_name == \"get_historical_price\":\n", + " return get_historical_price(**arguments)\n", + " else:\n", + " return f\"Unknown function: {function_name}\"\n", + "\n", + "def process_stock_query(query: str, plot_chart: bool = True):\n", + " \"\"\"Process a stock query and optionally plot results\"\"\"\n", + "\n", + " # Get initial response from Groq\n", + " response = client.chat.completions.create(\n", + " model=\"llama-3.3-70b-versatile\",\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": \"You are a financial assistant. Use the available tools to get stock information and provide helpful analysis.\"},\n", + " {\"role\": \"user\", \"content\": query}\n", + " ],\n", + " tools=functions,\n", + " )\n", + "\n", + " messages = [\n", + " {\"role\": \"system\", \"content\": \"You are a financial assistant. Use the available tools to get stock information and provide helpful analysis.\"},\n", + " {\"role\": \"user\", \"content\": query}\n", + " ]\n", + "\n", + " # Process tool calls if any\n", + " if response.choices[0].message.tool_calls:\n", + " messages.append(response.choices[0].message)\n", + "\n", + " for tool_call in response.choices[0].message.tool_calls:\n", + " function_name = tool_call.function.name\n", + " arguments = json.loads(tool_call.function.arguments)\n", + "\n", + " print(f\"Calling function: {function_name} with arguments: {arguments}\")\n", + "\n", + " # Execute the function\n", + " function_result = execute_function_call(function_name, arguments)\n", + "\n", + " # Add function result to messages\n", + " messages.append({\n", + " \"role\": \"tool\",\n", + " \"tool_call_id\": tool_call.id,\n", + " \"content\": str(function_result)\n", + " })\n", + "\n", + " # If it's historical price data and plotting is requested, create a chart\n", + " if function_name == \"get_historical_price\" and plot_chart and not isinstance(function_result, str):\n", + " symbol = arguments.get('symbol', 'Unknown')\n", + " plot_stock_price(function_result, symbol)\n", + "\n", + " # Get final response with function results\n", + " final_response = client.chat.completions.create(\n", + " model=\"llama-3.3-70b-versatile\",\n", + " messages=messages,\n", + " tools=functions,\n", + " )\n", + "\n", + " return final_response.choices[0].message.content\n", + " else:\n", + " return response.choices[0].message.content\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Check Responses on Maxim Dashboard" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8ZXg8_7dnudI", + "outputId": "dc1925ec-1003-43a6-bfa4-4ec43bb611be" + }, + "outputs": [], + "source": [ + "print(\"=== Stock Info Query ===\")\n", + "result1 = process_stock_query(\"What is the beta of Meta stock?\", plot_chart=False)\n", + "print(result1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 391 + }, + "id": "mnSjWMARuJzE", + "outputId": "6fde04a4-235b-491a-eb42-7a95ab56bd23" + }, + "outputs": [], + "source": [ + "print(\"=== Historical Price with Chart ===\")\n", + "result2 = process_stock_query(\"Show me Apple's stock price for the last 6 months\", plot_chart=True)\n", + "print(result2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![Maxim Dashboard](images/maxim_groq.gif)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Complete Code" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "vwCDeqt-uUx2", + "outputId": "0d9ade69-a210-4444-9f9f-21ad10753be6" + }, + "outputs": [], + "source": [ + "# -*- coding: utf-8 -*-\n", + "\"\"\"Enhanced Stock Market Function Calling with Plotting\"\"\"\n", + "\n", + "import os\n", + "import json\n", + "import pandas as pd\n", + "import plotly.graph_objects as go\n", + "import plotly.express as px\n", + "from datetime import datetime, timedelta\n", + "import yfinance as yf\n", + "from groq import Groq\n", + "from maxim import Config, Maxim\n", + "from maxim.logger import LoggerConfig\n", + "from maxim.logger.groq import instrument_groq\n", + "\n", + "# Initialize Maxim and Groq (assuming environment variables are set)\n", + "maxim = Maxim(Config(api_key=os.getenv(\"MAXIM_API_KEY\")))\n", + "logger = maxim.logger(LoggerConfig(id=os.getenv(\"MAXIM_LOG_REPO_ID\")))\n", + "\n", + "instrument_groq(logger)\n", + "client = Groq()\n", + "\n", + "# Date parsing function\n", + "def parse_relative_date(date_str: str) -> str:\n", + " \"\"\"Convert relative date strings to YYYY-MM-DD format\"\"\"\n", + " date_str = date_str.lower().strip()\n", + " today = datetime.now()\n", + "\n", + " if date_str in ['today', 'now']:\n", + " return today.strftime('%Y-%m-%d')\n", + " elif date_str == 'yesterday':\n", + " return (today - timedelta(days=1)).strftime('%Y-%m-%d')\n", + " elif 'month' in date_str:\n", + " # Extract number of months\n", + " import re\n", + " numbers = re.findall(r'\\d+', date_str)\n", + " months = int(numbers[0]) if numbers else 1\n", + " # Approximate months as 30 days each\n", + " return (today - timedelta(days=months * 30)).strftime('%Y-%m-%d')\n", + " elif 'week' in date_str:\n", + " numbers = re.findall(r'\\d+', date_str)\n", + " weeks = int(numbers[0]) if numbers else 1\n", + " return (today - timedelta(weeks=weeks)).strftime('%Y-%m-%d')\n", + " elif 'day' in date_str:\n", + " numbers = re.findall(r'\\d+', date_str)\n", + " days = int(numbers[0]) if numbers else 1\n", + " return (today - timedelta(days=days)).strftime('%Y-%m-%d')\n", + " elif 'year' in date_str:\n", + " numbers = re.findall(r'\\d+', date_str)\n", + " years = int(numbers[0]) if numbers else 1\n", + " return (today - timedelta(days=years * 365)).strftime('%Y-%m-%d')\n", + " else:\n", + " # Try to parse as regular date, if it fails return as-is\n", + " try:\n", + " parsed_date = datetime.strptime(date_str, '%Y-%m-%d')\n", + " return date_str\n", + " except:\n", + " # If all else fails, assume it's today\n", + " return today.strftime('%Y-%m-%d')\n", + "\n", + "# Your existing functions with date parsing\n", + "def get_stock_info(symbol: str, key: str):\n", + " \"\"\"Retrieve specific info about a stock\"\"\"\n", + " try:\n", + " data = yf.Ticker(symbol)\n", + " return data.info.get(key, f\"Key '{key}' not found for symbol '{symbol}'\")\n", + " except Exception as e:\n", + " return f\"Error retrieving data for {symbol}: {str(e)}\"\n", + "\n", + "def get_historical_price(symbol: str, start_date: str, end_date: str):\n", + " \"\"\"Retrieve historical stock price data\"\"\"\n", + " try:\n", + " # Parse relative dates\n", + " parsed_start = parse_relative_date(start_date)\n", + " parsed_end = parse_relative_date(end_date)\n", + "\n", + " print(f\"Parsed dates: {start_date} -> {parsed_start}, {end_date} -> {parsed_end}\")\n", + "\n", + " hist = yf.Ticker(symbol).history(start=parsed_start, end=parsed_end).reset_index()\n", + " hist[symbol] = hist['Close']\n", + " return hist[['Date', symbol]].to_dict(orient='records')\n", + " except Exception as e:\n", + " return f\"Error retrieving historical data: {str(e)}\"\n", + "\n", + "def plot_stock_price(data: list, symbol: str, title: str = None):\n", + " \"\"\"Plot stock price data using plotly\"\"\"\n", + " if isinstance(data, str): # Error message\n", + " print(f\"Cannot plot: {data}\")\n", + " return None\n", + "\n", + " df = pd.DataFrame(data)\n", + " df['Date'] = pd.to_datetime(df['Date'])\n", + "\n", + " if title is None:\n", + " title = f\"{symbol} Stock Price Over Time\"\n", + "\n", + " fig = go.Figure()\n", + " fig.add_trace(go.Scatter(\n", + " x=df['Date'],\n", + " y=df[symbol],\n", + " mode='lines',\n", + " name=f'{symbol} Price',\n", + " line=dict(width=2)\n", + " ))\n", + "\n", + " fig.update_layout(\n", + " title=title,\n", + " xaxis_title=\"Date\",\n", + " yaxis_title=\"Price ($)\",\n", + " hovermode='x unified',\n", + " template='plotly_white'\n", + " )\n", + "\n", + " fig.show()\n", + " return fig\n", + "\n", + "def compare_stocks(symbols: list, start_date: str, end_date: str):\n", + " \"\"\"Compare multiple stocks on the same chart\"\"\"\n", + " fig = go.Figure()\n", + "\n", + " for symbol in symbols:\n", + " data = get_historical_price(symbol, start_date, end_date)\n", + " if isinstance(data, str): # Error message\n", + " print(f\"Skipping {symbol}: {data}\")\n", + " continue\n", + "\n", + " df = pd.DataFrame(data)\n", + " df['Date'] = pd.to_datetime(df['Date'])\n", + "\n", + " fig.add_trace(go.Scatter(\n", + " x=df['Date'],\n", + " y=df[symbol],\n", + " mode='lines',\n", + " name=f'{symbol}',\n", + " line=dict(width=2)\n", + " ))\n", + "\n", + " fig.update_layout(\n", + " title=f\"Stock Price Comparison: {', '.join(symbols)}\",\n", + " xaxis_title=\"Date\",\n", + " yaxis_title=\"Price ($)\",\n", + " hovermode='x unified',\n", + " template='plotly_white'\n", + " )\n", + "\n", + " fig.show()\n", + " return fig\n", + "\n", + "# Function definitions for Groq\n", + "functions = [\n", + " {\n", + " \"type\": \"function\",\n", + " \"function\": {\n", + " \"name\": \"get_stock_info\",\n", + " \"description\": \"Retrieve specific info about a stock\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"symbol\": {\"type\": \"string\", \"description\": \"Stock ticker like AAPL or GOOGL\"},\n", + " \"key\": {\"type\": \"string\", \"description\": \"The financial attribute to retrieve (e.g., 'marketCap', 'beta', 'currentPrice')\"}\n", + " },\n", + " \"required\": [\"symbol\", \"key\"]\n", + " }\n", + " }\n", + " },\n", + " {\n", + " \"type\": \"function\",\n", + " \"function\": {\n", + " \"name\": \"get_historical_price\",\n", + " \"description\": \"Retrieve historical stock price data. Accepts both absolute dates (YYYY-MM-DD) and relative dates (like '6 months ago', 'today', '1 year ago', etc.)\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"symbol\": {\"type\": \"string\", \"description\": \"Stock ticker symbol\"},\n", + " \"start_date\": {\"type\": \"string\", \"description\": \"Start date in YYYY-MM-DD format OR relative date like '6 months ago', '1 year ago'\"},\n", + " \"end_date\": {\"type\": \"string\", \"description\": \"End date in YYYY-MM-DD format OR relative date like 'today', 'yesterday'\"}\n", + " },\n", + " \"required\": [\"symbol\", \"start_date\", \"end_date\"]\n", + " }\n", + " }\n", + " }\n", + "]\n", + "\n", + "def execute_function_call(function_name: str, arguments: dict):\n", + " \"\"\"Execute the appropriate function based on the function call\"\"\"\n", + " if function_name == \"get_stock_info\":\n", + " return get_stock_info(**arguments)\n", + " elif function_name == \"get_historical_price\":\n", + " return get_historical_price(**arguments)\n", + " else:\n", + " return f\"Unknown function: {function_name}\"\n", + "\n", + "def process_stock_query(query: str, plot_chart: bool = True):\n", + " \"\"\"Process a stock query and optionally plot results\"\"\"\n", + "\n", + " # Enhanced system message with date handling instructions\n", + " system_message = \"\"\"You are a financial assistant. Use the available tools to get stock information and provide helpful analysis.\n", + "\n", + "For date parameters in get_historical_price:\n", + "- You can use relative dates like: \"6 months ago\", \"1 year ago\", \"3 weeks ago\", \"today\", \"yesterday\"\n", + "- Or absolute dates in YYYY-MM-DD format\n", + "- The function will automatically parse relative dates to the correct format\n", + "\n", + "Be helpful and provide insightful analysis of the stock data you retrieve.\"\"\"\n", + "\n", + " # Get initial response from Groq\n", + " response = client.chat.completions.create(\n", + " model=\"llama-3.3-70b-versatile\",\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": system_message},\n", + " {\"role\": \"user\", \"content\": query}\n", + " ],\n", + " tools=functions,\n", + " )\n", + "\n", + " messages = [\n", + " {\"role\": \"system\", \"content\": system_message},\n", + " {\"role\": \"user\", \"content\": query}\n", + " ]\n", + "\n", + " # Process tool calls if any\n", + " if response.choices[0].message.tool_calls:\n", + " messages.append(response.choices[0].message)\n", + "\n", + " for tool_call in response.choices[0].message.tool_calls:\n", + " function_name = tool_call.function.name\n", + " arguments = json.loads(tool_call.function.arguments)\n", + "\n", + " print(f\"Calling function: {function_name} with arguments: {arguments}\")\n", + "\n", + " # Execute the function\n", + " function_result = execute_function_call(function_name, arguments)\n", + "\n", + " # Add function result to messages\n", + " messages.append({\n", + " \"role\": \"tool\",\n", + " \"tool_call_id\": tool_call.id,\n", + " \"content\": str(function_result)\n", + " })\n", + "\n", + " # If it's historical price data and plotting is requested, create a chart\n", + " if function_name == \"get_historical_price\" and plot_chart and not isinstance(function_result, str):\n", + " symbol = arguments.get('symbol', 'Unknown')\n", + " plot_stock_price(function_result, symbol)\n", + "\n", + " # Get final response with function results\n", + " final_response = client.chat.completions.create(\n", + " model=\"llama-3.3-70b-versatile\",\n", + " messages=messages,\n", + " tools=functions,\n", + " )\n", + "\n", + " return final_response.choices[0].message.content\n", + " else:\n", + " return response.choices[0].message.content\n", + "\n", + "# Example usage\n", + "if __name__ == \"__main__\":\n", + " # Test the enhanced functionality\n", + "\n", + " # 1. Get stock info (like your original query)\n", + " print(\"=== Stock Info Query ===\")\n", + " result1 = process_stock_query(\"What is the beta of Meta stock?\", plot_chart=False)\n", + " print(result1)\n", + " print()\n", + "\n", + " # 2. Get historical data and plot\n", + " print(\"=== Historical Price with Chart ===\")\n", + " result2 = process_stock_query(\"Show me Apple's stock price for the last 6 months\", plot_chart=True)\n", + " print(result2)\n", + " print()\n", + "\n", + " # 3. Manual comparison chart\n", + " print(\"=== Stock Comparison Chart ===\")\n", + " end_date = datetime.now().strftime('%Y-%m-%d')\n", + " start_date = (datetime.now() - timedelta(days=180)).strftime('%Y-%m-%d')\n", + "\n", + " compare_stocks(['AAPL', 'GOOGL', 'MSFT', 'META'], start_date, end_date)\n", + "\n", + " # 4. More complex query\n", + " print(\"=== Complex Analysis ===\")\n", + " result3 = process_stock_query(\n", + " \"Get Tesla's stock price data for the last 3 months and tell me about its recent performance\",\n", + " plot_chart=True\n", + " )\n", + " print(result3)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +}