Eddyhzd commited on
Commit
1823435
·
1 Parent(s): 05183f1
Files changed (3) hide show
  1. app.py +12 -102
  2. csv_provider.py +0 -50
  3. serveur_mcp.py +45 -0
app.py CHANGED
@@ -1,106 +1,16 @@
1
- import gradio as gr
2
- from openai import OpenAI
3
- import os
4
- import asyncio
5
- import re
6
- from mcp import ClientSession
7
- from mcp.client.stdio import stdio_client
8
 
9
- cle_api = os.environ.get("CLE_API_MISTRAL")
 
 
10
 
11
- # Initialisation du client Mistral (API compatible OpenAI)
12
- client = OpenAI(api_key=cle_api, base_url="https://api.mistral.ai/v1")
13
 
14
- def extract_csv_uri_and_question(text: str):
15
- """Retourne (uri, question). Si pas de uri, uri == None."""
16
- m = re.search(r"(csv://\S+)", text)
17
- if not m:
18
- return None, text.strip()
19
- uri = m.group(1).rstrip(",.;") # retire ponctuation courante
20
- question = text.replace(m.group(1), "").strip()
21
- if question == "":
22
- question = "Donne un aperçu et un résumé des données."
23
- return uri, question
24
 
25
- async def fetch_csv_preview(uri: str) -> str:
26
- # Paramètres pour lancer le provider en tant que sous-processus
27
- params = {
28
- "command": "python",
29
- "args": ["csv_provider.py"],
30
- }
31
- # Création de la session
32
- async with ClientSession(stdio_client, params) as session:
33
- resp = await session.get_resource(uri)
34
- return resp.resource.text
35
-
36
- def trim_context(text: str, max_chars: int = 4000) -> str:
37
- if not text:
38
- return ""
39
- if len(text) <= max_chars:
40
- return text
41
- # garder le début (head) utile pour les CSV preview
42
- return text[:max_chars] + "\n... (truncated)"
43
-
44
- def call_llm(messages, model="mistral-small-latest", max_tokens=512, temperature=0.2):
45
- """Appel synchrones au LLM (OpenAI-compatible)."""
46
- resp = client.chat.completions.create(
47
- model=model,
48
- messages=messages,
49
- max_tokens=max_tokens,
50
- temperature=temperature,
51
- )
52
- return resp.choices[0].message["content"].strip()
53
-
54
- # --- LOGIQUE DU CHATBOT ---
55
- def chatbot(message, history):
56
- history = history or []
57
- history.append(("Vous: " + message, ""))
58
-
59
- uri, question = extract_csv_uri_and_question(message)
60
-
61
- # Préparer prompt system
62
- system_prompt = (
63
- "Tu es un assistant utile. Si une ressource csv://... est fournie, utilise SON CONTENU "
64
- "pour répondre précisément aux questions. Ne devine pas les valeurs absentes. "
65
- "Rends la réponse concise et fournis des exemples si pertinent."
66
- )
67
-
68
- # Si l'utilisateur demande d'utiliser un CSV -> récupérer via MCP
69
- csv_text = None
70
- if uri:
71
- try:
72
- csv_text = asyncio.run(fetch_csv_preview(uri))
73
- except Exception as e:
74
- reply = f"Erreur de récupération MCP pour {uri} : {e}"
75
- history[-1] = (history[-1][0], "Bot: " + reply)
76
- return history, history
77
-
78
- # Construire messages pour LLM
79
- messages = [{"role": "system", "content": system_prompt}]
80
- if csv_text:
81
- context = trim_context(csv_text, max_chars=4000)
82
- messages.append({"role": "system", "content": f"--- CONTEXTE CSV ({uri}) ---\n{context}\n--- FIN CONTEXTE ---"})
83
- user_content = f"Question (en se basant sur le CSV):\n{question}"
84
- else:
85
- user_content = question
86
-
87
- messages.append({"role": "user", "content": user_content})
88
-
89
- # Appel LLM
90
- try:
91
- llm_reply = call_llm(messages)
92
- except Exception as e:
93
- llm_reply = f"Erreur LLM : {e}"
94
-
95
- history[-1] = (history[-1][0], "Bot: " + llm_reply)
96
- return history, history
97
-
98
- with gr.Blocks() as demo:
99
-
100
-
101
- chatbot_ui = gr.Chatbot(label="ChatBot")
102
- msg = gr.Textbox(placeholder="Écrivez un message...")
103
-
104
- msg.submit(chatbot, [msg, chatbot_ui], [chatbot_ui, chatbot_ui])
105
-
106
- demo.launch()
 
