API résumé et gestion
Creado el: 17 de enero de 2025
Creado el: 17 de enero de 2025
Debugging the issue with API calls, I noticed the "api_key" should go into the query parameters, not the header. This tweak should prevent errors from wrongful endpoint or key issues.
Crafting the request
I’m thinking through how to adjust the API key location in headers or query parameters. The goal is to align the request with NASA's requirements to avoid 403 errors.
Allocating API key
I’m noting that unifying the API key's location to optional parameters ensures it’s always a query parameter, making it compatible with NASA's endpoint requirements.
Integrating API key
Aligning the API key with optional parameters for consistent query parameter inclusion, ensuring seamless integration with NASA's endpoints. This leads me to consider more efficient approaches.
Tweaking the approach
Merging parameters, excluding headers for API keys, and preserving 'Accept: application/json' headers. Considering fallback strategies for non-existent endpoints might be next on the agenda.
Refining parameter methods
I’m streamlining api_key
handling, ensuring it’s a query parameter for NASA's endpoints. Also, I’m setting up unified parameter treatment and maintaining custom handling for api_key
.
Thought about improving API integration pendant 28 secondes
Voici une version améliorée du script qui prend en compte les spécificités de l’API de la NASA, notamment le fait que l’api_key
doit être transmise sous forme de paramètre de requête (query parameter) et non dans les headers. J’ai également ajouté quelques garde-fous pour éviter que le menu principal ne plante si l’utilisateur saisit une valeur non prévue (par exemple taper la clé d’API au lieu d’un chiffre).
Principales améliorations apportées :
Gestion de l'API key :
auth_info["key_name"]
est "api_key"
, alors on force son utilisation en tant que paramètre de requête."Authorization"
(ou autre) et que auth_info["type"]
contient "Bearer"
ou "Basic"
, on la passe dans les headers.Fusion des paramètres :
api_key
mais n’existe pas sous la forme /{api_key}
dans l’URL, on le passera en query param.Robustesse du menu :
Affichage minimal quand on fait un test d’endpoint :
Optionnel : Ajout d’un header "Accept": "application/json"
par défaut (peut être enlevé si non nécessaire).
import sys import json import requests from groq import Groq # Stockage en mémoire des APIs (à adapter au besoin) apis_configurees = [] # Paramètre : longueur max avant de résumer MAX_CHARS_DIRECT_ANALYSIS = 3000 # --------------------------------------------------------------------------- # 1. Fonction de résumé (texte brut) si la doc est trop volumineuse def summarizer_groq(long_text, groq_api_key="gsk_XXXXXXXXXXXXXXX"): """ Découpe la documentation en chunks et génère un résumé global en langage naturel. Ceci n'utilise pas le mode JSON : on veut juste un résumé texte. """ client = Groq(api_key=groq_api_key) chunk_size = 3000 chunks = [] start = 0 while start < len(long_text): end = start + chunk_size chunks.append(long_text[start:end]) start = end summaries = [] for i, chunk_text in enumerate(chunks, start=1): print(f"[INFO] Résumé du chunk {i}/{len(chunks)}...") messages = [ { "role": "system", "content": ( "Tu es un assistant chargé de résumer la documentation d'API. " "Retourne un résumé concis (en langage courant) du texte fourni. " "Ne génère pas de JSON, pas de code, juste un texte résumé clair." ) }, { "role": "user", "content": chunk_text } ] try: completion = client.chat.completions.create( messages=messages, model="llama-3.3-70b-versatile", temperature=0.2, max_completion_tokens=1024, top_p=1, stream=False ) summary_text = completion.choices[0].message.content.strip() summaries.append(summary_text) except Exception as e: print(f"[ERREUR] Impossible de résumer le chunk {i} : {e}") summaries.append("") combined_summary = "\n".join(summaries) print(f"[INFO] Résumé global généré. Longueur du résumé : {len(combined_summary)} caractères.") return combined_summary # --------------------------------------------------------------------------- # 2. Fonction d'analyse finale en JSON def analyser_api_avec_groq(input_text, groq_api_key="gsk_XXXXXXXXXXXXXXXXXXX"): """ 1) Si le texte est trop long, on génère un résumé texte via summarizer_groq. 2) On passe ensuite (ou directement) le texte final dans un prompt qui demande un JSON strict, avec le schéma suivant : { "api_type": "REST | GraphQL | SOAP", "base_url": "string", "auth_info": { "type": "string (Bearer, Basic, etc.)", "key_name": "string", "required": true/false }, "common_headers": { "Header-Name": "Header-Value" }, "endpoints": [ { "name": "string", "path": "string", "method": "GET | POST | ...", "required_params": ["param1", "param2"], "optional_params": ["param1", "param2"], "description": "string" } ], "suggested_name": "string", "rate_limit": "string", "confidence_score": 0.0 } """ # 1) Résume si trop volumineux text_for_analysis = input_text if len(input_text) > MAX_CHARS_DIRECT_ANALYSIS: print("[INFO] La documentation est trop volumineuse, on génère un résumé d’abord.") text_for_analysis = summarizer_groq(input_text, groq_api_key=groq_api_key) # 2) Appel Chat Completion en mode JSON client = Groq(api_key=groq_api_key) messages = [ { "role": "system", "content": ( "Tu es un assistant d'analyse d'API, tu dois répondre UNIQUEMENT en JSON valide, sans code superflu. " "Respecte le schéma JSON suivant (toutes les clés doivent exister) :\n\n" "{\n" " \"api_type\": \"REST | GraphQL | SOAP\",\n" " \"base_url\": \"string\",\n" " \"auth_info\": {\n" " \"type\": \"string (Bearer, Basic, etc.)\",\n" " \"key_name\": \"string\",\n" " \"required\": true/false\n" " },\n" " \"common_headers\": {\n" " \"Header-Name\": \"Header-Value\"\n" " },\n" " \"endpoints\": [\n" " {\n" " \"name\": \"string\",\n" " \"path\": \"string\",\n" " \"method\": \"GET | POST | ...\",\n" " \"required_params\": [\"param1\", \"param2\"],\n" " \"optional_params\": [\"param1\", \"param2\"],\n" " \"description\": \"string\"\n" " }\n" " ],\n" " \"suggested_name\": \"string\",\n" " \"rate_limit\": \"string\",\n" " \"confidence_score\": 0.0\n" "}\n\n" "Analyse le texte ci-après et renvoie STRICTEMENT un JSON conforme, sans texte additionnel." ) }, { "role": "user", "content": text_for_analysis } ] try: chat_completion = client.chat.completions.create( messages=messages, model="llama-3.3-70b-versatile", temperature=0.0, max_completion_tokens=1024, top_p=1, stream=False, response_format={"type": "json_object"} ) content = chat_completion.choices[0].message.content data = json.loads(content) return data except Exception as e: print(f"[ERREUR] Échec de la conversion en JSON : {e}") print("[INFO] Nous allons retourner un squelette par défaut.") return { "api_type": None, "base_url": None, "auth_info": { "type": None, "key_name": None, "required": False }, "common_headers": {}, "endpoints": [], "suggested_name": "API_Inconnue", "rate_limit": None, "confidence_score": 0.0 } # --------------------------------------------------------------------------- # 3. Fonctions du menu def ajouter_api(): print("=== Ajout d’une nouvelle API ===") # On ne se sert pas de la réponse (o/n) ici, mais on le laisse par cohérence. _ = input("Voulez-vous coller la documentation de l’API ? (o/n) : ") print("Collez la documentation ci-dessous (finissez par un skibidi vide) :") lignes_doc = [] while True: ligne = input() if ligne == "skibidi": break lignes_doc.append(ligne) doc_api = "\n".join(lignes_doc) # Simple fallback si rien n’est fourni if not doc_api.strip(): doc_api = "Pas d'information sur l'API." # Appel Groq pour analyse resultats_analyse = analyser_api_avec_groq(doc_api, groq_api_key="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") confidence_score = resultats_analyse.get("confidence_score", 0.0) if confidence_score < 0.5: print( "Le score de confiance de l’analyse est faible. " "Les informations retournées peuvent être partielles ou incorrectes.\n" ) # On lit les champs api_type = resultats_analyse.get("api_type") base_url = resultats_analyse.get("base_url") auth_info = resultats_analyse.get("auth_info", {}) common_headers = resultats_analyse.get("common_headers", {}) endpoints = resultats_analyse.get("endpoints", []) suggested_name = resultats_analyse.get("suggested_name", "NouvelleAPI") rate_limit = resultats_analyse.get("rate_limit") print(f"Nom suggéré pour l’API : {suggested_name}") nom_api = input(f"Entrez le nom de l’API ({suggested_name}) : ") if not nom_api.strip(): nom_api = suggested_name # Si auth_info.required == True, on demande la clé api_key_value = None if auth_info.get("required", False): api_key_value = input("Entrez la valeur de la clé API ou du token : ") # Construction de la config nouvelle_api = { "name": nom_api, "type": api_type, "base_url": base_url, "auth_info": auth_info, "api_key_value": api_key_value, "common_headers": common_headers, "endpoints": endpoints, "rate_limit": rate_limit } apis_configurees.append(nouvelle_api) print(f"\n[OK] L’API '{nom_api}' a été ajoutée.") print(f" - Type : {api_type}") print(f" - URL de base : {base_url}\n") def lister_apis(): print("=== Liste des API configurées ===") if not apis_configurees: print("Aucune API n’est configurée.\n") return for i, api in enumerate(apis_configurees, start=1): print(f"{i}. Nom : {api['name']}") print(f" URL de base : {api['base_url'] or 'Non renseignée'}") print(f" Type : {api['type'] or 'Inconnu'}") if api["rate_limit"]: print(f" Limitation de débit : {api['rate_limit']}") if api["endpoints"]: print(" Endpoints :") for ep in api["endpoints"]: print(f" - {ep['name']} ({ep['method']} {ep['path']})") else: print(" Aucun endpoint configuré.") print() print() def modifier_api(): print("=== Modification d’une API ===") if not apis_configurees: print("Aucune API n’est configurée.\n") return # On affiche la liste for i, api in enumerate(apis_configurees, start=1): print(f"{i}. {api['name']}") choix = input("Quelle API souhaitez-vous modifier ? (nom ou numéro) : ") api_choisie = None # On tente un cast en int pour voir si c’est un numéro try: idx = int(choix) - 1 if 0 <= idx < len(apis_configurees): api_choisie = apis_configurees[idx] except ValueError: # Sinon, on tente par nom for a in apis_configurees: if a["name"].lower() == choix.lower(): api_choisie = a break if not api_choisie: print("API introuvable.\n") return print(f"\nAPI sélectionnée : {api_choisie['name']}") print("Que voulez-vous modifier ? (ex: 'changer l'URL de base', 'changer la clé API', " "'ajouter un endpoint', 'modifier un endpoint', 'supprimer un endpoint')") action = input("> ").lower() if "url" in action: new_url = input("Nouvelle URL de base : ") api_choisie["base_url"] = new_url print("URL de base modifiée.\n") if "clé" in action or "key" in action: new_key = input("Nouvelle clé API : ") api_choisie["api_key_value"] = new_key print("Clé API modifiée.\n") if "ajouter un endpoint" in action: print("=== Ajout d'un endpoint ===") nom_ep = input("Nom du nouvel endpoint : ") path_ep = input("Chemin (ex: /playlists/{playlist_id}/tracks) : ") methode = input("Méthode HTTP (GET, POST, etc.) : ") params_req = [p.strip() for p in input("Paramètres requis (séparés par des virgules) : ").split(",") if p.strip()] params_opt = [p.strip() for p in input("Paramètres optionnels (séparés par des virgules) : ").split(",") if p.strip()] new_ep = { "name": nom_ep, "path": path_ep, "method": methode.upper(), "required_params": params_req, "optional_params": params_opt, "description": "" } api_choisie["endpoints"].append(new_ep) print(f"Endpoint '{nom_ep}' ajouté.\n") if "modifier un endpoint" in action: print("=== Modification d'un endpoint ===") if not api_choisie["endpoints"]: print("Aucun endpoint à modifier.") return for i, ep in enumerate(api_choisie["endpoints"], start=1): print(f"{i}. {ep['name']} ({ep['method']} {ep['path']})") choix_ep = input("Quel endpoint ? (numéro ou nom) : ") ep_choisi = None try: eidx = int(choix_ep) - 1 if 0 <= eidx < len(api_choisie["endpoints"]): ep_choisi = api_choisie["endpoints"][eidx] except ValueError: for ep in api_choisie["endpoints"]: if ep["name"].lower() == choix_ep.lower(): ep_choisi = ep break if not ep_choisi: print("Endpoint introuvable.") return print(f"Endpoint sélectionné : {ep_choisi['name']}") modif_ep = input("Que voulez-vous faire ? (ex: 'ajouter un paramètre', 'changer la méthode', etc.) : ").lower() if "ajouter un paramètre" in modif_ep: nom_p = input("Nom du paramètre : ") r_o = input("Paramètre requis ou optionnel ? (r/o) : ") if r_o.lower() == 'r': ep_choisi["required_params"].append(nom_p) else: ep_choisi["optional_params"].append(nom_p) print(f"Paramètre '{nom_p}' ajouté.\n") # D’autres modifications possibles... if "supprimer un endpoint" in action: print("=== Suppression d'un endpoint ===") if not api_choisie["endpoints"]: print("Aucun endpoint à supprimer.") return for i, ep in enumerate(api_choisie["endpoints"], start=1): print(f"{i}. {ep['name']} ({ep['method']} {ep['path']})") choix_ep = input("Quel endpoint supprimer ? (numéro ou nom) : ") ep_asup = None try: eidx = int(choix_ep) - 1 if 0 <= eidx < len(api_choisie["endpoints"]): ep_asup = api_choisie["endpoints"][eidx] except ValueError: for ep in api_choisie["endpoints"]: if ep["name"].lower() == choix_ep.lower(): ep_asup = ep break if ep_asup: api_choisie["endpoints"].remove(ep_asup) print(f"Endpoint '{ep_asup['name']}' supprimé.") else: print("Endpoint introuvable.") print("Modification(s) terminée(s).\n") def supprimer_api(): print("=== Suppression d’une API ===") if not apis_configurees: print("Aucune API n’est configurée.\n") return for i, api in enumerate(apis_configurees, start=1): print(f"{i}. {api['name']}") choix = input("Quelle API supprimer ? (nom ou numéro) : ") api_asup = None try: idx = int(choix) - 1 if 0 <= idx < len(apis_configurees): api_asup = apis_configurees[idx] except ValueError: for a in apis_configurees: if a["name"].lower() == choix.lower(): api_asup = a break if not api_asup: print("API introuvable.\n") return confirm = input(f"Êtes-vous sûr de vouloir supprimer '{api_asup['name']}' ? (o/n) : ") if confirm.lower() == 'o': apis_configurees.remove(api_asup) print(f"L’API '{api_asup['name']}' a été supprimée.\n") else: print("Opération annulée.\n") def tester_endpoint(): print("=== Tester un endpoint ===") if not apis_configurees: print("Aucune API n’est configurée.\n") return for i, api in enumerate(apis_configurees, start=1): print(f"{i}. {api['name']}") choix = input("Sur quelle API tester un endpoint ? (nom ou numéro) : ") api_choisie = None try: idx = int(choix) - 1 if 0 <= idx < len(apis_configurees): api_choisie = apis_configurees[idx] except ValueError: for a in apis_configurees: if a["name"].lower() == choix.lower(): api_choisie = a break if not api_choisie: print("API introuvable.\n") return if not api_choisie["endpoints"]: print("Cette API n’a aucun endpoint configuré.\n") return print(f"Endpoints disponibles pour {api_choisie['name']} :") for i, ep in enumerate(api_choisie["endpoints"], start=1): print(f"{i}. {ep['name']} ({ep['method']} {ep['path']})") choix_ep = input("Quel endpoint tester ? (nom ou numéro) : ") ep_choisi = None try: eidx = int(choix_ep) - 1 if 0 <= eidx < len(api_choisie["endpoints"]): ep_choisi = api_choisie["endpoints"][eidx] except ValueError: for ep in api_choisie["endpoints"]: if ep["name"].lower() == choix_ep.lower(): ep_choisi = ep break if not ep_choisi: print("Endpoint introuvable.") return # On va demander les valeurs pour chaque param requis required_params_values = {} for rp in ep_choisi.get("required_params", []): val = input(f"Entrez la valeur pour '{rp}' : ") required_params_values[rp] = val # On va demander les valeurs pour chaque param optionnel optional_params_values = {} for op in ep_choisi.get("optional_params", []): val = input(f"Valeur pour '{op}' (laisser vide si aucun) : ") if val: optional_params_values[op] = val # Préparation de l’URL base_url = api_choisie.get("base_url") or "" path = ep_choisi["path"] # Remplacement dans le path si on a des placeholders {param} # et retrait du param si on a pu le remplacer query_params = {} for k, v in required_params_values.items(): placeholder = f"{{{k}}}" if placeholder in path: path = path.replace(placeholder, v) else: # S'il n'y a pas de placeholder correspondant, on le mettra en query param query_params[k] = v # Tous les optionnels vont en query params query_params.update(optional_params_values) # Gestion de l’authentification headers = dict(api_choisie.get("common_headers", {})) # Ex : on force "Accept: application/json" par défaut (ou non) if "Accept" not in headers: headers["Accept"] = "application/json" auth_info = api_choisie.get("auth_info", {}) api_key_value = api_choisie.get("api_key_value") if auth_info.get("required") and api_key_value: # Exemple : si c’est un Bearer if auth_info["type"] and "bearer" in auth_info["type"].lower(): key_name = auth_info.get("key_name", "Authorization") headers[key_name] = f"Bearer {api_key_value}" # Exemple : si c’est un Basic elif auth_info["type"] and "basic" in auth_info["type"].lower(): key_name = auth_info.get("key_name", "Authorization") headers[key_name] = f"Basic {api_key_value}" else: # Si key_name = "api_key", NASA veut un query param "api_key=XXXX" if auth_info.get("key_name", "").lower() == "api_key": query_params["api_key"] = api_key_value else: # Sinon on le place dans un header key_name = auth_info.get("key_name", "X-API-Key") headers[key_name] = api_key_value # Construction de l'URL finale url = base_url + path method = ep_choisi["method"].upper() print(f"\nTest de l'endpoint : {method} {url}") print(f"En-têtes : {headers}") print(f"Paramètres : {query_params}\n") try: if method == "GET": r = requests.get(url, headers=headers, params=query_params) elif method == "POST": # On envoie ici en JSON, à adapter si l’API l’exige autrement r = requests.post(url, headers=headers, json=query_params) elif method == "PUT": r = requests.put(url, headers=headers, json=query_params) elif method == "DELETE": r = requests.delete(url, headers=headers, json=query_params) else: print("Méthode non gérée.") return print(f"Statut : {r.status_code}") # Tentative de parsing JSON try: print("Réponse JSON :") print(json.dumps(r.json(), indent=2)) except: print("Réponse brute :") print(r.text) except Exception as e: print(f"Erreur lors de l’appel : {e}") def main(): while True: print("=== Menu Principal ===") print("1. Ajouter une API (assisté par IA)") print("2. Liste des API configurées") print("3. Modifier une API") print("4. Supprimer une API") print("5. Tester un endpoint") print("6. Quitter") choix = input("Choisissez une option : ").strip() # On essaie de caster en int pour éviter l'erreur # s'il tape '5xK9...' par inadvertance try: choix_num = int(choix) except ValueError: print("Choix invalide.\n") continue if choix_num == 1: ajouter_api() elif choix_num == 2: lister_apis() elif choix_num == 3: modifier_api() elif choix_num == 4: supprimer_api() elif choix_num == 5: tester_endpoint() elif choix_num == 6: print("Au revoir.") sys.exit(0) else: print("Choix invalide.\n") if __name__ == "__main__": main()
Transmission de la clé API NASA :
{ "type": "", "key_name": "api_key", "required": true }
api["api_key_value"]
.key_name = "api_key"
. Donc on insère "api_key": "<LA_CLE>"
dans query_params
.?api_key=<LA_CLE>
.Placeholders dans le path (ex. "/neo/rest/v1/neo/{asteroid_id}"
) :
asteroid_id
, on remplace "{asteroid_id}"
dans l’URL par la valeur saisie.Robustesse du menu :
try/except ValueError
pour s’assurer que la saisie est bien un entier. Si ce n’est pas le cas, on réaffiche le menu.En-tête Accept: application/json
ajouté par défaut, pour faciliter le parsing des réponses JSON.
En l’état, ce script devrait mieux gérer le cas de la NASA (et d’autres APIs) et éviter l’erreur No api_key was supplied
puisque la clé sera maintenant passée dans la query string.
Enriching input validation
I'm thinking of enhancing input validation in the main menu by using regex to identify and manage non-integer or unclear inputs, like '?' or phrases in another language.
Validating input robustness
I’m outlining a Python function to sequentially trim input, check for help requests, and parse it as an integer. If the integer isn't within 1 to 6, an error message is shown.
Enhancing input robustness
Crafting a function to read user choices between 1 and 6, retrying if invalid or on help requests, ensures reliable input validation for various commands.
Thought about API type verification pendant 18 secondes
Voici une version améliorée qui effectue une vérification plus stricte des choix dans le menu principal (et dans d’autres parties), notamment pour éviter qu’un simple point d’interrogation ou une saisie non numérique provoque une erreur ou soit interprété comme un entier.
Fonction lire_int_in_range
:
Utilisation de lire_int_in_range
:
main()
), au lieu de faire un simple input() + try/except ValueError
, on appelle lire_int_in_range
.Exemple d’aide si user_input == '?'
:
Validation du type d’API (optionnel) :
api_type
est bien "REST"
, "GraphQL"
ou "SOAP"
quand Groq vous le fournit (ou quand vous le modifiez). Mais cela dépend de votre usage : ce n’est pas toujours nécessaire.import sys import json import requests from groq import Groq # Stockage en mémoire des APIs (à adapter au besoin) apis_configurees = [] # Paramètre : longueur max avant de résumer MAX_CHARS_DIRECT_ANALYSIS = 3000 # --------------------------------------------------------------------------- # 1. Fonction de résumé (texte brut) si la doc est trop volumineuse def summarizer_groq(long_text, groq_api_key="gsk_XXXXXXXXXXXXXXX"): """ Découpe la documentation en chunks et génère un résumé global en langage naturel. Ceci n'utilise pas le mode JSON : on veut juste un résumé texte. """ client = Groq(api_key=groq_api_key) chunk_size = 3000 chunks = [] start = 0 while start < len(long_text): end = start + chunk_size chunks.append(long_text[start:end]) start = end summaries = [] for i, chunk_text in enumerate(chunks, start=1): print(f"[INFO] Résumé du chunk {i}/{len(chunks)}...") messages = [ { "role": "system", "content": ( "Tu es un assistant chargé de résumer la documentation d'API. " "Retourne un résumé concis (en langage courant) du texte fourni. " "Ne génère pas de JSON, pas de code, juste un texte résumé clair." ) }, { "role": "user", "content": chunk_text } ] try: completion = client.chat.completions.create( messages=messages, model="llama-3.3-70b-versatile", temperature=0.2, max_completion_tokens=1024, top_p=1, stream=False ) summary_text = completion.choices[0].message.content.strip() summaries.append(summary_text) except Exception as e: print(f"[ERREUR] Impossible de résumer le chunk {i} : {e}") summaries.append("") combined_summary = "\n".join(summaries) print(f"[INFO] Résumé global généré. Longueur du résumé : {len(combined_summary)} caractères.") return combined_summary # --------------------------------------------------------------------------- # 2. Fonction d'analyse finale en JSON def analyser_api_avec_groq(input_text, groq_api_key="gsk_XXXXXXXXXXXXXXXXXXX"): """ 1) Si le texte est trop long, on génère un résumé texte via summarizer_groq. 2) On passe ensuite (ou directement) le texte final dans un prompt qui demande un JSON strict (voir schéma ci-dessous). """ # 1) Résume si trop volumineux text_for_analysis = input_text if len(input_text) > MAX_CHARS_DIRECT_ANALYSIS: print("[INFO] La documentation est trop volumineuse, on génère un résumé d’abord.") text_for_analysis = summarizer_groq(input_text, groq_api_key=groq_api_key) # 2) Appel Chat Completion en mode JSON client = Groq(api_key=groq_api_key) messages = [ { "role": "system", "content": ( "Tu es un assistant d'analyse d'API, tu dois répondre UNIQUEMENT en JSON valide, sans code superflu. " "Respecte le schéma JSON suivant (toutes les clés doivent exister) :\n\n" "{\n" " \"api_type\": \"REST | GraphQL | SOAP\",\n" " \"base_url\": \"string\",\n" " \"auth_info\": {\n" " \"type\": \"string (Bearer, Basic, etc.)\",\n" " \"key_name\": \"string\",\n" " \"required\": true/false\n" " },\n" " \"common_headers\": {\n" " \"Header-Name\": \"Header-Value\"\n" " },\n" " \"endpoints\": [\n" " {\n" " \"name\": \"string\",\n" " \"path\": \"string\",\n" " \"method\": \"GET | POST | ...\",\n" " \"required_params\": [\"param1\", \"param2\"],\n" " \"optional_params\": [\"param1\", \"param2\"],\n" " \"description\": \"string\"\n" " }\n" " ],\n" " \"suggested_name\": \"string\",\n" " \"rate_limit\": \"string\",\n" " \"confidence_score\": 0.0\n" "}\n\n" "Analyse le texte ci-après et renvoie STRICTEMENT un JSON conforme, sans texte additionnel." ) }, { "role": "user", "content": text_for_analysis } ] try: chat_completion = client.chat.completions.create( messages=messages, model="llama-3.3-70b-versatile", temperature=0.0, max_completion_tokens=1024, top_p=1, stream=False, response_format={"type": "json_object"} ) content = chat_completion.choices[0].message.content data = json.loads(content) return data except Exception as e: print(f"[ERREUR] Échec de la conversion en JSON : {e}") print("[INFO] Nous allons retourner un squelette par défaut.") return { "api_type": None, "base_url": None, "auth_info": { "type": None, "key_name": None, "required": False }, "common_headers": {}, "endpoints": [], "suggested_name": "API_Inconnue", "rate_limit": None, "confidence_score": 0.0 } # --------------------------------------------------------------------------- # 3. FONCTIONS D’OUTIL POUR L’INTERFACE def lire_int_in_range(prompt: str, min_val: int, max_val: int) -> int: """ Demande à l'utilisateur de saisir un entier dans la plage [min_val..max_val]. Si l'utilisateur saisit '?', on affiche un petit message d'aide (personnalisable). Répète la question tant que la valeur n'est pas un entier correct. Retourne l'entier validé. """ while True: saisie = input(prompt).strip() # Exemple de gestion d'un '?' ou autre commande spéciale if saisie == "?": print("Aide : veuillez saisir un entier compris entre " f"{min_val} et {max_val}.") print("Tapez un nombre valide, ou Ctrl+C pour annuler.") continue # Sinon, on tente la conversion try: val = int(saisie) if min_val <= val <= max_val: return val else: print(f"Choix invalide (doit être entre {min_val} et {max_val}).\n") except ValueError: print("Choix invalide (entrez un nombre). Réessayez.\n") def selectionner_api() -> dict: """ Affiche la liste des API configurées, demande à l'utilisateur d'en choisir une par numéro. Retourne le dict correspondant ou None si aucune API. """ if not apis_configurees: print("Aucune API n’est configurée.\n") return None print("=== Liste des APIs disponibles ===") for i, api in enumerate(apis_configurees, start=1): print(f"{i}. {api['name']}") # On lit un choix (numéro) dans la plage [1..len(apis_configurees)] choix_num = lire_int_in_range( prompt=f"Quelle API souhaitez-vous sélectionner ? [1..{len(apis_configurees)}] : ", min_val=1, max_val=len(apis_configurees) ) return apis_configurees[choix_num - 1] def selectionner_endpoint(api: dict) -> dict: """ Affiche la liste des endpoints pour l'API donnée, demande à l'utilisateur d'en choisir un par numéro. Retourne le dict correspondant ou None si aucun endpoint. """ if not api["endpoints"]: print("Cette API n’a aucun endpoint configuré.\n") return None print(f"Endpoints disponibles pour {api['name']} :") for i, ep in enumerate(api["endpoints"], start=1): print(f"{i}. {ep['name']} ({ep['method']} {ep['path']})") choix_ep_num = lire_int_in_range( prompt=f"Quel endpoint tester ? [1..{len(api['endpoints'])}] : ", min_val=1, max_val=len(api["endpoints"]) ) return api["endpoints"][choix_ep_num - 1] # --------------------------------------------------------------------------- # 4. Fonctions du menu def ajouter_api(): print("=== Ajout d’une nouvelle API ===") _ = input("Voulez-vous coller la documentation de l’API ? (o/n) : ") print("Collez la documentation ci-dessous (finissez par un skibidi vide) :") lignes_doc = [] while True: ligne = input() if ligne == "skibidi": break lignes_doc.append(ligne) doc_api = "\n".join(lignes_doc) if not doc_api.strip(): doc_api = "Pas d'information sur l'API." # Appel Groq pour analyse resultats_analyse = analyser_api_avec_groq(doc_api, groq_api_key="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") confidence_score = resultats_analyse.get("confidence_score", 0.0) if confidence_score < 0.5: print( "Le score de confiance de l’analyse est faible. " "Les informations retournées peuvent être partielles ou incorrectes.\n" ) # Champs retournés api_type = resultats_analyse.get("api_type") base_url = resultats_analyse.get("base_url") auth_info = resultats_analyse.get("auth_info", {}) common_headers = resultats_analyse.get("common_headers", {}) endpoints = resultats_analyse.get("endpoints", []) suggested_name = resultats_analyse.get("suggested_name", "NouvelleAPI") rate_limit = resultats_analyse.get("rate_limit") print(f"Nom suggéré pour l’API : {suggested_name}") nom_api = input(f"Entrez le nom de l’API ({suggested_name}) : ") if not nom_api.strip(): nom_api = suggested_name # Si auth_info.required == True, on demande la clé api_key_value = None if auth_info.get("required", False): api_key_value = input("Entrez la valeur de la clé API ou du token : ") # Construction de la config nouvelle_api = { "name": nom_api, "type": api_type, "base_url": base_url, "auth_info": auth_info, "api_key_value": api_key_value, "common_headers": common_headers, "endpoints": endpoints, "rate_limit": rate_limit } apis_configurees.append(nouvelle_api) print(f"\n[OK] L’API '{nom_api}' a été ajoutée.") print(f" - Type : {api_type}") print(f" - URL de base : {base_url}\n") def lister_apis(): print("=== Liste des API configurées ===") if not apis_configurees: print("Aucune API n’est configurée.\n") return for i, api in enumerate(apis_configurees, start=1): print(f"{i}. Nom : {api['name']}") print(f" URL de base : {api['base_url'] or 'Non renseignée'}") print(f" Type : {api['type'] or 'Inconnu'}") if api["rate_limit"]: print(f" Limitation de débit : {api['rate_limit']}") if api["endpoints"]: print(" Endpoints :") for ep in api["endpoints"]: print(f" - {ep['name']} ({ep['method']} {ep['path']})") else: print(" Aucun endpoint configuré.") print() print() def modifier_api(): print("=== Modification d’une API ===") api_choisie = selectionner_api() if not api_choisie: return print(f"\nAPI sélectionnée : {api_choisie['name']}") print("Que voulez-vous modifier ? (ex: 'changer l'URL de base', 'changer la clé API', " "'ajouter un endpoint', 'modifier un endpoint', 'supprimer un endpoint')") action = input("> ").lower() if "url" in action: new_url = input("Nouvelle URL de base : ") api_choisie["base_url"] = new_url print("URL de base modifiée.\n") if "clé" in action or "key" in action: new_key = input("Nouvelle clé API : ") api_choisie["api_key_value"] = new_key print("Clé API modifiée.\n") if "ajouter un endpoint" in action: print("=== Ajout d'un endpoint ===") nom_ep = input("Nom du nouvel endpoint : ") path_ep = input("Chemin (ex: /playlists/{playlist_id}/tracks) : ") methode = input("Méthode HTTP (GET, POST, etc.) : ") params_req = [p.strip() for p in input("Paramètres requis (séparés par des virgules) : ").split(",") if p.strip()] params_opt = [p.strip() for p in input("Paramètres optionnels (séparés par des virgules) : ").split(",") if p.strip()] new_ep = { "name": nom_ep, "path": path_ep, "method": methode.upper(), "required_params": params_req, "optional_params": params_opt, "description": "" } api_choisie["endpoints"].append(new_ep) print(f"Endpoint '{nom_ep}' ajouté.\n") if "modifier un endpoint" in action: print("=== Modification d'un endpoint ===") if not api_choisie["endpoints"]: print("Aucun endpoint à modifier.") return print("Liste des endpoints :") for i, ep in enumerate(api_choisie["endpoints"], start=1): print(f"{i}. {ep['name']} ({ep['method']} {ep['path']})") choix_ep = lire_int_in_range( f"Quel endpoint modifier ? [1..{len(api_choisie['endpoints'])}] : ", 1, len(api_choisie["endpoints"]) ) ep_choisi = api_choisie["endpoints"][choix_ep - 1] print(f"Endpoint sélectionné : {ep_choisi['name']}") modif_ep = input("Que voulez-vous faire ? (ex: 'ajouter un paramètre', 'changer la méthode', etc.) : ").lower() if "ajouter un paramètre" in modif_ep: nom_p = input("Nom du paramètre : ") r_o = input("Paramètre requis ou optionnel ? (r/o) : ") if r_o.lower() == 'r': ep_choisi["required_params"].append(nom_p) else: ep_choisi["optional_params"].append(nom_p) print(f"Paramètre '{nom_p}' ajouté.\n") # D’autres modifications possibles... if "supprimer un endpoint" in action: print("=== Suppression d'un endpoint ===") if not api_choisie["endpoints"]: print("Aucun endpoint à supprimer.") return print("Liste des endpoints :") for i, ep in enumerate(api_choisie["endpoints"], start=1): print(f"{i}. {ep['name']} ({ep['method']} {ep['path']})") choix_ep = lire_int_in_range( f"Quel endpoint supprimer ? [1..{len(api_choisie['endpoints'])}] : ", 1, len(api_choisie["endpoints"]) ) ep_asup = api_choisie["endpoints"][choix_ep - 1] api_choisie["endpoints"].remove(ep_asup) print(f"Endpoint '{ep_asup['name']}' supprimé.") print("Modification(s) terminée(s).\n") def supprimer_api(): print("=== Suppression d’une API ===") api_choisie = selectionner_api() if not api_choisie: return confirm = input(f"Êtes-vous sûr de vouloir supprimer '{api_choisie['name']}' ? (o/n) : ") if confirm.lower() == 'o': apis_configurees.remove(api_choisie) print(f"L’API '{api_choisie['name']}' a été supprimée.\n") else: print("Opération annulée.\n") def tester_endpoint(): print("=== Tester un endpoint ===") api_choisie = selectionner_api() if not api_choisie: return ep_choisi = selectionner_endpoint(api_choisie) if not ep_choisi: return # On va demander les valeurs pour chaque param requis required_params_values = {} for rp in ep_choisi.get("required_params", []): val = input(f"Entrez la valeur pour '{rp}' : ") required_params_values[rp] = val # On va demander les valeurs pour chaque param optionnel optional_params_values = {} for op in ep_choisi.get("optional_params", []): val = input(f"Valeur pour '{op}' (laisser vide si aucun) : ") if val: optional_params_values[op] = val # Préparation de l’URL base_url = api_choisie.get("base_url") or "" path = ep_choisi["path"] # Remplacement dans le path si on a des placeholders {param} # et retrait du param si on a pu le remplacer query_params = {} for k, v in required_params_values.items(): placeholder = f"{{{k}}}" if placeholder in path: path = path.replace(placeholder, v) else: # S'il n'y a pas de placeholder correspondant, on le mettra en query param query_params[k] = v # Tous les optionnels vont en query params query_params.update(optional_params_values) # Gestion de l’authentification headers = dict(api_choisie.get("common_headers", {})) if "Accept" not in headers: headers["Accept"] = "application/json" auth_info = api_choisie.get("auth_info", {}) api_key_value = api_choisie.get("api_key_value") if auth_info.get("required") and api_key_value: # Exemple : si c’est un Bearer if auth_info["type"] and "bearer" in auth_info["type"].lower(): key_name = auth_info.get("key_name", "Authorization") headers[key_name] = f"Bearer {api_key_value}" # Exemple : si c’est un Basic elif auth_info["type"] and "basic" in auth_info["type"].lower(): key_name = auth_info.get("key_name", "Authorization") headers[key_name] = f"Basic {api_key_value}" else: # Si key_name = "api_key", NASA veut un query param "api_key=XXXX" if auth_info.get("key_name", "").lower() == "api_key": query_params["api_key"] = api_key_value else: # Sinon on le place dans un header key_name = auth_info.get("key_name", "X-API-Key") headers[key_name] = api_key_value # Construction de l'URL finale url = base_url + path method = ep_choisi["method"].upper() print(f"\nTest de l'endpoint : {method} {url}") print(f"En-têtes : {headers}") print(f"Paramètres : {query_params}\n") try: if method == "GET": r = requests.get(url, headers=headers, params=query_params) elif method == "POST": r = requests.post(url, headers=headers, json=query_params) elif method == "PUT": r = requests.put(url, headers=headers, json=query_params) elif method == "DELETE": r = requests.delete(url, headers=headers, json=query_params) else: print("Méthode non gérée.") return print(f"Statut : {r.status_code}") try: print("Réponse JSON :") print(json.dumps(r.json(), indent=2)) except: print("Réponse brute :") print(r.text) except Exception as e: print(f"Erreur lors de l’appel : {e}") # --------------------------------------------------------------------------- # 5. MAIN def main(): while True: print("=== Menu Principal ===") print("1. Ajouter une API (assisté par IA)") print("2. Liste des API configurées") print("3. Modifier une API") print("4. Supprimer une API") print("5. Tester un endpoint") print("6. Quitter") choix_num = lire_int_in_range("Choisissez une option [1..6] : ", 1, 6) if choix_num == 1: ajouter_api() elif choix_num == 2: lister_apis() elif choix_num == 3: modifier_api() elif choix_num == 4: supprimer_api() elif choix_num == 5: tester_endpoint() elif choix_num == 6: print("Au revoir.") sys.exit(0) if __name__ == "__main__": main()
lire_int_in_range
vous évite de devoir gérer manuellement les try/except
partout. Elle boucle jusqu’à obtenir un entier valide dans la fourchette souhaitée.De cette manière, si l’utilisateur tape un point d’interrogation ou une chaîne non prévue, on ne casse pas le programme : on l’informe juste que c’est invalide, et on le redirige vers une nouvelle saisie.
Mapping out JSON
I’m detailing steps to manage API configurations using a JSON file. I’m focusing on loading, modifying, and saving API data, ensuring persistence.
Ensuring persistence
To maintain data integrity, sauvegarder_apis()
is invoked after each change to APIs or endpoints. Initially, charger_apis()
loads configuration, and import os, pathlib
is considered for file paths.
Thought about API storage in JSON pendant 15 secondes
Voici une version améliorée du script qui sauvegarde la liste des APIs dans un fichier JSON (par exemple apis_configurees.json
). À chaque fois qu’on ajoute, modifie ou supprime une API, on met à jour ce fichier. Ainsi, même si le script est relancé, on retrouve les APIs déjà configurées.
Fichier de configuration :
charger_apis()
et sauvegarder_apis()
.apis_configurees.json
(vous pouvez changer son nom au besoin).Chargement au démarrage :
main()
, on essaie de charger la liste des APIs depuis le fichier.apis_configurees
vide.Sauvegarde après chaque opération :
ajouter_api()
, modifier_api()
, supprimer_api()
, on appelle sauvegarder_apis()
.Format du JSON :
apis_configurees
. Chaque élément est un dictionnaire décrivant l’API, incluant endpoints
, etc.import sys import json import os import requests from groq import Groq # Nom du fichier où l'on stocke les APIs FICHIER_APIS = "apis_configurees.json" # Stockage en mémoire des APIs apis_configurees = [] # Paramètre : longueur max avant de résumer MAX_CHARS_DIRECT_ANALYSIS = 3000 # --------------------------------------------------------------------------- # 1. Fonctions d'IO pour sauvegarder/charger les APIs def sauvegarder_apis(): """Sauvegarde la liste des APIs dans FICHIER_APIS au format JSON.""" try: with open(FICHIER_APIS, "w", encoding="utf-8") as f: json.dump(apis_configurees, f, indent=2, ensure_ascii=False) print(f"[INFO] Liste des APIs sauvegardée dans {FICHIER_APIS}.") except Exception as e: print(f"[ERREUR] Impossible de sauvegarder dans {FICHIER_APIS} : {e}") def charger_apis(): """Charge la liste des APIs depuis FICHIER_APIS (si présent).""" global apis_configurees if os.path.exists(FICHIER_APIS): try: with open(FICHIER_APIS, "r", encoding="utf-8") as f: apis_configurees = json.load(f) print(f"[INFO] {len(apis_configurees)} API(s) chargée(s) depuis {FICHIER_APIS}.") except Exception as e: print(f"[ERREUR] Impossible de lire {FICHIER_APIS} : {e}") apis_configurees = [] else: apis_configurees = [] # --------------------------------------------------------------------------- # 2. Fonction de résumé (texte brut) si la doc est trop volumineuse def summarizer_groq(long_text, groq_api_key="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"): """ Découpe la documentation en chunks et génère un résumé global en langage naturel. Ceci n'utilise pas le mode JSON : on veut juste un résumé texte. """ client = Groq(api_key=groq_api_key) chunk_size = 3000 chunks = [] start = 0 while start < len(long_text): end = start + chunk_size chunks.append(long_text[start:end]) start = end summaries = [] for i, chunk_text in enumerate(chunks, start=1): print(f"[INFO] Résumé du chunk {i}/{len(chunks)}...") messages = [ { "role": "system", "content": ( "Tu es un assistant chargé de résumer la documentation d'API. " "Retourne un résumé concis (en langage courant) du texte fourni. " "Ne génère pas de JSON, pas de code, juste un texte résumé clair." ) }, { "role": "user", "content": chunk_text } ] try: completion = client.chat.completions.create( messages=messages, model="llama-3.3-70b-versatile", temperature=0.2, max_completion_tokens=1024, top_p=1, stream=False ) summary_text = completion.choices[0].message.content.strip() summaries.append(summary_text) except Exception as e: print(f"[ERREUR] Impossible de résumer le chunk {i} : {e}") summaries.append("") combined_summary = "\n".join(summaries) print(f"[INFO] Résumé global généré. Longueur du résumé : {len(combined_summary)} caractères.") return combined_summary # --------------------------------------------------------------------------- # 3. Fonction d'analyse finale en JSON def analyser_api_avec_groq(input_text, groq_api_key="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"): """ 1) Si le texte est trop long, on génère un résumé texte via summarizer_groq. 2) On passe ensuite (ou directement) le texte final dans un prompt qui demande un JSON strict (voir schéma ci-dessous). """ # 1) Résume si trop volumineux text_for_analysis = input_text if len(input_text) > MAX_CHARS_DIRECT_ANALYSIS: print("[INFO] La documentation est trop volumineuse, on génère un résumé d’abord.") text_for_analysis = summarizer_groq(input_text, groq_api_key=groq_api_key) # 2) Appel Chat Completion en mode JSON client = Groq(api_key=groq_api_key) messages = [ { "role": "system", "content": ( "Tu es un assistant d'analyse d'API, tu dois répondre UNIQUEMENT en JSON valide, sans code superflu. " "Respecte le schéma JSON suivant (toutes les clés doivent exister) :\n\n" "{\n" " \"api_type\": \"REST | GraphQL | SOAP\",\n" " \"base_url\": \"string\",\n" " \"auth_info\": {\n" " \"type\": \"string (Bearer, Basic, etc.)\",\n" " \"key_name\": \"string\",\n" " \"required\": true/false\n" " },\n" " \"common_headers\": {\n" " \"Header-Name\": \"Header-Value\"\n" " },\n" " \"endpoints\": [\n" " {\n" " \"name\": \"string\",\n" " \"path\": \"string\",\n" " \"method\": \"GET | POST | ...\",\n" " \"required_params\": [\"param1\", \"param2\"],\n" " \"optional_params\": [\"param1\", \"param2\"],\n" " \"description\": \"string\"\n" " }\n" " ],\n" " \"suggested_name\": \"string\",\n" " \"rate_limit\": \"string\",\n" " \"confidence_score\": 0.0\n" "}\n\n" "Analyse le texte ci-après et renvoie STRICTEMENT un JSON conforme, sans texte additionnel." ) }, { "role": "user", "content": text_for_analysis } ] try: chat_completion = client.chat.completions.create( messages=messages, model="llama-3.3-70b-versatile", temperature=0.0, max_completion_tokens=1024, top_p=1, stream=False, response_format={"type": "json_object"} ) content = chat_completion.choices[0].message.content data = json.loads(content) return data except Exception as e: print(f"[ERREUR] Échec de la conversion en JSON : {e}") print("[INFO] Nous allons retourner un squelette par défaut.") return { "api_type": None, "base_url": None, "auth_info": { "type": None, "key_name": None, "required": False }, "common_headers": {}, "endpoints": [], "suggested_name": "API_Inconnue", "rate_limit": None, "confidence_score": 0.0 } # --------------------------------------------------------------------------- # 4. FONCTIONS D’OUTIL POUR L’INTERFACE def lire_int_in_range(prompt: str, min_val: int, max_val: int) -> int: """ Demande à l'utilisateur de saisir un entier dans la plage [min_val..max_val]. Si l'utilisateur saisit '?', on affiche un petit message d'aide (personnalisable). Répète la question tant que la valeur n'est pas un entier correct. Retourne l'entier validé. """ while True: saisie = input(prompt).strip() # Exemple de gestion d'un '?' ou autre commande spéciale if saisie == "?": print("Aide : veuillez saisir un entier compris entre " f"{min_val} et {max_val}.") print("Tapez un nombre valide, ou Ctrl+C pour annuler.") continue # Sinon, on tente la conversion try: val = int(saisie) if min_val <= val <= max_val: return val else: print(f"Choix invalide (doit être entre {min_val} et {max_val}).\n") except ValueError: print("Choix invalide (entrez un nombre). Réessayez.\n") def selectionner_api() -> dict: """ Affiche la liste des API configurées, demande à l'utilisateur d'en choisir une par numéro. Retourne le dict correspondant ou None si aucune API. """ if not apis_configurees: print("Aucune API n’est configurée.\n") return None print("=== Liste des APIs disponibles ===") for i, api in enumerate(apis_configurees, start=1): print(f"{i}. {api['name']}") # On lit un choix (numéro) dans la plage [1..len(apis_configurees)] choix_num = lire_int_in_range( prompt=f"Quelle API souhaitez-vous sélectionner ? [1..{len(apis_configurees)}] : ", min_val=1, max_val=len(apis_configurees) ) return apis_configurees[choix_num - 1] def selectionner_endpoint(api: dict) -> dict: """ Affiche la liste des endpoints pour l'API donnée, demande à l'utilisateur d'en choisir un par numéro. Retourne le dict correspondant ou None si aucun endpoint. """ if not api["endpoints"]: print("Cette API n’a aucun endpoint configuré.\n") return None print(f"Endpoints disponibles pour {api['name']} :") for i, ep in enumerate(api["endpoints"], start=1): print(f"{i}. {ep['name']} ({ep['method']} {ep['path']})") choix_ep_num = lire_int_in_range( prompt=f"Quel endpoint tester ? [1..{len(api['endpoints'])}] : ", min_val=1, max_val=len(api["endpoints"]) ) return api["endpoints"][choix_ep_num - 1] # --------------------------------------------------------------------------- # 5. Fonctions du menu def ajouter_api(): print("=== Ajout d’une nouvelle API ===") _ = input("Voulez-vous coller la documentation de l’API ? (o/n) : ") print("Collez la documentation ci-dessous (finissez par un skibidi vide) :") lignes_doc = [] while True: ligne = input() if ligne == "skibidi": break lignes_doc.append(ligne) doc_api = "\n".join(lignes_doc) if not doc_api.strip(): doc_api = "Pas d'information sur l'API." # Appel Groq pour analyse resultats_analyse = analyser_api_avec_groq(doc_api, groq_api_key="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") confidence_score = resultats_analyse.get("confidence_score", 0.0) if confidence_score < 0.5: print( "Le score de confiance de l’analyse est faible. " "Les informations retournées peuvent être partielles ou incorrectes.\n" ) # Champs retournés api_type = resultats_analyse.get("api_type") base_url = resultats_analyse.get("base_url") auth_info = resultats_analyse.get("auth_info", {}) common_headers = resultats_analyse.get("common_headers", {}) endpoints = resultats_analyse.get("endpoints", []) suggested_name = resultats_analyse.get("suggested_name", "NouvelleAPI") rate_limit = resultats_analyse.get("rate_limit") print(f"Nom suggéré pour l’API : {suggested_name}") nom_api = input(f"Entrez le nom de l’API ({suggested_name}) : ") if not nom_api.strip(): nom_api = suggested_name # Si auth_info.required == True, on demande la clé api_key_value = None if auth_info.get("required", False): api_key_value = input("Entrez la valeur de la clé API ou du token : ") # Construction de la config nouvelle_api = { "name": nom_api, "type": api_type, "base_url": base_url, "auth_info": auth_info, "api_key_value": api_key_value, "common_headers": common_headers, "endpoints": endpoints, "rate_limit": rate_limit } apis_configurees.append(nouvelle_api) print(f"\n[OK] L’API '{nom_api}' a été ajoutée.") print(f" - Type : {api_type}") print(f" - URL de base : {base_url}\n") # On sauvegarde dans le fichier JSON sauvegarder_apis() def lister_apis(): print("=== Liste des API configurées ===") if not apis_configurees: print("Aucune API n’est configurée.\n") return for i, api in enumerate(apis_configurees, start=1): print(f"{i}. Nom : {api['name']}") print(f" URL de base : {api['base_url'] or 'Non renseignée'}") print(f" Type : {api['type'] or 'Inconnu'}") if api["rate_limit"]: print(f" Limitation de débit : {api['rate_limit']}") if api["endpoints"]: print(" Endpoints :") for ep in api["endpoints"]: print(f" - {ep['name']} ({ep['method']} {ep['path']})") else: print(" Aucun endpoint configuré.") print() print() def modifier_api(): print("=== Modification d’une API ===") api_choisie = selectionner_api() if not api_choisie: return print(f"\nAPI sélectionnée : {api_choisie['name']}") print("Que voulez-vous modifier ? (ex: 'changer l'URL de base', 'changer la clé API', " "'ajouter un endpoint', 'modifier un endpoint', 'supprimer un endpoint')") action = input("> ").lower() if "url" in action: new_url = input("Nouvelle URL de base : ") api_choisie["base_url"] = new_url print("URL de base modifiée.\n") if "clé" in action or "key" in action: new_key = input("Nouvelle clé API : ") api_choisie["api_key_value"] = new_key print("Clé API modifiée.\n") if "ajouter un endpoint" in action: print("=== Ajout d'un endpoint ===") nom_ep = input("Nom du nouvel endpoint : ") path_ep = input("Chemin (ex: /playlists/{playlist_id}/tracks) : ") methode = input("Méthode HTTP (GET, POST, etc.) : ") params_req = [p.strip() for p in input("Paramètres requis (séparés par des virgules) : ").split(",") if p.strip()] params_opt = [p.strip() for p in input("Paramètres optionnels (séparés par des virgules) : ").split(",") if p.strip()] new_ep = { "name": nom_ep, "path": path_ep, "method": methode.upper(), "required_params": params_req, "optional_params": params_opt, "description": "" } api_choisie["endpoints"].append(new_ep) print(f"Endpoint '{nom_ep}' ajouté.\n") if "modifier un endpoint" in action: print("=== Modification d'un endpoint ===") if not api_choisie["endpoints"]: print("Aucun endpoint à modifier.") return print("Liste des endpoints :") for i, ep in enumerate(api_choisie["endpoints"], start=1): print(f"{i}. {ep['name']} ({ep['method']} {ep['path']})") choix_ep = lire_int_in_range( f"Quel endpoint modifier ? [1..{len(api_choisie['endpoints'])}] : ", 1, len(api_choisie["endpoints"]) ) ep_choisi = api_choisie["endpoints"][choix_ep - 1] print(f"Endpoint sélectionné : {ep_choisi['name']}") modif_ep = input("Que voulez-vous faire ? (ex: 'ajouter un paramètre', 'changer la méthode', etc.) : ").lower() if "ajouter un paramètre" in modif_ep: nom_p = input("Nom du paramètre : ") r_o = input("Paramètre requis ou optionnel ? (r/o) : ") if r_o.lower() == 'r': ep_choisi["required_params"].append(nom_p) else: ep_choisi["optional_params"].append(nom_p) print(f"Paramètre '{nom_p}' ajouté.\n") # D’autres modifications possibles... if "supprimer un endpoint" in action: print("=== Suppression d'un endpoint ===") if not api_choisie["endpoints"]: print("Aucun endpoint à supprimer.") return print("Liste des endpoints :") for i, ep in enumerate(api_choisie["endpoints"], start=1): print(f"{i}. {ep['name']} ({ep['method']} {ep['path']})") choix_ep = lire_int_in_range( f"Quel endpoint supprimer ? [1..{len(api_choisie['endpoints'])}] : ", 1, len(api_choisie["endpoints"]) ) ep_asup = api_choisie["endpoints"][choix_ep - 1] api_choisie["endpoints"].remove(ep_asup) print(f"Endpoint '{ep_asup['name']}' supprimé.") print("Modification(s) terminée(s).\n") # On sauvegarde les modifications sauvegarder_apis() def supprimer_api(): print("=== Suppression d’une API ===") api_choisie = selectionner_api() if not api_choisie: return confirm = input(f"Êtes-vous sûr de vouloir supprimer '{api_choisie['name']}' ? (o/n) : ") if confirm.lower() == 'o': apis_configurees.remove(api_choisie) print(f"L’API '{api_choisie['name']}' a été supprimée.\n") sauvegarder_apis() else: print("Opération annulée.\n") def tester_endpoint(): print("=== Tester un endpoint ===") api_choisie = selectionner_api() if not api_choisie: return ep_choisi = selectionner_endpoint(api_choisie) if not ep_choisi: return # On va demander les valeurs pour chaque param requis required_params_values = {} for rp in ep_choisi.get("required_params", []): val = input(f"Entrez la valeur pour '{rp}' : ") required_params_values[rp] = val # On va demander les valeurs pour chaque param optionnel optional_params_values = {} for op in ep_choisi.get("optional_params", []): val = input(f"Valeur pour '{op}' (laisser vide si aucun) : ") if val: optional_params_values[op] = val # Préparation de l’URL base_url = api_choisie.get("base_url") or "" path = ep_choisi["path"] # Remplacement dans le path si on a des placeholders {param} # et retrait du param si on a pu le remplacer query_params = {} for k, v in required_params_values.items(): placeholder = f"{{{k}}}" if placeholder in path: path = path.replace(placeholder, v) else: # S'il n'y a pas de placeholder correspondant, on le mettra en query param query_params[k] = v # Tous les optionnels vont en query params query_params.update(optional_params_values) # Gestion de l’authentification headers = dict(api_choisie.get("common_headers", {})) if "Accept" not in headers: headers["Accept"] = "application/json" auth_info = api_choisie.get("auth_info", {}) api_key_value = api_choisie.get("api_key_value") if auth_info.get("required") and api_key_value: # Exemple : si c’est un Bearer if auth_info["type"] and "bearer" in auth_info["type"].lower(): key_name = auth_info.get("key_name", "Authorization") headers[key_name] = f"Bearer {api_key_value}" # Exemple : si c’est un Basic elif auth_info["type"] and "basic" in auth_info["type"].lower(): key_name = auth_info.get("key_name", "Authorization") headers[key_name] = f"Basic {api_key_value}" else: # Si key_name = "api_key", NASA veut un query param "api_key=XXXX" if auth_info.get("key_name", "").lower() == "api_key": query_params["api_key"] = api_key_value else: # Sinon on le place dans un header key_name = auth_info.get("key_name", "X-API-Key") headers[key_name] = api_key_value # Construction de l'URL finale url = base_url + path method = ep_choisi["method"].upper() print(f"\nTest de l'endpoint : {method} {url}") print(f"En-têtes : {headers}") print(f"Paramètres : {query_params}\n") try: if method == "GET": r = requests.get(url, headers=headers, params=query_params) elif method == "POST": r = requests.post(url, headers=headers, json=query_params) elif method == "PUT": r = requests.put(url, headers=headers, json=query_params) elif method == "DELETE": r = requests.delete(url, headers=headers, json=query_params) else: print("Méthode non gérée.") return print(f"Statut : {r.status_code}") try: print("Réponse JSON :") print(json.dumps(r.json(), indent=2)) except: print("Réponse brute :") print(r.text) except Exception as e: print(f"Erreur lors de l’appel : {e}") # --------------------------------------------------------------------------- # 6. MAIN def main(): # On charge la liste des APIs en début de programme charger_apis() while True: print("=== Menu Principal ===") print("1. Ajouter une API (assisté par IA)") print("2. Liste des API configurées") print("3. Modifier une API") print("4. Supprimer une API") print("5. Tester un endpoint") print("6. Quitter") choix_num = lire_int_in_range("Choisissez une option [1..6] : ", 1, 6) if choix_num == 1: ajouter_api() elif choix_num == 2: lister_apis() elif choix_num == 3: modifier_api() elif choix_num == 4: supprimer_api() elif choix_num == 5: tester_endpoint() elif choix_num == 6: print("Au revoir.") sys.exit(0) if __name__ == "__main__": main()
FICHIER_APIS
: c’est le nom du fichier (par défaut apis_configurees.json
) où sont stockées les informations sur les APIs.charger_apis()
: appelé au lancement du script, il charge (si présent) le fichier JSON dans la variable globale apis_configurees
.sauvegarder_apis()
: à chaque fois qu’on apporte une modification (ajout, édition ou suppression), on appelle cette fonction pour réécrire le fichier JSON."name"
, "base_url"
, "type"
, "auth_info"
, "endpoints"
, etc.Ainsi, lorsque vous relancerez votre script, il relira automatiquement le fichier apis_configurees.json
et vous retrouverez vos APIs.