[{"data":1,"prerenderedAt":669},["ShallowReactive",2],{"\u002Fnext\u002Fdodock\u002Fdeveloppeurs\u002Fgrove-flow":3,"section:\u002Fnext\u002Fdodock\u002Fdeveloppeurs\u002Fgrove-flow":668},{"id":4,"title":5,"body":6,"description":644,"extension":645,"meta":646,"navigation":660,"path":664,"seo":665,"stem":666,"__hash__":667},"docs\u002F2.next\u002F3.dodock\u002F10.developpeurs\u002Fgrove-flow.md","Grove dans Flow",{"type":7,"value":8,"toc":635},"minimark",[9,13,30,39,47,65,157,191,200,206,232,240,275,290,294,310,364,371,394,400,431,435,466,472,528,545,568,572,631],[10,11,5],"h1",{"id":12},"grove-dans-flow",[14,15,16,17,21,22,25,26,29],"p",{},"Grove est la passerelle d'IA de Frappe. Une seule clé donne accès à deux API : l'API OpenAI sous ",[18,19,20],"code",{},"\u002Fopenai\u002Fv1"," et l'API Anthropic sous ",[18,23,24],{},"\u002Fanthropic",". Flow utilise Grove comme un fournisseur de modèles appelé ",[18,27,28],{},"grove",". La clé n'est pas enregistrée dans Flow : elle est lue dans la configuration du site.",[14,31,32,33,38],{},"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 ",[34,35,37],"a",{"href":36},"\u002Fnext\u002Fdodock\u002Finterface\u002Fassistant-flow","Assistant Flow",".",[40,41,43,44],"h2",{"id":42},"clé-du-site-ai_keys","Clé du site : ",[18,45,46],{},"ai_keys",[14,48,49,50,52,53,56,57,60,61,64],{},"Flow lit la première entrée de la liste ",[18,51,46],{}," du fichier ",[18,54,55],{},"site_config.json",". Cette entrée doit contenir ",[18,58,59],{},"gateway_url"," et ",[18,62,63],{},"api_key"," :",[66,67,72],"pre",{"className":68,"code":69,"language":70,"meta":71,"style":71},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"ai_keys\": [\n    {\"gateway_url\": \"https:\u002F\u002Fai.example.com\", \"api_key\": \"grove-key\"}\n  ]\n}\n","json","",[18,73,74,83,101,146,152],{"__ignoreMap":71},[75,76,79],"span",{"class":77,"line":78},"line",1,[75,80,82],{"class":81},"sMK4o","{\n",[75,84,86,89,92,95,98],{"class":77,"line":85},2,[75,87,88],{"class":81},"  \"",[75,90,46],{"class":91},"spNyl",[75,93,94],{"class":81},"\"",[75,96,97],{"class":81},":",[75,99,100],{"class":81}," [\n",[75,102,104,107,109,112,114,116,119,123,125,128,130,132,134,136,138,141,143],{"class":77,"line":103},3,[75,105,106],{"class":81},"    {",[75,108,94],{"class":81},[75,110,59],{"class":111},"sBMFI",[75,113,94],{"class":81},[75,115,97],{"class":81},[75,117,118],{"class":81}," \"",[75,120,122],{"class":121},"sfazB","https:\u002F\u002Fai.example.com",[75,124,94],{"class":81},[75,126,127],{"class":81},",",[75,129,118],{"class":81},[75,131,63],{"class":111},[75,133,94],{"class":81},[75,135,97],{"class":81},[75,137,118],{"class":81},[75,139,140],{"class":121},"grove-key",[75,142,94],{"class":81},[75,144,145],{"class":81},"}\n",[75,147,149],{"class":77,"line":148},4,[75,150,151],{"class":81},"  ]\n",[75,153,155],{"class":77,"line":154},5,[75,156,145],{"class":81},[158,159,160,175,188],"ul",{},[161,162,163,166,167,170,171,174],"li",{},[18,164,165],{},"site_key()"," (",[18,168,169],{},"frappe\u002Fflow\u002Flib\u002Fmodel.py",") renvoie cette entrée, ou ",[18,172,173],{},"None"," si l'entrée est absente ou si un des deux champs est vide.",[161,176,177,179,180,183,184,187],{},[18,178,165],{}," lit d'abord ",[18,181,182],{},"frappe.conf",". Si la clé n'y est pas, elle relit le fichier avec ",[18,185,186],{},"frappe.get_site_config()",", car la configuration en cache peut avoir jusqu'à une minute de retard.",[161,189,190],{},"Seule la première entrée compte. Les entrées suivantes sont ignorées.",[14,192,193,194,196,197,38],{},"Dodock lit cette clé mais ne l'écrit jamais. Sur Frappe Cloud, Press écrit ",[18,195,46],{}," 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 ",[18,198,199],{},"bench --site \u003Csite> set-config -p ai_keys '[{\"gateway_url\": \"…\", \"api_key\": \"…\"}]'",[40,201,203,204],{"id":202},"fournisseur-grove","Fournisseur ",[18,205,28],{},[14,207,208,209,166,212,214,215,219,220,222,223,225,226,229,230,38],{},"La constante ",[18,210,211],{},"SITE_PROVIDER = \"grove\"",[18,213,169],{},") nomme le fournisseur. Le document ",[216,217,218],"strong",{},"Flow Provider"," ",[18,221,28],{}," n'a pas de clé. LiteLLM ne connaît pas ce fournisseur : la validation de ",[216,224,218],{}," et de ",[216,227,228],{},"Flow Model"," ne vérifie pas son nom auprès de LiteLLM quand le fournisseur est ",[18,231,28],{},[14,233,234,237,238,64],{},[18,235,236],{},"resolve_provider_credentials(model_id, provider)"," prend le fournisseur du modèle en second argument. Pour ",[18,239,28],{},[241,242,243,251,269],"ol",{},[161,244,245,246,219,248,250],{},"Si le ",[216,247,218],{},[18,249,28],{}," est désactivé, la fonction lève l'erreur « Grove is turned off. Turn it on, or pick another model, in Settings → Models. ».",[161,252,253,254,257,258,60,260,263,264,266,267,38],{},"Sinon, ",[18,255,256],{},"site_credentials()"," renvoie ",[18,259,63],{},[18,261,262],{},"base_url"," (la valeur de ",[18,265,59],{},") depuis ",[18,268,165],{},[161,270,271,272,274],{},"Si la configuration n'a plus de clé, ",[18,273,256],{}," lève l'erreur « This site no longer has a Grove key. Connect another provider in Settings → Models. ».",[14,276,277,278,281,282,284,285,287,288,38],{},"Dans ",[18,279,280],{},"model_config()",", une clé saisie sur le ",[216,283,228],{}," a priorité sur la clé du site. L'adresse de la passerelle vient toujours de la configuration du site, sauf si le ",[216,286,228],{}," a sa propre ",[18,289,262],{},[40,291,293],{"id":292},"ajout-automatique-au-premier-affichage","Ajout automatique au premier affichage",[14,295,296,166,299,302,303,166,306,309],{},[18,297,298],{},"boot()",[18,300,301],{},"frappe\u002Fflow\u002Fapi\u002Fapi.py",") appelle ",[18,304,305],{},"sync_site_provider()",[18,307,308],{},"frappe\u002Fflow\u002Fapi\u002Fsetup.py",") à chaque ouverture de Flow. La fonction :",[241,311,312,327,334,345,351],{},[161,313,314,315,219,317,319,320,323,324,326],{},"Ne fait rien si le ",[216,316,218],{},[18,318,28],{}," existe déjà, si la valeur de cache ",[18,321,322],{},"flow_grove_down"," est présente ou si ",[18,325,165],{}," ne renvoie rien.",[161,328,329,330,333],{},"Demande ",[18,331,332],{},"GET \u003Cgateway_url>\u002Fopenai\u002Fv1\u002Fmodels"," avec la clé du site, avec un délai maximal de 5 secondes.",[161,335,336,337,340,341,344],{},"Cherche dans la réponse le modèle ",[18,338,339],{},"openai\u002Fgpt-6-luna"," (constante ",[18,342,343],{},"GROVE_MODEL","). C'est le seul modèle Grove pris en charge par Flow dans cette version.",[161,346,347,348,350],{},"Si la passerelle ne répond pas ou ne liste pas ce modèle, écrit ",[18,349,322],{}," dans le cache pour 5 minutes et s'arrête.",[161,352,353,354,219,356,358,359,361,362,38],{},"Sinon, crée le ",[216,355,218],{},[18,357,28],{}," (activé), puis un ",[216,360,228],{}," activé pour ",[18,363,339],{},[14,365,366,367,370],{},"Les deux documents sont créés avec ",[18,368,369],{},"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.",[14,372,373,374,376,377,380,381,384,385,387,388,390,391,38],{},"Le titre du ",[216,375,228],{}," vient de ",[18,378,379],{},"_title()"," : ",[18,382,383],{},"gpt-6-luna"," devient « GPT-6 luna ». Si un ",[216,386,228],{}," de ce titre existe déjà, le titre devient « GPT-6 luna (Grove) ». À l'enregistrement, ",[216,389,228],{}," préfixe l'identifiant par le fournisseur : l'identifiant enregistré est ",[18,392,393],{},"grove\u002Fopenai\u002Fgpt-6-luna",[14,395,396,397,399],{},"Après la création du fournisseur, ",[18,398,305],{}," 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… ».",[14,401,402,403,60,406,409,410,413,414,416,417,420,421,423,424,426,427,430],{},"Les points d'entrée ",[18,404,405],{},"connect()",[18,407,408],{},"add_model()"," de ",[18,411,412],{},"setup.py"," ne gèrent pas Grove. ",[18,415,405],{}," refuse un fournisseur absent de ",[18,418,419],{},"MODEL_ENDPOINTS",", et ",[18,422,28],{}," n'y est pas. ",[18,425,408],{}," vérifie le modèle avec LiteLLM sans ",[18,428,429],{},"litellm_route()"," et sans la clé du site.",[40,432,434],{"id":433},"routage-des-modèles-grove","Routage des modèles Grove",[14,436,437,438,441,442,445,446,448,449,166,452,455,456,459,460,463,464,38],{},"Un modèle Grove se nomme ",[18,439,440],{},"grove\u002F\u003Cidentifiant du modèle>",". Le moteur de Flow (",[18,443,444],{},"flow_harness",") enregistre le fournisseur ",[18,447,28],{}," avec ",[18,450,451],{},"grove_model()",[18,453,454],{},"flow\u002Fharness\u002Fsrc\u002Fflow_harness\u002Fmodel\u002Fproviders\u002Fgrove.py","). Cette fonction appelle ",[18,457,458],{},"grove_route(name, api_base)"," puis crée un ",[18,461,462],{},"LiteLLMModel"," avec le fournisseur ",[18,465,28],{},[14,467,468,471],{},[18,469,470],{},"grove_route()"," choisit l'API de la passerelle :",[473,474,475,491],"table",{},[476,477,478],"thead",{},[479,480,481,485,488],"tr",{},[482,483,484],"th",{},"Identifiant du modèle",[482,486,487],{},"Modèle LiteLLM",[482,489,490],{},"Adresse appelée",[492,493,494,513],"tbody",{},[479,495,496,503,508],{},[497,498,499,500],"td",{},"commence par ",[18,501,502],{},"claude",[497,504,505],{},[18,506,507],{},"anthropic\u002F\u003Cidentifiant>",[497,509,510],{},[18,511,512],{},"\u003Cgateway_url>\u002Fanthropic",[479,514,515,518,523],{},[497,516,517],{},"autre",[497,519,520],{},[18,521,522],{},"openai\u002F\u003Cidentifiant>",[497,524,525],{},[18,526,527],{},"\u003Cgateway_url>\u002Fopenai\u002Fv1",[14,529,530,532,533,536,537,540,541,544],{},[18,531,470],{}," lève ",[18,534,535],{},"ValueError"," si le nom n'a pas d'identifiant après ",[18,538,539],{},"grove\u002F"," ou si ",[18,542,543],{},"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é.",[14,546,547,548,166,551,553,554,556,557,560,561,563,564,567],{},"Les appels faits hors du moteur de Flow passent par ",[18,549,550],{},"litellm_route(model_id, api_base)",[18,552,169],{},"). Cette fonction applique ",[18,555,470],{}," aux modèles ",[18,558,559],{},"grove\u002F…"," et renvoie les autres modèles sans changement. Elle sert au test de connexion de ",[216,562,228],{}," et au calcul des vecteurs (",[18,565,566],{},"frappe\u002Fflow\u002Fknowledge\u002Fembedder.py",").",[40,569,571],{"id":570},"erreurs","Erreurs",[158,573,574,604],{},[161,575,576,579,580,166,583,586,587,590,591,594,595,597,598,601,602,38],{},[216,577,578],{},"Crédits épuisés."," La passerelle répond par HTTP 402 quand les crédits de la clé sont épuisés. Dans l'interface, ",[18,581,582],{},"presentEvent()",[18,584,585],{},"flow\u002Ffrontend\u002Fsrc\u002Fapi\u002Fevents.js",") remplace le message d'un événement ",[18,588,589],{},"run_error"," de code 402 par la constante ",[18,592,593],{},"CREDIT_SPENT"," si le modèle de la conversation a le fournisseur ",[18,596,28],{},". L'événement reçoit aussi ",[18,599,600],{},"settings_tab: \"models\""," : la page affiche alors le bouton « Connect a provider » à un utilisateur qui a le droit d'écriture sur ",[216,603,228],{},[161,605,606,219,609,166,612,615,616,619,620,622,623,626,627,630],{},[216,607,608],{},"Message d'un fournisseur.",[18,610,611],{},"_run_error()",[18,613,614],{},"flow\u002Fharness\u002Fsrc\u002Fflow_harness\u002Fsession\u002Fsession.py",") écrit le message sous la forme ",[18,617,618],{},"\u003Cfournisseur> (\u003Ccode HTTP>): \u003Cdétail>",". Le fournisseur est son nom d'affichage dans le catalogue, sinon son identifiant. Grove n'est pas dans le catalogue : son message commence par ",[18,621,28],{},". ",[18,624,625],{},"ProviderError"," retire l'enveloppe de LiteLLM (",[18,628,629],{},"litellm.APIError: …Exception - ",") pour ne garder que le texte du fournisseur.",[632,633,634],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":71,"searchDepth":78,"depth":85,"links":636},[637,639,641,642,643],{"id":42,"depth":85,"text":638},"Clé du site : ai_keys",{"id":202,"depth":85,"text":640},"Fournisseur grove",{"id":292,"depth":85,"text":293},{"id":433,"depth":85,"text":434},{"id":570,"depth":85,"text":571},"Référence développeur du fournisseur grove de Flow : clé ai_keys de la configuration du site, ajout automatique du fournisseur, routage des modèles vers la passerelle et messages d'erreur.","md",{"reviewed":647,"sources":648,"generated":660,"generated_at":661,"source_mrs":662},false,[649,650,651,652,653,654,655,656,657,658,659],"dodock@develop:frappe\u002Fflow\u002Flib\u002Fmodel.py","dodock@develop:frappe\u002Fflow\u002Fapi\u002Fsetup.py","dodock@develop:frappe\u002Fflow\u002Fapi\u002Fapi.py","dodock@develop:frappe\u002Fflow\u002Fdoctype\u002Fflow_model\u002Fflow_model.py","dodock@develop:frappe\u002Fflow\u002Fdoctype\u002Fflow_provider\u002Fflow_provider.py","dodock@develop:frappe\u002Fflow\u002Fknowledge\u002Fembedder.py","dodock@develop:flow\u002Fharness\u002Fsrc\u002Fflow_harness\u002Fmodel\u002Fproviders\u002Fgrove.py","dodock@develop:flow\u002Fharness\u002Fsrc\u002Fflow_harness\u002Fmodel\u002F__init__.py","dodock@develop:flow\u002Fharness\u002Fsrc\u002Fflow_harness\u002Fsession\u002Fsession.py","dodock@develop:flow\u002Ffrontend\u002Fsrc\u002Fapi\u002Fevents.js","dodock@develop:flow\u002Ffrontend\u002Fsrc\u002Fapi\u002Fclient.js",true,"2026-11-14",[663],"https:\u002F\u002Fgitlab.com\u002Fdokos\u002Fdodock\u002F-\u002Fmerge_requests\u002F13083","\u002Fnext\u002Fdodock\u002Fdeveloppeurs\u002Fgrove-flow",{"title":5,"description":644},"2.next\u002F3.dodock\u002F10.developpeurs\u002Fgrove-flow","lPZKgBCdWoGZzJOEX8J6V45HJT9ufBbip2jf7UUSCvU",{},1794695078299]