Spaces:
Sleeping
Sleeping
File size: 11,738 Bytes
2bc88b5 0111f03 2bc88b5 0111f03 2bc88b5 0111f03 2bc88b5 0111f03 2bc88b5 0111f03 2bc88b5 0111f03 2bc88b5 0111f03 2bc88b5 0111f03 2812187 2bc88b5 0111f03 79a4707 0111f03 2bc88b5 0111f03 2bc88b5 79a4707 0111f03 2bc88b5 0111f03 79a4707 0111f03 2bc88b5 0111f03 2bc88b5 0111f03 9b523a6 0111f03 cd68aa5 0111f03 2bc88b5 0111f03 2bc88b5 0111f03 2bc88b5 9b523a6 0111f03 2bc88b5 0111f03 9b523a6 0111f03 2bc88b5 0111f03 2bc88b5 0111f03 2bc88b5 9b523a6 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
import asyncio
import os
from typing import List, Dict, Any, Optional
from pathlib import Path
from functools import partial
from dotenv import load_dotenv
from psycopg2 import pool
from psycopg2.extras import RealDictCursor
load_dotenv()
from pydantic_ai import Agent, RunContext
from pydantic_ai.providers.groq import GroqProvider
from pydantic_ai.models.groq import GroqModel
from pydantic_ai.messages import PartDeltaEvent, TextPartDelta
from pydantic_graph import End
from pydantic import BaseModel
# PROMPTS
from prompts import matches_prompt, players_prompt, player_matchid_prompt, graph_agent_prompt
MATCHES_SYSTEM_PROMPT = matches_prompt.MATCHES_SYSTEM_PROMPT
PLAYERS_SYSTEM_PROMPT = players_prompt.PLAYERS_SYSTEM_PROMPT
PLAYER_MATCHID_SYSTEM_PROMPT = player_matchid_prompt.SYSTEM_PROMPT
GRAPH_AGENT_SYSTEM_PROMPT = graph_agent_prompt.GRAPH_AGENT_SYSTEM_PROMPT # Ajustado
import pandas as pd
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import io
from PIL import Image, ImageDraw, ImageFont
from dataclasses import dataclass
SUPABASE_DB_URI = os.getenv("SUPABASE_DB_URI")
# ========================= SupabaseConnection (migrada do MCP) =========================
class SupabaseConnection:
def __init__(self, dsn: str):
self.pool = pool.ThreadedConnectionPool(minconn=1, maxconn=10, dsn=dsn)
print("✓ Pool Supabase criado")
# Teste conexão
conn = self.pool.getconn()
try:
with conn.cursor() as cur:
cur.execute("SELECT 1")
finally:
self.pool.putconn(conn)
async def execute_query_async(self, query: str, parameters: Optional[tuple] = None):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, partial(self._execute_query_sync, query, parameters))
def _execute_query_sync(self, query: str, parameters: Optional[tuple] = None):
conn = None
try:
conn = self.pool.getconn()
conn.autocommit = True
with conn.cursor(cursor_factory=RealDictCursor) as cur:
if parameters:
cur.execute(query, parameters)
else:
cur.execute(query)
if cur.description:
return [dict(r) for r in cur.fetchall()]
return []
except Exception as e:
raise Exception(f"Erro query: {str(e)}")
finally:
if conn:
self.pool.putconn(conn)
def close(self):
if hasattr(self, 'pool') and self.pool:
self.pool.closeall()
print("✓ Pool fechado")
db = SupabaseConnection(SUPABASE_DB_URI)
# ========================= Agents (sem MCP) =========================
class Deps(BaseModel):
ai_query: str
api_key = os.getenv("GROQ_DEV_API_KEY")
groq_model = GroqModel("moonshotai/kimi-k2-instruct-0905", provider=GroqProvider(api_key=api_key))
api_key_2 = os.getenv("GROQ_DEV_API_KEY_2")
groq_model_2 = GroqModel("openai/gpt-oss-20b", provider=GroqProvider(api_key=api_key_2))
one_player_agent = Agent(
model=groq_model,
system_prompt=PLAYER_MATCHID_SYSTEM_PROMPT
)
# ========================= TOOLS DIRETAS no one_player_agent =========================
@one_player_agent.tool_plain()
async def execute_sql_query(query: str, limit: int = 100) -> str:
"""Executa query SQL READ-ONLY."""
query_upper = query.upper().strip()
dangerous = ['DELETE', 'DROP', 'INSERT', 'UPDATE', 'ALTER', 'CREATE', 'TRUNCATE']
if any(k in query_upper for k in dangerous):
return "❌ Apenas SELECT permitidas."
try:
if 'LIMIT' not in query_upper:
query += f" LIMIT {limit}"
results = await db.execute_query_async(query)
if not results:
return "✓ Query OK, sem resultados."
lines = [f"📊 {len(results)} registros:\n"]
for i, record in enumerate(results[:10], 1):
items = [f"{k}={v}" for k, v in record.items()]
lines.append(f"{i}. {', '.join(items)}")
if len(results) > 10:
lines.append(f"\n... +{len(results)-10}")
return "\n".join(lines)
except Exception as e:
return f"❌ Erro: {str(e)}"
@one_player_agent.tool_plain()
async def get_player_match_history(player_name: str, limit: int = 10) -> str:
query = """
SELECT match_date, opponent, home_away, minutes_played, goals, assists, shots, xg, pass_completion_pct, player_nickname
FROM player_match_stats WHERE player_nickname ILIKE %s ORDER BY match_date DESC LIMIT %s
"""
try:
results = await db.execute_query_async(query, (f'%{player_name}%', limit))
if not results: return f"❌ Nenhum dado para '{player_name}'"
out = [f"📊 HISTÓRICO - {player_name.upper()}\n"]
for r in results:
xg = f"{(r.get('xg') or 0):.2f}"
pcp = f"{(r.get('pass_completion_pct') or 0):.1f}"
out.append(f"📅 {r['match_date']} vs {r['opponent']} ({r['home_away']}) - {r['minutes_played']}min | ⚽{r['goals']}G 🤝{r['assists']}A | 🎯{r['shots']} (xG:{xg}) | 📈{pcp}%")
return "\n".join(out)
except Exception as e:
return f"❌ Erro: {str(e)}"
@one_player_agent.tool_plain()
async def get_match_performances(match_date: Optional[str] = None, opponent: Optional[str] = None, limit: int = 15) -> str:
if not match_date and not opponent: return "❌ Forneça match_date OU opponent"
where_clauses, params = [], []
if match_date:
where_clauses.append("match_date = %s"); params.append(match_date)
if opponent:
where_clauses.append("opponent ILIKE %s"); params.append(f'%{opponent}%')
where_sql = " AND ".join(where_clauses)
params.append(limit)
query = f"""
SELECT player_nickname, minutes_played, goals, assists, (goals + assists) as contributions, shots, xg, pass_completion_pct, touches
FROM player_match_stats WHERE {where_sql} ORDER BY (goals + assists) DESC, xg DESC LIMIT %s
"""
try:
results = await db.execute_query_async(query, tuple(params))
if not results: return "❌ Partida não encontrada"
info = f"{match_date or ''} vs {opponent or ''}".strip()
out = [f"🏟️ PERFORMANCES - {info}\n"]
for i, r in enumerate(results, 1):
xg = f"{(r.get('xg') or 0):.2f}"; pcp = f"{(r.get('pass_completion_pct') or 0):.1f}"
touches = r.get('touches', 0)
out.append(f"{i}. {r['player_nickname']} ({r['minutes_played']}min): ⚽{r['goals']}G + 🤝{r['assists']}A = {r['contributions']} | 🎯{r['shots']} (xG:{xg}) | 📈{pcp}% | 👟{touches}")
return "\n".join(out)
except Exception as e:
return f"❌ Erro: {str(e)}"
@one_player_agent.tool_plain()
async def show_available_tables() -> str:
query = """
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY table_name
""" # listar tabelas via information_schema [web:61]
try:
results = await db.execute_query_async(query)
if not results:
return "❌ Nenhuma tabela encontrada no schema public."
return "📚 TABELAS:\n" + "\n".join([f"- {r['table_name']}" for r in results])
except Exception as e:
return f"❌ Erro: {str(e)}"
@one_player_agent.tool_plain()
async def get_goal_events(player_scorer: str = None,
assist_from: str = None,
opponent: str = None,
only_crosses: bool = False,
only_high_crosses: bool = False,
limit: int = 50) -> str:
# IMPORTANT: troque player_goals_stats pelo nome REAL da sua tabela
table = "player_goals_stats"
where = ["team = 'Barcelona'"]
params = []
if player_scorer:
where.append("player_scorer_nick ILIKE %s"); params.append(f"%{player_scorer}%")
if assist_from:
where.append("pass_from_nick ILIKE %s"); params.append(f"%{assist_from}%")
if opponent:
where.append("opponent ILIKE %s"); params.append(f"%{opponent}%")
if only_crosses:
where.append("is_cross_pass = TRUE")
if only_high_crosses:
where.append("is_cross_pass_high = TRUE")
params.append(limit)
where_sql = " AND ".join(where)
query = f"""
SELECT match_date, opponent, home_away,
minute, second,
player_scorer_nick, pass_from_nick,
is_cross_pass, is_cross_pass_high,
play_pattern, shot_type, shot_body_part,
xg
FROM {table}
WHERE {where_sql}
ORDER BY match_date DESC, minute DESC, second DESC
LIMIT %s
"""
try:
results = await db.execute_query_async(query, tuple(params))
if not results:
return "❌ Sem gols encontrados para esse filtro."
out = ["⚽ EVENTOS DE GOL\n"]
for i, r in enumerate(results, 1):
out.append(
f"{i}. {r['match_date']} vs {r['opponent']} ({r['home_away']}) "
f"{r.get('minute')}:{r.get('second')} - "
f"{r.get('player_scorer_nick')} (assist: {r.get('pass_from_nick')}) | "
f"cross={r.get('is_cross_pass')} high_cross={r.get('is_cross_pass_high')} | "
f"{r.get('shot_body_part')} | xG={r.get('xg')}"
)
return "\n".join(out)
except Exception as e:
return f"❌ Erro: {str(e)}"
@one_player_agent.tool_plain()
async def get_top_performances(metric: str = "goals", limit: int = 10) -> str:
valid = {"goals": ("goals", "Gols"), "assists": ("assists", "Assistências"), "contributions": ("goals + assists", "Contribuições"), "xg": ("xg", "xG"), "shots": ("shots", "Finalizações")}
if metric not in valid: return f"❌ Métricas: {', '.join(valid)}"
metric_sql, metric_name = valid[metric]
query = f"""
SELECT player_nickname, match_date, opponent, home_away, goals, assists, xg, shots, pass_completion_pct
FROM player_match_stats ORDER BY {metric_sql} DESC LIMIT %s
"""
try:
results = await db.execute_query_async(query, (limit,))
if not results: return "❌ Sem dados"
out = [f"🏆 TOP {metric_name.upper()}\n"]
for i, r in enumerate(results, 1):
xg = f"{(r.get('xg') or 0):.2f}"
out.append(f"{i}. {r['player_nickname']} - {r['match_date']} vs {r['opponent']} ({r['home_away']}): ⚽{r['goals']}G 🤝{r['assists']}A xG:{xg}")
return "\n".join(out)
except Exception as e:
return f"❌ Erro: {str(e)}"
# Manter create_chart (já era tool local)
last_chart_image = None
# ... (código do create_chart igual, global last_chart_image)
# Funções de response (já sem MCP)
async def agent_conventional_response(user_query: str) -> str:
res = await one_player_agent.run(user_prompt=user_query)
#print(res.output)
return res.output
async def stream_agent_response_safe(user_query: str) -> str:
try:
async with one_player_agent.iter(user_query) as agent_run:
async for node in agent_run:
if isinstance(node, End) and agent_run.result:
print(str(agent_run.result.output))
return str(agent_run.result.output)
except Exception as e:
import traceback
traceback.print_exc()
return f"Erro: {str(e)}"
return "Nenhuma resposta."
# Cleanup global
async def shutdown():
db.close()
if __name__ == "__main__":
_ = asyncio.run(agent_conventional_response("quantos sao os gols baseados em cruzamentos?"))
print(_) |