RicardoDataScience36's picture
Upload app.py
87fd7b2 verified
import os
import gradio as gr
from pydantic_ai import Agent
from pydantic import BaseModel
import yfinance as yf
import asyncio
from datetime import datetime
import pandas as pd
from typing import List
from openai import OpenAI
import json
# Modelo para resultados de precios de acciones individuales
class ResultadoPrecioAccion(BaseModel):
simbolo: str
precio: float
moneda: str = "USD"
company_name: str
previous_close: float
percentage_change: float
timestamp: str
# Modelo para agrupar los resultados de múltiples acciones
class ResultadosPreciosAcciones(BaseModel):
resultados: List[ResultadoPrecioAccion]
# Función para ejecutar la herramienta de obtención de precio de acción
async def obtener_precio_accion_async(simbolo: str) -> dict:
ticker = yf.Ticker(simbolo)
try:
info = ticker.fast_info
precio = info.last_price
if precio is None:
raise ValueError(f"No se encontró información para el símbolo '{simbolo}'.")
company_name = ticker.info.get('shortName', 'N/A')
previous_close = info.previous_close
percentage_change = ((precio - previous_close) / previous_close) * 100 if previous_close != 0 else 0
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return {
"simbolo": simbolo,
"precio": round(precio, 2),
"moneda": "USD",
"company_name": company_name,
"previous_close": round(previous_close, 2),
"percentage_change": round(percentage_change, 2),
"timestamp": timestamp
}
except Exception as e:
raise ValueError(f"Error al obtener datos para '{simbolo}': {str(e)}")
# Función para llamar a la API de Grok (síncrona)
def call_grok_api(api_key, consulta):
client = OpenAI(
api_key=api_key,
base_url="https://api.x.ai/v1",
)
functions = [
{
"name": "obtener_info_accion",
"description": "Obtiene información sobre el precio de una o varias acciones.",
"parameters": {
"type": "object",
"properties": {
"consulta": {
"type": "string",
"description": "La consulta del usuario sobre el precio de las acciones.",
},
},
"required": ["consulta"],
},
}
]
tools = [{"type": "function", "function": f} for f in functions]
messages = [
{"role": "system", "content": "Eres un asistente financiero útil."},
{"role": "user", "content": consulta}
]
try:
# Llama a la API
response = client.chat.completions.create(
model="grok-beta", # Asegúrate de que el modelo es correcto
messages=messages,
tools=tools,
)
# Verifica si la respuesta tiene contenido esperado
if hasattr(response, "choices") and len(response.choices) > 0:
response_message = response.choices[0].message
if hasattr(response_message, "tool_calls") and response_message.tool_calls:
tool_call = response_message.tool_calls[0]
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
if function_name == "obtener_info_accion":
# Ejecutar la función asíncrona usando asyncio.run
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
resultados = loop.run_until_complete(
obtener_info_accion(
function_args["consulta"],
os.environ.get("GROQ_API_KEY"),
api_key, True, False
)
)
finally:
loop.close()
return resultados
else:
return f"Función desconocida: {function_name}"
else:
return response_message.content
else:
return "La API no devolvió una respuesta válida."
except Exception as e:
return f"Error al llamar a la API de Grok: {e}"
# Función principal para obtener información de acciones (asíncrona)
async def obtener_info_accion(consulta: str, groq_api_key: str, grok_api_key: str, use_groq: bool, use_grok: bool) -> str:
try:
if use_groq:
# Usar Groq AI
os.environ["GROQ_API_KEY"] = groq_api_key
# Inicializa el agente de Pydantic AI
agente_acciones = Agent(
"groq:llama3-groq-70b-8192-tool-use-preview",
result_type=ResultadosPreciosAcciones,
system_prompt="Eres un asistente financiero útil que puede consultar precios de acciones. Usa la herramienta obtener_precio_accion para obtener datos actuales y devuelve la información en el formato especificado. Debes proporcionar información para todas las acciones solicitadas."
)
# Herramienta para obtener el precio de una acción
@agente_acciones.tool_plain
async def obtener_precio_accion_tool(simbolo: str) -> dict:
return await obtener_precio_accion_async(simbolo)
# Ejecutar la consulta
response = await agente_acciones.run(consulta)
# Procesar los resultados
data_frames = []
errores = []
for resultado in response.data.resultados:
try:
data_frames.append(pd.DataFrame([{
'Símbolo': resultado.simbolo,
'Nombre de la Empresa': resultado.company_name,
'Precio Actual': f"${resultado.precio:.2f} {resultado.moneda}",
'Última Actualización': resultado.timestamp,
'Cambio en Precio': f"{resultado.percentage_change:.2f}%",
'Tendencia': '📈' if resultado.percentage_change >= 0 else '📉'
}]))
except Exception as e:
errores.append(pd.DataFrame([{'Símbolo': resultado.simbolo, 'Error': str(e)}]))
# Concatena todos los DataFrames
if data_frames:
data_df = pd.concat(data_frames, ignore_index=True)
else:
data_df = pd.DataFrame()
if errores:
error_df = pd.concat(errores, ignore_index=True)
else:
error_df = pd.DataFrame()
respuesta = "📈 Información de las Acciones\n\n"
if not data_df.empty:
respuesta += data_df.to_string(index=False) + "\n\n"
if not error_df.empty:
respuesta += "### Errores:\n" + error_df.to_string(index=False) + "\n\n"
if not data_df.empty:
respuesta += "### Análisis Comparativo:\n"
respuesta += f"- **Máximo Precio:** {data_df['Precio Actual'].max()}\n"
respuesta += f"- **Mínimo Precio:** {data_df['Precio Actual'].min()}\n"
return respuesta
elif use_grok:
# Usar Grok API (llamada asíncrona a call_grok_api)
# return await asyncio.to_thread(call_grok_api, grok_api_key, consulta)
return await asyncio.get_event_loop().run_in_executor(None, call_grok_api, grok_api_key, consulta)
else:
return "⚠️ Por favor, selecciona al menos un modelo para usar."
except Exception as e:
return f"⚠️ **Error:** {str(e)}\nPor favor, ingresa una consulta válida."
# Tema personalizado con tonos claros
tema_personalizado = gr.themes.Soft(
primary_hue=gr.themes.Color(
c50='#e6f2ff',
c100='#b3dcff',
c200='#4dabf7',
c300='#2196f3',
c400='#1e88e5',
c500='#1976d2',
c600='#1565c0',
c700='#0b3d91',
c800='#082963',
c900='#051839',
c950='#030c1f'
),
neutral_hue=gr.themes.Color(
c50='#f5f5f5',
c100='#e0e0e0',
c200='#bdbdbd',
c300='#9e9e9e',
c400='#757575',
c500='#616161',
c600='#424242',
c700='#212121',
c800='#121212',
c900='#010101',
c950='#000000'
)
)
# Crear la interfaz de Gradio con el tema personalizado
with gr.Blocks(title="Asistente Financiero AI", theme=tema_personalizado) as demo:
# Encabezado con markdown
gr.Markdown(
"""
# 💹 Asistente de Precios de Acciones AI
## Consulta Instantánea de Información Bursátil
"""
)
# Sección de instrucciones
gr.Markdown(
"""
### 🎯 Guía Rápida
- Ingresa una consulta en lenguaje coloquial (ej. "¿Cuál es el precio de Apple y Microsoft?")
- Obtén información de precio en tiempo real
- Soporta mercados de valores globales
"""
)
# Diseño de entrada y salida
with gr.Column():
consulta = gr.Textbox(
label="Ingrese la consulta",
placeholder="Ingrese una consulta en lenguaje coloquial",
lines=1,
elem_id="input-textbox"
)
groq_api_key = gr.Textbox(
label="Groq AI API Key",
placeholder="Ingrese su API Key de Groq AI (opcional si no usa Groq)",
type="password",
value=os.environ.get("GROQ_API_KEY", "")
)
grok_api_key = gr.Textbox(
label="Grok API Key",
placeholder="Ingrese su API Key de Grok (opcional si no usa Grok)",
type="password",
value=os.environ.get("GROK_API_KEY", "")
)
with gr.Row():
use_groq = gr.Checkbox(label="Usar Groq AI", value=True)
use_grok = gr.Checkbox(label="Usar Grok", value=False)
boton = gr.Button("Consultar Precio 🔍", variant="primary", elem_id="consultar-button")
# Sección de salida
salida = gr.Textbox(
label="Resultados",
placeholder="La información de la acción aparecerá aquí...",
lines=10,
interactive=False,
elem_id="output-textbox"
)
# Pie de página con autor
gr.Markdown(
"""
---
### 🚀 Potenciado por Tecnología AI
Desarrollado con ❤️ usando Python, Gradio, Groq y Grok
**Creado por: Ricardo Fernández**
"""
)
# Conectar evento de clic del botón
boton.click(
obtener_info_accion,
inputs=[consulta, groq_api_key, grok_api_key, use_groq, use_grok],
outputs=salida
)
# Lanzar la aplicación
if __name__ == "__main__":
demo.launch(share=True)