Files
aivanov_database/aivanov_project/vanna/run_server.py
2026-03-05 01:20:15 +01:00

145 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""AIVANOV server Ollama (gpt-oss:120b-cloud) + PostgreSQL (Chinook)."""
import os
import sys
# Ensure src is on path for editable install
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
from vanna import Agent, AgentConfig
from vanna.core.registry import ToolRegistry
from vanna.core.user import User
from vanna.core.user.resolver import UserResolver
from vanna.core.user.request_context import RequestContext
from vanna.integrations.ollama import OllamaLlmService
from vanna.integrations.postgres import PostgresRunner
from vanna.integrations.local.agent_memory import DemoAgentMemory
from vanna.integrations.local import FileSystemConversationStore
from vanna.tools import RunSqlTool, VisualizeDataTool, ExportPdfTool, LocalFileSystem
from vanna.core.system_prompt import DefaultSystemPromptBuilder
from vanna.servers.fastapi.app import VannaFastAPIServer
SYSTEM_PROMPT = """\
Vous êtes l'assistant AIVANOV, un analyste de données IA. Vous répondez aux questions en écrivant et exécutant des requêtes SQL sur une base de données PostgreSQL. Répondez toujours en français.
SCHÉMA DE LA BASE DE DONNÉES (Chinook - magasin de musique) :
Tables et colonnes :
- artist(artist_id, name)
- album(album_id, title, artist_id) → FK artist
- track(track_id, name, album_id, media_type_id, genre_id, composer, milliseconds, bytes, unit_price) → FK album, media_type, genre
- genre(genre_id, name)
- media_type(media_type_id, name)
- playlist(playlist_id, name)
- playlist_track(playlist_id, track_id) → FK playlist, track
- customer(customer_id, first_name, last_name, company, address, city, state, country, postal_code, phone, fax, email, support_rep_id) → FK employee
- employee(employee_id, last_name, first_name, title, reports_to, birth_date, hire_date, address, city, state, country, postal_code, phone, fax, email)
- invoice(invoice_id, customer_id, invoice_date, billing_address, billing_city, billing_state, billing_country, billing_postal_code, total) → FK customer
- invoice_line(invoice_line_id, invoice_id, track_id, unit_price, quantity) → FK invoice, track
INSTRUCTIONS CRITIQUES — LISEZ ATTENTIVEMENT :
1. EXÉCUTEZ TOUJOURS les requêtes SQL avec l'outil run_sql. Ne montrez JAMAIS uniquement du code SQL sans l'exécuter.
2. INTERDIT : NE GÉNÉREZ JAMAIS de tableaux markdown (|---|---|). Les données sont affichées automatiquement par le frontend. Si vous affichez un tableau markdown, c'est une ERREUR.
3. GRAPHIQUES ET DIAGRAMMES — OBLIGATOIRE :
Quand l'utilisateur demande un diagramme, graphique, camembert, histogramme, courbe, visualisation ou chart :
ÉTAPE 1 : Appelez run_sql pour récupérer les données.
ÉTAPE 2 : Lisez le nom du fichier CSV dans la réponse de run_sql (format: res_XXXXX.csv).
ÉTAPE 3 : Appelez visualize_data en copiant le nom EXACT du fichier. Ne modifiez PAS le nom.
ATTENTION AU NOM DE FICHIER :
- Le fichier s'appelle "res_XXXXX.csv" (5 chiffres)
- Copiez-le EXACTEMENT tel qu'il apparaît dans le résultat de run_sql
- N'inventez PAS de nom. N'ajoutez PAS "..." ou de troncature.
Types de graphiques (paramètre chart_type) :
"pie" = camembert | "bar" = barres | "scatter" = nuage de points
"histogram" = histogramme | "line" = courbe | "heatmap" = carte de chaleur
Exemple :
→ run_sql(sql="SELECT genre.name, COUNT(*) as total FROM track JOIN genre USING(genre_id) GROUP BY 1 ORDER BY 2 DESC LIMIT 10")
(résultat contient: FICHIER CSV SAUVEGARDÉ: res_42851.csv)
→ visualize_data(filename="res_42851.csv", title="Top 10 genres", chart_type="bar")
4. Ne générez JAMAIS de liens markdown d'images. Le graphique est rendu automatiquement.
5. Gardez vos commentaires textuels COURTS (2-3 phrases max). Les données sont déjà visibles.
"""
class DemoUserResolver(UserResolver):
"""Always returns a demo user - no auth required."""
async def resolve_user(self, request_context: RequestContext) -> User:
return User(
id="demo_user",
email="demo@example.com",
group_memberships=["user"],
)
def create_agent() -> Agent:
llm_service = OllamaLlmService(
model="gpt-oss:120b-cloud",
host="http://localhost:11434",
)
postgres_runner = PostgresRunner(
host="localhost",
port=5432,
database="chinook",
user="dom",
password="loli",
)
file_system = LocalFileSystem()
run_sql_tool = RunSqlTool(sql_runner=postgres_runner, file_system=file_system)
visualize_tool = VisualizeDataTool(file_system=file_system)
export_pdf_tool = ExportPdfTool(file_system=file_system)
tool_registry = ToolRegistry()
tool_registry.register_local_tool(run_sql_tool, access_groups=[])
tool_registry.register_local_tool(visualize_tool, access_groups=[])
tool_registry.register_local_tool(export_pdf_tool, access_groups=[])
agent_memory = DemoAgentMemory(max_items=1000)
user_resolver = DemoUserResolver()
conversation_store = FileSystemConversationStore(
base_dir=os.path.join(os.path.dirname(__file__), "data", "conversations")
)
return Agent(
llm_service=llm_service,
tool_registry=tool_registry,
user_resolver=user_resolver,
agent_memory=agent_memory,
conversation_store=conversation_store,
system_prompt_builder=DefaultSystemPromptBuilder(base_prompt=SYSTEM_PROMPT),
config=AgentConfig(
stream_responses=True,
include_thinking_indicators=True,
),
)
if __name__ == "__main__":
agent = create_agent()
static_dir = os.path.join(os.path.dirname(__file__), "frontends", "webcomponent", "dist")
server = VannaFastAPIServer(agent, config={
"dev_mode": True,
"static_folder": static_dir,
})
print("Démarrage d'AIVANOV sur http://localhost:8084")
print(" LLM : Ollama gpt-oss:120b-cloud")
print(" Base : PostgreSQL chinook (localhost:5432)")
print(" Frontend : build local (avec graphiques Plotly)")
print(" API docs : http://localhost:8084/docs")
server.run(host="0.0.0.0", port=8084)