[{"data":1,"prerenderedAt":3822},["ShallowReactive",2],{"article-en-\u002Fai\u002Fai-budget-with-litellm":3,"article-sibling-en-\u002Fai\u002Fai-budget-with-litellm":2039,"surround-en-\u002Fai\u002Fai-budget-with-litellm":3781,"related-en-\u002Fai\u002Fai-budget-with-litellm":3788},{"id":4,"title":5,"body":6,"date":2017,"description":2018,"draft":2019,"extension":2020,"img":2021,"meta":2022,"navigation":290,"path":2023,"seo":2024,"slug":2025,"stem":2026,"tags":2027,"topics":2034,"__hash__":2038},"content\u002Fai\u002F2.ai-budget-with-litellm.md","Giving Your Teams a Real AI Budget with LiteLLM",{"type":7,"value":8,"toc":2002},"minimark",[9,13,16,21,24,27,30,34,37,40,43,47,55,454,469,472,671,686,689,719,723,726,822,829,832,928,931,934,1038,1041,1045,1048,1051,1109,1112,1198,1201,1204,1262,1265,1269,1272,1330,1333,1337,1340,1343,1402,1405,1411,1469,1472,1476,1479,1559,1562,1602,1605,1609,1612,1672,1679,1682,1685,1689,1692,1695,1837,1840,1843,1865,1868,1872,1875,1882,1888,1894,1898,1901,1904,1908,1998],[10,11,12],"p",{},"The previous article argued that AI governance by policy fails because there is no point of passage where a rule could be applied, and that the way in is to build the accounting layer first: measure, then show back, then govern. This article builds that layer. At the end of it, every call made by a person or a script in your organization is attributed to a named identity and a team, each team has a spending ceiling that resets on a schedule, a daily bound sits underneath the monthly one so a single runaway process cannot consume the month, alerts fire before anything blocks, and the finance team can be handed a per team breakdown that reconciles against a provider invoice.",[10,14,15],{},"The tool is LiteLLM, deployed as a proxy. Everything below has been arranged so you can follow it in order on a single machine and end with something you could actually put in front of a team.",[17,18,20],"h2",{"id":19},"what-we-are-building","What we are building",[10,22,23],{},"The shape is a reverse proxy for inference. Your applications, your developer tools and your internal scripts stop holding provider credentials and instead point at one internal endpoint. That endpoint speaks the OpenAI API, which matters more than it sounds: almost every SDK, agent framework and editor integration already speaks it, so adoption costs a base URL and a key rather than a rewrite.",[10,25,26],{},"Behind the endpoint, the proxy holds the real provider credentials, decides which upstream a request goes to, applies the limits attached to the caller, writes a spend record to Postgres, and forwards logs to wherever you keep them. The path of a request is: caller presents a virtual key, proxy resolves that key to a user and a team, checks the model is allowed and the budget is not exhausted, forwards to the provider, computes the cost of the response from its price table, writes a spend row, returns the answer.",[10,28,29],{},"Postgres is not optional for what we are doing. Without a database the proxy is a routing convenience; with one it becomes the ledger, and the ledger is the entire point.",[17,31,33],{"id":32},"why-litellm","Why LiteLLM",[10,35,36],{},"There are several products in this category and the honest summary is that they differ mostly on hosting model and on which primitives are free.",[10,38,39],{},"Hosted routers like OpenRouter are the fastest path to multi-provider access, but they place a third party between you and your prompts and give you no say in where the ledger lives, which is disqualifying for anyone with a data residency obligation. Observability-first tools like Langfuse or Helicone are excellent at showing you what happened and are not, primarily, enforcement points. General purpose API gateways can be made to do this with enough configuration, but you will be modelling budgets and per model pricing yourself.",[10,41,42],{},"LiteLLM is chosen here because it is self-hostable, open source, exposes an OpenAI compatible surface so nothing upstream needs to change, and ships the specific primitives this problem needs already built: virtual keys, teams, budgets with reset windows, rate limits, model allowlists, request tags and a spend ledger. One caveat to check before you plan around it: a subset of features, including parts of the SSO and enterprise administration surface, sit behind a commercial license. Verify the current licensing for the specific features you intend to depend on rather than discovering it during rollout.",[17,44,46],{"id":45},"deploying-the-proxy","Deploying the proxy",[10,48,49,50,54],{},"Start with the official compose file, which brings up the proxy and its database together. The ",[51,52,53],"code",{},"litellm-database"," image bundles the Prisma toolchain and runs the schema migration on start, which is what you want for a single node deployment.",[56,57,62],"pre",{"className":58,"code":59,"language":60,"meta":61,"style":61},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","services:\n  litellm:\n    image: docker.litellm.ai\u002Fberriai\u002Flitellm-database:v1.81.9-stable\n    ports:\n      - \"4000:4000\"\n    environment:\n      LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}\n      LITELLM_SALT_KEY: ${LITELLM_SALT_KEY}\n      DATABASE_URL: postgresql:\u002F\u002Flitellm:${POSTGRES_PASSWORD}@db:5432\u002Flitellm\n      STORE_MODEL_IN_DB: \"True\"\n      OPENAI_API_KEY: ${OPENAI_API_KEY}\n      ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}\n      GEMINI_API_KEY: ${GEMINI_API_KEY}\n    volumes:\n      - .\u002Fconfig.yaml:\u002Fapp\u002Fconfig.yaml\n    command: [\"--config\", \"\u002Fapp\u002Fconfig.yaml\"]\n    depends_on:\n      db:\n        condition: service_healthy\n\n  db:\n    image: postgres:16\n    environment:\n      POSTGRES_USER: litellm\n      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}\n      POSTGRES_DB: litellm\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U litellm\"]\n      interval: 5s\n      timeout: 5s\n      retries: 10\n    volumes:\n      - postgres_data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n\nvolumes:\n  postgres_data:\n","yaml","",[51,63,64,77,85,98,106,121,129,140,151,162,177,188,199,210,218,226,258,266,274,285,292,300,310,317,328,339,349,357,385,396,406,418,425,433,438,446],{"__ignoreMap":61},[65,66,69,73],"span",{"class":67,"line":68},"line",1,[65,70,72],{"class":71},"swJcz","services",[65,74,76],{"class":75},"sMK4o",":\n",[65,78,80,83],{"class":67,"line":79},2,[65,81,82],{"class":71},"  litellm",[65,84,76],{"class":75},[65,86,88,91,94],{"class":67,"line":87},3,[65,89,90],{"class":71},"    image",[65,92,93],{"class":75},":",[65,95,97],{"class":96},"sfazB"," docker.litellm.ai\u002Fberriai\u002Flitellm-database:v1.81.9-stable\n",[65,99,101,104],{"class":67,"line":100},4,[65,102,103],{"class":71},"    ports",[65,105,76],{"class":75},[65,107,109,112,115,118],{"class":67,"line":108},5,[65,110,111],{"class":75},"      -",[65,113,114],{"class":75}," \"",[65,116,117],{"class":96},"4000:4000",[65,119,120],{"class":75},"\"\n",[65,122,124,127],{"class":67,"line":123},6,[65,125,126],{"class":71},"    environment",[65,128,76],{"class":75},[65,130,132,135,137],{"class":67,"line":131},7,[65,133,134],{"class":71},"      LITELLM_MASTER_KEY",[65,136,93],{"class":75},[65,138,139],{"class":96}," ${LITELLM_MASTER_KEY}\n",[65,141,143,146,148],{"class":67,"line":142},8,[65,144,145],{"class":71},"      LITELLM_SALT_KEY",[65,147,93],{"class":75},[65,149,150],{"class":96}," ${LITELLM_SALT_KEY}\n",[65,152,154,157,159],{"class":67,"line":153},9,[65,155,156],{"class":71},"      DATABASE_URL",[65,158,93],{"class":75},[65,160,161],{"class":96}," postgresql:\u002F\u002Flitellm:${POSTGRES_PASSWORD}@db:5432\u002Flitellm\n",[65,163,165,168,170,172,175],{"class":67,"line":164},10,[65,166,167],{"class":71},"      STORE_MODEL_IN_DB",[65,169,93],{"class":75},[65,171,114],{"class":75},[65,173,174],{"class":96},"True",[65,176,120],{"class":75},[65,178,180,183,185],{"class":67,"line":179},11,[65,181,182],{"class":71},"      OPENAI_API_KEY",[65,184,93],{"class":75},[65,186,187],{"class":96}," ${OPENAI_API_KEY}\n",[65,189,191,194,196],{"class":67,"line":190},12,[65,192,193],{"class":71},"      ANTHROPIC_API_KEY",[65,195,93],{"class":75},[65,197,198],{"class":96}," ${ANTHROPIC_API_KEY}\n",[65,200,202,205,207],{"class":67,"line":201},13,[65,203,204],{"class":71},"      GEMINI_API_KEY",[65,206,93],{"class":75},[65,208,209],{"class":96}," ${GEMINI_API_KEY}\n",[65,211,213,216],{"class":67,"line":212},14,[65,214,215],{"class":71},"    volumes",[65,217,76],{"class":75},[65,219,221,223],{"class":67,"line":220},15,[65,222,111],{"class":75},[65,224,225],{"class":96}," .\u002Fconfig.yaml:\u002Fapp\u002Fconfig.yaml\n",[65,227,229,232,234,237,240,243,245,248,250,253,255],{"class":67,"line":228},16,[65,230,231],{"class":71},"    command",[65,233,93],{"class":75},[65,235,236],{"class":75}," [",[65,238,239],{"class":75},"\"",[65,241,242],{"class":96},"--config",[65,244,239],{"class":75},[65,246,247],{"class":75},",",[65,249,114],{"class":75},[65,251,252],{"class":96},"\u002Fapp\u002Fconfig.yaml",[65,254,239],{"class":75},[65,256,257],{"class":75},"]\n",[65,259,261,264],{"class":67,"line":260},17,[65,262,263],{"class":71},"    depends_on",[65,265,76],{"class":75},[65,267,269,272],{"class":67,"line":268},18,[65,270,271],{"class":71},"      db",[65,273,76],{"class":75},[65,275,277,280,282],{"class":67,"line":276},19,[65,278,279],{"class":71},"        condition",[65,281,93],{"class":75},[65,283,284],{"class":96}," service_healthy\n",[65,286,288],{"class":67,"line":287},20,[65,289,291],{"emptyLinePlaceholder":290},true,"\n",[65,293,295,298],{"class":67,"line":294},21,[65,296,297],{"class":71},"  db",[65,299,76],{"class":75},[65,301,303,305,307],{"class":67,"line":302},22,[65,304,90],{"class":71},[65,306,93],{"class":75},[65,308,309],{"class":96}," postgres:16\n",[65,311,313,315],{"class":67,"line":312},23,[65,314,126],{"class":71},[65,316,76],{"class":75},[65,318,320,323,325],{"class":67,"line":319},24,[65,321,322],{"class":71},"      POSTGRES_USER",[65,324,93],{"class":75},[65,326,327],{"class":96}," litellm\n",[65,329,331,334,336],{"class":67,"line":330},25,[65,332,333],{"class":71},"      POSTGRES_PASSWORD",[65,335,93],{"class":75},[65,337,338],{"class":96}," ${POSTGRES_PASSWORD}\n",[65,340,342,345,347],{"class":67,"line":341},26,[65,343,344],{"class":71},"      POSTGRES_DB",[65,346,93],{"class":75},[65,348,327],{"class":96},[65,350,352,355],{"class":67,"line":351},27,[65,353,354],{"class":71},"    healthcheck",[65,356,76],{"class":75},[65,358,360,363,365,367,369,372,374,376,378,381,383],{"class":67,"line":359},28,[65,361,362],{"class":71},"      test",[65,364,93],{"class":75},[65,366,236],{"class":75},[65,368,239],{"class":75},[65,370,371],{"class":96},"CMD-SHELL",[65,373,239],{"class":75},[65,375,247],{"class":75},[65,377,114],{"class":75},[65,379,380],{"class":96},"pg_isready -U litellm",[65,382,239],{"class":75},[65,384,257],{"class":75},[65,386,388,391,393],{"class":67,"line":387},29,[65,389,390],{"class":71},"      interval",[65,392,93],{"class":75},[65,394,395],{"class":96}," 5s\n",[65,397,399,402,404],{"class":67,"line":398},30,[65,400,401],{"class":71},"      timeout",[65,403,93],{"class":75},[65,405,395],{"class":96},[65,407,409,412,414],{"class":67,"line":408},31,[65,410,411],{"class":71},"      retries",[65,413,93],{"class":75},[65,415,417],{"class":416},"sbssI"," 10\n",[65,419,421,423],{"class":67,"line":420},32,[65,422,215],{"class":71},[65,424,76],{"class":75},[65,426,428,430],{"class":67,"line":427},33,[65,429,111],{"class":75},[65,431,432],{"class":96}," postgres_data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n",[65,434,436],{"class":67,"line":435},34,[65,437,291],{"emptyLinePlaceholder":290},[65,439,441,444],{"class":67,"line":440},35,[65,442,443],{"class":71},"volumes",[65,445,76],{"class":75},[65,447,449,452],{"class":67,"line":448},36,[65,450,451],{"class":71},"  postgres_data",[65,453,76],{"class":75},[10,455,456,457,460,461,464,465,468],{},"Two of those variables deserve more attention than they usually get. ",[51,458,459],{},"LITELLM_MASTER_KEY"," is the administrative credential for the whole proxy: it creates keys, reads everyone's spend, and changes every limit described in this article. It is not an application key and it should never be handed to a team. ",[51,462,463],{},"LITELLM_SALT_KEY"," encrypts provider credentials stored in the database, and it cannot be rotated after models have been added without making those stored credentials unreadable. Generate both from a real random source, put them in your secret manager on day one, and pin the image to a version tag rather than ",[51,466,467],{},"latest"," so that a restart is never a surprise upgrade.",[10,470,471],{},"The configuration file declares which models exist and under what internal names.",[56,473,475],{"className":58,"code":474,"language":60,"meta":61,"style":61},"model_list:\n  - model_name: chat-default\n    litellm_params:\n      model: anthropic\u002Fclaude-haiku-4-5\n      api_key: os.environ\u002FANTHROPIC_API_KEY\n\n  - model_name: chat-frontier\n    litellm_params:\n      model: anthropic\u002Fclaude-sonnet-5\n      api_key: os.environ\u002FANTHROPIC_API_KEY\n\n  - model_name: chat-cheap\n    litellm_params:\n      model: gemini\u002Fgemini-2.5-flash\n      api_key: os.environ\u002FGEMINI_API_KEY\n\n  - model_name: embeddings\n    litellm_params:\n      model: openai\u002Ftext-embedding-3-small\n      api_key: os.environ\u002FOPENAI_API_KEY\n\ngeneral_settings:\n  master_key: os.environ\u002FLITELLM_MASTER_KEY\n  database_url: os.environ\u002FDATABASE_URL\n",[51,476,477,484,497,504,514,524,528,539,545,554,562,566,577,583,592,601,605,616,622,631,640,644,651,661],{"__ignoreMap":61},[65,478,479,482],{"class":67,"line":68},[65,480,481],{"class":71},"model_list",[65,483,76],{"class":75},[65,485,486,489,492,494],{"class":67,"line":79},[65,487,488],{"class":75},"  -",[65,490,491],{"class":71}," model_name",[65,493,93],{"class":75},[65,495,496],{"class":96}," chat-default\n",[65,498,499,502],{"class":67,"line":87},[65,500,501],{"class":71},"    litellm_params",[65,503,76],{"class":75},[65,505,506,509,511],{"class":67,"line":100},[65,507,508],{"class":71},"      model",[65,510,93],{"class":75},[65,512,513],{"class":96}," anthropic\u002Fclaude-haiku-4-5\n",[65,515,516,519,521],{"class":67,"line":108},[65,517,518],{"class":71},"      api_key",[65,520,93],{"class":75},[65,522,523],{"class":96}," os.environ\u002FANTHROPIC_API_KEY\n",[65,525,526],{"class":67,"line":123},[65,527,291],{"emptyLinePlaceholder":290},[65,529,530,532,534,536],{"class":67,"line":131},[65,531,488],{"class":75},[65,533,491],{"class":71},[65,535,93],{"class":75},[65,537,538],{"class":96}," chat-frontier\n",[65,540,541,543],{"class":67,"line":142},[65,542,501],{"class":71},[65,544,76],{"class":75},[65,546,547,549,551],{"class":67,"line":153},[65,548,508],{"class":71},[65,550,93],{"class":75},[65,552,553],{"class":96}," anthropic\u002Fclaude-sonnet-5\n",[65,555,556,558,560],{"class":67,"line":164},[65,557,518],{"class":71},[65,559,93],{"class":75},[65,561,523],{"class":96},[65,563,564],{"class":67,"line":179},[65,565,291],{"emptyLinePlaceholder":290},[65,567,568,570,572,574],{"class":67,"line":190},[65,569,488],{"class":75},[65,571,491],{"class":71},[65,573,93],{"class":75},[65,575,576],{"class":96}," chat-cheap\n",[65,578,579,581],{"class":67,"line":201},[65,580,501],{"class":71},[65,582,76],{"class":75},[65,584,585,587,589],{"class":67,"line":212},[65,586,508],{"class":71},[65,588,93],{"class":75},[65,590,591],{"class":96}," gemini\u002Fgemini-2.5-flash\n",[65,593,594,596,598],{"class":67,"line":220},[65,595,518],{"class":71},[65,597,93],{"class":75},[65,599,600],{"class":96}," os.environ\u002FGEMINI_API_KEY\n",[65,602,603],{"class":67,"line":228},[65,604,291],{"emptyLinePlaceholder":290},[65,606,607,609,611,613],{"class":67,"line":260},[65,608,488],{"class":75},[65,610,491],{"class":71},[65,612,93],{"class":75},[65,614,615],{"class":96}," embeddings\n",[65,617,618,620],{"class":67,"line":268},[65,619,501],{"class":71},[65,621,76],{"class":75},[65,623,624,626,628],{"class":67,"line":276},[65,625,508],{"class":71},[65,627,93],{"class":75},[65,629,630],{"class":96}," openai\u002Ftext-embedding-3-small\n",[65,632,633,635,637],{"class":67,"line":287},[65,634,518],{"class":71},[65,636,93],{"class":75},[65,638,639],{"class":96}," os.environ\u002FOPENAI_API_KEY\n",[65,641,642],{"class":67,"line":294},[65,643,291],{"emptyLinePlaceholder":290},[65,645,646,649],{"class":67,"line":302},[65,647,648],{"class":71},"general_settings",[65,650,76],{"class":75},[65,652,653,656,658],{"class":67,"line":312},[65,654,655],{"class":71},"  master_key",[65,657,93],{"class":75},[65,659,660],{"class":96}," os.environ\u002FLITELLM_MASTER_KEY\n",[65,662,663,666,668],{"class":67,"line":319},[65,664,665],{"class":71},"  database_url",[65,667,93],{"class":75},[65,669,670],{"class":96}," os.environ\u002FDATABASE_URL\n",[10,672,673,674,677,678,681,682,685],{},"Name the models by role, not by vendor. ",[51,675,676],{},"chat-default"," and ",[51,679,680],{},"chat-frontier"," are internal contracts you can repoint at a different provider later without touching a single caller, which is the same reason you put a hostname in front of a database. If you expose ",[51,683,684],{},"claude-sonnet-5"," as the public name, you have leaked a vendor decision into every application in the company and you will pay for it the day you want to change it.",[10,687,688],{},"Bring it up and confirm it answers:",[56,690,694],{"className":691,"code":692,"language":693,"meta":61,"style":61},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","docker compose up -d\ncurl http:\u002F\u002Flocalhost:4000\u002Fhealth\u002Fliveliness\n","bash",[51,695,696,711],{"__ignoreMap":61},[65,697,698,702,705,708],{"class":67,"line":68},[65,699,701],{"class":700},"sBMFI","docker",[65,703,704],{"class":96}," compose",[65,706,707],{"class":96}," up",[65,709,710],{"class":96}," -d\n",[65,712,713,716],{"class":67,"line":79},[65,714,715],{"class":700},"curl",[65,717,718],{"class":96}," http:\u002F\u002Flocalhost:4000\u002Fhealth\u002Fliveliness\n",[17,720,722],{"id":721},"identity-before-budgets","Identity before budgets",[10,724,725],{},"The single most common way this deployment fails is to skip straight to generating keys. Create the teams first, because the team is where the money lives and a key without a team is spend you cannot roll up to anyone.",[56,727,729],{"className":691,"code":728,"language":693,"meta":61,"style":61},"curl -X POST 'http:\u002F\u002Flocalhost:4000\u002Fteam\u002Fnew' \\\n  -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \\\n  -H 'Content-Type: application\u002Fjson' \\\n  -d '{\n    \"team_alias\": \"support\",\n    \"max_budget\": 400,\n    \"budget_duration\": \"30d\",\n    \"models\": [\"chat-default\", \"chat-cheap\"]\n  }'\n",[51,730,731,754,771,784,794,799,804,809,814],{"__ignoreMap":61},[65,732,733,735,738,741,744,747,750],{"class":67,"line":68},[65,734,715],{"class":700},[65,736,737],{"class":96}," -X",[65,739,740],{"class":96}," POST",[65,742,743],{"class":75}," '",[65,745,746],{"class":96},"http:\u002F\u002Flocalhost:4000\u002Fteam\u002Fnew",[65,748,749],{"class":75},"'",[65,751,753],{"class":752},"sTEyZ"," \\\n",[65,755,756,759,761,764,767,769],{"class":67,"line":79},[65,757,758],{"class":96},"  -H",[65,760,114],{"class":75},[65,762,763],{"class":96},"Authorization: Bearer ",[65,765,766],{"class":752},"$LITELLM_MASTER_KEY",[65,768,239],{"class":75},[65,770,753],{"class":752},[65,772,773,775,777,780,782],{"class":67,"line":87},[65,774,758],{"class":96},[65,776,743],{"class":75},[65,778,779],{"class":96},"Content-Type: application\u002Fjson",[65,781,749],{"class":75},[65,783,753],{"class":752},[65,785,786,789,791],{"class":67,"line":100},[65,787,788],{"class":96},"  -d",[65,790,743],{"class":75},[65,792,793],{"class":96},"{\n",[65,795,796],{"class":67,"line":108},[65,797,798],{"class":96},"    \"team_alias\": \"support\",\n",[65,800,801],{"class":67,"line":123},[65,802,803],{"class":96},"    \"max_budget\": 400,\n",[65,805,806],{"class":67,"line":131},[65,807,808],{"class":96},"    \"budget_duration\": \"30d\",\n",[65,810,811],{"class":67,"line":142},[65,812,813],{"class":96},"    \"models\": [\"chat-default\", \"chat-cheap\"]\n",[65,815,816,819],{"class":67,"line":153},[65,817,818],{"class":96},"  }",[65,820,821],{"class":75},"'\n",[10,823,824,825,828],{},"That returns a ",[51,826,827],{},"team_id",", which every key you issue to that department will carry. The team also carries its own ceiling and its own model allowlist, and both are inherited as an outer bound: a key inside the team can be more restricted, never less. This is the structure that makes the finance conversation possible, because a department head can be shown a number that corresponds exactly to a cost center they already own.",[10,830,831],{},"Now issue a key to a person.",[56,833,835],{"className":691,"code":834,"language":693,"meta":61,"style":61},"curl -X POST 'http:\u002F\u002Flocalhost:4000\u002Fkey\u002Fgenerate' \\\n  -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \\\n  -H 'Content-Type: application\u002Fjson' \\\n  -d '{\n    \"key_alias\": \"aminata.support\",\n    \"user_id\": \"aminata@example.com\",\n    \"team_id\": \"\u003Cteam_id from above>\",\n    \"models\": [\"chat-default\", \"chat-cheap\"],\n    \"max_budget\": 40,\n    \"budget_duration\": \"30d\",\n    \"rpm_limit\": 60\n  }'\n",[51,836,837,854,868,880,888,893,898,903,908,913,917,922],{"__ignoreMap":61},[65,838,839,841,843,845,847,850,852],{"class":67,"line":68},[65,840,715],{"class":700},[65,842,737],{"class":96},[65,844,740],{"class":96},[65,846,743],{"class":75},[65,848,849],{"class":96},"http:\u002F\u002Flocalhost:4000\u002Fkey\u002Fgenerate",[65,851,749],{"class":75},[65,853,753],{"class":752},[65,855,856,858,860,862,864,866],{"class":67,"line":79},[65,857,758],{"class":96},[65,859,114],{"class":75},[65,861,763],{"class":96},[65,863,766],{"class":752},[65,865,239],{"class":75},[65,867,753],{"class":752},[65,869,870,872,874,876,878],{"class":67,"line":87},[65,871,758],{"class":96},[65,873,743],{"class":75},[65,875,779],{"class":96},[65,877,749],{"class":75},[65,879,753],{"class":752},[65,881,882,884,886],{"class":67,"line":100},[65,883,788],{"class":96},[65,885,743],{"class":75},[65,887,793],{"class":96},[65,889,890],{"class":67,"line":108},[65,891,892],{"class":96},"    \"key_alias\": \"aminata.support\",\n",[65,894,895],{"class":67,"line":123},[65,896,897],{"class":96},"    \"user_id\": \"aminata@example.com\",\n",[65,899,900],{"class":67,"line":131},[65,901,902],{"class":96},"    \"team_id\": \"\u003Cteam_id from above>\",\n",[65,904,905],{"class":67,"line":142},[65,906,907],{"class":96},"    \"models\": [\"chat-default\", \"chat-cheap\"],\n",[65,909,910],{"class":67,"line":153},[65,911,912],{"class":96},"    \"max_budget\": 40,\n",[65,914,915],{"class":67,"line":164},[65,916,808],{"class":96},[65,918,919],{"class":67,"line":179},[65,920,921],{"class":96},"    \"rpm_limit\": 60\n",[65,923,924,926],{"class":67,"line":190},[65,925,818],{"class":96},[65,927,821],{"class":75},[10,929,930],{},"One key per person. The temptation to issue a single key per application and let five engineers share it will destroy the attribution you are building, and it will do so silently: the numbers keep coming, they are just no longer about anybody. If several services need access, give each service its own key with a service account identity, so that \"the nightly enrichment job\" is as attributable as a human.",[10,932,933],{},"At real headcount, provisioning keys by hand stops scaling and you want identity to come from your identity provider. LiteLLM supports SSO and can map JWT claims onto users and teams, so that group membership in the IdP becomes team membership on the proxy and a departure handled in the IdP is a revocation on the proxy. Defaults for anyone arriving through SSO are set in configuration:",[56,935,937],{"className":58,"code":936,"language":60,"meta":61,"style":61},"litellm_settings:\n  default_internal_user_params:\n    user_role: \"internal_user\"\n    models: [\"chat-default\", \"chat-cheap\"]\n  default_team_params:\n    max_budget: 100\n    budget_duration: 30d\n    team_member_permissions:\n      - \"\u002Fteam\u002Fdaily\u002Factivity\"\n",[51,938,939,946,953,967,993,1000,1010,1020,1027],{"__ignoreMap":61},[65,940,941,944],{"class":67,"line":68},[65,942,943],{"class":71},"litellm_settings",[65,945,76],{"class":75},[65,947,948,951],{"class":67,"line":79},[65,949,950],{"class":71},"  default_internal_user_params",[65,952,76],{"class":75},[65,954,955,958,960,962,965],{"class":67,"line":87},[65,956,957],{"class":71},"    user_role",[65,959,93],{"class":75},[65,961,114],{"class":75},[65,963,964],{"class":96},"internal_user",[65,966,120],{"class":75},[65,968,969,972,974,976,978,980,982,984,986,989,991],{"class":67,"line":100},[65,970,971],{"class":71},"    models",[65,973,93],{"class":75},[65,975,236],{"class":75},[65,977,239],{"class":75},[65,979,676],{"class":96},[65,981,239],{"class":75},[65,983,247],{"class":75},[65,985,114],{"class":75},[65,987,988],{"class":96},"chat-cheap",[65,990,239],{"class":75},[65,992,257],{"class":75},[65,994,995,998],{"class":67,"line":108},[65,996,997],{"class":71},"  default_team_params",[65,999,76],{"class":75},[65,1001,1002,1005,1007],{"class":67,"line":123},[65,1003,1004],{"class":71},"    max_budget",[65,1006,93],{"class":75},[65,1008,1009],{"class":416}," 100\n",[65,1011,1012,1015,1017],{"class":67,"line":131},[65,1013,1014],{"class":71},"    budget_duration",[65,1016,93],{"class":75},[65,1018,1019],{"class":96}," 30d\n",[65,1021,1022,1025],{"class":67,"line":142},[65,1023,1024],{"class":71},"    team_member_permissions",[65,1026,76],{"class":75},[65,1028,1029,1031,1033,1036],{"class":67,"line":153},[65,1030,111],{"class":75},[65,1032,114],{"class":75},[65,1034,1035],{"class":96},"\u002Fteam\u002Fdaily\u002Factivity",[65,1037,120],{"class":75},[10,1039,1040],{},"That last permission is small and worth setting early: it lets ordinary team members see their own team's consumption without an administrator in the loop. Visibility is the mechanism from the first article, and it does not work if the only person who can see the numbers is the platform engineer.",[17,1042,1044],{"id":1043},"budgets-that-reset-and-the-daily-bound","Budgets that reset, and the daily bound",[10,1046,1047],{},"Here is the part that answers the objection every finance director raises, which is that token billing makes a fixed commitment impossible.",[10,1049,1050],{},"A budget with a duration is a recurring ceiling. It is not a prepayment and not a forecast, it is a maximum loss per period, and it resets on its own:",[56,1052,1054],{"className":691,"code":1053,"language":693,"meta":61,"style":61},"curl -X POST 'http:\u002F\u002Flocalhost:4000\u002Fkey\u002Fgenerate' \\\n  -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \\\n  -H 'Content-Type: application\u002Fjson' \\\n  -d '{ \"key_alias\": \"marketing.batch\", \"max_budget\": 50, \"budget_duration\": \"30d\" }'\n",[51,1055,1056,1072,1086,1098],{"__ignoreMap":61},[65,1057,1058,1060,1062,1064,1066,1068,1070],{"class":67,"line":68},[65,1059,715],{"class":700},[65,1061,737],{"class":96},[65,1063,740],{"class":96},[65,1065,743],{"class":75},[65,1067,849],{"class":96},[65,1069,749],{"class":75},[65,1071,753],{"class":752},[65,1073,1074,1076,1078,1080,1082,1084],{"class":67,"line":79},[65,1075,758],{"class":96},[65,1077,114],{"class":75},[65,1079,763],{"class":96},[65,1081,766],{"class":752},[65,1083,239],{"class":75},[65,1085,753],{"class":752},[65,1087,1088,1090,1092,1094,1096],{"class":67,"line":87},[65,1089,758],{"class":96},[65,1091,743],{"class":75},[65,1093,779],{"class":96},[65,1095,749],{"class":75},[65,1097,753],{"class":752},[65,1099,1100,1102,1104,1107],{"class":67,"line":100},[65,1101,788],{"class":96},[65,1103,743],{"class":75},[65,1105,1106],{"class":96},"{ \"key_alias\": \"marketing.batch\", \"max_budget\": 50, \"budget_duration\": \"30d\" }",[65,1108,821],{"class":75},[10,1110,1111],{},"That alone leaves one hole, and it is the hole that actually bites: nothing stops the entire monthly allowance from being consumed in twenty minutes by a misconfigured loop on a Tuesday morning. The month is protected. The month is also over. Stacked budget windows close it:",[56,1113,1115],{"className":691,"code":1114,"language":693,"meta":61,"style":61},"curl -X POST 'http:\u002F\u002Flocalhost:4000\u002Fkey\u002Fgenerate' \\\n  -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \\\n  -H 'Content-Type: application\u002Fjson' \\\n  -d '{\n    \"key_alias\": \"eng.agent-runner\",\n    \"budget_limits\": [\n      {\"budget_duration\": \"24h\", \"max_budget\": 20},\n      {\"budget_duration\": \"30d\", \"max_budget\": 200}\n    ]\n  }'\n",[51,1116,1117,1133,1147,1159,1167,1172,1177,1182,1187,1192],{"__ignoreMap":61},[65,1118,1119,1121,1123,1125,1127,1129,1131],{"class":67,"line":68},[65,1120,715],{"class":700},[65,1122,737],{"class":96},[65,1124,740],{"class":96},[65,1126,743],{"class":75},[65,1128,849],{"class":96},[65,1130,749],{"class":75},[65,1132,753],{"class":752},[65,1134,1135,1137,1139,1141,1143,1145],{"class":67,"line":79},[65,1136,758],{"class":96},[65,1138,114],{"class":75},[65,1140,763],{"class":96},[65,1142,766],{"class":752},[65,1144,239],{"class":75},[65,1146,753],{"class":752},[65,1148,1149,1151,1153,1155,1157],{"class":67,"line":87},[65,1150,758],{"class":96},[65,1152,743],{"class":75},[65,1154,779],{"class":96},[65,1156,749],{"class":75},[65,1158,753],{"class":752},[65,1160,1161,1163,1165],{"class":67,"line":100},[65,1162,788],{"class":96},[65,1164,743],{"class":75},[65,1166,793],{"class":96},[65,1168,1169],{"class":67,"line":108},[65,1170,1171],{"class":96},"    \"key_alias\": \"eng.agent-runner\",\n",[65,1173,1174],{"class":67,"line":123},[65,1175,1176],{"class":96},"    \"budget_limits\": [\n",[65,1178,1179],{"class":67,"line":131},[65,1180,1181],{"class":96},"      {\"budget_duration\": \"24h\", \"max_budget\": 20},\n",[65,1183,1184],{"class":67,"line":142},[65,1185,1186],{"class":96},"      {\"budget_duration\": \"30d\", \"max_budget\": 200}\n",[65,1188,1189],{"class":67,"line":153},[65,1190,1191],{"class":96},"    ]\n",[65,1193,1194,1196],{"class":67,"line":164},[65,1195,818],{"class":96},[65,1197,821],{"class":75},[10,1199,1200],{},"Both windows are enforced. Normal work never approaches the daily bound, so nobody notices it exists; a runaway agent hits it within the hour and stops, having cost you twenty dollars and one afternoon instead of the department's quarter. This is the concrete answer to \"we cannot commit a fixed amount\": you commit a maximum, per day and per month, and you size it from measurement rather than from anxiety.",[10,1202,1203],{},"Then set the soft budget, which is the part that keeps humans in the loop:",[56,1205,1207],{"className":691,"code":1206,"language":693,"meta":61,"style":61},"curl -X POST 'http:\u002F\u002Flocalhost:4000\u002Fkey\u002Fgenerate' \\\n  -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \\\n  -H 'Content-Type: application\u002Fjson' \\\n  -d '{ \"key_alias\": \"eng.agent-runner\", \"soft_budget\": 150, \"max_budget\": 200, \"budget_duration\": \"30d\" }'\n",[51,1208,1209,1225,1239,1251],{"__ignoreMap":61},[65,1210,1211,1213,1215,1217,1219,1221,1223],{"class":67,"line":68},[65,1212,715],{"class":700},[65,1214,737],{"class":96},[65,1216,740],{"class":96},[65,1218,743],{"class":75},[65,1220,849],{"class":96},[65,1222,749],{"class":75},[65,1224,753],{"class":752},[65,1226,1227,1229,1231,1233,1235,1237],{"class":67,"line":79},[65,1228,758],{"class":96},[65,1230,114],{"class":75},[65,1232,763],{"class":96},[65,1234,766],{"class":752},[65,1236,239],{"class":75},[65,1238,753],{"class":752},[65,1240,1241,1243,1245,1247,1249],{"class":67,"line":87},[65,1242,758],{"class":96},[65,1244,743],{"class":75},[65,1246,779],{"class":96},[65,1248,749],{"class":75},[65,1250,753],{"class":752},[65,1252,1253,1255,1257,1260],{"class":67,"line":100},[65,1254,788],{"class":96},[65,1256,743],{"class":75},[65,1258,1259],{"class":96},"{ \"key_alias\": \"eng.agent-runner\", \"soft_budget\": 150, \"max_budget\": 200, \"budget_duration\": \"30d\" }",[65,1261,821],{"class":75},[10,1263,1264],{},"A soft budget triggers an alert rather than a rejection. Wire it to the channel the team lead actually reads, and put real distance between soft and hard so the alert arrives while there is still time to decide something. An alert that fires at ninety-eight percent of the ceiling is not a warning, it is a notification of an outage that has already started.",[17,1266,1268],{"id":1267},"rate-limits-and-why-agents-need-the-token-one","Rate limits, and why agents need the token one",[10,1270,1271],{},"Budgets bound the money over a period. Rate limits bound the rate, and the two failure modes are different enough that you want both.",[56,1273,1275],{"className":691,"code":1274,"language":693,"meta":61,"style":61},"curl -X POST 'http:\u002F\u002Flocalhost:4000\u002Fkey\u002Fgenerate' \\\n  -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \\\n  -H 'Content-Type: application\u002Fjson' \\\n  -d '{ \"key_alias\": \"eng.agent-runner\", \"rpm_limit\": 120, \"tpm_limit\": 400000 }'\n",[51,1276,1277,1293,1307,1319],{"__ignoreMap":61},[65,1278,1279,1281,1283,1285,1287,1289,1291],{"class":67,"line":68},[65,1280,715],{"class":700},[65,1282,737],{"class":96},[65,1284,740],{"class":96},[65,1286,743],{"class":75},[65,1288,849],{"class":96},[65,1290,749],{"class":75},[65,1292,753],{"class":752},[65,1294,1295,1297,1299,1301,1303,1305],{"class":67,"line":79},[65,1296,758],{"class":96},[65,1298,114],{"class":75},[65,1300,763],{"class":96},[65,1302,766],{"class":752},[65,1304,239],{"class":75},[65,1306,753],{"class":752},[65,1308,1309,1311,1313,1315,1317],{"class":67,"line":87},[65,1310,758],{"class":96},[65,1312,743],{"class":75},[65,1314,779],{"class":96},[65,1316,749],{"class":75},[65,1318,753],{"class":752},[65,1320,1321,1323,1325,1328],{"class":67,"line":100},[65,1322,788],{"class":96},[65,1324,743],{"class":75},[65,1326,1327],{"class":96},"{ \"key_alias\": \"eng.agent-runner\", \"rpm_limit\": 120, \"tpm_limit\": 400000 }",[65,1329,821],{"class":75},[10,1331,1332],{},"For conversational traffic, a requests-per-minute limit is the natural guard: a human generates few requests and each is small. For agentic traffic it is close to useless, because the pathological case is not a high request count, it is a modest number of requests each dragging an enormous accumulated context. An agent on its fortieth step may be sending a hundred thousand tokens per call while making one call every few seconds, which sails under any reasonable rpm limit while burning money at a rate no rpm number can express. Cap tokens per minute for anything automated. Cap requests per minute for anything with a person on the other end.",[17,1334,1336],{"id":1335},"model-tiering","Model tiering",[10,1338,1339],{},"The largest single cost lever is which model serves the request, and the gap between the cheapest capable model and the frontier one is a multiple rather than a margin. Tiering is how you exploit that without holding a review meeting about it.",[10,1341,1342],{},"Default everyone to a fast, inexpensive model that handles the large majority of requests acceptably. Grant the frontier tier at the team level to the roles that demonstrably need it. Since keys inherit the team's allowlist as an outer bound, moving someone up a tier is a change to their team membership or their key, not a negotiation:",[56,1344,1346],{"className":691,"code":1345,"language":693,"meta":61,"style":61},"curl -X POST 'http:\u002F\u002Flocalhost:4000\u002Fkey\u002Fupdate' \\\n  -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \\\n  -H 'Content-Type: application\u002Fjson' \\\n  -d '{ \"key\": \"sk-...\", \"models\": [\"chat-default\", \"chat-cheap\", \"chat-frontier\"] }'\n",[51,1347,1348,1365,1379,1391],{"__ignoreMap":61},[65,1349,1350,1352,1354,1356,1358,1361,1363],{"class":67,"line":68},[65,1351,715],{"class":700},[65,1353,737],{"class":96},[65,1355,740],{"class":96},[65,1357,743],{"class":75},[65,1359,1360],{"class":96},"http:\u002F\u002Flocalhost:4000\u002Fkey\u002Fupdate",[65,1362,749],{"class":75},[65,1364,753],{"class":752},[65,1366,1367,1369,1371,1373,1375,1377],{"class":67,"line":79},[65,1368,758],{"class":96},[65,1370,114],{"class":75},[65,1372,763],{"class":96},[65,1374,766],{"class":752},[65,1376,239],{"class":75},[65,1378,753],{"class":752},[65,1380,1381,1383,1385,1387,1389],{"class":67,"line":87},[65,1382,758],{"class":96},[65,1384,743],{"class":75},[65,1386,779],{"class":96},[65,1388,749],{"class":75},[65,1390,753],{"class":752},[65,1392,1393,1395,1397,1400],{"class":67,"line":100},[65,1394,788],{"class":96},[65,1396,743],{"class":75},[65,1398,1399],{"class":96},"{ \"key\": \"sk-...\", \"models\": [\"chat-default\", \"chat-cheap\", \"chat-frontier\"] }",[65,1401,821],{"class":75},[10,1403,1404],{},"Make this a group membership a manager can grant, not a ticket. The moment the upgrade path involves a queue, people go back to their personal accounts and you lose the traffic, which is the only thing you were ever really protecting.",[10,1406,1407,1408,1410],{},"Fallbacks belong in the same conversation, because they are what makes the internal model names honest. If ",[51,1409,676],{}," is unavailable, the proxy can route to an equivalent rather than returning an error to a user who has no idea what a provider outage is:",[56,1412,1414],{"className":58,"code":1413,"language":60,"meta":61,"style":61},"router_settings:\n  fallbacks:\n    - chat-default: [\"chat-cheap\"]\n    - chat-frontier: [\"chat-default\"]\n",[51,1415,1416,1423,1430,1450],{"__ignoreMap":61},[65,1417,1418,1421],{"class":67,"line":68},[65,1419,1420],{"class":71},"router_settings",[65,1422,76],{"class":75},[65,1424,1425,1428],{"class":67,"line":79},[65,1426,1427],{"class":71},"  fallbacks",[65,1429,76],{"class":75},[65,1431,1432,1435,1438,1440,1442,1444,1446,1448],{"class":67,"line":87},[65,1433,1434],{"class":75},"    -",[65,1436,1437],{"class":71}," chat-default",[65,1439,93],{"class":75},[65,1441,236],{"class":75},[65,1443,239],{"class":75},[65,1445,988],{"class":96},[65,1447,239],{"class":75},[65,1449,257],{"class":75},[65,1451,1452,1454,1457,1459,1461,1463,1465,1467],{"class":67,"line":100},[65,1453,1434],{"class":75},[65,1455,1456],{"class":71}," chat-frontier",[65,1458,93],{"class":75},[65,1460,236],{"class":75},[65,1462,239],{"class":75},[65,1464,676],{"class":96},[65,1466,239],{"class":75},[65,1468,257],{"class":75},[10,1470,1471],{},"This is also the mechanism that makes a provider migration a configuration change instead of a project, which matters a great deal in the sovereignty scenario the next article takes up.",[17,1473,1475],{"id":1474},"attribution-beyond-the-key","Attribution beyond the key",[10,1477,1478],{},"Keys tell you who spent. They do not tell you what for, and the moment someone asks you to bill a client or justify a project's cost, that distinction becomes the whole question. Tags carry the second dimension.",[56,1480,1482],{"className":691,"code":1481,"language":693,"meta":61,"style":61},"curl http:\u002F\u002Flocalhost:4000\u002Fv1\u002Fchat\u002Fcompletions \\\n  -H \"Authorization: Bearer $VIRTUAL_KEY\" \\\n  -H 'Content-Type: application\u002Fjson' \\\n  -d '{\n    \"model\": \"chat-default\",\n    \"messages\": [{\"role\": \"user\", \"content\": \"summarize this ticket\"}],\n    \"metadata\": {\n      \"tags\": [\"project:helpdesk-triage\", \"customer:acme\"]\n    }\n  }'\n",[51,1483,1484,1493,1508,1520,1528,1533,1538,1543,1548,1553],{"__ignoreMap":61},[65,1485,1486,1488,1491],{"class":67,"line":68},[65,1487,715],{"class":700},[65,1489,1490],{"class":96}," http:\u002F\u002Flocalhost:4000\u002Fv1\u002Fchat\u002Fcompletions",[65,1492,753],{"class":752},[65,1494,1495,1497,1499,1501,1504,1506],{"class":67,"line":79},[65,1496,758],{"class":96},[65,1498,114],{"class":75},[65,1500,763],{"class":96},[65,1502,1503],{"class":752},"$VIRTUAL_KEY",[65,1505,239],{"class":75},[65,1507,753],{"class":752},[65,1509,1510,1512,1514,1516,1518],{"class":67,"line":87},[65,1511,758],{"class":96},[65,1513,743],{"class":75},[65,1515,779],{"class":96},[65,1517,749],{"class":75},[65,1519,753],{"class":752},[65,1521,1522,1524,1526],{"class":67,"line":100},[65,1523,788],{"class":96},[65,1525,743],{"class":75},[65,1527,793],{"class":96},[65,1529,1530],{"class":67,"line":108},[65,1531,1532],{"class":96},"    \"model\": \"chat-default\",\n",[65,1534,1535],{"class":67,"line":123},[65,1536,1537],{"class":96},"    \"messages\": [{\"role\": \"user\", \"content\": \"summarize this ticket\"}],\n",[65,1539,1540],{"class":67,"line":131},[65,1541,1542],{"class":96},"    \"metadata\": {\n",[65,1544,1545],{"class":67,"line":142},[65,1546,1547],{"class":96},"      \"tags\": [\"project:helpdesk-triage\", \"customer:acme\"]\n",[65,1549,1550],{"class":67,"line":153},[65,1551,1552],{"class":96},"    }\n",[65,1554,1555,1557],{"class":67,"line":164},[65,1556,818],{"class":96},[65,1558,821],{"class":75},[10,1560,1561],{},"Applications you control can set tags directly. For everything else, promote a header into a spend tag so that callers you cannot modify still produce attributable traffic:",[56,1563,1565],{"className":58,"code":1564,"language":60,"meta":61,"style":61},"litellm_settings:\n  extra_spend_tag_headers:\n    - \"x-project-id\"\n    - \"x-customer-id\"\n",[51,1566,1567,1573,1580,1591],{"__ignoreMap":61},[65,1568,1569,1571],{"class":67,"line":68},[65,1570,943],{"class":71},[65,1572,76],{"class":75},[65,1574,1575,1578],{"class":67,"line":79},[65,1576,1577],{"class":71},"  extra_spend_tag_headers",[65,1579,76],{"class":75},[65,1581,1582,1584,1586,1589],{"class":67,"line":87},[65,1583,1434],{"class":75},[65,1585,114],{"class":75},[65,1587,1588],{"class":96},"x-project-id",[65,1590,120],{"class":75},[65,1592,1593,1595,1597,1600],{"class":67,"line":100},[65,1594,1434],{"class":75},[65,1596,114],{"class":75},[65,1598,1599],{"class":96},"x-customer-id",[65,1601,120],{"class":75},[10,1603,1604],{},"Now the same spend can be sliced by person, by team, by project and by end customer, from one ledger. This is what turns the platform from a cost control into something the business asks for, because per customer cost is the input to pricing decisions that were previously made by feel.",[17,1606,1608],{"id":1607},"reading-the-money","Reading the money",[10,1610,1611],{},"The ledger is queryable. The aggregated view is what you send to a department head:",[56,1613,1615],{"className":691,"code":1614,"language":693,"meta":61,"style":61},"curl -G \"http:\u002F\u002Flocalhost:4000\u002Fspend\u002Flogs\" \\\n  -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \\\n  --data-urlencode \"start_date=2026-08-01\" \\\n  --data-urlencode \"end_date=2026-08-31\"\n",[51,1616,1617,1633,1647,1661],{"__ignoreMap":61},[65,1618,1619,1621,1624,1626,1629,1631],{"class":67,"line":68},[65,1620,715],{"class":700},[65,1622,1623],{"class":96}," -G",[65,1625,114],{"class":75},[65,1627,1628],{"class":96},"http:\u002F\u002Flocalhost:4000\u002Fspend\u002Flogs",[65,1630,239],{"class":75},[65,1632,753],{"class":752},[65,1634,1635,1637,1639,1641,1643,1645],{"class":67,"line":79},[65,1636,758],{"class":96},[65,1638,114],{"class":75},[65,1640,763],{"class":96},[65,1642,766],{"class":752},[65,1644,239],{"class":75},[65,1646,753],{"class":752},[65,1648,1649,1652,1654,1657,1659],{"class":67,"line":87},[65,1650,1651],{"class":96},"  --data-urlencode",[65,1653,114],{"class":75},[65,1655,1656],{"class":96},"start_date=2026-08-01",[65,1658,239],{"class":75},[65,1660,753],{"class":752},[65,1662,1663,1665,1667,1670],{"class":67,"line":100},[65,1664,1651],{"class":96},[65,1666,114],{"class":75},[65,1668,1669],{"class":96},"end_date=2026-08-31",[65,1671,120],{"class":75},[10,1673,1674,1675,1678],{},"Passing ",[51,1676,1677],{},"summarize=false"," returns individual transactions instead, each carrying the key, the user, the team and any tags. That per row view is what you need for an audit question, for reconciling an anomaly, and for the first month of the rollout when the interesting output is not a total but a distribution.",[10,1680,1681],{},"For continuous reporting, send the events somewhere that does dashboards properly rather than building them on the proxy. LiteLLM emits to the usual destinations through its callback system, including OpenTelemetry, so LLM spend can land in the same Grafana the rest of your platform already uses. That is the cheapest possible integration for most teams, because the pipeline already exists and cost simply becomes one more signal next to latency and error rate.",[10,1683,1684],{},"The daily activity endpoints are the ones to expose to teams themselves. Self service visibility is what makes the optimization phase happen without you.",[17,1686,1688],{"id":1687},"guardrails-and-what-to-log","Guardrails, and what to log",[10,1690,1691],{},"Once every prompt passes through one place, that place can inspect them, and you have to decide deliberately what it does with that power.",[10,1693,1694],{},"Presidio-based guardrails detect and act on personal data before the request leaves your infrastructure:",[56,1696,1698],{"className":58,"code":1697,"language":60,"meta":61,"style":61},"guardrails:\n  - guardrail_name: \"pii-mask\"\n    litellm_params:\n      guardrail: presidio\n      mode: \"pre_call\"\n      presidio_filter_scope: both\n      presidio_score_thresholds:\n        ALL: 0.7\n        CREDIT_CARD: 0.8\n      pii_entities_config:\n        CREDIT_CARD: \"MASK\"\n        EMAIL_ADDRESS: \"MASK\"\n        IBAN_CODE: \"BLOCK\"\n",[51,1699,1700,1707,1723,1729,1739,1753,1763,1770,1780,1790,1797,1810,1823],{"__ignoreMap":61},[65,1701,1702,1705],{"class":67,"line":68},[65,1703,1704],{"class":71},"guardrails",[65,1706,76],{"class":75},[65,1708,1709,1711,1714,1716,1718,1721],{"class":67,"line":79},[65,1710,488],{"class":75},[65,1712,1713],{"class":71}," guardrail_name",[65,1715,93],{"class":75},[65,1717,114],{"class":75},[65,1719,1720],{"class":96},"pii-mask",[65,1722,120],{"class":75},[65,1724,1725,1727],{"class":67,"line":87},[65,1726,501],{"class":71},[65,1728,76],{"class":75},[65,1730,1731,1734,1736],{"class":67,"line":100},[65,1732,1733],{"class":71},"      guardrail",[65,1735,93],{"class":75},[65,1737,1738],{"class":96}," presidio\n",[65,1740,1741,1744,1746,1748,1751],{"class":67,"line":108},[65,1742,1743],{"class":71},"      mode",[65,1745,93],{"class":75},[65,1747,114],{"class":75},[65,1749,1750],{"class":96},"pre_call",[65,1752,120],{"class":75},[65,1754,1755,1758,1760],{"class":67,"line":123},[65,1756,1757],{"class":71},"      presidio_filter_scope",[65,1759,93],{"class":75},[65,1761,1762],{"class":96}," both\n",[65,1764,1765,1768],{"class":67,"line":131},[65,1766,1767],{"class":71},"      presidio_score_thresholds",[65,1769,76],{"class":75},[65,1771,1772,1775,1777],{"class":67,"line":142},[65,1773,1774],{"class":71},"        ALL",[65,1776,93],{"class":75},[65,1778,1779],{"class":416}," 0.7\n",[65,1781,1782,1785,1787],{"class":67,"line":153},[65,1783,1784],{"class":71},"        CREDIT_CARD",[65,1786,93],{"class":75},[65,1788,1789],{"class":416}," 0.8\n",[65,1791,1792,1795],{"class":67,"line":164},[65,1793,1794],{"class":71},"      pii_entities_config",[65,1796,76],{"class":75},[65,1798,1799,1801,1803,1805,1808],{"class":67,"line":179},[65,1800,1784],{"class":71},[65,1802,93],{"class":75},[65,1804,114],{"class":75},[65,1806,1807],{"class":96},"MASK",[65,1809,120],{"class":75},[65,1811,1812,1815,1817,1819,1821],{"class":67,"line":190},[65,1813,1814],{"class":71},"        EMAIL_ADDRESS",[65,1816,93],{"class":75},[65,1818,114],{"class":75},[65,1820,1807],{"class":96},[65,1822,120],{"class":75},[65,1824,1825,1828,1830,1832,1835],{"class":67,"line":201},[65,1826,1827],{"class":71},"        IBAN_CODE",[65,1829,93],{"class":75},[65,1831,114],{"class":75},[65,1833,1834],{"class":96},"BLOCK",[65,1836,120],{"class":75},[10,1838,1839],{},"Masking replaces the detected entity before the call goes upstream; blocking rejects the request outright. Use blocking sparingly and only for categories where the correct answer is genuinely \"never\", because a guardrail that blocks legitimate work teaches people to route around the proxy, and detection is statistical rather than exact. Treat it as risk reduction, not as a compliance guarantee, and be careful about how you describe it internally, because \"we mask PII\" is heard as \"we cannot leak PII\" by everyone who is not in the room.",[10,1841,1842],{},"The logging decision is the more consequential one. By default, logging integrations can record message content, which means your observability stack becomes a second copy of every prompt in the company, including the ones containing credentials someone pasted by accident. For most organizations the right default is to log metadata and keep the content out:",[56,1844,1846],{"className":58,"code":1845,"language":60,"meta":61,"style":61},"litellm_settings:\n  turn_off_message_logging: true\n",[51,1847,1848,1854],{"__ignoreMap":61},[65,1849,1850,1852],{"class":67,"line":68},[65,1851,943],{"class":71},[65,1853,76],{"class":75},[65,1855,1856,1859,1861],{"class":67,"line":79},[65,1857,1858],{"class":71},"  turn_off_message_logging",[65,1860,93],{"class":75},[65,1862,1864],{"class":1863},"sfNiH"," true\n",[10,1866,1867],{},"You lose the ability to debug a bad response by reading it, which is a real cost. You avoid building a searchable archive of everything your employees have ever asked, which is a real liability and, under some regimes, a processing activity in its own right. If you need content logging for a specific workflow, scope it to that workflow deliberately, with a retention period and a named owner, rather than leaving it on globally because it was the default.",[17,1869,1871],{"id":1870},"day-two","Day two",[10,1873,1874],{},"Three things will surprise you after this is running, and they are worth planning for rather than discovering.",[10,1876,1877,1881],{},[1878,1879,1880],"strong",{},"The numbers will not match the invoice."," The proxy computes cost from a price table maintained in software, and it will drift from what the provider actually bills: cached prefixes discounted, batch tiers, enterprise rates you negotiated, a price change shipped on a Tuesday. The gateway's figures are excellent for relative comparison and allocation between teams, which is what you use them for. They are not accounting truth. Assign someone the monthly reconciliation against the real invoice and treat a widening gap as a signal that a price table or a routing assumption has gone stale.",[10,1883,1884,1887],{},[1878,1885,1886],{},"The proxy is now a single point of failure and an extremely interesting target."," Every AI-dependent workflow in the company fails when it does, so it needs more than one replica, a health check that means something, and a latency budget, since you have inserted a hop into every inference call. It also holds every provider credential and sees every prompt, which makes it a higher value target than most internal services. Its threat model deserves the treatment you would give an identity provider, not the treatment you would give an internal dashboard.",[10,1889,1890,1893],{},[1878,1891,1892],{},"Keys need a lifecycle."," They leak into shell history, CI configuration and laptops. Set expiry at creation, rotate on a schedule, revoke on departure, and prefer SSO-derived keys precisely because they inherit a lifecycle you already operate. A key that outlives its owner is the exact failure the whole exercise was meant to prevent.",[17,1895,1897],{"id":1896},"recap","Recap",[10,1899,1900],{},"The order matters more than any individual setting, and it mirrors the sequence from the first article. Deploy the proxy with its database and pin the version. Create teams before keys, and one key per identity, human or service. Set generous limits and measure for a month without blocking anything. Publish the per team numbers and let the obvious waste get fixed without instruction. Then set budgets from what you measured, with a daily bound stacked under the monthly one, a soft alert far enough below the hard ceiling to be actionable, token-per-minute limits on anything automated, and a default model tier that most people never need to leave.",[10,1902,1903],{},"What you have at that point is the attribution layer, which was the tractable one of the three unknowns. The next article takes up the second one, exposure, in a setting where it stops being a matter of internal policy: what a gateway does and does not change when the prompts leaving your building are personal data and the law asking about them is Togo's 2019-014.",[17,1905,1907],{"id":1906},"sources","Sources",[1909,1910,1911,1930,1949,1977,1988],"ul",{},[1912,1913,1914,1915,1919,1920,1922,1923],"li",{},"LiteLLM documentation, ",[1916,1917,1918],"em",{},"Proxy deployment",": images, environment variables, ",[51,1921,463],{}," semantics and version pinning. ",[1924,1925,1929],"a",{"href":1926,"rel":1927},"https:\u002F\u002Fdocs.litellm.ai\u002Fdocs\u002Fproxy\u002Fdeploy",[1928],"nofollow","docs.litellm.ai\u002Fdocs\u002Fproxy\u002Fdeploy",[1912,1931,1914,1932,1935,1936,1939,1940,1943,1944],{},[1916,1933,1934],{},"Cost tracking and spend logs",": ",[51,1937,1938],{},"\u002Fspend\u002Flogs",", custom metadata, request tags and ",[51,1941,1942],{},"extra_spend_tag_headers",". ",[1924,1945,1948],{"href":1946,"rel":1947},"https:\u002F\u002Fdocs.litellm.ai\u002Fdocs\u002Fproxy\u002Fcost_tracking",[1928],"docs.litellm.ai\u002Fdocs\u002Fproxy\u002Fcost_tracking",[1912,1950,1914,1951,1935,1954,1957,1958,1957,1961,1964,1965,1957,1968,1971,1972],{},[1916,1952,1953],{},"Users, teams and budgets",[51,1955,1956],{},"\u002Fteam\u002Fnew",", ",[51,1959,1960],{},"\u002Fkey\u002Fgenerate",[51,1962,1963],{},"budget_duration",", stacked ",[51,1966,1967],{},"budget_limits",[51,1969,1970],{},"soft_budget",", rpm and tpm limits. ",[1924,1973,1976],{"href":1974,"rel":1975},"https:\u002F\u002Fdocs.litellm.ai\u002Fdocs\u002Fproxy\u002Fusers",[1928],"docs.litellm.ai\u002Fdocs\u002Fproxy\u002Fusers",[1912,1978,1914,1979,1982,1983],{},[1916,1980,1981],{},"Model access control and JWT auth",": team based model access and SSO defaults. ",[1924,1984,1987],{"href":1985,"rel":1986},"https:\u002F\u002Fdocs.litellm.ai\u002Fdocs\u002Fproxy\u002Fmodel_access",[1928],"docs.litellm.ai\u002Fdocs\u002Fproxy\u002Fmodel_access",[1912,1989,1914,1990,1943,1993],{},[1916,1991,1992],{},"Guardrails: PII masking with Presidio",[1924,1994,1997],{"href":1995,"rel":1996},"https:\u002F\u002Fdocs.litellm.ai\u002Fdocs\u002Fproxy\u002Fguardrails\u002Fpii_masking_v2",[1928],"docs.litellm.ai\u002Fdocs\u002Fproxy\u002Fguardrails\u002Fpii_masking_v2",[1999,2000,2001],"style",{},"html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}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);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":61,"searchDepth":79,"depth":79,"links":2003},[2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016],{"id":19,"depth":79,"text":20},{"id":32,"depth":79,"text":33},{"id":45,"depth":79,"text":46},{"id":721,"depth":79,"text":722},{"id":1043,"depth":79,"text":1044},{"id":1267,"depth":79,"text":1268},{"id":1335,"depth":79,"text":1336},{"id":1474,"depth":79,"text":1475},{"id":1607,"depth":79,"text":1608},{"id":1687,"depth":79,"text":1688},{"id":1870,"depth":79,"text":1871},{"id":1896,"depth":79,"text":1897},{"id":1906,"depth":79,"text":1907},"2026-08-30","A hands-on guide to putting a gateway between your people and the model vendors, so that every call is attributed to a named person and a team, every team has a ceiling that resets on a schedule, a daily bound contains the runaway agent, and the finance team finally gets a per team breakdown. Covers deployment with Docker and Postgres, teams and keys, stacked budget windows, rate limits, model tiering, request tags for chargeback, PII guardrails, and the day two work nobody warns you about.",false,"md","https:\u002F\u002Fres.cloudinary.com\u002Fdpdwhd6ka\u002Fimage\u002Fupload\u002Ff_auto,q_auto\u002Fv1\u002FBlog\u002Fimages\u002Fhbcudyxllyjvbkjxvs7g",{},"\u002Fai\u002Fai-budget-with-litellm",{"title":5,"description":2018},"ai-budget-litellm-gateway","ai\u002F2.ai-budget-with-litellm",[2028,2029,2030,2031,2032,2033],"LiteLLM","FinOps","LLM-Gateway","Budgets","Observability","Platform-Engineering",[2035,2036,2037],"ai","finops","platform-engineering","fbNCDvxI5anRkqjl0X6v1bSnTuBZdNSqH5T6EC5sB_Y",{"id":2040,"title":2041,"body":2042,"date":2017,"description":3773,"draft":2019,"extension":2020,"img":2021,"meta":3774,"navigation":290,"path":3775,"seo":3776,"slug":2025,"stem":3777,"tags":3778,"topics":3779,"__hash__":3780},"content\u002Fai\u002F2.ai-budget-with-litellm.fr.md","Donner un vrai budget IA à ses équipes avec LiteLLM",{"type":7,"value":2043,"toc":3758},[2044,2047,2050,2054,2057,2060,2063,2067,2070,2073,2076,2080,2086,2380,2392,2395,2571,2583,2586,2606,2610,2613,2689,2695,2698,2788,2791,2794,2884,2887,2891,2894,2897,2953,2956,3036,3039,3042,3098,3101,3105,3108,3164,3167,3171,3174,3177,3233,3236,3242,3294,3297,3301,3304,3378,3381,3417,3420,3424,3427,3481,3487,3490,3493,3497,3500,3503,3625,3628,3631,3649,3652,3656,3659,3665,3671,3677,3681,3684,3687,3689,3755],[10,2045,2046],{},"L'article précédent défendait l'idée que la gouvernance IA par la politique échoue faute de point de passage où une règle pourrait s'appliquer, et que la voie d'entrée consiste à construire d'abord la couche comptable : mesurer, puis restituer, puis gouverner. Cet article construit cette couche. À la fin, chaque appel émis par une personne ou par un script de votre organisation est attribué à une identité nommée et à une équipe, chaque équipe dispose d'un plafond de dépense qui se réinitialise selon un cycle, une borne journalière se place sous la borne mensuelle pour qu'un seul processus emballé ne consomme pas le mois, des alertes se déclenchent avant tout blocage, et la direction financière peut recevoir une ventilation par équipe qui se réconcilie avec une facture fournisseur.",[10,2048,2049],{},"L'outil est LiteLLM, déployé en proxy. Tout ce qui suit est ordonné pour être suivi dans l'ordre sur une seule machine et aboutir à quelque chose que vous pourriez réellement mettre devant une équipe.",[17,2051,2053],{"id":2052},"ce-que-nous-construisons","Ce que nous construisons",[10,2055,2056],{},"La forme est celle d'un reverse proxy pour l'inférence. Vos applications, vos outils de développement et vos scripts internes cessent de détenir des identifiants fournisseurs et pointent vers un unique point d'entrée interne. Ce point d'entrée parle l'API OpenAI, ce qui compte plus qu'il n'y paraît : presque tous les SDK, frameworks d'agents et intégrations d'éditeur la parlent déjà, donc l'adoption coûte une URL de base et une clé, pas une réécriture.",[10,2058,2059],{},"Derrière ce point d'entrée, le proxy détient les véritables identifiants fournisseurs, décide vers quel amont part une requête, applique les limites attachées à l'appelant, écrit un enregistrement de dépense dans Postgres, et transmet les journaux là où vous les conservez. Le chemin d'une requête est le suivant : l'appelant présente une clé virtuelle, le proxy résout cette clé vers un utilisateur et une équipe, vérifie que le modèle est autorisé et que le budget n'est pas épuisé, transmet au fournisseur, calcule le coût de la réponse à partir de sa table de prix, écrit une ligne de dépense, retourne la réponse.",[10,2061,2062],{},"Postgres n'est pas optionnel pour ce que nous faisons. Sans base de données, le proxy est une commodité de routage ; avec une base, il devient le grand livre, et le grand livre est tout l'enjeu.",[17,2064,2066],{"id":2065},"pourquoi-litellm","Pourquoi LiteLLM",[10,2068,2069],{},"Plusieurs produits existent dans cette catégorie et le résumé honnête est qu'ils diffèrent surtout par le modèle d'hébergement et par les primitives qui sont gratuites.",[10,2071,2072],{},"Les routeurs hébergés comme OpenRouter constituent le chemin le plus rapide vers un accès multi-fournisseurs, mais ils placent un tiers entre vous et vos prompts et ne vous laissent aucun mot à dire sur l'endroit où réside le grand livre, ce qui est rédhibitoire pour quiconque a une obligation de résidence des données. Les outils centrés observabilité comme Langfuse ou Helicone excellent à montrer ce qui s'est passé et ne sont pas, principalement, des points d'application. Les passerelles d'API généralistes peuvent être configurées pour faire ce travail, mais vous modéliserez vous-même les budgets et la tarification par modèle.",[10,2074,2075],{},"LiteLLM est retenu ici parce qu'il est auto-hébergeable, open source, expose une surface compatible OpenAI donc rien en amont ne change, et embarque déjà les primitives dont ce problème a besoin : clés virtuelles, équipes, budgets avec fenêtres de réinitialisation, limites de débit, listes blanches de modèles, marquage des requêtes et grand livre de dépense. Une réserve à vérifier avant de bâtir dessus : une partie des fonctionnalités, dont des pans de la surface SSO et d'administration entreprise, relève d'une licence commerciale. Vérifiez les conditions de licence en vigueur pour les fonctions précises dont vous comptez dépendre, plutôt que de le découvrir pendant le déploiement.",[17,2077,2079],{"id":2078},"déployer-le-proxy","Déployer le proxy",[10,2081,2082,2083,2085],{},"Partez du fichier compose officiel, qui démarre le proxy et sa base ensemble. L'image ",[51,2084,53],{}," embarque l'outillage Prisma et exécute la migration de schéma au démarrage, ce qui convient à un déploiement mono-nœud.",[56,2087,2089],{"className":58,"code":2088,"language":60,"meta":61,"style":61},"services:\n  litellm:\n    image: docker.litellm.ai\u002Fberriai\u002Flitellm-database:v1.81.9-stable\n    ports:\n      - \"4000:4000\"\n    environment:\n      LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}\n      LITELLM_SALT_KEY: ${LITELLM_SALT_KEY}\n      DATABASE_URL: postgresql:\u002F\u002Flitellm:${POSTGRES_PASSWORD}@db:5432\u002Flitellm\n      STORE_MODEL_IN_DB: \"True\"\n      OPENAI_API_KEY: ${OPENAI_API_KEY}\n      ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}\n      GEMINI_API_KEY: ${GEMINI_API_KEY}\n    volumes:\n      - .\u002Fconfig.yaml:\u002Fapp\u002Fconfig.yaml\n    command: [\"--config\", \"\u002Fapp\u002Fconfig.yaml\"]\n    depends_on:\n      db:\n        condition: service_healthy\n        \n  db:\n    image: postgres:16\n    environment:\n      POSTGRES_USER: litellm\n      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}\n      POSTGRES_DB: litellm\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U litellm\"]\n      interval: 5s\n      timeout: 5s\n      retries: 10\n    volumes:\n      - postgres_data:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n\nvolumes:\n  postgres_data:\n",[51,2090,2091,2097,2103,2111,2117,2127,2133,2141,2149,2157,2169,2177,2185,2193,2199,2205,2229,2235,2241,2249,2254,2260,2268,2274,2282,2290,2298,2304,2328,2336,2344,2352,2358,2364,2368,2374],{"__ignoreMap":61},[65,2092,2093,2095],{"class":67,"line":68},[65,2094,72],{"class":71},[65,2096,76],{"class":75},[65,2098,2099,2101],{"class":67,"line":79},[65,2100,82],{"class":71},[65,2102,76],{"class":75},[65,2104,2105,2107,2109],{"class":67,"line":87},[65,2106,90],{"class":71},[65,2108,93],{"class":75},[65,2110,97],{"class":96},[65,2112,2113,2115],{"class":67,"line":100},[65,2114,103],{"class":71},[65,2116,76],{"class":75},[65,2118,2119,2121,2123,2125],{"class":67,"line":108},[65,2120,111],{"class":75},[65,2122,114],{"class":75},[65,2124,117],{"class":96},[65,2126,120],{"class":75},[65,2128,2129,2131],{"class":67,"line":123},[65,2130,126],{"class":71},[65,2132,76],{"class":75},[65,2134,2135,2137,2139],{"class":67,"line":131},[65,2136,134],{"class":71},[65,2138,93],{"class":75},[65,2140,139],{"class":96},[65,2142,2143,2145,2147],{"class":67,"line":142},[65,2144,145],{"class":71},[65,2146,93],{"class":75},[65,2148,150],{"class":96},[65,2150,2151,2153,2155],{"class":67,"line":153},[65,2152,156],{"class":71},[65,2154,93],{"class":75},[65,2156,161],{"class":96},[65,2158,2159,2161,2163,2165,2167],{"class":67,"line":164},[65,2160,167],{"class":71},[65,2162,93],{"class":75},[65,2164,114],{"class":75},[65,2166,174],{"class":96},[65,2168,120],{"class":75},[65,2170,2171,2173,2175],{"class":67,"line":179},[65,2172,182],{"class":71},[65,2174,93],{"class":75},[65,2176,187],{"class":96},[65,2178,2179,2181,2183],{"class":67,"line":190},[65,2180,193],{"class":71},[65,2182,93],{"class":75},[65,2184,198],{"class":96},[65,2186,2187,2189,2191],{"class":67,"line":201},[65,2188,204],{"class":71},[65,2190,93],{"class":75},[65,2192,209],{"class":96},[65,2194,2195,2197],{"class":67,"line":212},[65,2196,215],{"class":71},[65,2198,76],{"class":75},[65,2200,2201,2203],{"class":67,"line":220},[65,2202,111],{"class":75},[65,2204,225],{"class":96},[65,2206,2207,2209,2211,2213,2215,2217,2219,2221,2223,2225,2227],{"class":67,"line":228},[65,2208,231],{"class":71},[65,2210,93],{"class":75},[65,2212,236],{"class":75},[65,2214,239],{"class":75},[65,2216,242],{"class":96},[65,2218,239],{"class":75},[65,2220,247],{"class":75},[65,2222,114],{"class":75},[65,2224,252],{"class":96},[65,2226,239],{"class":75},[65,2228,257],{"class":75},[65,2230,2231,2233],{"class":67,"line":260},[65,2232,263],{"class":71},[65,2234,76],{"class":75},[65,2236,2237,2239],{"class":67,"line":268},[65,2238,271],{"class":71},[65,2240,76],{"class":75},[65,2242,2243,2245,2247],{"class":67,"line":276},[65,2244,279],{"class":71},[65,2246,93],{"class":75},[65,2248,284],{"class":96},[65,2250,2251],{"class":67,"line":287},[65,2252,2253],{"class":752},"        \n",[65,2255,2256,2258],{"class":67,"line":294},[65,2257,297],{"class":71},[65,2259,76],{"class":75},[65,2261,2262,2264,2266],{"class":67,"line":302},[65,2263,90],{"class":71},[65,2265,93],{"class":75},[65,2267,309],{"class":96},[65,2269,2270,2272],{"class":67,"line":312},[65,2271,126],{"class":71},[65,2273,76],{"class":75},[65,2275,2276,2278,2280],{"class":67,"line":319},[65,2277,322],{"class":71},[65,2279,93],{"class":75},[65,2281,327],{"class":96},[65,2283,2284,2286,2288],{"class":67,"line":330},[65,2285,333],{"class":71},[65,2287,93],{"class":75},[65,2289,338],{"class":96},[65,2291,2292,2294,2296],{"class":67,"line":341},[65,2293,344],{"class":71},[65,2295,93],{"class":75},[65,2297,327],{"class":96},[65,2299,2300,2302],{"class":67,"line":351},[65,2301,354],{"class":71},[65,2303,76],{"class":75},[65,2305,2306,2308,2310,2312,2314,2316,2318,2320,2322,2324,2326],{"class":67,"line":359},[65,2307,362],{"class":71},[65,2309,93],{"class":75},[65,2311,236],{"class":75},[65,2313,239],{"class":75},[65,2315,371],{"class":96},[65,2317,239],{"class":75},[65,2319,247],{"class":75},[65,2321,114],{"class":75},[65,2323,380],{"class":96},[65,2325,239],{"class":75},[65,2327,257],{"class":75},[65,2329,2330,2332,2334],{"class":67,"line":387},[65,2331,390],{"class":71},[65,2333,93],{"class":75},[65,2335,395],{"class":96},[65,2337,2338,2340,2342],{"class":67,"line":398},[65,2339,401],{"class":71},[65,2341,93],{"class":75},[65,2343,395],{"class":96},[65,2345,2346,2348,2350],{"class":67,"line":408},[65,2347,411],{"class":71},[65,2349,93],{"class":75},[65,2351,417],{"class":416},[65,2353,2354,2356],{"class":67,"line":420},[65,2355,215],{"class":71},[65,2357,76],{"class":75},[65,2359,2360,2362],{"class":67,"line":427},[65,2361,111],{"class":75},[65,2363,432],{"class":96},[65,2365,2366],{"class":67,"line":435},[65,2367,291],{"emptyLinePlaceholder":290},[65,2369,2370,2372],{"class":67,"line":440},[65,2371,443],{"class":71},[65,2373,76],{"class":75},[65,2375,2376,2378],{"class":67,"line":448},[65,2377,451],{"class":71},[65,2379,76],{"class":75},[10,2381,2382,2383,2385,2386,2388,2389,2391],{},"Deux de ces variables méritent plus d'attention qu'elles n'en reçoivent d'ordinaire. ",[51,2384,459],{}," est l'identifiant d'administration de tout le proxy : elle crée les clés, lit la dépense de tout le monde, et modifie chacune des limites décrites dans cet article. Ce n'est pas une clé applicative et elle ne doit jamais être remise à une équipe. ",[51,2387,463],{}," chiffre les identifiants fournisseurs stockés en base, et elle ne peut plus être changée une fois des modèles ajoutés sans rendre ces identifiants illisibles. Générez les deux depuis une véritable source aléatoire, placez-les dans votre gestionnaire de secrets dès le premier jour, et figez l'image sur un tag de version plutôt que sur ",[51,2390,467],{},", pour qu'un redémarrage ne soit jamais une montée de version surprise.",[10,2393,2394],{},"Le fichier de configuration déclare quels modèles existent et sous quels noms internes.",[56,2396,2397],{"className":58,"code":474,"language":60,"meta":61,"style":61},[51,2398,2399,2405,2415,2421,2429,2437,2441,2451,2457,2465,2473,2477,2487,2493,2501,2509,2513,2523,2529,2537,2545,2549,2555,2563],{"__ignoreMap":61},[65,2400,2401,2403],{"class":67,"line":68},[65,2402,481],{"class":71},[65,2404,76],{"class":75},[65,2406,2407,2409,2411,2413],{"class":67,"line":79},[65,2408,488],{"class":75},[65,2410,491],{"class":71},[65,2412,93],{"class":75},[65,2414,496],{"class":96},[65,2416,2417,2419],{"class":67,"line":87},[65,2418,501],{"class":71},[65,2420,76],{"class":75},[65,2422,2423,2425,2427],{"class":67,"line":100},[65,2424,508],{"class":71},[65,2426,93],{"class":75},[65,2428,513],{"class":96},[65,2430,2431,2433,2435],{"class":67,"line":108},[65,2432,518],{"class":71},[65,2434,93],{"class":75},[65,2436,523],{"class":96},[65,2438,2439],{"class":67,"line":123},[65,2440,291],{"emptyLinePlaceholder":290},[65,2442,2443,2445,2447,2449],{"class":67,"line":131},[65,2444,488],{"class":75},[65,2446,491],{"class":71},[65,2448,93],{"class":75},[65,2450,538],{"class":96},[65,2452,2453,2455],{"class":67,"line":142},[65,2454,501],{"class":71},[65,2456,76],{"class":75},[65,2458,2459,2461,2463],{"class":67,"line":153},[65,2460,508],{"class":71},[65,2462,93],{"class":75},[65,2464,553],{"class":96},[65,2466,2467,2469,2471],{"class":67,"line":164},[65,2468,518],{"class":71},[65,2470,93],{"class":75},[65,2472,523],{"class":96},[65,2474,2475],{"class":67,"line":179},[65,2476,291],{"emptyLinePlaceholder":290},[65,2478,2479,2481,2483,2485],{"class":67,"line":190},[65,2480,488],{"class":75},[65,2482,491],{"class":71},[65,2484,93],{"class":75},[65,2486,576],{"class":96},[65,2488,2489,2491],{"class":67,"line":201},[65,2490,501],{"class":71},[65,2492,76],{"class":75},[65,2494,2495,2497,2499],{"class":67,"line":212},[65,2496,508],{"class":71},[65,2498,93],{"class":75},[65,2500,591],{"class":96},[65,2502,2503,2505,2507],{"class":67,"line":220},[65,2504,518],{"class":71},[65,2506,93],{"class":75},[65,2508,600],{"class":96},[65,2510,2511],{"class":67,"line":228},[65,2512,291],{"emptyLinePlaceholder":290},[65,2514,2515,2517,2519,2521],{"class":67,"line":260},[65,2516,488],{"class":75},[65,2518,491],{"class":71},[65,2520,93],{"class":75},[65,2522,615],{"class":96},[65,2524,2525,2527],{"class":67,"line":268},[65,2526,501],{"class":71},[65,2528,76],{"class":75},[65,2530,2531,2533,2535],{"class":67,"line":276},[65,2532,508],{"class":71},[65,2534,93],{"class":75},[65,2536,630],{"class":96},[65,2538,2539,2541,2543],{"class":67,"line":287},[65,2540,518],{"class":71},[65,2542,93],{"class":75},[65,2544,639],{"class":96},[65,2546,2547],{"class":67,"line":294},[65,2548,291],{"emptyLinePlaceholder":290},[65,2550,2551,2553],{"class":67,"line":302},[65,2552,648],{"class":71},[65,2554,76],{"class":75},[65,2556,2557,2559,2561],{"class":67,"line":312},[65,2558,655],{"class":71},[65,2560,93],{"class":75},[65,2562,660],{"class":96},[65,2564,2565,2567,2569],{"class":67,"line":319},[65,2566,665],{"class":71},[65,2568,93],{"class":75},[65,2570,670],{"class":96},[10,2572,2573,2574,2576,2577,2579,2580,2582],{},"Nommez les modèles par rôle, pas par fournisseur. ",[51,2575,676],{}," et ",[51,2578,680],{}," sont des contrats internes que vous pourrez repointer vers un autre fournisseur plus tard sans toucher un seul appelant, pour la même raison qu'on place un nom d'hôte devant une base de données. Si vous exposez ",[51,2581,684],{}," comme nom public, vous avez fait fuiter une décision fournisseur dans toutes les applications de l'entreprise, et vous le paierez le jour où vous voudrez en changer.",[10,2584,2585],{},"Démarrez et vérifiez que le service répond :",[56,2587,2588],{"className":691,"code":692,"language":693,"meta":61,"style":61},[51,2589,2590,2600],{"__ignoreMap":61},[65,2591,2592,2594,2596,2598],{"class":67,"line":68},[65,2593,701],{"class":700},[65,2595,704],{"class":96},[65,2597,707],{"class":96},[65,2599,710],{"class":96},[65,2601,2602,2604],{"class":67,"line":79},[65,2603,715],{"class":700},[65,2605,718],{"class":96},[17,2607,2609],{"id":2608},"lidentité-avant-les-budgets","L'identité avant les budgets",[10,2611,2612],{},"La manière la plus courante de rater ce déploiement est de passer directement à la génération des clés. Créez les équipes d'abord, parce que l'équipe est l'endroit où vit l'argent et qu'une clé sans équipe est une dépense que vous ne pourrez consolider vers personne.",[56,2614,2615],{"className":691,"code":728,"language":693,"meta":61,"style":61},[51,2616,2617,2633,2647,2659,2667,2671,2675,2679,2683],{"__ignoreMap":61},[65,2618,2619,2621,2623,2625,2627,2629,2631],{"class":67,"line":68},[65,2620,715],{"class":700},[65,2622,737],{"class":96},[65,2624,740],{"class":96},[65,2626,743],{"class":75},[65,2628,746],{"class":96},[65,2630,749],{"class":75},[65,2632,753],{"class":752},[65,2634,2635,2637,2639,2641,2643,2645],{"class":67,"line":79},[65,2636,758],{"class":96},[65,2638,114],{"class":75},[65,2640,763],{"class":96},[65,2642,766],{"class":752},[65,2644,239],{"class":75},[65,2646,753],{"class":752},[65,2648,2649,2651,2653,2655,2657],{"class":67,"line":87},[65,2650,758],{"class":96},[65,2652,743],{"class":75},[65,2654,779],{"class":96},[65,2656,749],{"class":75},[65,2658,753],{"class":752},[65,2660,2661,2663,2665],{"class":67,"line":100},[65,2662,788],{"class":96},[65,2664,743],{"class":75},[65,2666,793],{"class":96},[65,2668,2669],{"class":67,"line":108},[65,2670,798],{"class":96},[65,2672,2673],{"class":67,"line":123},[65,2674,803],{"class":96},[65,2676,2677],{"class":67,"line":131},[65,2678,808],{"class":96},[65,2680,2681],{"class":67,"line":142},[65,2682,813],{"class":96},[65,2684,2685,2687],{"class":67,"line":153},[65,2686,818],{"class":96},[65,2688,821],{"class":75},[10,2690,2691,2692,2694],{},"Cela retourne un ",[51,2693,827],{},", que portera chaque clé émise pour ce département. L'équipe porte aussi son propre plafond et sa propre liste blanche de modèles, et les deux s'héritent comme une borne extérieure : une clé dans l'équipe peut être plus restreinte, jamais moins. C'est cette structure qui rend la conversation financière possible, parce qu'un responsable de département peut se voir présenter un chiffre qui correspond exactement à un centre de coût qu'il porte déjà.",[10,2696,2697],{},"Émettez maintenant une clé pour une personne.",[56,2699,2701],{"className":691,"code":2700,"language":693,"meta":61,"style":61},"curl -X POST 'http:\u002F\u002Flocalhost:4000\u002Fkey\u002Fgenerate' \\\n  -H \"Authorization: Bearer $LITELLM_MASTER_KEY\" \\\n  -H 'Content-Type: application\u002Fjson' \\\n  -d '{\n    \"key_alias\": \"aminata.support\",\n    \"user_id\": \"aminata@example.com\",\n    \"team_id\": \"\u003Cteam_id obtenu ci-dessus>\",\n    \"models\": [\"chat-default\", \"chat-cheap\"],\n    \"max_budget\": 40,\n    \"budget_duration\": \"30d\",\n    \"rpm_limit\": 60\n  }'\n",[51,2702,2703,2719,2733,2745,2753,2757,2761,2766,2770,2774,2778,2782],{"__ignoreMap":61},[65,2704,2705,2707,2709,2711,2713,2715,2717],{"class":67,"line":68},[65,2706,715],{"class":700},[65,2708,737],{"class":96},[65,2710,740],{"class":96},[65,2712,743],{"class":75},[65,2714,849],{"class":96},[65,2716,749],{"class":75},[65,2718,753],{"class":752},[65,2720,2721,2723,2725,2727,2729,2731],{"class":67,"line":79},[65,2722,758],{"class":96},[65,2724,114],{"class":75},[65,2726,763],{"class":96},[65,2728,766],{"class":752},[65,2730,239],{"class":75},[65,2732,753],{"class":752},[65,2734,2735,2737,2739,2741,2743],{"class":67,"line":87},[65,2736,758],{"class":96},[65,2738,743],{"class":75},[65,2740,779],{"class":96},[65,2742,749],{"class":75},[65,2744,753],{"class":752},[65,2746,2747,2749,2751],{"class":67,"line":100},[65,2748,788],{"class":96},[65,2750,743],{"class":75},[65,2752,793],{"class":96},[65,2754,2755],{"class":67,"line":108},[65,2756,892],{"class":96},[65,2758,2759],{"class":67,"line":123},[65,2760,897],{"class":96},[65,2762,2763],{"class":67,"line":131},[65,2764,2765],{"class":96},"    \"team_id\": \"\u003Cteam_id obtenu ci-dessus>\",\n",[65,2767,2768],{"class":67,"line":142},[65,2769,907],{"class":96},[65,2771,2772],{"class":67,"line":153},[65,2773,912],{"class":96},[65,2775,2776],{"class":67,"line":164},[65,2777,808],{"class":96},[65,2779,2780],{"class":67,"line":179},[65,2781,921],{"class":96},[65,2783,2784,2786],{"class":67,"line":190},[65,2785,818],{"class":96},[65,2787,821],{"class":75},[10,2789,2790],{},"Une clé par personne. La tentation d'émettre une seule clé par application et de la faire partager par cinq ingénieurs détruira l'attribution que vous construisez, et elle le fera silencieusement : les chiffres continuent d'arriver, ils ne parlent simplement plus de personne. Si plusieurs services ont besoin d'un accès, donnez à chacun sa propre clé avec une identité de compte de service, pour que « le job d'enrichissement nocturne » soit aussi attribuable qu'un humain.",[10,2792,2793],{},"À effectif réel, provisionner les clés à la main cesse de passer à l'échelle et vous voulez que l'identité vienne de votre fournisseur d'identité. LiteLLM prend en charge le SSO et sait projeter des revendications JWT sur des utilisateurs et des équipes, de sorte que l'appartenance à un groupe dans l'IdP devienne l'appartenance à une équipe sur le proxy, et qu'un départ traité dans l'IdP soit une révocation sur le proxy. Les valeurs par défaut pour toute personne arrivant par SSO se posent en configuration :",[56,2795,2796],{"className":58,"code":936,"language":60,"meta":61,"style":61},[51,2797,2798,2804,2810,2822,2846,2852,2860,2868,2874],{"__ignoreMap":61},[65,2799,2800,2802],{"class":67,"line":68},[65,2801,943],{"class":71},[65,2803,76],{"class":75},[65,2805,2806,2808],{"class":67,"line":79},[65,2807,950],{"class":71},[65,2809,76],{"class":75},[65,2811,2812,2814,2816,2818,2820],{"class":67,"line":87},[65,2813,957],{"class":71},[65,2815,93],{"class":75},[65,2817,114],{"class":75},[65,2819,964],{"class":96},[65,2821,120],{"class":75},[65,2823,2824,2826,2828,2830,2832,2834,2836,2838,2840,2842,2844],{"class":67,"line":100},[65,2825,971],{"class":71},[65,2827,93],{"class":75},[65,2829,236],{"class":75},[65,2831,239],{"class":75},[65,2833,676],{"class":96},[65,2835,239],{"class":75},[65,2837,247],{"class":75},[65,2839,114],{"class":75},[65,2841,988],{"class":96},[65,2843,239],{"class":75},[65,2845,257],{"class":75},[65,2847,2848,2850],{"class":67,"line":108},[65,2849,997],{"class":71},[65,2851,76],{"class":75},[65,2853,2854,2856,2858],{"class":67,"line":123},[65,2855,1004],{"class":71},[65,2857,93],{"class":75},[65,2859,1009],{"class":416},[65,2861,2862,2864,2866],{"class":67,"line":131},[65,2863,1014],{"class":71},[65,2865,93],{"class":75},[65,2867,1019],{"class":96},[65,2869,2870,2872],{"class":67,"line":142},[65,2871,1024],{"class":71},[65,2873,76],{"class":75},[65,2875,2876,2878,2880,2882],{"class":67,"line":153},[65,2877,111],{"class":75},[65,2879,114],{"class":75},[65,2881,1035],{"class":96},[65,2883,120],{"class":75},[10,2885,2886],{},"Cette dernière permission est modeste et mérite d'être posée tôt : elle permet aux membres ordinaires d'une équipe de consulter la consommation de leur propre équipe sans passer par un administrateur. La visibilité est le mécanisme du premier article, et elle ne fonctionne pas si la seule personne qui peut voir les chiffres est l'ingénieur plateforme.",[17,2888,2890],{"id":2889},"des-budgets-qui-se-réinitialisent-et-la-borne-journalière","Des budgets qui se réinitialisent, et la borne journalière",[10,2892,2893],{},"Voici la partie qui répond à l'objection que soulève chaque directeur financier : la facturation au token rendrait tout engagement fixe impossible.",[10,2895,2896],{},"Un budget assorti d'une durée est un plafond récurrent. Ce n'est ni un prépaiement ni une prévision, c'est une perte maximale par période, et il se réinitialise tout seul :",[56,2898,2899],{"className":691,"code":1053,"language":693,"meta":61,"style":61},[51,2900,2901,2917,2931,2943],{"__ignoreMap":61},[65,2902,2903,2905,2907,2909,2911,2913,2915],{"class":67,"line":68},[65,2904,715],{"class":700},[65,2906,737],{"class":96},[65,2908,740],{"class":96},[65,2910,743],{"class":75},[65,2912,849],{"class":96},[65,2914,749],{"class":75},[65,2916,753],{"class":752},[65,2918,2919,2921,2923,2925,2927,2929],{"class":67,"line":79},[65,2920,758],{"class":96},[65,2922,114],{"class":75},[65,2924,763],{"class":96},[65,2926,766],{"class":752},[65,2928,239],{"class":75},[65,2930,753],{"class":752},[65,2932,2933,2935,2937,2939,2941],{"class":67,"line":87},[65,2934,758],{"class":96},[65,2936,743],{"class":75},[65,2938,779],{"class":96},[65,2940,749],{"class":75},[65,2942,753],{"class":752},[65,2944,2945,2947,2949,2951],{"class":67,"line":100},[65,2946,788],{"class":96},[65,2948,743],{"class":75},[65,2950,1106],{"class":96},[65,2952,821],{"class":75},[10,2954,2955],{},"Cela laisse un trou, et c'est celui qui mord vraiment : rien n'empêche l'allocation mensuelle entière d'être consommée en vingt minutes par une boucle mal configurée un mardi matin. Le mois est protégé. Le mois est aussi terminé. Les fenêtres de budget empilées referment ce trou :",[56,2957,2958],{"className":691,"code":1114,"language":693,"meta":61,"style":61},[51,2959,2960,2976,2990,3002,3010,3014,3018,3022,3026,3030],{"__ignoreMap":61},[65,2961,2962,2964,2966,2968,2970,2972,2974],{"class":67,"line":68},[65,2963,715],{"class":700},[65,2965,737],{"class":96},[65,2967,740],{"class":96},[65,2969,743],{"class":75},[65,2971,849],{"class":96},[65,2973,749],{"class":75},[65,2975,753],{"class":752},[65,2977,2978,2980,2982,2984,2986,2988],{"class":67,"line":79},[65,2979,758],{"class":96},[65,2981,114],{"class":75},[65,2983,763],{"class":96},[65,2985,766],{"class":752},[65,2987,239],{"class":75},[65,2989,753],{"class":752},[65,2991,2992,2994,2996,2998,3000],{"class":67,"line":87},[65,2993,758],{"class":96},[65,2995,743],{"class":75},[65,2997,779],{"class":96},[65,2999,749],{"class":75},[65,3001,753],{"class":752},[65,3003,3004,3006,3008],{"class":67,"line":100},[65,3005,788],{"class":96},[65,3007,743],{"class":75},[65,3009,793],{"class":96},[65,3011,3012],{"class":67,"line":108},[65,3013,1171],{"class":96},[65,3015,3016],{"class":67,"line":123},[65,3017,1176],{"class":96},[65,3019,3020],{"class":67,"line":131},[65,3021,1181],{"class":96},[65,3023,3024],{"class":67,"line":142},[65,3025,1186],{"class":96},[65,3027,3028],{"class":67,"line":153},[65,3029,1191],{"class":96},[65,3031,3032,3034],{"class":67,"line":164},[65,3033,818],{"class":96},[65,3035,821],{"class":75},[10,3037,3038],{},"Les deux fenêtres sont appliquées. Le travail normal n'approche jamais la borne journalière, donc personne ne remarque son existence ; un agent emballé l'atteint dans l'heure et s'arrête, vous ayant coûté vingt dollars et un après-midi au lieu du trimestre du département. C'est la réponse concrète à « nous ne pouvons pas engager un montant fixe » : vous engagez un maximum, par jour et par mois, et vous le dimensionnez à partir de la mesure plutôt que de l'inquiétude.",[10,3040,3041],{},"Posez ensuite le budget souple, qui est la partie qui garde les humains dans la boucle :",[56,3043,3044],{"className":691,"code":1206,"language":693,"meta":61,"style":61},[51,3045,3046,3062,3076,3088],{"__ignoreMap":61},[65,3047,3048,3050,3052,3054,3056,3058,3060],{"class":67,"line":68},[65,3049,715],{"class":700},[65,3051,737],{"class":96},[65,3053,740],{"class":96},[65,3055,743],{"class":75},[65,3057,849],{"class":96},[65,3059,749],{"class":75},[65,3061,753],{"class":752},[65,3063,3064,3066,3068,3070,3072,3074],{"class":67,"line":79},[65,3065,758],{"class":96},[65,3067,114],{"class":75},[65,3069,763],{"class":96},[65,3071,766],{"class":752},[65,3073,239],{"class":75},[65,3075,753],{"class":752},[65,3077,3078,3080,3082,3084,3086],{"class":67,"line":87},[65,3079,758],{"class":96},[65,3081,743],{"class":75},[65,3083,779],{"class":96},[65,3085,749],{"class":75},[65,3087,753],{"class":752},[65,3089,3090,3092,3094,3096],{"class":67,"line":100},[65,3091,788],{"class":96},[65,3093,743],{"class":75},[65,3095,1259],{"class":96},[65,3097,821],{"class":75},[10,3099,3100],{},"Un budget souple déclenche une alerte plutôt qu'un rejet. Branchez-la sur le canal que le responsable d'équipe lit réellement, et mettez une vraie distance entre le souple et le dur pour que l'alerte arrive tant qu'il reste du temps pour décider quelque chose. Une alerte qui se déclenche à quatre-vingt-dix-huit pour cent du plafond n'est pas un avertissement, c'est la notification d'une panne déjà commencée.",[17,3102,3104],{"id":3103},"limites-de-débit-et-pourquoi-les-agents-ont-besoin-de-celle-en-tokens","Limites de débit, et pourquoi les agents ont besoin de celle en tokens",[10,3106,3107],{},"Les budgets bornent l'argent sur une période. Les limites de débit bornent le rythme, et les deux modes de défaillance sont assez différents pour justifier les deux.",[56,3109,3110],{"className":691,"code":1274,"language":693,"meta":61,"style":61},[51,3111,3112,3128,3142,3154],{"__ignoreMap":61},[65,3113,3114,3116,3118,3120,3122,3124,3126],{"class":67,"line":68},[65,3115,715],{"class":700},[65,3117,737],{"class":96},[65,3119,740],{"class":96},[65,3121,743],{"class":75},[65,3123,849],{"class":96},[65,3125,749],{"class":75},[65,3127,753],{"class":752},[65,3129,3130,3132,3134,3136,3138,3140],{"class":67,"line":79},[65,3131,758],{"class":96},[65,3133,114],{"class":75},[65,3135,763],{"class":96},[65,3137,766],{"class":752},[65,3139,239],{"class":75},[65,3141,753],{"class":752},[65,3143,3144,3146,3148,3150,3152],{"class":67,"line":87},[65,3145,758],{"class":96},[65,3147,743],{"class":75},[65,3149,779],{"class":96},[65,3151,749],{"class":75},[65,3153,753],{"class":752},[65,3155,3156,3158,3160,3162],{"class":67,"line":100},[65,3157,788],{"class":96},[65,3159,743],{"class":75},[65,3161,1327],{"class":96},[65,3163,821],{"class":75},[10,3165,3166],{},"Pour le trafic conversationnel, une limite de requêtes par minute est le garde-fou naturel : un humain génère peu de requêtes et chacune est petite. Pour le trafic agentique elle est presque inutile, car le cas pathologique n'est pas un nombre élevé de requêtes, c'est un nombre modeste de requêtes traînant chacune un contexte accumulé énorme. Un agent à sa quarantième étape peut envoyer cent mille tokens par appel en ne faisant qu'un appel toutes les quelques secondes, ce qui passe largement sous n'importe quelle limite rpm raisonnable tout en brûlant de l'argent à un rythme qu'aucun chiffre de rpm ne sait exprimer. Plafonnez les tokens par minute pour tout ce qui est automatisé. Plafonnez les requêtes par minute pour tout ce qui a une personne au bout.",[17,3168,3170],{"id":3169},"hiérarchiser-les-modèles","Hiérarchiser les modèles",[10,3172,3173],{},"Le plus gros levier de coût unitaire est le choix du modèle qui sert la requête, et l'écart entre le modèle le moins cher qui fait le travail et le modèle frontier se compte en multiples et non en marge. La hiérarchisation est la façon d'exploiter cet écart sans tenir une réunion à ce sujet.",[10,3175,3176],{},"Placez tout le monde par défaut sur un modèle rapide et peu coûteux qui traite correctement la grande majorité des demandes. Accordez le niveau frontier au niveau de l'équipe, aux rôles qui en ont démontrablement besoin. Comme les clés héritent de la liste blanche de l'équipe comme borne extérieure, faire monter quelqu'un d'un niveau est une modification de son appartenance ou de sa clé, pas une négociation :",[56,3178,3179],{"className":691,"code":1345,"language":693,"meta":61,"style":61},[51,3180,3181,3197,3211,3223],{"__ignoreMap":61},[65,3182,3183,3185,3187,3189,3191,3193,3195],{"class":67,"line":68},[65,3184,715],{"class":700},[65,3186,737],{"class":96},[65,3188,740],{"class":96},[65,3190,743],{"class":75},[65,3192,1360],{"class":96},[65,3194,749],{"class":75},[65,3196,753],{"class":752},[65,3198,3199,3201,3203,3205,3207,3209],{"class":67,"line":79},[65,3200,758],{"class":96},[65,3202,114],{"class":75},[65,3204,763],{"class":96},[65,3206,766],{"class":752},[65,3208,239],{"class":75},[65,3210,753],{"class":752},[65,3212,3213,3215,3217,3219,3221],{"class":67,"line":87},[65,3214,758],{"class":96},[65,3216,743],{"class":75},[65,3218,779],{"class":96},[65,3220,749],{"class":75},[65,3222,753],{"class":752},[65,3224,3225,3227,3229,3231],{"class":67,"line":100},[65,3226,788],{"class":96},[65,3228,743],{"class":75},[65,3230,1399],{"class":96},[65,3232,821],{"class":75},[10,3234,3235],{},"Faites-en une appartenance de groupe qu'un responsable peut accorder, pas un ticket. Dès l'instant où la montée en niveau passe par une file d'attente, les gens repartent sur leurs comptes personnels et vous perdez le trafic, qui est la seule chose que vous protégiez réellement.",[10,3237,3238,3239,3241],{},"Les fallbacks appartiennent à la même conversation, car ils rendent honnêtes les noms de modèles internes. Si ",[51,3240,676],{}," est indisponible, le proxy peut router vers un équivalent au lieu de retourner une erreur à un utilisateur qui n'a aucune idée de ce qu'est une panne fournisseur :",[56,3243,3244],{"className":58,"code":1413,"language":60,"meta":61,"style":61},[51,3245,3246,3252,3258,3276],{"__ignoreMap":61},[65,3247,3248,3250],{"class":67,"line":68},[65,3249,1420],{"class":71},[65,3251,76],{"class":75},[65,3253,3254,3256],{"class":67,"line":79},[65,3255,1427],{"class":71},[65,3257,76],{"class":75},[65,3259,3260,3262,3264,3266,3268,3270,3272,3274],{"class":67,"line":87},[65,3261,1434],{"class":75},[65,3263,1437],{"class":71},[65,3265,93],{"class":75},[65,3267,236],{"class":75},[65,3269,239],{"class":75},[65,3271,988],{"class":96},[65,3273,239],{"class":75},[65,3275,257],{"class":75},[65,3277,3278,3280,3282,3284,3286,3288,3290,3292],{"class":67,"line":100},[65,3279,1434],{"class":75},[65,3281,1456],{"class":71},[65,3283,93],{"class":75},[65,3285,236],{"class":75},[65,3287,239],{"class":75},[65,3289,676],{"class":96},[65,3291,239],{"class":75},[65,3293,257],{"class":75},[10,3295,3296],{},"C'est aussi le mécanisme qui transforme une migration de fournisseur en changement de configuration plutôt qu'en projet, ce qui compte beaucoup dans le scénario de souveraineté que reprend l'article suivant.",[17,3298,3300],{"id":3299},"lattribution-au-delà-de-la-clé","L'attribution au-delà de la clé",[10,3302,3303],{},"Les clés disent qui a dépensé. Elles ne disent pas pour quoi, et dès qu'on vous demande de facturer un client ou de justifier le coût d'un projet, cette distinction devient toute la question. Les tags portent la seconde dimension.",[56,3305,3307],{"className":691,"code":3306,"language":693,"meta":61,"style":61},"curl http:\u002F\u002Flocalhost:4000\u002Fv1\u002Fchat\u002Fcompletions \\\n  -H \"Authorization: Bearer $VIRTUAL_KEY\" \\\n  -H 'Content-Type: application\u002Fjson' \\\n  -d '{\n    \"model\": \"chat-default\",\n    \"messages\": [{\"role\": \"user\", \"content\": \"résume ce ticket\"}],\n    \"metadata\": {\n      \"tags\": [\"project:helpdesk-triage\", \"customer:acme\"]\n    }\n  }'\n",[51,3308,3309,3317,3331,3343,3351,3355,3360,3364,3368,3372],{"__ignoreMap":61},[65,3310,3311,3313,3315],{"class":67,"line":68},[65,3312,715],{"class":700},[65,3314,1490],{"class":96},[65,3316,753],{"class":752},[65,3318,3319,3321,3323,3325,3327,3329],{"class":67,"line":79},[65,3320,758],{"class":96},[65,3322,114],{"class":75},[65,3324,763],{"class":96},[65,3326,1503],{"class":752},[65,3328,239],{"class":75},[65,3330,753],{"class":752},[65,3332,3333,3335,3337,3339,3341],{"class":67,"line":87},[65,3334,758],{"class":96},[65,3336,743],{"class":75},[65,3338,779],{"class":96},[65,3340,749],{"class":75},[65,3342,753],{"class":752},[65,3344,3345,3347,3349],{"class":67,"line":100},[65,3346,788],{"class":96},[65,3348,743],{"class":75},[65,3350,793],{"class":96},[65,3352,3353],{"class":67,"line":108},[65,3354,1532],{"class":96},[65,3356,3357],{"class":67,"line":123},[65,3358,3359],{"class":96},"    \"messages\": [{\"role\": \"user\", \"content\": \"résume ce ticket\"}],\n",[65,3361,3362],{"class":67,"line":131},[65,3363,1542],{"class":96},[65,3365,3366],{"class":67,"line":142},[65,3367,1547],{"class":96},[65,3369,3370],{"class":67,"line":153},[65,3371,1552],{"class":96},[65,3373,3374,3376],{"class":67,"line":164},[65,3375,818],{"class":96},[65,3377,821],{"class":75},[10,3379,3380],{},"Les applications que vous contrôlez peuvent poser les tags directement. Pour tout le reste, promouvez un en-tête en tag de dépense afin que des appelants que vous ne pouvez pas modifier produisent quand même du trafic attribuable :",[56,3382,3383],{"className":58,"code":1564,"language":60,"meta":61,"style":61},[51,3384,3385,3391,3397,3407],{"__ignoreMap":61},[65,3386,3387,3389],{"class":67,"line":68},[65,3388,943],{"class":71},[65,3390,76],{"class":75},[65,3392,3393,3395],{"class":67,"line":79},[65,3394,1577],{"class":71},[65,3396,76],{"class":75},[65,3398,3399,3401,3403,3405],{"class":67,"line":87},[65,3400,1434],{"class":75},[65,3402,114],{"class":75},[65,3404,1588],{"class":96},[65,3406,120],{"class":75},[65,3408,3409,3411,3413,3415],{"class":67,"line":100},[65,3410,1434],{"class":75},[65,3412,114],{"class":75},[65,3414,1599],{"class":96},[65,3416,120],{"class":75},[10,3418,3419],{},"La même dépense se découpe désormais par personne, par équipe, par projet et par client final, depuis un seul grand livre. C'est ce qui fait passer la plateforme du statut de contrôle de coût à celui de service que le métier réclame, parce que le coût par client est l'entrée de décisions de tarification qui se prenaient jusque-là au feeling.",[17,3421,3423],{"id":3422},"lire-largent","Lire l'argent",[10,3425,3426],{},"Le grand livre est interrogeable. La vue agrégée est celle que vous envoyez à un responsable de département :",[56,3428,3429],{"className":691,"code":1614,"language":693,"meta":61,"style":61},[51,3430,3431,3445,3459,3471],{"__ignoreMap":61},[65,3432,3433,3435,3437,3439,3441,3443],{"class":67,"line":68},[65,3434,715],{"class":700},[65,3436,1623],{"class":96},[65,3438,114],{"class":75},[65,3440,1628],{"class":96},[65,3442,239],{"class":75},[65,3444,753],{"class":752},[65,3446,3447,3449,3451,3453,3455,3457],{"class":67,"line":79},[65,3448,758],{"class":96},[65,3450,114],{"class":75},[65,3452,763],{"class":96},[65,3454,766],{"class":752},[65,3456,239],{"class":75},[65,3458,753],{"class":752},[65,3460,3461,3463,3465,3467,3469],{"class":67,"line":87},[65,3462,1651],{"class":96},[65,3464,114],{"class":75},[65,3466,1656],{"class":96},[65,3468,239],{"class":75},[65,3470,753],{"class":752},[65,3472,3473,3475,3477,3479],{"class":67,"line":100},[65,3474,1651],{"class":96},[65,3476,114],{"class":75},[65,3478,1669],{"class":96},[65,3480,120],{"class":75},[10,3482,3483,3484,3486],{},"Passer ",[51,3485,1677],{}," retourne les transactions individuelles, chacune portant la clé, l'utilisateur, l'équipe et les tags éventuels. Cette vue ligne à ligne est celle dont vous avez besoin pour une question d'audit, pour réconcilier une anomalie, et pour le premier mois du déploiement, où la sortie intéressante n'est pas un total mais une distribution.",[10,3488,3489],{},"Pour le reporting continu, envoyez les événements vers un endroit qui fait correctement des tableaux de bord plutôt que de les construire sur le proxy. LiteLLM émet vers les destinations habituelles via son système de callbacks, dont OpenTelemetry, si bien que la dépense LLM peut atterrir dans le même Grafana que le reste de votre plateforme. C'est l'intégration la moins coûteuse pour la plupart des équipes, parce que le pipeline existe déjà et que le coût devient simplement un signal de plus à côté de la latence et du taux d'erreur.",[10,3491,3492],{},"Les endpoints d'activité journalière sont ceux à exposer aux équipes elles-mêmes. La visibilité en libre-service est ce qui fait advenir la phase d'optimisation sans vous.",[17,3494,3496],{"id":3495},"garde-fous-et-que-journaliser","Garde-fous, et que journaliser",[10,3498,3499],{},"Dès lors que chaque prompt transite par un même endroit, cet endroit peut les inspecter, et vous devez décider délibérément de ce qu'il fait de ce pouvoir.",[10,3501,3502],{},"Les garde-fous fondés sur Presidio détectent et traitent les données personnelles avant que la requête ne quitte votre infrastructure :",[56,3504,3505],{"className":58,"code":1697,"language":60,"meta":61,"style":61},[51,3506,3507,3513,3527,3533,3541,3553,3561,3567,3575,3583,3589,3601,3613],{"__ignoreMap":61},[65,3508,3509,3511],{"class":67,"line":68},[65,3510,1704],{"class":71},[65,3512,76],{"class":75},[65,3514,3515,3517,3519,3521,3523,3525],{"class":67,"line":79},[65,3516,488],{"class":75},[65,3518,1713],{"class":71},[65,3520,93],{"class":75},[65,3522,114],{"class":75},[65,3524,1720],{"class":96},[65,3526,120],{"class":75},[65,3528,3529,3531],{"class":67,"line":87},[65,3530,501],{"class":71},[65,3532,76],{"class":75},[65,3534,3535,3537,3539],{"class":67,"line":100},[65,3536,1733],{"class":71},[65,3538,93],{"class":75},[65,3540,1738],{"class":96},[65,3542,3543,3545,3547,3549,3551],{"class":67,"line":108},[65,3544,1743],{"class":71},[65,3546,93],{"class":75},[65,3548,114],{"class":75},[65,3550,1750],{"class":96},[65,3552,120],{"class":75},[65,3554,3555,3557,3559],{"class":67,"line":123},[65,3556,1757],{"class":71},[65,3558,93],{"class":75},[65,3560,1762],{"class":96},[65,3562,3563,3565],{"class":67,"line":131},[65,3564,1767],{"class":71},[65,3566,76],{"class":75},[65,3568,3569,3571,3573],{"class":67,"line":142},[65,3570,1774],{"class":71},[65,3572,93],{"class":75},[65,3574,1779],{"class":416},[65,3576,3577,3579,3581],{"class":67,"line":153},[65,3578,1784],{"class":71},[65,3580,93],{"class":75},[65,3582,1789],{"class":416},[65,3584,3585,3587],{"class":67,"line":164},[65,3586,1794],{"class":71},[65,3588,76],{"class":75},[65,3590,3591,3593,3595,3597,3599],{"class":67,"line":179},[65,3592,1784],{"class":71},[65,3594,93],{"class":75},[65,3596,114],{"class":75},[65,3598,1807],{"class":96},[65,3600,120],{"class":75},[65,3602,3603,3605,3607,3609,3611],{"class":67,"line":190},[65,3604,1814],{"class":71},[65,3606,93],{"class":75},[65,3608,114],{"class":75},[65,3610,1807],{"class":96},[65,3612,120],{"class":75},[65,3614,3615,3617,3619,3621,3623],{"class":67,"line":201},[65,3616,1827],{"class":71},[65,3618,93],{"class":75},[65,3620,114],{"class":75},[65,3622,1834],{"class":96},[65,3624,120],{"class":75},[10,3626,3627],{},"Le masquage remplace l'entité détectée avant que l'appel ne parte vers l'amont ; le blocage rejette la requête. Utilisez le blocage avec parcimonie et seulement pour les catégories dont la bonne réponse est réellement « jamais », car un garde-fou qui bloque du travail légitime apprend aux gens à contourner le proxy, et la détection est statistique et non exacte. Traitez-la comme une réduction de risque, pas comme une garantie de conformité, et soyez prudent dans la façon de la présenter en interne, parce que « nous masquons les données personnelles » est entendu comme « nous ne pouvons pas fuiter de données personnelles » par tous ceux qui ne sont pas dans la pièce.",[10,3629,3630],{},"La décision de journalisation est la plus lourde de conséquences. Par défaut, les intégrations de logs peuvent enregistrer le contenu des messages, ce qui signifie que votre pile d'observabilité devient une seconde copie de tous les prompts de l'entreprise, y compris ceux contenant des identifiants collés par mégarde. Pour la plupart des organisations, le bon défaut est de journaliser les métadonnées et de laisser le contenu dehors :",[56,3632,3633],{"className":58,"code":1845,"language":60,"meta":61,"style":61},[51,3634,3635,3641],{"__ignoreMap":61},[65,3636,3637,3639],{"class":67,"line":68},[65,3638,943],{"class":71},[65,3640,76],{"class":75},[65,3642,3643,3645,3647],{"class":67,"line":79},[65,3644,1858],{"class":71},[65,3646,93],{"class":75},[65,3648,1864],{"class":1863},[10,3650,3651],{},"Vous perdez la possibilité de déboguer une mauvaise réponse en la lisant, ce qui est un coût réel. Vous évitez de constituer une archive interrogeable de tout ce que vos collaborateurs ont jamais demandé, ce qui est un risque réel et, sous certains régimes, un traitement à part entière. Si vous avez besoin de journaliser le contenu pour un workflow précis, restreignez-le délibérément à ce workflow, avec une durée de conservation et un propriétaire nommé, plutôt que de le laisser actif globalement parce que c'était le défaut.",[17,3653,3655],{"id":3654},"le-jour-deux","Le jour deux",[10,3657,3658],{},"Trois choses vous surprendront une fois le dispositif en route, et il vaut mieux les prévoir que les découvrir.",[10,3660,3661,3664],{},[1878,3662,3663],{},"Les chiffres ne correspondront pas à la facture."," Le proxy calcule le coût à partir d'une table de prix maintenue dans du logiciel, et elle divergera de ce que le fournisseur facture réellement : préfixes en cache avec remise, offres batch, tarifs entreprise négociés, changement de prix livré un mardi. Les chiffres de la passerelle sont excellents pour la comparaison relative et l'allocation entre équipes, ce qui est l'usage que vous en faites. Ils ne sont pas une vérité comptable. Confiez à quelqu'un la réconciliation mensuelle avec la facture réelle et traitez un écart qui se creuse comme le signal qu'une table de prix ou une hypothèse de routage a vieilli.",[10,3666,3667,3670],{},[1878,3668,3669],{},"Le proxy est désormais un point unique de défaillance et une cible très intéressante."," Tout workflow dépendant de l'IA tombe avec lui, il lui faut donc plus d'une réplique, un health check qui veut dire quelque chose, et un budget de latence, puisque vous avez inséré un saut dans chaque appel d'inférence. Il détient aussi chaque identifiant fournisseur et voit chaque prompt, ce qui en fait une cible de plus grande valeur que la plupart des services internes. Son modèle de menace mérite le traitement que vous réserveriez à un fournisseur d'identité, pas celui d'un tableau de bord interne.",[10,3672,3673,3676],{},[1878,3674,3675],{},"Les clés ont besoin d'un cycle de vie."," Elles fuitent dans les historiques de shell, les configurations de CI et les portables. Posez une expiration à la création, faites tourner selon un cycle, révoquez au départ, et préférez les clés issues du SSO précisément parce qu'elles héritent d'un cycle de vie que vous opérez déjà. Une clé qui survit à son propriétaire est exactement la défaillance que tout cet exercice devait empêcher.",[17,3678,3680],{"id":3679},"récapitulatif","Récapitulatif",[10,3682,3683],{},"L'ordre compte plus que n'importe quel réglage pris isolément, et il reprend la séquence du premier article. Déployez le proxy avec sa base et figez la version. Créez les équipes avant les clés, et une clé par identité, humaine ou de service. Posez des limites généreuses et mesurez pendant un mois sans rien bloquer. Publiez les chiffres par équipe et laissez le gaspillage évident se corriger sans consigne. Posez ensuite les budgets à partir de ce que vous avez mesuré, avec une borne journalière empilée sous la borne mensuelle, une alerte souple suffisamment sous le plafond dur pour être actionnable, des limites en tokens par minute sur tout ce qui est automatisé, et un niveau de modèle par défaut que la plupart des gens n'ont jamais besoin de quitter.",[10,3685,3686],{},"Ce que vous détenez à ce stade est la couche d'attribution, celle des trois inconnues qui était traitable. L'article suivant s'attaque à la deuxième, l'exposition, dans un cadre où elle cesse d'être une affaire de politique interne : ce qu'une passerelle change et ne change pas quand les prompts qui sortent de vos murs sont des données personnelles et que la loi qui vous interroge est la 2019-014 togolaise.",[17,3688,1907],{"id":1906},[1909,3690,3691,3704,3718,3738,3747],{},[1912,3692,3693,3694,3697,3698,3700,3701],{},"Documentation LiteLLM, ",[1916,3695,3696],{},"Déploiement du proxy"," : images, variables d'environnement, sémantique de ",[51,3699,463],{}," et figeage de version. ",[1924,3702,1929],{"href":1926,"rel":3703},[1928],[1912,3705,3693,3706,3709,3710,3712,3713,1943,3715],{},[1916,3707,3708],{},"Suivi des coûts et journaux de dépense"," : ",[51,3711,1938],{},", métadonnées personnalisées, tags de requête et ",[51,3714,1942],{},[1924,3716,1948],{"href":1946,"rel":3717},[1928],[1912,3719,3693,3720,3709,3723,1957,3725,1957,3727,1957,3729,3731,3732,3734,3735],{},[1916,3721,3722],{},"Utilisateurs, équipes et budgets",[51,3724,1956],{},[51,3726,1960],{},[51,3728,1963],{},[51,3730,1967],{}," empilés, ",[51,3733,1970],{},", limites rpm et tpm. ",[1924,3736,1976],{"href":1974,"rel":3737},[1928],[1912,3739,3693,3740,3743,3744],{},[1916,3741,3742],{},"Contrôle d'accès aux modèles et authentification JWT"," : accès aux modèles par équipe et valeurs par défaut SSO. ",[1924,3745,1987],{"href":1985,"rel":3746},[1928],[1912,3748,3693,3749,1943,3752],{},[1916,3750,3751],{},"Garde-fous : masquage PII avec Presidio",[1924,3753,1997],{"href":1995,"rel":3754},[1928],[1999,3756,3757],{},"html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}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);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":61,"searchDepth":79,"depth":79,"links":3759},[3760,3761,3762,3763,3764,3765,3766,3767,3768,3769,3770,3771,3772],{"id":2052,"depth":79,"text":2053},{"id":2065,"depth":79,"text":2066},{"id":2078,"depth":79,"text":2079},{"id":2608,"depth":79,"text":2609},{"id":2889,"depth":79,"text":2890},{"id":3103,"depth":79,"text":3104},{"id":3169,"depth":79,"text":3170},{"id":3299,"depth":79,"text":3300},{"id":3422,"depth":79,"text":3423},{"id":3495,"depth":79,"text":3496},{"id":3654,"depth":79,"text":3655},{"id":3679,"depth":79,"text":3680},{"id":1906,"depth":79,"text":1907},"Guide pratique pour placer une passerelle entre vos collaborateurs et les éditeurs de modèles, de sorte que chaque appel soit attribué à une personne nommée et à une équipe, que chaque équipe dispose d'un plafond qui se réinitialise, qu'une borne journalière contienne l'agent devenu fou, et que la direction financière obtienne enfin une ventilation par équipe. Déploiement avec Docker et Postgres, équipes et clés, fenêtres de budget empilées, limites de débit, hiérarchisation des modèles, marquage des requêtes pour la refacturation, garde-fous PII, et le travail de jour deux dont personne ne vous parle.",{},"\u002Fai\u002Fai-budget-with-litellm.fr",{"title":2041,"description":3773},"ai\u002F2.ai-budget-with-litellm.fr",[2028,2029,2030,2031,2032,2033],[2035,2036,2037],"SPL4Drt3_8UhfO2GfOx16Q8-rDO3og6cG5EsTYIPjr8",[3782,3785],{"title":3783,"path":3784},"Nobody Knows What Your Company Spends on AI","\u002Fai\u002Fai-spend-nobody-tracks",{"title":3786,"path":3787},"Every Prompt Is a Cross-Border Transfer: AI Governance Under Law 2019-014","\u002Fai\u002Fai-prompts-cross-border-transfer",[3789,3799,3813],{"path":3784,"title":3783,"description":3790,"date":3791,"tags":3792,"topics":3797},"Every company now has employees using OpenAI, Anthropic and Gemini every day, and almost none of them can say who asked what, what data left the building, or what any of it returned. This article argues that AI governance by policy fails for the same reason shadow IT policies failed, that the request for a fixed AI budget is the wrong question, and that FinOps is the practical way in: the only one of the three unknowns you can measure today is cost, and measuring it is what buys you the other two.","2026-08-29",[2029,3793,3794,3795,3796],"AI-Governance","Shadow-AI","LLM","Cost-Attribution",[2035,2036,3798],"security",{"path":3800,"title":3801,"description":3802,"date":3803,"tags":3804,"topics":3809},"\u002Fkubernetes\u002Fsovereign-govcloud-reference-architecture","A Reference Architecture for a Sovereign Government Cloud","The two previous articles showed where Togolese law and Kubernetes fail to meet, then why multi-cloud does not answer a jurisdictional question. This one proposes what to build: a reference architecture for a sovereign government cloud in the WAEMU context. Requirements derived from the legal texts, layer-by-layer design choices with their justifications, an operating model, stated limits, and an honest comparison with the alternatives. An architecture document, not a tutorial.","2026-08-15",[3805,3806,3807,3808,2033],"Sovereignty","Kubernetes","Reference-Architecture","Togo",[3810,3811,2037,3798,3812],"kubernetes","cloud","authz",{"path":3787,"title":3786,"description":3814,"date":3815,"tags":3816,"topics":3821},"A company in Lomé or Dakar wires two hundred employees to American inference APIs, and nobody files anything. This article argues that a prompt containing customer data is a transfer of personal data to a third country in the sense of Togo's law 2019-014, that an LLM gateway does not change the legal nature of that transfer but is what makes it declarable, and that the residency options available to a West African company rank very differently on paper than they do once GPU prices, currency exposure and payment friction are counted.","2026-08-31",[3805,3808,3817,3818,3795,3819,3820],"Law-2019-014","ANCY","Data-Residency","Compliance",[2035,3798,3811],1788067431863]