1"use strict";(globalThis.webpackChunklimitr_site||=[]).push([[8613],{5499(e,n,r){r.r(n),r.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>o,frontMatter:()=>c,metadata:()=>t,toc:()=>d});let t=JSON.parse('{"id":"spec/reference/exchange","title":"Exchange","description":"The exchange table \u2014 rune system, exchange pairs, grant strategy, credit conversion, and multi-step chains.","source":"@site/docs/spec/reference/exchange.mdx","sourceDirName":"spec/reference","slug":"/spec/reference/exchange","permalink":"/spec/reference/exchange","draft":false,"unlisted":false,"tags":[],"version":"current","lastUpdatedAt":1785420178000,"sidebarPosition":4,"frontMatter":{"title":"Exchange","sidebar_position":4,"description":"The exchange table \u2014 rune system, exchange pairs, grant strategy, credit conversion, and multi-step chains."},"sidebar":"specSidebar","previous":{"title":"Credits","permalink":"/spec/reference/credits"},"next":{"title":"Plans","permalink":"/spec/reference/plans"}}');var i=r(4848),s=r(8453);let c={title:"Exchange",sidebar_position:4,description:"The exchange table \u2014 rune system, exchange pairs, grant strategy, credit conversion, and multi-step chains."},a="Exchange",l={},d=[{value:"The rune",id:"rune",level:2},{value:"Schema",id:"schema",level:2},{value:"How resolution works",id:"resolution",level:2},{value:"Grant strategy",id:"grant-strategy",level:2},{value:"Multi-step chains",id:"chains",level:2},{value:"Exchange and grants",id:"exchange-and-grants",level:2},{value:"SDK",id:"sdk",level:2},{value:"<code>policy.creditExchange()</code>",id:"creditexchange",level:3}];function h(e){let n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"exch
1ange",children:"Exchange"})}),"\n",(0,i.jsx)(n.p,{children:"Map credits into one another."}),"\n",(0,i.jsxs)(n.p,{children:["The exchange table maps credits to one another through a common base currency \u2014 the ",(0,i.jsx)(n.strong,{children:"rune"}),". It's what allows a pool of abstract credits to drain across multiple discrete entitlements, and what enables Limitr to compute per-customer margin without external tooling."]}),"\n",(0,i.jsx)(n.hr,{}),"\n",(0,i.jsx)(n.h2,{id:"rune",children:"The rune"}),"\n",(0,i.jsx)(n.p,{children:"Every value in the exchange table is expressed relative to the rune. By default, 1 rune = 1 USD. You don't need to define the rune explicitly \u2014 it's automatically set as:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:"exchange:\r\n rune:\r\n value: 1\r\n currency: usd\n"})}),"\n",(0,i.jsxs)(n.p,{children:["All other exchange pairs chain back to the rune. Limitr resolves any conversion by walking the chain: ",(0,i.jsx)(n.code,{children:"credit A \u2192 credit B \u2192 rune"}),"."]}),"\n",(0,i.jsx)(n.hr,{}),"\n",(0,i.jsx)(n.h2,{id:"schema",children:"Schema"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:"exchange:\r\n grant_strategy: string # 'expires_first' | 'cheapest_first' | 'valuable_first'. Default: 'expires_first'.\r\n rune:\r\n value: float # Always 1 by convention.\r\n currency: string # 'usd' or another terminal currency.\r\n <credit-id>:\r\n value: float # How many units of `currency` does 1 unit of this credit equal?\r\n currency: string # The credit ID or terminal currency this pair prices into.\n"})}),"\n",(0,i.jsx)(n.p,{children:"Each exchange pair has two fields:"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.code,{children:"value"})})," \u2014 The multiplier. Must be \u2265 0. Represents how many units of ",(0,i.jsx)(n.code,{children:"currency"})," one unit of this credit is worth."]}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.code,{children:"currency"})})," \u2014 The credit ID or terminal currency this pair prices into. Use ",(0,i.jsx)(n.code,{children:"'rune'"})," to price directly in runes. Use another credit's ID to chain through it."]}),"\n",(0,i.jsx)(n.admonition,{title:"Matching price to exchange",type:"tip",children:(0,i.jsxs)(n.p,{children:["Setting a credit's exchange ",(0,i.jsx)(n.code,{children:"value"})," equal to its ",(0,i.jsx)(n.code,{children:"price.amount"})," keeps the two in sync and makes the policy easier to maintain. Chains let you adjust the relationship between abstract and discrete credits independently of the base price."]})}),"\n",(0,i.jsx)(n.hr,{}),"\n",(0,i.jsx)(n.h2,{id:"resolution",children:"How resolution works"}),"\n",(0,i.jsxs)(n.p,{children:["Limitr resolves conversions by walking the chain from the source credit toward ",(0,i.jsx)(n.code,{children:"rune"}),", multiplying values at each step. If a cycle is detected, the conversion returns ",(0,i.jsx)(n.code,{children:"null"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["For credits with a ",(0,i.jsx)(n.code,{children:"flat"})," pricing model and a defined ",(0,i.jsx)(n.code,{children:"price.amount"}),", an explicit exchange entry is optional \u2014 Limitr uses the credit's price as its rune value automatically. Adding an explicit entry overrides this."]}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Example:"})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:"exchange:\r\n rune: { value: 1, currency: usd }\r\n ai_credit: { value: 1.25, currency: rune }\r\n sonnet_input: { value: 0.000004, currency: ai_credit }\r\n sonnet_output: { value: 0.00002, currency: ai_credit }\n"})}),"\n",(0,i.jsxs)(n.p,{children:["To find how many ",(0,i.jsx)(n.code,{children:"sonnet_input"})," tokens 1 ",(0,i.jsx)(n.code,{children:"ai_credit"})," buys:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"1 ai_credit / 0.000004 = 250,000 input tokens\n"})}),"\n",(0,i.jsx)(n.hr,{}),"\n",(0,i.jsx)(n.h2,{id:"grant-strategy",children:"Grant strategy"}),"\n",(0,i.jsxs)(n.p,{children:["When a customer has multiple grants eligible to cover overage, ",(0,i.jsx)(n.code,{children:"grant_strategy"})," controls which is consumed first."]}),"\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{children:"Strategy"}),(0,i.jsx)(n.th,{children:"Behavior"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"expires_first"})}),(0,i.jsxs)(n.td,{children:["The grant with the earliest expiry is consumed first. Prevents credit from going to waste. ",(0,i.jsx)(n.strong,{children:"Default."})]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"cheapest_first"})}),(0,i.jsx)(n.td,{children:"The grant with the lowest rune value per unit is consumed first. Preserves higher-value grants."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"valuable_first"})}),(0,i.jsx)(n.td,{children:"The grant with the highest rune value per unit is consumed first."})]})]})]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:"exchange:\r\n grant_strategy: expires_first\r\n rune: { value: 1, currency: usd }\r\n # ...\n"})}),"\n",(0,i.jsx)(n.hr,{}),"\n",(0,i.jsx)(n.h2,{id:"chains",children:"Multi-step chains"}),"\n",(0,i.jsx)(n.p,{children:"Exchange chains can be arbitrarily deep. A common pattern is a two-level chain: an abstract customer-facing credit \u2192 a discrete model credit \u2192 rune."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:"exchange:\r\n rune: { value: 1, currency: usd }\r\n ai_credit: { value: 1.25, currency: rune }\r\n sonnet_input: { value: 0.000004, currency: ai_credit }\r\n sonnet_output: { value: 0.00002, currency: ai_credit }\r\n haiku_input: { value: 0.000001, currency: ai_credit }\r\n haiku_output: { value: 0.000005, currency: ai_credit }\n"})}),"\n",(0,i.jsxs)(n.p,{children:["When a customer's ",(0,i.jsx)(n.code,{children:"ai_credit"})," grant is drawn against a ",(0,i.jsx)(n.code,{children:"sonnet_input"})," overage, Limitr walks ",(0,i.jsx)(n.code,{children:"sonnet_input \u2192 ai_credit \u2192 rune"})," to determine how much grant balance to deduct."]}),"\n",(0,i.jsx)(n.hr,{}
1),"\n",(0,i.jsx)(n.h2,{id:"exchange-and-grants",children:"Exchange and grants"}),"\n",(0,i.jsxs)(n.p,{children:["The exchange table is what makes grants work across different credit types. A grant is denominated in one credit (e.g. ",(0,i.jsx)(n.code,{children:"ai_credit"}),"). An entitlement limit is denominated in another (e.g. ",(0,i.jsx)(n.code,{children:"sonnet_input"}),"). When overage occurs on a ",(0,i.jsx)(n.code,{children:"soft"}),"-limit entitlement, Limitr:"]}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["Finds the best eligible grant per ",(0,i.jsx)(n.code,{children:"grant_strategy"})]}),"\n",(0,i.jsx)(n.li,{children:"Uses the exchange table to convert the overage amount from the entitlement's credit into the grant's credit"}),"\n",(0,i.jsx)(n.li,{children:"Deducts from the grant balance"}),"\n",(0,i.jsxs)(n.li,{children:["Fires ",(0,i.jsx)(n.code,{children:"meter-overage"})," only if no grants remain to cover the overage"]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["See ",(0,i.jsx)(n.a,{href:"./topups",children:"Topups & Grants"})," for grant lifecycle and ",(0,i.jsx)(n.a,{href:"./enforcement",children:"Enforcement"})," for the full event flow."]}),"\n",(0,i.jsx)(n.hr,{}),"\n",(0,i.jsx)(n.h2,{id:"sdk",children:"SDK"}),"\n",(0,i.jsx)(n.h3,{id:"creditexchange",children:(0,i.jsx)(n.code,{children:"policy.creditExchange()"})}),"\n",(0,i.jsxs)(n.p,{children:["Converts a value from one credit to another using the exchange table. Returns ",(0,i.jsx)(n.code,{children:"null"})," if no path exists between the two credits."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// How many sonnet input tokens does 10 ai_credits buy?\r\nconst tokens = await policy.creditExchange('ai_credit', 'sonnet_input', 10);\r\n// \u2192 2,500,000\r\n\r\n// Returns null if no path exists\r\nconst result = await policy.creditExchange('sonnet_input', 'unrelated_credit', 1);\r\n// \u2192 null\n"})})]})}function o(e={}){let{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},8453(e,n,r){r.d(n,{R:()=>c,x:()=>a});var t=r(6540);let i={},s=t.createContext(i);function c(e){let n=t.useContext(s);return t.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:c(e.components),t.createElement(s.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.