Grove dans Flow
Grove dans Flow
Grove est la passerelle d'IA de Frappe. Une seule clé donne accès à deux API : l'API OpenAI sous /openai/v1 et l'API Anthropic sous /anthropic. Flow utilise Grove comme un fournisseur de modèles appelé grove. La clé n'est pas enregistrée dans Flow : elle est lue dans la configuration du site.
Cette page décrit la clé du site, l'ajout automatique du fournisseur, le routage des appels et les erreurs. Pour le comportement vu par l'utilisateur, voir Assistant Flow.
Clé du site : ai_keys
Flow lit la première entrée de la liste ai_keys du fichier site_config.json. Cette entrée doit contenir gateway_url et api_key :
{
"ai_keys": [
{"gateway_url": "https://ai.example.com", "api_key": "grove-key"}
]
}
site_key()(frappe/flow/lib/model.py) renvoie cette entrée, ouNonesi l'entrée est absente ou si un des deux champs est vide.site_key()lit d'abordfrappe.conf. Si la clé n'y est pas, elle relit le fichier avecfrappe.get_site_config(), car la configuration en cache peut avoir jusqu'à une minute de retard.- Seule la première entrée compte. Les entrées suivantes sont ignorées.
Dodock lit cette clé mais ne l'écrit jamais. Sur Frappe Cloud, Press écrit ai_keys dans la configuration d'un site d'essai de produit (Product Trial), avec une clé Grove d'essai à plafond de crédits. Dokos ne fournit pas de clé Grove. Sur un site auto-hébergé, un administrateur peut écrire l'entrée lui-même s'il a une clé Grove, par exemple avec bench --site <site> set-config -p ai_keys '[{"gateway_url": "…", "api_key": "…"}]'.
Fournisseur grove
La constante SITE_PROVIDER = "grove" (frappe/flow/lib/model.py) nomme le fournisseur. Le document Flow Provider grove n'a pas de clé. LiteLLM ne connaît pas ce fournisseur : la validation de Flow Provider et de Flow Model ne vérifie pas son nom auprès de LiteLLM quand le fournisseur est grove.
resolve_provider_credentials(model_id, provider) prend le fournisseur du modèle en second argument. Pour grove :
- Si le Flow Provider
groveest désactivé, la fonction lève l'erreur « Grove is turned off. Turn it on, or pick another model, in Settings → Models. ». - Sinon,
site_credentials()renvoieapi_keyetbase_url(la valeur degateway_url) depuissite_key(). - Si la configuration n'a plus de clé,
site_credentials()lève l'erreur « This site no longer has a Grove key. Connect another provider in Settings → Models. ».
Dans model_config(), une clé saisie sur le Flow Model a priorité sur la clé du site. L'adresse de la passerelle vient toujours de la configuration du site, sauf si le Flow Model a sa propre base_url.
Ajout automatique au premier affichage
boot() (frappe/flow/api/api.py) appelle sync_site_provider() (frappe/flow/api/setup.py) à chaque ouverture de Flow. La fonction :
- Ne fait rien si le Flow Provider
groveexiste déjà, si la valeur de cacheflow_grove_downest présente ou sisite_key()ne renvoie rien. - Demande
GET <gateway_url>/openai/v1/modelsavec la clé du site, avec un délai maximal de 5 secondes. - Cherche dans la réponse le modèle
openai/gpt-6-luna(constanteGROVE_MODEL). C'est le seul modèle Grove pris en charge par Flow dans cette version. - Si la passerelle ne répond pas ou ne liste pas ce modèle, écrit
flow_grove_downdans le cache pour 5 minutes et s'arrête. - Sinon, crée le Flow Provider
grove(activé), puis un Flow Model activé pouropenai/gpt-6-luna.
Les deux documents sont créés avec ignore_permissions=True. Un point de sauvegarde protège la création du fournisseur : si une autre ouverture simultanée l'a déjà créé, la fonction annule sa création et s'arrête.
Le titre du Flow Model vient de _title() : gpt-6-luna devient « GPT-6 luna ». Si un Flow Model de ce titre existe déjà, le titre devient « GPT-6 luna (Grove) ». À l'enregistrement, Flow Model préfixe l'identifiant par le fournisseur : l'identifiant enregistré est grove/openai/gpt-6-luna.
Après la création du fournisseur, sync_site_provider() ne fait plus rien. Le retrait de la clé de la configuration ne supprime pas le fournisseur : les appels échouent avec le message « This site no longer has a Grove key… ».
Les points d'entrée connect() et add_model() de setup.py ne gèrent pas Grove. connect() refuse un fournisseur absent de MODEL_ENDPOINTS, et grove n'y est pas. add_model() vérifie le modèle avec LiteLLM sans litellm_route() et sans la clé du site.
Routage des modèles Grove
Un modèle Grove se nomme grove/<identifiant du modèle>. Le moteur de Flow (flow_harness) enregistre le fournisseur grove avec grove_model() (flow/harness/src/flow_harness/model/providers/grove.py). Cette fonction appelle grove_route(name, api_base) puis crée un LiteLLMModel avec le fournisseur grove.
grove_route() choisit l'API de la passerelle :
| Identifiant du modèle | Modèle LiteLLM | Adresse appelée |
|---|---|---|
commence par claude | anthropic/<identifiant> | <gateway_url>/anthropic |
| autre | openai/<identifiant> | <gateway_url>/openai/v1 |
grove_route() lève ValueError si le nom n'a pas d'identifiant après grove/ ou si api_base est vide. La prise en charge des images est celle du modèle de l'éditeur, lue sur le modèle LiteLLM routé.
Les appels faits hors du moteur de Flow passent par litellm_route(model_id, api_base) (frappe/flow/lib/model.py). Cette fonction applique grove_route() aux modèles grove/… et renvoie les autres modèles sans changement. Elle sert au test de connexion de Flow Model et au calcul des vecteurs (frappe/flow/knowledge/embedder.py).
Erreurs
- Crédits épuisés. La passerelle répond par HTTP 402 quand les crédits de la clé sont épuisés. Dans l'interface,
presentEvent()(flow/frontend/src/api/events.js) remplace le message d'un événementrun_errorde code 402 par la constanteCREDIT_SPENTsi le modèle de la conversation a le fournisseurgrove. L'événement reçoit aussisettings_tab: "models": la page affiche alors le bouton « Connect a provider » à un utilisateur qui a le droit d'écriture sur Flow Model. - Message d'un fournisseur.
_run_error()(flow/harness/src/flow_harness/session/session.py) écrit le message sous la forme<fournisseur> (<code HTTP>): <détail>. Le fournisseur est son nom d'affichage dans le catalogue, sinon son identifiant. Grove n'est pas dans le catalogue : son message commence pargrove.ProviderErrorretire l'enveloppe de LiteLLM (litellm.APIError: …Exception -) pour ne garder que le texte du fournisseur.