Skip to content

Commit 004248e

Browse files
authored
Release 1.5.0 (#216)
* Play llm (#202) * WIP on llm still some fail in test Signed-off-by: Armand <arm.gilles@gmail.com> * Pass the CI test now Signed-off-by: Armand <arm.gilles@gmail.com> * Tests more determinastic Signed-off-by: Armand <arm.gilles@gmail.com> * Ajout d'un marker pytest pour limiter les tests LLM et contraites delay call API Signed-off-by: Armand <arm.gilles@gmail.com> * Better conf in llm test to reduce alerte rating api / better data in test Signed-off-by: Armand <arm.gilles@gmail.com> * Prepare conf in llm toml Signed-off-by: Armand <arm.gilles@gmail.com> * keep track to how many request we call every second to fight again rate limited Signed-off-by: Armand <arm.gilles@gmail.com> * Pass ci test but still some loop in chat llm Signed-off-by: Armand <arm.gilles@gmail.com> * All CI test pass, remove api call limited to mistral with suscription Signed-off-by: Armand <arm.gilles@gmail.com> * remove marker on LLM test, pytestmark = pytest.mark.llm_api on file is OK Signed-off-by: Armand <arm.gilles@gmail.com> * Minor change in dataset test to have unique station with max available_bikes Signed-off-by: Armand <arm.gilles@gmail.com> * Using toml config Signed-off-by: Armand <arm.gilles@gmail.com> * Add history in chat Signed-off-by: Armand <arm.gilles@gmail.com> * Update notebook to play with LLM Signed-off-by: Armand <arm.gilles@gmail.com> * Update lib dep with llm stuff #202 Signed-off-by: Armand <arm.gilles@gmail.com> * fix typo Signed-off-by: Armand <arm.gilles@gmail.com> * Update readme with mistral api key #202 Signed-off-by: Armand <arm.gilles@gmail.com> * Fix new lib LLM dep for package #202 Signed-off-by: Armand <arm.gilles@gmail.com> * Add mistral api key in github action #202 Signed-off-by: Armand <arm.gilles@gmail.com> * fix typo Signed-off-by: Armand <arm.gilles@gmail.com> * Cleaning old prompt #202 Signed-off-by: Armand <arm.gilles@gmail.com> * Moins de contraintes dans le timing du CI LLM #202 Signed-off-by: Armand <arm.gilles@gmail.com> * Add chat response to analyse error during CI #202 Signed-off-by: Armand <arm.gilles@gmail.com> * Improve historic chat with no error file not found #202 Signed-off-by: Armand <arm.gilles@gmail.com> * Cleaning memory process for llm #202 Signed-off-by: Armand <arm.gilles@gmail.com> --------- Signed-off-by: Armand <arm.gilles@gmail.com> * Move config toml file for LLM outside package as model and data #203 (#204) Signed-off-by: Armand <arm.gilles@gmail.com> * Histo data llm (#207) * LLM use new data with new column #206 Signed-off-by: Armand <arm.gilles@gmail.com> * Amélioration soft prompt du l'agent global + cleaning Signed-off-by: Armand <arm.gilles@gmail.com> * Change test message history avec l'ajout de contrainte sur discussion sur agent Signed-off-by: Armand <arm.gilles@gmail.com> --------- Signed-off-by: Armand <arm.gilles@gmail.com> * Agent geocoding (#209) * WIP for #208 Signed-off-by: Armand <arm.gilles@gmail.com> * WIP #208 Signed-off-by: Armand <arm.gilles@gmail.com> * Add test bonjour agent Signed-off-by: Armand <arm.gilles@gmail.com> * Gestion location non trouve #208 Signed-off-by: Armand <arm.gilles@gmail.com> * Add tool decorator and small update #208 Signed-off-by: Armand <arm.gilles@gmail.com> * Pass base llm test Signed-off-by: Armand <arm.gilles@gmail.com> * Add wrapper to pass chaine caract into find_nearest_stations and store df info #208 Signed-off-by: Armand <arm.gilles@gmail.com> * Modify agent.py with previous commit #208 Signed-off-by: Armand <arm.gilles@gmail.com> * WIP test for find_nearest_stations #208 Signed-off-by: Armand <arm.gilles@gmail.com> * Update prompt for find_nearest_stations #208 Signed-off-by: Armand <arm.gilles@gmail.com> * Rename test for find_nearest_stations given lat / lon #208 Signed-off-by: Armand <arm.gilles@gmail.com> * Update desc test Signed-off-by: Armand <arm.gilles@gmail.com> * Fix typo in test #208 Signed-off-by: Armand <arm.gilles@gmail.com> * Test ok to find X station near une adresse #208 Signed-off-by: Armand <arm.gilles@gmail.com> * Improve prompt to avoid loop. Pass all test CI Signed-off-by: Armand <arm.gilles@gmail.com> * Add geopy in dep #208 Signed-off-by: Armand <arm.gilles@gmail.com> * Update notebook #208 Signed-off-by: Armand <arm.gilles@gmail.com> --------- Signed-off-by: Armand <arm.gilles@gmail.com> * Prediction station velo (#213) * Script to some prediction by station OK #210 Signed-off-by: Armand <arm.gilles@gmail.com> * Add a fonction to create features to use in model depend target #210 Signed-off-by: Armand <arm.gilles@gmail.com> * Add test for create_target #210 Signed-off-by: Armand <arm.gilles@gmail.com> * Add test for get_feature_to_use_for_model #210 Signed-off-by: Armand <arm.gilles@gmail.com> * Add test for build_feat_for_regression #210 Signed-off-by: Armand <arm.gilles@gmail.com> * test learning and prediction process #210 Signed-off-by: Armand <arm.gilles@gmail.com> * Small update #210 Signed-off-by: Armand <arm.gilles@gmail.com> * Cleaning notebook #210 Signed-off-by: Armand <arm.gilles@gmail.com> * WIP LLM chatbot prediction station #210 Signed-off-by: Armand <arm.gilles@gmail.com> * Adapt code to legacy #210 Signed-off-by: Armand <arm.gilles@gmail.com> * small update on prompt vcub_agent Signed-off-by: Armand <arm.gilles@gmail.com> * Try to improve prompt Signed-off-by: Armand <arm.gilles@gmail.com> * Add test for regression message chat but sill to WIP #210 Signed-off-by: Armand <arm.gilles@gmail.com> * Better prompt for prompt parsing overall tests Signed-off-by: Armand <arm.gilles@gmail.com> * Use wrapper for get_distance tool and fix test Signed-off-by: Armand <arm.gilles@gmail.com> * Improve result for test regression chatbot #210 Signed-off-by: Armand <arm.gilles@gmail.com> * Small typo Signed-off-by: Armand <arm.gilles@gmail.com> * Small adjustement Signed-off-by: Armand <arm.gilles@gmail.com> * Delete useless check now Signed-off-by: Armand <arm.gilles@gmail.com> * Better prompt #210 Signed-off-by: Armand <arm.gilles@gmail.com> * Use return_df=True, better and more stable result #210 Signed-off-by: Armand <arm.gilles@gmail.com> * Change again correct result for calcul distance... Signed-off-by: Armand <arm.gilles@gmail.com> * Update notebook pretty drafty #210 Signed-off-by: Armand <arm.gilles@gmail.com> * Better prompt and test for distance calculation Signed-off-by: Armand <arm.gilles@gmail.com> * Improve prompt to don't have final answer and a parse-able action in prediction #210 Signed-off-by: Armand <arm.gilles@gmail.com> * Improve prompt to calcul distance Signed-off-by: Armand <arm.gilles@gmail.com> --------- Signed-off-by: Armand <arm.gilles@gmail.com> * Update k (frein) to 20 (previous was 10) #214 (#215) * Update k (frein) to 20 (previous was 10) #214 Signed-off-by: Armand <arm.gilles@gmail.com> * Update test ml with change on K #214 Signed-off-by: Armand <arm.gilles@gmail.com> --------- Signed-off-by: Armand <arm.gilles@gmail.com> * Release 1.5.0 Signed-off-by: Armand <arm.gilles@gmail.com> --------- Signed-off-by: Armand <arm.gilles@gmail.com>
1 parent dd2be42 commit 004248e

27 files changed

+1700
-7
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ jobs:
2828
run: |
2929
touch .env
3030
echo KEY_API_BDX=${{ secrets.KEY_API_BDX }} >> .env
31+
echo MISTRAL_API_KEY=${{ secrets.MISTRAL_API_KEY }} >> .env
3132
- name: Copy test data
3233
run: |
3334
cp -r ${{ env.ROOT_TESTS_DATA }}/* ${{ github.workspace }}/tests/

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ model
88
*.html
99
# test data for CI
1010
!/tests/data_for_tests/*
11+
*.parquet
1112

1213
# Byte-compiled / optimized / DLL files
1314
__pycache__/

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Créer un .env à la racine du projet avec :
4949
- API_METEO="YOUR TOKEN HERE" (finalement, la météo n'est plus utilisé dans le projet).
5050
- MAPBOX_TOKEN="YOUR TOKEN HERE" (pour l'utilisation des graphiques avec mapbox).
5151
- KEY_API_BDX="YOUR KEY HERE" (pour l'utilisation de l'API open data de Bordeaux. Pour obtenir une [clef](https://data.bordeaux-metropole.fr/opendata/key))
52+
- MISTRAL_API_KEY="YOUR KEY HERE" (pour l'utilisation de l'API Mistral. Pour obtenir une [clef](https://mistral.ai/))
5253

5354
## Études :
5455

config_llm/prompt_tools.toml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
[get_distance_prompt]
2+
# Description du tool
3+
prompt_descrption = """Calculer la distance entre deux stations (en km) grâce à leurs coordonnées (lat, lon).
4+
5+
IMPORTANT: Les paramètres doivent être fournis en gardant leurs précisions:
6+
7+
Exemple d'utilisation:
8+
1. Chercher les coordonnées de la station "Stalingrad":
9+
df[df["station_name"] == "STALINGRAD"]
10+
2. Observer le résultat.
11+
3. Chercher les coordonnées de la station "Porte de Bourgogne":
12+
df[df["station_name"] == "PORTE DE BOURGOGNE"]
13+
4. Observer le résultat.
14+
5. Utiliser get_distance(lat1,lon1,lat2,lon2) avec les valeurs numériques obtenues:
15+
distance = get_distance(lat1=44.8378,lon1=-0.57921,lat2=44.8407,lon2=-0.5811) sans arrondir les valeurs !
16+
17+
Note: Dans le cas ou tu n'arrives pas à trouver les données pour une station, tu peux essayer
18+
df[df["station_name"].str.contains("meriadeck", case=False)] qui est plus général dans le nom de la station.
19+
20+
Cela renvoie la distance en kilomètres entre les deux stations, un vélo roule en moyenne à 15 km/h.
21+
donc tu peux indiquer aussi le temps de trajet en minutes.
22+
"""
23+
24+
[get_geocoding_prompt]
25+
prompt_descrption = """Récupérer la latitude et la longitude d'une adresse postale en France.
26+
Exemple d'utilisation: lat, lon = get_geocoding("1 rue de la République, Bordeaux").
27+
"""
28+
29+
[find_nearest_stations_prompt]
30+
prompt_descrption = """Trouve les X stations les plus proches d'une coordonnée GPS.
31+
IMPORTANT: Les paramètres doivent être fournis comme des arguments séparés et typés correctement:
32+
lat (float), lon (float), nombre_station_proche (int, optionnel).
33+
Exemple pour avoir les 2 stations les plus proche d'une latitude (ex: 44.0485) et d'une longitude (ex: -0.5785) qui sont des float:
34+
nearest_stations_json = find_nearest_stations(last_info_station=df,
35+
lat=44.0485, lon=-0.5785, nombre_station_proche=2)
36+
Cela renvoie un json avec les 2 stations les plus proches avec une colonne "distance" en km
37+
ainsi que les informations liées à la station :
38+
[{'station_id': 103,
39+
'date': Timestamp('2025-03-05 15:40:00'),
40+
'available_stands': 10,
41+
'available_bikes': 22,
42+
'status': 1,
43+
'anomaly': 1.0,
44+
'station_name': 'Place du Palais',
45+
'commune_name': 'Bordeaux',
46+
'lat': 44.837799072265625,
47+
'lon': -0.5702999830245972,
48+
'distance': 0.0672468849653659},
49+
{'station_id': 42,
50+
'date': Timestamp('2025-03-05 15:40:00'),
51+
'available_stands': 15,
52+
'available_bikes': 0,
53+
'status': 1,
54+
'anomaly': 1.0,
55+
'station_name': 'Camille Jullian',
56+
'commune_name': 'Bordeaux',
57+
'lat': 44.83919906616211,
58+
'lon': -0.5720000267028809,
59+
'distance': 0.15456434694289684}]
60+
"""
61+
62+
[get_prediction_station_prompt]
63+
prompt_descrption = """
64+
Permet de faire une prédiction sur une station donnée à partir des données historiques disponibles.
65+
Réflexion :
66+
1. Trouver l'ID de la station à partir du nom de la station dans le DataFrame last_info_station.
67+
2. Calculer l'horizon de prédiction.
68+
3. Utiliser la fonction get_prediction_station pour faire la prédiction.
69+
70+
Paramètres requis :
71+
- 'target_station_id' (int) : ID de la station à prédire.
72+
- 'target_col' (str) : Colonne cible à prédire, par exemple 'available_bike_stands' ou 'available_bikes' UNIQUEMENT.
73+
- 'horizon_prediction' (str) : Horizon de prédiction par période de 10 minutes, par exemple '20m', '2h', '4h', '1d', etc.
74+
75+
Calcul de l'horizon de prédiction :
76+
- Utilisez la date actuelle (présente dans last_info_station).
77+
- Interprétez la demande de l'utilisateur pour en déduire l'horizon de prédiction -> date désirée par l'utilisateur - date actuelle !
78+
IMPORTANT : NE PAS UTILISER python_repl_ast pour calculer l'horizon de prédiction !
79+
- Ne faites pas de prédiction sur une période supérieure à 24 heures. Si la
80+
période dépasse 24 heures, indiquez à l'utilisateur que ce n'est pas possible.
81+
82+
Exemple d'utilisation :
83+
L'utilisateur souhaite prédire le nombre de vélos disponibles à la station Berges du Lac dans 2 heures.
84+
1. Trouvez l'ID de la station Berges du Lac dans le DataFrame last_info_station.
85+
2. Utilisez la date actuelle pour calculer l'horizon de prédiction.
86+
3. Exécutez la fonction get_prediction_station avec les paramètres suivants :
87+
params = "target_station_id=175,target_col=available_bike_stands,horizon_prediction=2h"
88+
4. La fonction renvoie la prédiction du nombre de vélos disponibles (y_pred) sous la forme d'un df Polars.
89+
90+
91+
Exemple de code :
92+
```python
93+
params = "target_station_id=175,target_col=available_bike_stands,horizon_prediction=2h"
94+
prediction = get_prediction_station(params)
95+
```
96+
"""

config_llm/prompt_vcub_agent.toml

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
[vcub_agent_prompt]
2+
# Configuration pour dicter à l'agent comment procéder
3+
template_llm = """
4+
Le dataframe contient des informations sur les stations à LA DATE LA PLUS RÉCENTE avec les colonnes suivantes:
5+
- station_id: l'id de la station
6+
- date: la date
7+
- available_stands: le nombre de place disponible
8+
- available_bikes: le nombre de vélos disponibles
9+
- status: le statut de la station (1: tout va bien / 0: Maintenance ou problème)
10+
- lat: la latitude de la station
11+
- lon: la longitude de la station
12+
- station_name: le nom de la station
13+
- anomaly: une colonne qui indique si la station a un problème (1: pas de problème / -1: problème / absence de données: station non surveillée par les algorithmes de machine learning)
14+
- commune_name: Le nom de la commune où se trouve la station
15+
16+
Assure-toi de d'utiliser les bonnes pratiques de Pandas pour manipuler ces données !
17+
La colonne "date" représente la date actuelle pour les différents calculs d'horizon de prédiction.
18+
19+
{tools}
20+
21+
RÈGLES DE FORMATAGE STRICTES À SUIVRE ABSOLUMENT:
22+
23+
1. JAMAIS d'action et de réponse finale dans le même message.
24+
2. RESPECTE toujours cet ordre pour l'utilisation des tools: Thought → Action → Observation → Thought → ... → Final Answer
25+
3. TOUJOURS terminer par "Final Answer:" uniquement quand tu as TOUTES les informations.
26+
4. JAMAIS utiliser "response_type:" dans tes réponses.
27+
5. **NE JAMAIS produire une action ou réflexion après avoir donné la réponse finale. Une fois que la réponse finale est donnée, l'exécution doit être considérée comme terminée.**
28+
6. **Si tu as toutes les informations nécessaires pour répondre à la question, passe directement à la réponse finale sans ajouter de réflexion supplémentaire.**
29+
7. **Ne fais pas de nouvelles actions une fois que tu as produit la réponse finale.**
30+
8. **Pour les interactions simples de chat (par exemple, dire bonjour, répondre à des questions générales), réponds directement sans utiliser de tools.**
31+
32+
FORMATS VALIDES:
33+
--------------------
34+
FORMAT POUR CONTINUER AVEC UNE ACTION:
35+
Thought: <ta réflexion>
36+
Action: <nom_outil>
37+
Action Input: <paramètres>
38+
Observation: <résultat de l'outil utilisé>
39+
40+
FORMAT POUR LA RÉPONSE FINALE (uniquement quand tu as toutes les informations):
41+
Final Answer: <réponse concise>
42+
43+
FORMAT POUR LES INTERACTIONS SIMPLES DE CHAT:
44+
Final Answer: <réponse concise>
45+
--------------------
46+
47+
Utilise un des outils suivants: {tool_names}
48+
49+
Question: {input}
50+
{agent_scratchpad}
51+
"""
52+
53+
# Présentation de l'agent
54+
prefix_agent = """Tu es un assistant spécialisé dans l'analyse des données des stations VCub de Bordeaux.
55+
Tu peux répondre uniquement aux questions liées à ton activité sur la zone de Bordeaux et de la métropole.
56+
57+
Les données ne sont pas triées ou filtrées. Tu dois utiliser les bonnes pratiques de Pandas!
58+
Tu as accès à l'historique de la conversation précédente dans
59+
{chat_history}
60+
"""
61+
62+
# Gestion des erreurs
63+
prompt_gestion_erreurs = """
64+
ERREUR DE FORMAT DÉTECTÉE! Suivez STRICTEMENT ce format:
65+
66+
Si "Final Answer" est présent dans votre réponse précédente, il FAUT
67+
IMPÉRATIVEMENT donner UNIQUEMENT cette réponse dans la réponse actuelle sans
68+
autres nouvelles Action Observation ou Thought, simplement la réponse finale.
69+
70+
Dans le cas où vous devez effectuer une action, utilisez le format suivant:
71+
Pour utiliser un outil:
72+
Thought: <votre réflexion>
73+
Action: <nom_outil>
74+
Action Input: <paramètres>
75+
76+
Pour donner la réponse finale:
77+
Final Answer: <réponse concise> Sans ajout de contexte ou d’explication supplémentaire.
78+
79+
N'UTILISEZ JAMAIS "response_type:".
80+
N'INCLUEZ JAMAIS une action ET une réponse finale ensemble.
81+
"""

config_llm/vcub_agent.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[vcub_agent]
2+
return_intermediate_steps = true
3+
prefix = "" # Vous devrez définir la valeur appropriée
4+
max_iterations = 7
5+
allow_dangerous_code = true
6+
verbose = true
7+
early_stopping_method = "force"

conftest.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import time
2+
3+
import pytest
4+
5+
# Variables globales pour suivre le timing des appels API Mistral
6+
last_api_call = 0
7+
MIN_DELAY = 1.4 # Minimum delay in seconds
8+
9+
10+
# Créez un marqueur pour les tests LLM
11+
def pytest_configure(config):
12+
"""
13+
Add a marker for LLM API tests.
14+
This allows us to easily identify and manage tests that interact with the LLM API.
15+
"""
16+
config.addinivalue_line("markers", "llm_api: mark a test that calls the LLM API")
17+
18+
19+
@pytest.fixture(autouse=True)
20+
def api_rate_limit(request):
21+
"""Fixture to ensure API rate limits are respected."""
22+
# N'appliquer le délai qu'aux tests marqués avec llm_api
23+
if request.node.get_closest_marker("llm_api") is None:
24+
yield
25+
return
26+
27+
global last_api_call
28+
29+
# Calculate time since last API call
30+
current_time = time.time()
31+
elapsed = current_time - last_api_call
32+
33+
# If not enough time has passed, wait
34+
if elapsed < MIN_DELAY:
35+
wait_time = MIN_DELAY - elapsed
36+
time.sleep(wait_time)
37+
38+
# Update the last API call time
39+
last_api_call = time.time()
40+
41+
yield # This is where the test runs
42+
43+
# Update again after the test completes
44+
last_api_call = time.time()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:a6a8f12e5d0aa4fd7b28e2afb35598b50e7851ef1946d282037ccf1646b7cd10
3+
size 100359
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:2f94bb77999b6f2ddec2e54f32a62b84511274e5efce5762936410884a04dcc9
3+
size 145817

pyproject.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
55
[project]
66
name = "vcub_keeper"
77

8-
version = "1.4.1"
8+
version = "1.5.0"
99
requires-python = ">=3.12"
1010
readme = "README.md"
1111

@@ -26,6 +26,11 @@ dependencies = [
2626
"tables==3.10.1",
2727
"matplotlib==3.9.2",
2828
"seaborn==0.13.2",
29+
"langchain==0.3.19",
30+
"langchain-experimental==0.3.4",
31+
"langchain-mistralai==0.2.7",
32+
"tabulate==0.9.0",
33+
"geopy==2.4.1"
2934
]
3035

3136
[project.optional-dependencies]

0 commit comments

Comments
 (0)