B03 - Registre des providers
| Groupe | Technologie | Statut | Service | Phase de la feuille de route | Contrats |
|---|---|---|---|---|---|
| B - Gestion des LLM et Model Gateway | Développé (PostgreSQL + OpenBao) | à développer | llm | 2 | - |
Rôle et responsabilités
Providers configurés, endpoints, régions, identifiants en écriture seule, statut et kill switch.
Position dans l'architecture et interactions
Lu par le Router (B05) et le générateur de configuration LiteLLM ; écrit par les administrateurs tenant via le portail ou POST /v1/providers. GET /v1/providers/catalog liste les presets avec le statut configuré / non configuré / non connecté.
Composant open source et partie développée
Entièrement développé (table PostgreSQL + OpenBao pour les identifiants).
| Id provider | Provider | Chemin via LiteLLM | Champs de configuration admin |
|---|---|---|---|
openai | OpenAI | natif | api_key, organization, base_url |
anthropic | Anthropic | natif | api_key, base_url |
google_ai_studio | Google AI Studio / Gemini | natif (gemini/) | api_key |
vertex_ai | Vertex AI | natif (vertex_ai/) | project, location, compte de service JSON (ou workload identity) |
azure_openai | Azure OpenAI | natif (azure/) | endpoint, deployment name, api_version, api_key ou Entra ID |
azure_ai_foundry | Azure AI Foundry | adaptateur choisi selon le type d'endpoint (style OpenAI ou inférence de modèle) | endpoint, api_key ou Entra ID, api_version |
aws_bedrock | AWS Bedrock | natif (bedrock/) | aws_region_name, aws_access_key_id + aws_secret_access_key, ou rôle IAM (aws_role_arn / IRSA), ou jeton porteur de l'API Bedrock (aws_bearer_token) |
mistral | Mistral | natif | api_key |
cohere | Cohere | natif (chat, embed, rerank) | api_key |
deepseek | DeepSeek | natif | api_key |
groq | Groq | natif | api_key |
together_ai | Together AI | natif | api_key |
fireworks_ai | Fireworks AI | natif | api_key |
openrouter | OpenRouter | natif | api_key |
ollama | Ollama | natif | base_url |
vllm | vLLM (local, brique B02) | compatible OpenAI (hosted_vllm/) | base_url, api_key optionnel |
qwen | Qwen (DashScope) | compatible OpenAI | api_key, base_url |
zai | Z.AI (GLM) | compatible OpenAI | api_key, base_url |
minimax | MiniMax | compatible OpenAI | api_key, base_url |
lm_studio | LM Studio | compatible OpenAI (lm_studio/) | base_url |
openai_codex | OpenAI Codex | compatible OpenAI, compte OAuth | OAuth token (délégué) |
github_copilot | GitHub Copilot | natif (github_copilot/) | OAuth device token (délégué) |
huggingface | Hugging Face | natif (huggingface/) | api_key, endpoint (Inference Endpoints) |
custom | Compatible OpenAI personnalisé | openai/ avec base_url personnalisé | base_url, api_key, headers |
custom_anthropic | Compatible Anthropic personnalisé | anthropic/ avec base_url personnalisé | base_url, api_key |
AWS Bedrock champs admin : aws_region_name, puis au choix aws_access_key_id + aws_secret_access_key, aws_role_arn (IRSA / assume role, recommandé), aws_web_identity_token + aws_role_arn ou aws_bearer_token (clé API Bedrock).
Vertex AI champs admin : project, location, service_account_json (ou fédération d'identité de workload).
Modèle de données
eai.llm_providers(id, tenant_id, name, provider_type, api_base, config, secret_fields, credential_ref, regions, retention, enabled, connected, last_test, created_by, created_at, updated_at) ; credential_ref pointe vers OpenBao (les secrets du provider sont dans eai.llm_provider_secrets), la valeur n'est jamais stockée en clair.
Tables (schéma eai, chaque table tenant porte tenant_id uuid NOT NULL avec une politique RLS sur current_setting('app.tenant_id', true)::uuid) : eai.llm_providers, eai.audit_events.
Endpoints API
GET /v1/providersPOST /v1/providersGET /v1/providers/catalogGET /v1/providers/{id}PATCH /v1/providers/{id}DELETE /v1/providers/{id}POST /v1/providers/{id}/testPOST /v1/providers/{id}/disablePOST /v1/providers/{id}/enable
POST /v1/providers HTTP/1.1
Host: <control-plane-host>
X-API-Key: eai_xxx_secret
Accept-Language: fr
Content-Type: application/json
{"provider_type": "aws_bedrock", "name": "bedrock-eu-west-3", "regions": ["eu-west-3"], "credentials": {"aws_region_name": "eu-west-3", "aws_role_arn": "arn:aws:iam::123456789012:role/eai-bedrock"}}
{"id": "4d2c2cff-7f37-4110-ae93-f8fc8dd771a7", "name": "bedrock-eu-west-3", "provider_type": "aws_bedrock", "scope": "tenant", "api_base": "https://bedrock-runtime.{aws_region_name}.amazonaws.com", "base_url": "https://bedrock-runtime.{aws_region_name}.amazonaws.com", "type": "aws_bedrock", "config": {}, "credentials_set": ["aws_region_name", "aws_role_arn"], "credential_ref": "openbao://llm/providers/4d2c2cff-7f37-4110-ae93-f8fc8dd771a7", "regions": ["eu-west-3"], "retention": {}, "enabled": true, "connected": null, "last_test": null, "status": "configured", "created_at": "2026-10-30T02:38:20.544170+01:00", "updated_at": "2026-10-30T02:38:20.551425+01:00"}
Les erreurs utilisent l'enveloppe commune {"error": {"code", "message", "message_i18n", "details", "request_id"}} ; les listes sont paginées avec limit et cursor.
Utilisation des SDK
Python (enterprise_ai):
from enterprise_ai import EnterpriseAI
client = EnterpriseAI(base_url="<control-plane-url>", api_key="eai_xxx_secret")
items = client.providers.list()
TypeScript (@enterprise-ai/sdk):
import { EnterpriseAI } from "@enterprise-ai/sdk";
const eai = new EnterpriseAI({ baseUrl: "<control-plane-url>", apiKey: process.env.EAI_API_KEY });
const items = await eai.providers.list();
Java (enterprise-ai-java):
EnterpriseAIClient client = EnterpriseAIClient.builder()
.baseUrl("<control-plane-url>")
.apiKey(System.getenv("EAI_API_KEY"))
.build();
var items = client.providers().list();
Go (enterprise-ai-go):
client, _ := eai.NewClient(eai.WithBaseURL("<control-plane-url>"), eai.WithAPIKey(os.Getenv("EAI_API_KEY")))
items, err := client.Providers.List(ctx)
Configuration
- Environnement :
EAI_DATABASE_URL,EAI_OTEL_EXPORTER_OTLP_ENDPOINT. - Valeurs Helm :
controlPlane.enabled,controlPlane.replicas,controlPlane.resources,controlPlane.config,litellm.enabled,litellm.config.
Contrôles de sécurité
- Authentification par JWT OIDC, clé API tenant avec scopes, ou mTLS SPIFFE ; le tenant est résolu depuis l'identifiant, jamais depuis le corps.
- Autorisation par décisions OpenFGA et OPA ; chaque mutation et décision écrit une ligne
eai.audit_eventschaînée par hash. - Les secrets ne sont jamais renvoyés par l'API ; TLS partout ; les politiques réseau Cilium restreignent qui peut joindre le service.
| Contrôle | Application |
|---|---|
| Listes d'autorisation provider / modèle / région | Par tenant et projet dans eai.policies ; le Router ne sélectionne que des triplets (provider, modèle, région) autorisés |
| Classification des données | eai.data_classification de la requête doit être <= data_classification_max du modèle ; les données très sensibles (restricted, secret) restent sur des modèles locaux (vLLM) |
| DLP | La DLP Gateway (E02) inspecte puis masque ou tokenise les prompts avant tout appel distant |
| Isolation des secrets | Identifiants en écriture seule, stockés dans OpenBao, injectés dans LiteLLM lors de la synchronisation, jamais renvoyés par l'API ni journalisés |
| TLS | TLS 1.2+ vers chaque provider, mTLS SPIFFE dans le cluster |
| Rétention chez le provider | Le registre consigne les conditions de rétention / de non-conservation des données (zero data retention) ; les modèles sans conditions acceptables sont restreints |
| Politique de repli | Les chaînes de repli sont filtrées par les mêmes listes : jamais de région, de modèle ou de classification interdits |
| Garde-fous de coût | Estimation et réservation de quota avant exécution (POST /v1/gateway/estimate, POST /v1/quotas/reserve), coupure du streaming pendant l'exécution quand le budget est atteint |
| Journalisation sans exposition | Prompts et réponses journalisés uniquement selon la politique, masqués, jamais avec des identifiants |
| Kill switch | POST /v1/providers/{id}/disable et POST /v1/models/{model_id}/disable prennent effet dès l'appel suivant |
Les modèles distants sont autorisés pour certains usages (données publiques ou internes, finalités approuvées) tandis que les données très sensibles restent sur des modèles locaux.
Observabilité
- Métriques :
eai_provider_registry_requests_total,eai_provider_registry_errors_total,eai_provider_registry_latency_seconds(Prometheus). - Traces : spans OpenTelemetry
eai.providers.*avec propagation W3Ctraceparent, spans IA envoyés à Opik. - Actions d'audit :
provider.create,provider.update,provider.delete,provider.test,policy.evaluate.
Aspects FinOps
Le coût d'exploitation est imputé par tenant et projet via les labels des Usage Events et les requêtes de ressources Kubernetes ; pas de coût direct par appel.
Modes de défaillance et résilience
- Réplicas sans état derrière l'API Gateway, PostgreSQL HA pour l'état ; la sonde
GET /readyzretire les pods défaillants. - Système externe indisponible : échec fermé pour les décisions de sécurité, code d'erreur explicite, aucun contournement silencieux.
Adoption autonome (client d'un client)
Un client d'un client peut adopter cette brique seule avec le sous-client SDK Python client.providers (TS eai.providers, Java client.providers(), Go client.Providers). Déploiement minimal : Control Plane API + PostgreSQL + Keycloak (ou clés API), le Control Plane charge tous les domaines de services/control-plane/src/eai/domains ; les composants optionnels du chart se coupent dans deploy/helm/enterprise-ai/values.yaml (vllm.enabled, qdrant.enabled, nats.enabled, workers.enabled).
Tests d'acceptation
- Un tenant ne peut ni lire ni modifier les ressources d'un autre tenant (test RLS).
- Chaque mutation produit un événement d'audit vérifiable par
GET /v1/audit/verify. - Les erreurs sont renvoyées en anglais et en français selon
Accept-Language.