1
+ from mcp.client.session import MCPClient
 
 
 
 
 
 
2
 
3
+ # Connexion
4
+ client = MCPClient("csv_analyzer")
5
+ client.connect()
6
 
7
+ # Lister les colonnes
8
+ print("Colonnes dispo:", client.call("list_columns"))
9
 
10
+ # Filtrer des lignes
11
+ rows = client.call("filter_rows", column="pays", value="France", limit=3)
12
+ print("Lignes filtrées:", rows)
 
 
 
 
 
 
 
13
 
14
+ # Analyse avec Mistral
15
+ analysis = client.call("analyze_data", question="Quels sont les 3 produits les plus fréquents ?")
16
+ print("Analyse:", analysis)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
csv_provider.py DELETED
@@ -1,50 +0,0 @@
1
- import asyncio
2
- import pandas as pd
3
- from huggingface_hub import HfApi, hf_hub_download
4
- from mcp.server import Server
5
- from mcp.types import Resource, ResourceResponse
6
- import os
7
-
8
- server = Server("csv-provider")
9
-
10
- HF_TOKEN = os.environ.get("HF_TOKEN")
11
- api = HfApi()
12
-
13
- # Resource: csv://repo_id/path/to/file.csv
14
- @server.resource("csv://{repo_id}/{path}")
15
- async def get_csv(repo_id: str, path: str) -> ResourceResponse:
16
- try:
17
- # Télécharger le fichier CSV depuis le dataset privé
18
- local_path = hf_hub_download(
19
- repo_id=repo_id,
20
- filename=path,
21
- repo_type="dataset",
22
- token=HF_TOKEN
23
- )
24
-
25
- # Charger et prévisualiser le CSV
26
- df = pd.read_csv(local_path)
27
- preview = df.head(5).to_string()
28
-
29
- return ResourceResponse(
30
- resource=Resource(
31
- uri=f"csv://{repo_id}/{path}",
32
- name=f"CSV: {path}",
33
- description=f"Aperçu des données du dataset {repo_id}",
34
- mimeType="text/plain",
35
- text=preview,
36
- )
37
- )
38
- except Exception as e:
39
- return ResourceResponse(
40
- resource=Resource(
41
- uri=f"csv://{repo_id}/{path}",
42
- name=f"Erreur CSV",
43
- description=str(e),
44
- mimeType="text/plain",
45
- text=f"Impossible de lire {repo_id}/{path} : {e}",
46
- )
47
- )
48
-
49
- if __name__ == "__main__":
50
- asyncio.run(server.run())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
serveur_mcp.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ from datasets import load_dataset
3
+ from transformers import pipeline
4
+ from mcp.server.fastmcp import FastMCPServer
5
+
6
+ # Charger dataset Hugging Face privé
7
+ dataset = load_dataset("HackathonCRA/2024", split="train")
8
+ df = dataset.to_pandas()
9
+
10
+ # Charger Mistral
11
+ mistral = pipeline("text-generation", model="mistralai/Mistral-7B-Instruct-v0.2", device_map="auto")
12
+
13
+ # Créer serveur MCP
14
+ server = FastMCPServer("csv_analyzer")
15
+
16
+ @server.tool()
17
+ def list_columns() -> list[str]:
18
+ """Retourne la liste des colonnes disponibles dans le CSV."""
19
+ return df.columns.tolist()
20
+
21
+ @server.tool()
22
+ def filter_rows(column: str, value: str, limit: int = 5) -> list[dict]:
23
+ """Retourne des lignes où column == value."""
24
+ if column not in df.columns:
25
+ return [{"error": f"Colonne {column} inexistante"}]
26
+ subset = df[df[column] == value].head(limit)
27
+ return subset.to_dict(orient="records")
28
+
29
+ @server.tool()
30
+ def analyze_data(question: str) -> str:
31
+ """Interprète les données CSV avec Mistral."""
32
+ # On résume rapidement le dataframe
33
+ sample = df.head(20).to_string()
34
+ prompt = f"""
35
+ Voici un extrait de données tabulaires :
36
+ {sample}
37
+
38
+ Question: {question}
39
+ Réponds de manière concise et claire.
40
+ """
41
+ output = mistral(prompt, max_new_tokens=256)[0]["generated_text"]
42
+ return output
43
+
44
+ if __name__ == "__main__":
45
+ server.run()