Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4d02f8f
add multiple airplanes to schema context and types
abdratsd Mar 14, 2025
601484f
Issue #2: Improve line name readability by formatting the displayed l…
ahmerique Jul 1, 2025
351ec95
Issue #5: Send grid state after line disconnection in Anticipation cards
ahmerique Jul 3, 2025
5c88f09
added nocontext var to locales
abdratsd Oct 2, 2025
654e3c7
resources/checkPorts.sh to check if all needed ports are available
obretteville Jun 20, 2025
d87072c
Troubleshooting documentation updated
obretteville Jun 20, 2025
1d9bb87
Update scripts to use docker compose V2 instead of old docker-compose
ahmerique Jul 28, 2025
b3eab9c
Handle alert notifications (different than alarm)
ahmerique Sep 25, 2025
fdd96a3
plane id is now a string
abdratsd Oct 2, 2025
2305b65
replace old usecase name in filename
abdratsd Oct 2, 2025
8aafa1c
feat(map): add autoFit prop; auto-fit on contextWaypoints only when e…
abdratsd Oct 2, 2025
24e4215
fix(ATM): clear previous ROUTE markers before rebuild; stop removing …
abdratsd Oct 2, 2025
a3fc281
i18n(railway): change primary button label to “Get recommendation fro…
abdratsd Oct 2, 2025
5082775
fix: rename map markers of old usecases
abdratsd Oct 2, 2025
1f2c711
Update KPI name
MarouaMed Oct 15, 2025
9222fc2
Update recommendations titles and description in english
MarouaMed Oct 15, 2025
fd33d17
Update Events title and description in english
MarouaMed Oct 15, 2025
4ef5ad9
fix- rebase to drop modified access to files & keep minor changes fro…
abdratsd Oct 22, 2025
5d9e74d
fix-some statements were still in french
abdratsd Oct 23, 2025
2603fb2
Merge branch 'IRT-SystemX:main' into main
abdratsd Oct 23, 2025
4416fcd
remove repo link
abdratsd Oct 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
* text=auto
*.sh text eol=lf
*.conf text eol=lf
*.bash text eol=lf
*.yml text eol=lf
35 changes: 19 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,16 @@ The platform uses the project **OperatorFabric** for notification management.
### Prerequisites

- [Git (version 2.40.1)](https://git-scm.com/)
- [Docker (version 24.0.2)](https://www.docker.com/)
- [Docker Compose (version 1.25.0 or later)](https://www.docker.com/)
- [Docker Engine (version 27)](https://www.docker.com/)
- [Docker Compose V2](https://www.docker.com/)


### Setting Up the Environment

Clone the repo of the assistant

```sh
git clone https://github.com/IRT-SystemX/InteractiveAI.git
git clone [repo-url]
```

## Usage
Expand All @@ -68,7 +68,7 @@ Below are the steps to start all services. For other methods, please consult the

### Running All Services (Dev Mode)

1. Set-up environement variables
1. **Set-up environement variables**


`VITE_POWERGRID_SIMU`, `VITE_RAILWAY_SIMU` , `VITE_ATM_SIMU` are the simulators' endpoints.
Expand All @@ -84,31 +84,34 @@ export VITE_ATM_SIMU=http://[Service url]:[Service port]
> **_NOTE:_** For this step, you should already have a running simulator. If not, you can use the simulator we provided as an example. For this, please follow the tutorial provided in InteractiveAI/usecases_examples/PowerGrid/ then set the VITE_POWERGRID_SIMU variable to http://YOUR_SERVER_ADDRESS:5100/
>
>
2. Run InteractiveAI assistant
2. **Run InteractiveAI assistant**
```sh
cd config/dev/cab-standalone
./docker-compose.sh
```
> **_NOTE:_** You will see the word cab (Cockpit Assistant Bidirectionnel) on most files in the project. Note that it was the initial project name of InteractiveAI. Might be updated later.

3. Setting up Keycloak `Frontend URL`
* **Access Keycloak Interface**:
3. **Setting up Keycloak `Frontend URL`**
* Access Keycloak Interface:
- Ensure that your Keycloak instance is running and accessible.
- Open a web browser and navigate to the Keycloak admin console, typically available at `http://localhost:89/auth/admin`.
* **Login to Keycloak Admin Console**:
* Login to Keycloak Admin Console:
- Log in to the Keycloak admin console using your administrator credentials (`admin:admin` by default)
* **Navigate to Client Settings**:
* Configure frontendUrl:
- On the Keycloak admin console, locate and click on the "Realm Settings" section.
- In the Frontend URL setting, add the URL of your Assistant Platform frontend as a valid redirect URI. This URL is typically where your frontend application is hosted. For example, if your frontend is hosted locally for development purposes, you might add `http://localhost:3200/*`.
- After adding the frontend URL, save the changes to update the client settings.
* Configure Valid Redirect URIs:
- On the Keycloak admin console, locate and click on the "Clients" section.
- Select the client representing your Assistant Platform application.
* **Configure FrontendUrl**:
- Within the client settings, look for the "Valid Redirect URIs" or similar configuration field.
- Add the URL of your Assistant Platform frontend as a valid redirect URI. This URL is typically where your frontend application is hosted. For example, if your frontend is hosted locally for development purposes, you might add `http://localhost:3200/*`.
- Ensure that the frontend URL you specify matches the actual URL where your frontend application is accessible.
* **Save Changes**:
- After adding the frontend URL, save the changes to update the client settings.
- Add the URL of your Assistant Platform frontend, it should match the one used in the frontendUrl setting.
- After adding the Valid Redirect URIs, save the changes to update the client settings.


4. **Load resources**

4. Load resources
**WARINING:** You need to restart the frontend after updating the URL on keycloak do it before loading the resources.
**WARNING:** You need to restart the frontend after updating the URL on keycloak do it before loading the resources.
```sh
docker restart frontend
```
Expand Down
29 changes: 27 additions & 2 deletions backend/context-service/resources/ATM/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,35 @@

from api.schemas import MetadataSchema
from apiflask.fields import Dict, String, Float, List, Integer
class MetadataSchemaATM(MetadataSchema):
from apiflask import Schema, fields
from marshmallow import pre_load
class PlaneMetadataSchemaATM(MetadataSchema):
id_plane = String()
ApDest = Dict()
Current_airspeed = Float()
Latitude = Float()
Longitude = Float()
wpList = List(Dict())
id_plane = Integer()

class MetadataSchemaATM(MetadataSchema):
airplanes = List(fields.Nested(PlaneMetadataSchemaATM), required=True)

# Backward compatibility: optional fields for the single airplane case
ApDest = Dict(required=False)
Current_airspeed = Float(required=False)
Latitude = Float(required=False)
Longitude = Float(required=False)
wpList = List(Dict(), required=False)

@pre_load
def handle_backward_compatibility(self, data, **kwargs):
# If the new 'airplanes' field is not provided, assume the old format.
if 'airplanes' not in data:
airplane = {}
for field in ['ApDest', 'Current_airspeed', 'Latitude', 'Longitude', 'wpList']:
if field in data:
airplane[field] = data[field]
# Provide a default id_plane if not present.
airplane.setdefault('id_plane', "X")
data['airplanes'] = [airplane]
return data
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def get_parade_info(self, act):
"Redispatch" # pour renvoyer le kpi type_of_the_reco
)
title.append(
"Parade injection: redispatch de source de production"
"Injection recommendation: production source redispatch"
)
cpt = 0
for gen_idx in range(act.n_gen):
Expand All @@ -213,9 +213,9 @@ def get_parade_info(self, act):
# storage
if act._modif_storage:
kpis["type_of_the_reco"] = (
"Stockage" # pour renvoyer le kpi type_of_the_reco
"Storage" # pour renvoyer le kpi type_of_the_reco
)
title.append("Parade stockage")
title.append("Storage recommendation")
cpt = 0
for stor_idx in range(act.n_storage):
amount_ = act._storage_power[stor_idx]
Expand All @@ -225,8 +225,8 @@ def get_parade_info(self, act):
description.append(", ")
cpt = 1
description.append(
f'Demande à l\'unité "{name_}" de '
f'{"charger" if amount_ > 0.0 else "decharger"} '
f'Ask unit "{name_}" to '
f'{"charge" if amount_ > 0.0 else "discharge"} '
f'{abs(amount_):.2f} MW (setpoint: {amount_:.2f} MW)'
)

Expand All @@ -235,7 +235,7 @@ def get_parade_info(self, act):
kpis["type_of_the_reco"] = (
"Injection" # pour renvoyer le kpi type_of_the_reco
)
title.append("Parade injection")
title.append("Injection recommendation")
cpt = 0
for gen_idx in range(act.n_gen):
amount_ = act._curtail[gen_idx]
Expand All @@ -245,71 +245,71 @@ def get_parade_info(self, act):
description.append(", ")
cpt = 1
description.append(
f'Limiter l\'unité "{name_}" à '
f'{100.0 * amount_:.1f}% de sa capacité max '
f'Limit unit "{name_}" to '
f'{100.0 * amount_:.1f}% of its maximum capacity '
f'(setpoint: {amount_:.3f})'
)

# force line status
force_line_impact = impact["force_line"]
if force_line_impact["changed"]:
kpis["type_of_the_reco"] = (
"Topologique" # pour renvoyer le kpi type_of_the_reco
"Topological" # pour renvoyer le kpi type_of_the_reco
)
title.append(
"Parade topologique: connection/deconnection de ligne"
"Topological recommendation: connection/disconnection of line"
)
reconnections = force_line_impact["reconnections"]
if reconnections["count"] > 0:
description.append(
f"Reconnection de {reconnections['count']} lignes "
f"Reconnection of {reconnections['count']} lines "
f"({reconnections['powerlines']})"
)

disconnections = force_line_impact["disconnections"]
if disconnections["count"] > 0:
description.append(
f"Déconnection de {disconnections['count']} lignes "
f"Disconnection of {disconnections['count']} lines "
f"({disconnections['powerlines']})"
)

# swtich line status
swith_line_impact = impact["switch_line"]
if swith_line_impact["changed"]:
kpis["type_of_the_reco"] = (
"Topologique" # pour renvoyer le kpi type_of_the_reco
"Topological" # pour renvoyer le kpi type_of_the_reco
)
title.append("Parade topologique: changer l'état d'une ligne")
title.append("Topological: change a line state")
description.append(
f"Changer le statut de {swith_line_impact['count']} lignes "
f"Change the state of {swith_line_impact['count']} lines "
f"({swith_line_impact['powerlines']})"
)

# topology
bus_switch_impact = impact["topology"]["bus_switch"]
if len(bus_switch_impact) > 0:
kpis["type_of_the_reco"] = (
"Topologique" # pour renvoyer le kpi type_of_the_reco
"Topological" # pour renvoyer le kpi type_of_the_reco
)
title.append(
"Parade topologique: prise de schéma au poste "
"Topological recommendation: Schematic acquisition at substation "
+ str(bus_switch_impact["substation"])
)
description.append("Changement de bus:")
description.append("Busbar change:")
for switch in bus_switch_impact:
description.append(
f"\t \t - Switch bus de {switch['object_type']} id "
f"{switch['object_id']} [au poste {switch['substation']}]"
f"\t \t - Switch bus of {switch['object_type']} id "
f"{switch['object_id']} [at station {switch['substation']}]"
)

assigned_bus_impact = impact["topology"]["assigned_bus"]
disconnect_bus_impact = impact["topology"]["disconnect_bus"]
if len(assigned_bus_impact) > 0 or len(disconnect_bus_impact) > 0:
kpis["type_of_the_reco"] = (
"Topologique" # pour renvoyer le kpi type_of_the_reco
"Topological" # pour renvoyer le kpi type_of_the_reco
)
title.append(
"Parade topologique: prise de schéma au poste "
"Topological recommendation: Schematic acquisition at substation "
+ str(assigned_bus_impact[0]["substation"])
)
if assigned_bus_impact:
Expand All @@ -320,7 +320,7 @@ def get_parade_info(self, act):
description.append(", ")
cpt = 1
description.append(
f" Assigner le bus {assigned['bus']} à "
f" Assign bus {assigned['bus']} to "
f"{assigned['object_type']} id {assigned['object_id']}"
)
if disconnect_bus_impact:
Expand All @@ -331,20 +331,20 @@ def get_parade_info(self, act):
description.append(", ")
cpt = 1
description.append(
f"Déconnecter {disconnected['object_type']} avec l'id "
f"{disconnected['object_id']} [au niveau du poste "
f"Disconnect {disconnected['object_type']} with id "
f"{disconnected['object_id']} [at the substation level "
f"{disconnected['substation']}]"
)

# Any of the above cases,
# then the recommendation is most likely "Do nothing"
if not title and act == self.action_do_nothing:
kpis["type_of_the_reco"] = (
"Ne rien faire" # pour renvoyer le kpi type_of_the_reco
"Do nothing" # pour renvoyer le kpi type_of_the_reco
)
title.append("Poursuivre")
description.append(
"Poursuite du scénario sans intervention extérieur"
"Continuation of the scenario without operator action"
)

title = "".join(title)
Expand Down
8 changes: 4 additions & 4 deletions backend/recommendation-service/resources/PowerGrid/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ def get_onto_recommendation(self, event_line):
"""
# Default output
output_json = {
"title": "Parade ontologique par defaut",
"title": "Default Ontology Recommendation",
"description": (
"Aucune recommandation n’a pu être générée car cette surcharge "
"n’a jamais été observée dans le passé"
"No recommendation has been found for this overload, because "
"it has not been observed in the past."
),
"use_case": "PowerGrid",
"agent_type": AgentType.onto.name,
Expand Down Expand Up @@ -198,7 +198,7 @@ def get_onto_recommendation(self, event_line):
)[0][0]
output_json = {
"title": recommendation,
"description": f"Cette parade a été rencontrée {nb_similar_situations} fois dans le passé.",
"description": f"This pattern has been observed {nb_similar_situations} times in the past.",
"use_case": "PowerGrid",
"agent_type": AgentType.onto.name,
"actions": [action_dict],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# backend/recommendation-service/resources/RTE/manager.py
# backend/recommendation-service/resources/Railway/manager.py

from api.manager.base_manager import BaseRecommendationManager

Expand Down
2 changes: 1 addition & 1 deletion config/dev/cab-standalone/docker-compose.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ fi
echo "HOST_IP=${HOST_IP}" >> .env

cat .env
docker-compose up -d
docker compose up -d
2 changes: 1 addition & 1 deletion config/dev/cab-standalone/nginx-cors-permissive.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# docker-compose DNS used to resolved keycloak services
# docker compose DNS used to resolved keycloak services
resolver 127.0.0.11 ipv6=off;
server {
listen 80;
Expand Down
2 changes: 1 addition & 1 deletion config/dev/cab-standalone/nginx-kubernetes.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# docker-compose DNS used to resolved users service
# docker compose DNS used to resolved users service
# resolver 127.0.0.11 ipv6=off;

# Log format to have msec in time + request processing time
Expand Down
2 changes: 1 addition & 1 deletion config/dev/cab-standalone/nginx.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# docker-compose DNS used to resolved users service
# docker compose DNS used to resolved users service
resolver 127.0.0.11 ipv6=off;

# Log format to have msec in time + request processing time
Expand Down
2 changes: 1 addition & 1 deletion config/dev/cab-standalone/stopOpfab.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/bash

docker-compose down -v
docker compose down -v
2 changes: 1 addition & 1 deletion config/dev/recommendation-service/docker-compose.bash
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ fi

echo "HOST_IP=${HOST_IP}" > .env

docker-compose -f "docker-compose-recommendation-service.yml" up --build
docker compose -f "docker-compose-recommendation-service.yml" up --build
2 changes: 1 addition & 1 deletion config/dev/recommendation-service/nginx.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# docker-compose DNS used to resolved keycloak services
# docker compose DNS used to resolved keycloak services
resolver 127.0.0.11 ipv6=off;
server {
listen 80;
Expand Down
Loading