hyperbrane 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -3
- package/dist/agent/agentService.js +3 -2
- package/dist/agent/api.d.ts +26 -2
- package/dist/agent/control.js +1 -1
- package/dist/agent/dimensionAgentClient.d.ts +8 -0
- package/dist/agent/dimensionAgentClient.js +99 -0
- package/dist/agent/session/backend.d.ts +2 -1
- package/dist/agent/session/claude/transcript.d.ts +1 -0
- package/dist/agent/session/claude/transcript.js +1 -1
- package/dist/agent/session/worker/protocol.d.ts +6 -3
- package/dist/agent/session/worker/protocol.js +1 -1
- package/dist/agent/session/worker/workerParams.js +10 -3
- package/dist/agent/vendor-types/manifest.json +5 -60
- package/dist/agent/workerBundle.generated.js +1 -1
- package/dist/dimension/api.d.ts +3 -0
- package/dist/dimension/control.d.ts +2 -1
- package/dist/dimension/control.js +18 -0
- package/dist/dimension/service.js +27 -1
- package/dist/dimension/transportBundle.generated.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/lib/control.d.ts +4 -1
- package/dist/lib/control.js +2 -2
- package/dist/lib/jsonDirectory.js +24 -3
- package/dist/lib/unixSocket.d.ts +2 -0
- package/dist/lib/unixSocket.js +28 -0
- package/dist/portal-gateway/api.d.ts +1 -0
- package/dist/portal-gateway/gatewayService.js +14 -0
- package/dist/portal-gateway/transportBundle.generated.js +1 -1
- package/dist/restore.d.ts +9 -0
- package/dist/restore.js +10 -0
- package/dist/sandbox/api.d.ts +5 -0
- package/dist/sandbox/client.d.ts +2 -1
- package/dist/sandbox/client.js +4 -1
- package/dist/sandbox/lib/sshKey.d.ts +6 -0
- package/dist/sandbox/lib/sshKey.js +75 -0
- package/dist/sandbox/providers/boat/boatService.d.ts +2 -1
- package/dist/sandbox/providers/boat/boatService.js +72 -20
- package/dist/sandbox/providers/exedev/exeDevService.js +4 -2
- package/dist/sandbox/providers/exedev/exeDevToken.js +2 -6
- package/dist/sandbox/providers/local/localService.js +4 -26
- package/dist/sandbox/providers/ssh/sshService.js +2 -1
- package/dist/sandbox/service.d.ts +2 -1
- package/dist/sandbox/service.js +9 -1
- package/dist/vendor-types/claude/agent-sdk/bridge.d.ts +20 -4
- package/dist/vendor-types/claude/agent-sdk/browser-sdk.d.ts +24 -0
- package/dist/vendor-types/claude/agent-sdk/core.d.ts +2 -0
- package/dist/vendor-types/claude/agent-sdk/sdk-tools.d.ts +75 -6
- package/dist/vendor-types/claude/agent-sdk/sdk.d.ts +569 -100
- package/package.json +2 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by scripts/bundle-worker.ts — do not edit; run `bun run bundle:worker`.
|
|
2
|
-
export const WORKER_SOURCE = "// @bun\n// agent/session/main.ts\nimport { chown, rm as rm3, writeFile as writeFile3 } from \"fs/promises\";\nimport { dirname as dirname2 } from \"path\";\n\n// sandbox/providers/local/localService.ts\nimport { spawn } from \"child_process\";\nimport { randomUUID } from \"crypto\";\nimport * as fs from \"fs/promises\";\nimport { connect as netConnect, Socket } from \"net\";\nimport { homedir, userInfo } from \"os\";\nimport { basename, dirname, join, resolve } from \"path\";\n\n// sandbox/api.ts\nclass SandboxNotFoundError extends Error {\n name = \"SandboxNotFoundError\";\n}\nclass SandboxConnectionRefusedError extends Error {\n name = \"SandboxConnectionRefusedError\";\n}\n\n// sandbox/storage/descriptors.ts\nfunction makeDescriptor(ctx, id, spec) {\n return {\n id,\n kind: ctx.kind,\n name: spec.name,\n ...ctx.sshHost === undefined ? {} : { sshHost: ctx.sshHost },\n ...ctx.sshUser === undefined ? {} : { sshUser: ctx.sshUser },\n ...ctx.sshPort === undefined ? {} : { sshPort: ctx.sshPort },\n ...spec.dataEndpointOptions === undefined ? {} : { dataEndpointOptions: spec.dataEndpointOptions }\n };\n}\nasync function createDescriptor(ctx, storage, id, spec) {\n const descriptor = makeDescriptor(ctx, id, spec);\n await storage.saveDescriptor(descriptor);\n return [{ ...ctx, id }, descriptor];\n}\nasync function openDescriptor(ctx, storage, id) {\n return await storage.readDescriptor() ?? storage.insertDescriptor(makeDescriptor(ctx, id, { name: id }));\n}\nasync function getDescriptor(storage, where) {\n const descriptor = await storage.readDescriptor();\n if (!descriptor)\n throw new Error(`${where} has no descriptor: opening it saves one`);\n return descriptor;\n}\n\n// sandbox/data-endpoints/dataEndpoints.ts\nimport { randomBytes } from \"crypto\";\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/buffer_utils.js\nvar encoder = new TextEncoder;\nvar decoder = new TextDecoder;\nvar strictDecoder = new TextDecoder(\"utf-8\", { fatal: true });\nvar UINT32_RANGE = 2 ** 32;\nfunction concat(...buffers) {\n const size = buffers.reduce((acc, { length }) => acc + length, 0), buf = new Uint8Array(size);\n let i = 0;\n for (const buffer of buffers)\n buf.set(buffer, i), i += buffer.length;\n return buf;\n}\nvar NON_ASCII = /[^\\x00-\\x7f]/;\nfunction encode(string) {\n if (typeof string == \"string\" && string.length >= 128) {\n if (NON_ASCII.test(string))\n throw new TypeError(\"non-ASCII string encountered in encode()\");\n return encoder.encode(string);\n }\n const bytes = new Uint8Array(string.length);\n for (let i = 0;i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code > 127)\n throw new TypeError(\"non-ASCII string encountered in encode()\");\n bytes[i] = code;\n }\n return bytes;\n}\nfunction encodeBase64(input, url = false) {\n if (Uint8Array.prototype.toBase64)\n return input.toBase64({ alphabet: url ? \"base64url\" : \"base64\", omitPadding: url });\n const CHUNK_SIZE = 32768, arr = [];\n for (let i = 0;i < input.length; i += CHUNK_SIZE)\n arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));\n const encoded = btoa(arr.join(\"\"));\n return url ? encoded.replace(/=/g, \"\").replace(/\\+/g, \"-\").replace(/\\//g, \"_\") : encoded;\n}\nfunction decodeBase64(encoded, url = false) {\n if (Uint8Array.fromBase64)\n return Uint8Array.fromBase64(encoded, { alphabet: url ? \"base64url\" : \"base64\" });\n if (url) {\n if (encoded.includes(\"+\") || encoded.includes(\"/\"))\n throw new TypeError(\"Invalid base64url\");\n encoded = encoded.replace(/-/g, \"+\").replace(/_/g, \"/\");\n }\n const binary = atob(encoded), bytes = new Uint8Array(binary.length);\n for (let i = 0;i < binary.length; i++)\n bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/util/errors.js\nclass JOSEError extends Error {\n static code = \"ERR_JOSE_GENERIC\";\n code = \"ERR_JOSE_GENERIC\";\n constructor(message, options) {\n super(message, options), this.name = this.constructor.name, Error.captureStackTrace?.(this, this.constructor);\n }\n}\nclass JOSENotSupported extends JOSEError {\n static code = \"ERR_JOSE_NOT_SUPPORTED\";\n code = \"ERR_JOSE_NOT_SUPPORTED\";\n}\nclass JWSInvalid extends JOSEError {\n static code = \"ERR_JWS_INVALID\";\n code = \"ERR_JWS_INVALID\";\n}\n\nclass JWTInvalid extends JOSEError {\n static code = \"ERR_JWT_INVALID\";\n code = \"ERR_JWT_INVALID\";\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/util/base64url.js\nvar invalid = \"The input to be decoded is not correctly encoded.\";\nfunction decode(input) {\n try {\n return decodeBase64(typeof input == \"string\" ? input : decoder.decode(input), true);\n } catch (cause) {\n throw new TypeError(invalid, { cause });\n }\n}\nfunction encode2(input) {\n return encodeBase64(typeof input == \"string\" ? encoder.encode(input) : input, true);\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/validate.js\nfunction isObject(input) {\n if (typeof input != \"object\" || input === null || Object.prototype.toString.call(input) !== \"[object Object]\")\n return false;\n const prototype = Object.getPrototypeOf(input);\n return prototype === null || Object.getPrototypeOf(prototype) === null;\n}\nfunction isDisjoint(...headers) {\n const parameters = /* @__PURE__ */ new Set;\n for (const header of headers)\n if (header)\n for (const parameter of Object.keys(header)) {\n if (parameters.has(parameter))\n return false;\n parameters.add(parameter);\n }\n return true;\n}\nfunction assertNotSet(value, name) {\n if (value !== undefined)\n throw new TypeError(`${name} can only be called once`);\n}\nvar JWS_RECOGNIZED = { __proto__: null, b64: true };\nfunction validateCritDuplicates(Err, protectedHeader) {\n const { crit } = protectedHeader ?? {};\n if (Array.isArray(crit) && new Set(crit).size !== crit.length)\n throw new Err('\"crit\" (Critical) Header Parameter MUST NOT contain duplicate values');\n}\nfunction validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {\n if (joseHeader.crit !== undefined && protectedHeader?.crit === undefined)\n throw new Err('\"crit\" (Critical) Header Parameter MUST be integrity protected');\n if (!protectedHeader || protectedHeader.crit === undefined)\n return [];\n if (!Array.isArray(protectedHeader.crit) || protectedHeader.crit.length === 0 || protectedHeader.crit.some((input) => typeof input != \"string\" || input.length === 0))\n throw new Err('\"crit\" (Critical) Header Parameter MUST be an array of non-empty strings when present');\n const recognized = recognizedOption === undefined ? recognizedDefault : { __proto__: null, ...recognizedOption, ...recognizedDefault };\n for (const parameter of protectedHeader.crit) {\n if (!(parameter in recognized))\n throw new JOSENotSupported(`Extension Header Parameter \"${parameter}\" is not recognized`);\n if (!Object.hasOwn(joseHeader, parameter) || joseHeader[parameter] === undefined)\n throw new Err(`Extension Header Parameter \"${parameter}\" is missing`);\n if (recognized[parameter] && (!Object.hasOwn(protectedHeader, parameter) || protectedHeader[parameter] === undefined))\n throw new Err(`Extension Header Parameter \"${parameter}\" MUST be integrity protected`);\n }\n return protectedHeader.crit;\n}\nfunction validateB64(protectedHeader, extensions) {\n if (extensions.includes(\"b64\")) {\n const b64 = protectedHeader.b64;\n if (typeof b64 != \"boolean\")\n throw new JWSInvalid('The \"b64\" (base64url-encode payload) Header Parameter must be a boolean');\n return b64;\n }\n return true;\n}\nfunction serializeJoseHeader(Err, header) {\n let serialized, parsed;\n try {\n serialized = JSON.stringify(header), parsed = JSON.parse(serialized);\n } catch (cause) {\n throw new Err(\"JOSE Header is not valid JSON\", { cause });\n }\n if (!isObject(parsed))\n throw new Err(\"JOSE Header is not a JSON object\");\n return [parsed, serialized];\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/key.js\nvar tag = (key) => key[Symbol.toStringTag];\nvar jwkMatchesOp = (entry, key, usage) => {\n const { alg } = entry;\n if (key.use !== undefined) {\n const expected = usage === \"sign\" || usage === \"verify\" ? \"sig\" : \"enc\";\n if (key.use !== expected)\n throw new TypeError(`Invalid key for this operation, its \"use\" must be \"${expected}\" when present`);\n }\n if (key.alg !== undefined && key.alg !== alg)\n throw new TypeError(`Invalid key for this operation, its \"alg\" must be \"${alg}\" when present`);\n if (Array.isArray(key.key_ops)) {\n const expectedKeyOp = usage === \"encrypt\" || usage === \"decrypt\" ? entry.ops?.[usage === \"encrypt\" ? 0 : 1] : usage;\n if (expectedKeyOp && !key.key_ops.includes(expectedKeyOp))\n throw new TypeError(`Invalid key for this operation, its \"key_ops\" must include \"${expectedKeyOp}\" when present`);\n }\n};\nasync function prepareKey(entry, key, usage) {\n const { alg, secret } = entry, requiresPrivateKey = usage === \"decrypt\" || usage === \"sign\";\n if (secret && key instanceof Uint8Array)\n return key;\n let normalized, keyObject;\n if (isObject(key)) {\n if (normalized = normalizeJwk(key), typeof normalized.kty != \"string\")\n throw invalidKeyType(alg, key, secret);\n if (!(secret ? normalized.kty === \"oct\" && typeof normalized.k == \"string\" : normalized.kty !== \"oct\" && (requiresPrivateKey ? normalized.kty === \"AKP\" && typeof normalized.priv == \"string\" || typeof normalized.d == \"string\" : normalized.d === undefined && normalized.priv === undefined)))\n throw new TypeError(secret ? 'JSON Web Key for symmetric algorithms must have JWK \"kty\" (Key Type) equal to \"oct\" and the JWK \"k\" (Key Value) present' : `JSON Web Key for this operation must be a ${requiresPrivateKey ? \"private\" : \"public\"} JWK`);\n if (jwkMatchesOp(entry, normalized, usage), normalized.kty === \"oct\")\n return decode(normalized.k);\n if (!Object.isFrozen(key)) {\n const { key_ops } = key;\n Array.isArray(key_ops) && Object.freeze(key_ops), Object.freeze(key);\n }\n } else {\n if (!isKeyLike(key))\n throw invalidKeyType(alg, key, secret);\n const expectedType = secret ? \"secret\" : requiresPrivateKey ? \"private\" : \"public\";\n if (key.type !== expectedType && (secret || [\"secret\", \"public\", \"private\"].includes(key.type)))\n throw new TypeError(`${tag(key)} instances must be of type \"${expectedType}\" for the ${alg} algorithm`);\n if (isCryptoKey(key))\n return key;\n if (keyObject = key, keyObject.type === \"secret\")\n return keyObject.export();\n }\n cache ||= /* @__PURE__ */ new WeakMap;\n const cacheKey = key;\n let cached = cache.get(cacheKey);\n if (cached?.[alg])\n return cached[alg];\n if (cached || cache.set(cacheKey, cached = {}), keyObject && typeof keyObject.toCryptoKey == \"function\") {\n const isPublic = keyObject.type === \"public\", crv = nist[keyObject.asymmetricKeyDetails?.namedCurve], params = entry.resolve?.({ crv, asymmetricKeyType: keyObject.asymmetricKeyType }) ?? entry.subtle;\n return cached[alg] = keyObject.toCryptoKey(params, isPublic, entry.usages[isPublic ? 0 : 1]);\n }\n return normalized ??= keyObject.export({ format: \"jwk\" }), normalized.alg = alg, cached[alg] = await jwkToKey(entry, normalized);\n}\nvar cache;\nvar nist = {\n __proto__: null,\n prime256v1: \"P-256\",\n secp384r1: \"P-384\",\n secp521r1: \"P-521\"\n};\nvar isCryptoKey = (key) => {\n if (key?.[Symbol.toStringTag] === \"CryptoKey\")\n return true;\n try {\n return key instanceof CryptoKey;\n } catch {\n return false;\n }\n};\nvar isKeyObject = (key) => key?.[Symbol.toStringTag] === \"KeyObject\";\nvar isKeyLike = (key) => isCryptoKey(key) || isKeyObject(key);\nfunction message(msg, actual, ...types) {\n if (types.length > 2) {\n const last = types.pop();\n msg += `one of type ${types.join(\", \")}, or ${last}.`;\n } else\n types.length === 2 ? msg += `one of type ${types[0]} or ${types[1]}.` : msg += `of type ${types[0]}.`;\n return actual == null ? msg += ` Received ${actual}` : typeof actual == \"function\" && actual.name ? msg += ` Received function ${actual.name}` : typeof actual == \"object\" && actual != null && actual.constructor?.name && (msg += ` Received an instance of ${actual.constructor.name}`), msg;\n}\nfunction invalidKeyType(alg, actual, secret) {\n const types = [\"CryptoKey\", \"KeyObject\", \"JSON Web Key\"];\n return secret && types.push(\"Uint8Array\"), new TypeError(message(`Key for the ${alg} algorithm must be `, actual, ...types));\n}\nvar unusable = (name, prop = \"algorithm.name\") => new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);\nfunction checkUsage(key, usage) {\n if (usage && !key.usages.includes(usage))\n throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);\n}\nfunction checkModulusLength(alg, key) {\n const { modulusLength } = key.algorithm;\n if (typeof modulusLength != \"number\" || modulusLength < 2048)\n throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);\n}\nfunction checkCryptoKey(key, expected, usage) {\n const algorithm = key.algorithm;\n if (algorithm.name !== expected.name)\n throw unusable(expected.name);\n if (expected.hash && algorithm.hash?.name !== expected.hash)\n throw unusable(expected.hash, \"algorithm.hash\");\n if (expected.namedCurve && algorithm.namedCurve !== expected.namedCurve)\n throw unusable(expected.namedCurve, \"algorithm.namedCurve\");\n if (expected.length !== undefined && algorithm.length !== expected.length)\n throw unusable(expected.length, \"algorithm.length\");\n checkUsage(key, usage);\n}\nfunction snapshotJwk(jwk) {\n return { __proto__: null, ...jwk };\n}\nfunction normalizeJwk(jwk) {\n const normalized = snapshotJwk(jwk);\n if (normalized.ext !== undefined && typeof normalized.ext != \"boolean\")\n throw new TypeError('\"ext\" (Extractable) Parameter must be a boolean');\n if (normalized.key_ops !== undefined) {\n const value = normalized.key_ops, keyOps = Array.isArray(value) ? [...value] : undefined;\n if (!keyOps || keyOps.some((operation) => typeof operation != \"string\") || new Set(keyOps).size !== keyOps.length)\n throw new TypeError('\"key_ops\" (Key Operations) Parameter must be an array of unique strings');\n normalized.key_ops = keyOps;\n }\n return normalized;\n}\nasync function jwkToKey(entry, jwk, extractable) {\n if (!entry.kty.includes(jwk.kty))\n throw new JOSENotSupported('Invalid or unsupported JWK \"alg\" (Algorithm) Parameter value');\n const algorithm = entry.resolve?.({ kty: jwk.kty, crv: jwk.crv }) ?? entry.subtle, isPrivate = !!(jwk.d || jwk.priv), keyData = { ...jwk, ext: extractable ?? jwk.ext };\n return keyData.kty !== \"AKP\" && delete keyData.alg, delete keyData.use, crypto.subtle.importKey(\"jwk\", keyData, algorithm, keyData.ext ?? !isPrivate, jwk.key_ops ?? entry.usages[isPrivate ? 1 : 0]);\n}\nasync function rawKey(key, expected, usage, extractable = false) {\n return key instanceof Uint8Array && (key = await crypto.subtle.importKey(\"raw\", key, expected, extractable, [usage])), checkCryptoKey(key, expected, usage), key;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/key_descriptor.js\nfunction table(entries) {\n const out = { __proto__: null };\n for (const alg in entries)\n out[alg] = { ...entries[alg], alg };\n return out;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/jws_algorithms.js\nvar sig = [[\"verify\"], [\"sign\"]];\nfunction hmac(bits) {\n const subtle = { name: \"HMAC\", hash: `SHA-${bits}` };\n return { kty: [\"oct\"], secret: true, subtle, signing: subtle, usages: sig };\n}\nfunction rsa(bits, saltLength) {\n const subtle = { name: saltLength ? \"RSA-PSS\" : \"RSASSA-PKCS1-v1_5\", hash: `SHA-${bits}` };\n return {\n kty: [\"RSA\"],\n subtle,\n signing: saltLength ? { ...subtle, saltLength } : subtle,\n usages: sig,\n minRsaBits: 2048\n };\n}\nfunction ecdsa(crv, bits) {\n return {\n kty: [\"EC\"],\n crv,\n subtle: { name: \"ECDSA\", namedCurve: crv },\n signing: { name: \"ECDSA\", hash: `SHA-${bits}` },\n usages: sig\n };\n}\nfunction eddsa() {\n const subtle = { name: \"Ed25519\" };\n return {\n kty: [\"OKP\"],\n crv: \"Ed25519\",\n subtle,\n signing: subtle,\n usages: sig\n };\n}\nfunction mldsa(parameterSet) {\n const subtle = { name: `ML-DSA-${parameterSet}` };\n return {\n kty: [\"AKP\"],\n subtle,\n signing: subtle,\n usages: sig\n };\n}\nvar JWS = table({\n HS256: hmac(256),\n HS384: hmac(384),\n HS512: hmac(512),\n RS256: rsa(256),\n RS384: rsa(384),\n RS512: rsa(512),\n PS256: rsa(256, 32),\n PS384: rsa(384, 48),\n PS512: rsa(512, 64),\n ES256: ecdsa(\"P-256\", 256),\n ES384: ecdsa(\"P-384\", 384),\n ES512: ecdsa(\"P-521\", 512),\n EdDSA: eddsa(),\n Ed25519: eddsa(),\n \"ML-DSA-44\": mldsa(44),\n \"ML-DSA-65\": mldsa(65),\n \"ML-DSA-87\": mldsa(87)\n});\nfunction jwsAlgorithm(alg) {\n const entry = typeof alg == \"string\" ? JWS[alg] : undefined;\n if (!entry)\n throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);\n return entry;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/jwt_claims_set.js\nvar epoch = (date) => Math.floor(date.getTime() / 1000);\nvar multipliers = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n w: 604800,\n y: 31557600\n};\nvar REGEX = /^(\\+|\\-)? ?(\\d+|\\d+\\.\\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;\nfunction invalidDuration() {\n throw new TypeError(\"Invalid time period format\");\n}\nfunction secs(str) {\n typeof str != \"string\" && invalidDuration();\n const matched = REGEX.exec(str);\n (!matched || matched[4] && matched[1]) && invalidDuration();\n const value = parseFloat(matched[2]), durationSeconds = Math.round(value * multipliers[matched[3][0].toLowerCase()]);\n return Number.isFinite(durationSeconds) || invalidDuration(), matched[1] === \"-\" || matched[4] === \"ago\" ? -durationSeconds : durationSeconds;\n}\nfunction validateInput(label, input) {\n if (!Number.isFinite(input))\n throw new TypeError(`Invalid ${label} input`);\n return input;\n}\nfunction validateStringClaim(claim, value) {\n if (typeof value != \"string\")\n throw new TypeError(`\"${claim}\" claim must be a string`);\n}\nfunction validateAudienceClaim(value) {\n if (typeof value != \"string\" && (!Array.isArray(value) || Array.from(value).some((member) => typeof member != \"string\")))\n throw new TypeError('\"aud\" claim must be a string or an array of strings');\n}\nfunction numericDate(value, label) {\n return typeof value == \"number\" ? validateInput(label, value) : value instanceof Date ? validateInput(label, epoch(value)) : epoch(/* @__PURE__ */ new Date) + secs(value);\n}\nvar producerClaimsSets;\nfunction producerClaimsSet(producer) {\n return producerClaimsSets.get(producer);\n}\nfunction jwtClaimsSetBytes(producer) {\n const claimsSet = producerClaimsSet(producer);\n for (const claim of [\"iat\", \"nbf\", \"exp\"]) {\n const value = claimsSet[claim];\n if (typeof value == \"number\" && !Number.isFinite(value))\n throw new TypeError(`\"${claim}\" claim must be a finite number`);\n }\n return encoder.encode(JSON.stringify(claimsSet));\n}\nclass JWTClaimsBuilder {\n constructor(claimsSet = {}) {\n if (!isObject(claimsSet))\n throw new TypeError(\"JWT Claims Set MUST be an object\");\n (producerClaimsSets ||= /* @__PURE__ */ new WeakMap).set(this, structuredClone(claimsSet));\n }\n setIssuer(value) {\n return validateStringClaim(\"iss\", value), producerClaimsSet(this).iss = value, this;\n }\n setSubject(value) {\n return validateStringClaim(\"sub\", value), producerClaimsSet(this).sub = value, this;\n }\n setAudience(value) {\n return validateAudienceClaim(value), producerClaimsSet(this).aud = value, this;\n }\n setJti(value) {\n return validateStringClaim(\"jti\", value), producerClaimsSet(this).jti = value, this;\n }\n setNotBefore(value) {\n return producerClaimsSet(this).nbf = numericDate(value, \"setNotBefore\"), this;\n }\n setExpirationTime(value) {\n return producerClaimsSet(this).exp = numericDate(value, \"setExpirationTime\"), this;\n }\n setIssuedAt(value) {\n const claimsSet = producerClaimsSet(this);\n return value === undefined ? claimsSet.iat = epoch(/* @__PURE__ */ new Date) : typeof value == \"string\" ? claimsSet.iat = validateInput(\"setIssuedAt\", epoch(/* @__PURE__ */ new Date) + secs(value)) : claimsSet.iat = numericDate(value, \"setIssuedAt\"), this;\n }\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/jws_sign.js\nasync function createSignature(input, key, rejectUnencoded) {\n let [payload, protectedHeader, unprotectedHeader, crit] = input, encodedProtectedHeader = \"\";\n if (protectedHeader !== undefined) {\n const normalized = serializeJoseHeader(JWSInvalid, protectedHeader);\n protectedHeader = normalized[0], encodedProtectedHeader = encode2(normalized[1]);\n }\n if (unprotectedHeader !== undefined && (unprotectedHeader = serializeJoseHeader(JWSInvalid, unprotectedHeader)[0]), !protectedHeader && !unprotectedHeader)\n throw new JWSInvalid(\"either setProtectedHeader or setUnprotectedHeader must be called before #sign()\");\n if (!isDisjoint(protectedHeader, unprotectedHeader))\n throw new JWSInvalid(\"JWS Protected and JWS Unprotected Header Parameter names must be disjoint\");\n const joseHeader = { ...protectedHeader, ...unprotectedHeader };\n validateCritDuplicates(JWSInvalid, protectedHeader);\n const b64 = validateB64(protectedHeader, validateCrit(JWSInvalid, JWS_RECOGNIZED, crit, protectedHeader, joseHeader));\n b64 || rejectUnencoded?.();\n const { alg } = joseHeader;\n if (typeof alg != \"string\" || !alg)\n throw new JWSInvalid('JWS \"alg\" (Algorithm) Header Parameter missing or invalid');\n const entry = jwsAlgorithm(alg);\n let encodedPayload = \"\", signingPayload = payload, signingInput;\n if (b64) {\n const encodedPayloadCache = input[4];\n encodedPayloadCache ? (encodedPayload = encodedPayloadCache[0] ??= encode2(payload), signingPayload = encodedPayloadCache[1] ??= encode(encodedPayload)) : (encodedPayload = encode2(payload), signingInput = encoder.encode(`${encodedProtectedHeader}.${encodedPayload}`));\n }\n signingInput ??= concat(encode(encodedProtectedHeader), encode(\".\"), signingPayload);\n const signingKey = await rawKey(await prepareKey(entry, key, \"sign\"), entry.subtle, \"sign\");\n entry.minRsaBits && checkModulusLength(entry.alg, signingKey);\n const jws = {\n signature: encode2(new Uint8Array(await crypto.subtle.sign(entry.signing, signingKey, signingInput))),\n payload: encodedPayload\n };\n return protectedHeader && (jws.protected = encodedProtectedHeader), unprotectedHeader && (jws.header = unprotectedHeader), [jws, b64];\n}\nasync function createCompactSignature(payload, protectedHeader, crit, key, rejectUnencoded) {\n const [jws] = await createSignature([payload, protectedHeader, undefined, crit], key, rejectUnencoded);\n return `${jws.protected}.${jws.payload}.${jws.signature}`;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/jwt/sign.js\nvar SignJWT_base = JWTClaimsBuilder;\n\nclass SignJWT extends SignJWT_base {\n #protectedHeader;\n setProtectedHeader(protectedHeader) {\n return assertNotSet(this.#protectedHeader, \"setProtectedHeader\"), this.#protectedHeader = protectedHeader, this;\n }\n async sign(key, options) {\n return createCompactSignature(jwtClaimsSetBytes(this), this.#protectedHeader, options?.crit, key, () => {\n throw new JWTInvalid(\"JWTs MUST NOT use unencoded payload\");\n });\n }\n}\n\n// lib/sh.ts\nfunction sh(strings, ...values) {\n if (values.length > 0)\n throw new Error(\"sh`\\u2026` takes no interpolations: pass inputs as environment variables or arguments\");\n return scriptText(strings.raw[0]);\n}\nfunction scriptText(raw) {\n return raw.replace(/\\\\(\\$\\{|`)/g, \"$1\").replace(/^\\n/, \"\");\n}\n\n// sandbox/data-endpoints/dataEndpoints.ts\nvar DEFAULT_TTL_S = 12 * 3600;\nfunction signToken(secret, ttlSeconds) {\n return new SignJWT().setProtectedHeader({ alg: \"HS256\" }).setExpirationTime(`${ttlSeconds}s`).sign(new TextEncoder().encode(secret));\n}\nasync function listeningPorts(sandbox) {\n const listed = await sandbox.runCommand([\"sh\", \"-c\", LISTENING_PORTS]);\n if (listed.exitCode !== 0)\n throw new Error(`listing the ports in use failed: ${listed.stderr.trim()}`);\n return listed.stdout.split(`\n`).filter(Boolean).map(Number);\n}\nasync function withLock(sandbox, body) {\n const lock = `${await sandbox.homeDirectory()}/.local/state/hyperbrane/endpoints.lock`;\n const taken = await sandbox.runCommand([\"sh\", \"-c\", TAKE_LOCK, \"sh\", lock]);\n if (taken.exitCode !== 0)\n throw new Error(`${lock} stayed locked: ${taken.stderr.trim()}`);\n try {\n return await body();\n } finally {\n await sandbox.runCommand([\"rm\", \"-rf\", lock]);\n }\n}\nfunction under(base, endpoint) {\n if (base === undefined || new URL(base).pathname === \"/\")\n return endpoint;\n return { ...endpoint, url: `${base.replace(/\\/+$/, \"\")}/${endpoint.name}` };\n}\nfunction createEndpoints(sandbox, storage, ports) {\n const located = async (endpoints) => {\n const base = (await storage.readDescriptor())?.dataEndpointOptions?.url;\n return endpoints.map((endpoint) => under(base, endpoint));\n };\n async function openEndpoint(name) {\n const [endpoint] = await located([await openStored(name)]);\n return endpoint;\n }\n async function openStored(name) {\n return withLock(sandbox, async () => {\n const existing = await storage.readEndpoint(name);\n if (existing)\n return existing;\n const { from, to } = await ports.ports();\n const used = new Set([...(await storage.listEndpoints()).map((endpoint) => endpoint.port), ...await listeningPorts(sandbox)]);\n let port = from;\n while (port <= to && used.has(port))\n port++;\n if (port > to)\n throw new Error(`no data endpoint ports are left in ${from}-${to}`);\n const endpoint = { name, url: await ports.expose(port), port, secret: randomBytes(32).toString(\"base64url\") };\n await storage.saveEndpoint(endpoint);\n return endpoint;\n });\n }\n async function closeEndpoint(name) {\n await withLock(sandbox, async () => {\n const endpoint = await storage.readEndpoint(name);\n if (!endpoint)\n return;\n await ports.conceal(endpoint.port);\n await storage.removeEndpoint(name);\n });\n }\n async function credentials(name, {\n ttlSeconds = DEFAULT_TTL_S\n } = {}) {\n const endpoint = await storage.readEndpoint(name);\n if (!endpoint)\n throw new SandboxNotFoundError(`there is no data endpoint ${name}`);\n return { token: await signToken(endpoint.secret, ttlSeconds), ...await ports.credentials?.(endpoint, ttlSeconds) };\n }\n return {\n openEndpoint,\n closeEndpoint,\n listEndpoints: async () => located(await storage.listEndpoints()),\n credentials\n };\n}\nvar LISTENING_PORTS = sh`\nawk 'FNR > 1 && $4 == \"0A\" { n = split($2, local, \":\"); print local[n] }' /proc/net/tcp /proc/net/tcp6 2> /dev/null | while read -r hex; do\n printf '%d\\n' \"0x$hex\"\ndone\n`;\nvar TAKE_LOCK = sh`\nmkdir -p \"$(dirname \"$1\")\"\nwaited=0\nuntil mkdir \"$1\" 2> /dev/null; do\n if [ -n \"$(find \"$1\" -maxdepth 0 -mmin +2 2> /dev/null)\" ]; then\n rm -rf \"$1\"\n continue\n fi\n [ \"$waited\" -lt 1200 ] || exit 1\n sleep 0.1\n waited=$((waited + 1))\ndone\n`;\n\n// sandbox/data-endpoints/firewalledPorts.ts\nfunction firewalledPorts(sandbox, storage) {\n const options = async () => (await storage.readDescriptor())?.dataEndpointOptions;\n async function firewall(rule) {\n const result = await sandbox.runCommand([\"sudo\", \"-n\", \"ufw\", ...rule]);\n if (result.exitCode !== 0)\n throw new Error(`ufw ${rule.join(\" \")} failed: ${result.stderr.trim()}`);\n }\n return {\n async ports() {\n const ports = (await options())?.ports;\n if (!ports)\n throw new Error(\"this machine has no data endpoints: it's created with dataEndpointOptions.ports\");\n return ports;\n },\n async expose(port) {\n const found = await options();\n if (found?.needsFirewall)\n await firewall([\"allow\", `${port}/tcp`]);\n const url = new URL(found?.url ?? \"http://127.0.0.1\");\n url.port = String(port);\n return url.toString().replace(/\\/$/, \"\");\n },\n async conceal(port) {\n if ((await options())?.needsFirewall)\n await firewall([\"delete\", \"allow\", `${port}/tcp`]);\n }\n };\n}\n\n// sandbox/lib/httpOverStream.ts\nimport { request as httpRequest } from \"http\";\nimport { Readable } from \"stream\";\nfunction httpOver(url, connection, { method = \"GET\", headers = {}, body, signal, timeout }) {\n return new Promise((resolve, reject) => {\n const target = new URL(url);\n const request = httpRequest({ createConnection: connection, path: `${target.pathname}${target.search}`, method, headers: { host: target.host, ...headers }, signal }, (response) => {\n const responseHeaders = new Headers;\n for (let i = 0;i + 1 < response.rawHeaders.length; i += 2)\n responseHeaders.append(response.rawHeaders[i], response.rawHeaders[i + 1]);\n const status = response.statusCode ?? 502;\n const nullBody = status === 204 || status === 304 || method === \"HEAD\";\n if (nullBody)\n response.resume();\n resolve(new Response(nullBody ? null : Readable.toWeb(response), { status, headers: responseHeaders }));\n });\n request.once(\"error\", reject);\n if (typeof timeout === \"number\")\n request.setTimeout(timeout, () => request.destroy(new Error(`fetch ${url}: no data for ${timeout}ms`)));\n request.end(body === undefined ? undefined : typeof body === \"string\" ? body : Buffer.from(body));\n });\n}\n\n// lib/jsonDirectory.ts\nvar EXTENSION = \".json\";\nvar NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}$/;\n\nclass InvalidNameError extends Error {\n name = \"InvalidNameError\";\n}\nfunction checkName(name) {\n if (!NAME_PATTERN.test(name)) {\n throw new InvalidNameError(`Invalid name \"${name}\": use letters, digits, \"_\", \".\" and \"-\", up to 63 characters`);\n }\n return name;\n}\nfunction createJsonDirectory(sandbox, dir) {\n async function pathFor(name) {\n return `${await dir()}/${checkName(name)}${EXTENSION}`;\n }\n function serialize(record) {\n return JSON.stringify(record, null, 2) + `\n`;\n }\n async function readPath(path) {\n try {\n return JSON.parse(new TextDecoder().decode(await sandbox.readFile(path)));\n } catch (error) {\n if (error instanceof SandboxNotFoundError)\n return null;\n throw error;\n }\n }\n async function read(name) {\n return readPath(await pathFor(name));\n }\n async function insert(name, record) {\n const path = await pathFor(name);\n if (await sandbox.writeFileExclusive(path, serialize(record)))\n return record;\n return await readPath(path) ?? insert(name, record);\n }\n async function save(name, record) {\n await sandbox.writeFile(await pathFor(name), serialize(record));\n }\n async function remove(name) {\n await sandbox.removeFile(await pathFor(name));\n }\n async function entries() {\n const path = await dir();\n const all = await sandbox.runCommand([\"sh\", \"-c\", LIST_SCRIPT, \"sh\", path], { env: { MISSING: String(MISSING), EXTENSION } }).catch(() => null);\n if (all?.exitCode === MISSING)\n return [];\n if (all?.exitCode === 0) {\n return all.stdout.split(RECORD_END).filter((text) => text.trim()).flatMap((text) => {\n const newline = text.indexOf(`\n`);\n try {\n return [[text.slice(0, newline).slice(0, -EXTENSION.length), JSON.parse(text.slice(newline + 1))]];\n } catch {\n return [];\n }\n });\n }\n let files;\n try {\n files = await sandbox.listDirectory(path);\n } catch (error) {\n if (error instanceof SandboxNotFoundError)\n return [];\n throw error;\n }\n const records = await Promise.all(files.filter((file) => file.endsWith(EXTENSION)).map(async (file) => [file.slice(0, -EXTENSION.length), await readPath(`${path}/${file}`)]));\n return records.filter((entry) => entry[1] !== null);\n }\n async function list() {\n return (await entries()).map(([, record]) => record);\n }\n return { read, insert, save, remove, list, entries };\n}\nvar MISSING = 44;\nvar RECORD_END = `\n\\x1E\n`;\nvar LIST_SCRIPT = sh`\ncd \"$1\" 2>/dev/null || exit \"$MISSING\"\nfor file in *\"$EXTENSION\"; do\n [ -f \"$file\" ] || continue\n printf '%s\\n' \"$file\"\n cat \"$file\"\n printf '\\n\\036\\n'\ndone\n`;\n\n// sandbox/storage/sandboxStorage.ts\nvar DESCRIPTOR = \"sandbox\";\nfunction createSandboxStorage(sandbox) {\n const stateDirectory = async () => `${await sandbox.homeDirectory()}/.local/state/hyperbrane`;\n const endpointsDirectory = async () => `${await stateDirectory()}/endpoints`;\n const descriptors = createJsonDirectory(sandbox, stateDirectory);\n const endpoints = createJsonDirectory(sandbox, endpointsDirectory);\n async function readDescriptor() {\n return descriptors.read(DESCRIPTOR);\n }\n async function insertDescriptor(descriptor) {\n return descriptors.insert(DESCRIPTOR, descriptor);\n }\n async function saveDescriptor(descriptor) {\n await descriptors.save(DESCRIPTOR, descriptor);\n }\n async function readEndpoint(name) {\n return endpoints.read(name);\n }\n async function saveEndpoint(endpoint) {\n const directory = await endpointsDirectory();\n const made = await sandbox.runCommand([\"sh\", \"-c\", 'mkdir -p \"$1\" && chmod 700 \"$1\"', \"sh\", directory]);\n if (made.exitCode !== 0)\n throw new Error(`making ${directory} private failed: ${made.stderr.trim()}`);\n await endpoints.save(endpoint.name, endpoint);\n }\n async function removeEndpoint(name) {\n await endpoints.remove(name);\n }\n async function listEndpoints() {\n return endpoints.list();\n }\n return {\n readDescriptor,\n insertDescriptor,\n saveDescriptor,\n readEndpoint,\n saveEndpoint,\n removeEndpoint,\n listEndpoints\n };\n}\n\n// sandbox/providers/local/localService.ts\nfunction hostPath(path) {\n return resolve(\"/\", path);\n}\nasync function notFoundAs(path, operation) {\n try {\n return await operation;\n } catch (error) {\n if (error.code === \"ENOENT\")\n throw new SandboxNotFoundError(`Not found: \"${path}\"`);\n throw error;\n }\n}\nasync function writeTemp(full, data, mode = 438) {\n await fs.mkdir(dirname(full), { recursive: true });\n const tmp = join(dirname(full), `.${basename(full)}.${randomUUID()}.tmp`);\n await fs.writeFile(tmp, data, { mode });\n return tmp;\n}\nasync function readFile2(path) {\n return new Uint8Array(await notFoundAs(path, fs.readFile(hostPath(path))));\n}\nasync function readFileRange(path, start, end) {\n const file = await notFoundAs(path, fs.open(hostPath(path), \"r\"));\n try {\n const buffer = new Uint8Array(Math.max(0, end - start));\n let filled = 0;\n while (filled < buffer.length) {\n const { bytesRead } = await file.read(buffer, filled, buffer.length - filled, start + filled);\n if (bytesRead === 0)\n break;\n filled += bytesRead;\n }\n return buffer.subarray(0, filled);\n } finally {\n await file.close();\n }\n}\nasync function writeFile2(path, data) {\n const full = hostPath(path);\n await fs.rename(await writeTemp(full, data), full);\n}\nasync function writeFileExclusive(path, data) {\n const full = hostPath(path);\n const tmp = await writeTemp(full, data);\n try {\n await fs.link(tmp, full);\n return true;\n } catch (error) {\n if (error.code === \"EEXIST\")\n return false;\n throw error;\n } finally {\n await fs.unlink(tmp);\n }\n}\nasync function removeFile(path) {\n await fs.rm(hostPath(path), { force: true });\n}\nasync function createDirectory(path) {\n await fs.mkdir(hostPath(path), { recursive: true });\n}\nasync function removeDirectory(path) {\n await fs.rm(hostPath(path), { recursive: true, force: true });\n}\nasync function listDirectory(path) {\n return notFoundAs(path, fs.readdir(hostPath(path)));\n}\nasync function stat2(path) {\n const stats = await notFoundAs(path, fs.stat(hostPath(path)));\n const type = stats.isFile() ? \"file\" : stats.isDirectory() ? \"directory\" : \"other\";\n return { type, uid: stats.uid, gid: stats.gid, size: stats.size };\n}\nasync function homeDirectory() {\n return process.env.HOME || homedir();\n}\nfunction runCommand(cmd, { cwd, env = {}, stdin } = {}) {\n return new Promise((resolve, reject) => {\n const [command, ...args] = cmd;\n if (!command)\n return reject(new Error(\"no command to run\"));\n const child = spawn(command, args, {\n cwd: hostPath(cwd ?? \".\"),\n env: { ...process.env, ...env },\n stdio: [stdin === undefined ? \"ignore\" : \"pipe\", \"pipe\", \"pipe\"]\n });\n const stdout = [];\n const stderr = [];\n child.stdout.on(\"data\", (chunk) => stdout.push(chunk));\n child.stderr.on(\"data\", (chunk) => stderr.push(chunk));\n child.once(\"error\", reject);\n child.once(\"close\", (code) => resolve({ exitCode: code ?? -1, stdout: Buffer.concat(stdout).toString(), stderr: Buffer.concat(stderr).toString() }));\n if (stdin !== undefined)\n child.stdin.end(stdin);\n });\n}\nasync function state() {\n return \"running\";\n}\nasync function start() {}\nasync function destroy() {}\nfunction hasExpiration() {\n return false;\n}\nasync function getExpiration() {\n return null;\n}\nasync function setExpiration() {\n return null;\n}\nfunction hasShell(ctx) {\n return ctx.sshKey !== undefined;\n}\nasync function sshEndpoint(ctx) {\n if (ctx.sshKey === undefined)\n throw new Error(\"this machine has no shell: its context has no SSH key (sshKey)\");\n return {\n host: ctx.sshHost ?? \"127.0.0.1\",\n port: ctx.sshPort ?? 22,\n user: ctx.sshUser ?? userInfo().username,\n privateKey: ctx.sshKey\n };\n}\nasync function fetch(url, { unix, ...init } = {}) {\n if (unix === undefined || \"Bun\" in globalThis)\n return globalThis.fetch(url, { ...init, unix: unix === undefined ? undefined : hostPath(unix) });\n return httpOver(url, () => netConnect(hostPath(unix)), init);\n}\nfunction connect(path) {\n return new Promise((resolve, reject) => {\n const socket = new Socket;\n let markClosed;\n const closed = new Promise((done) => markClosed = done);\n const listeners = [];\n socket.on(\"data\", (chunk) => {\n for (const listener of listeners)\n listener(new Uint8Array(chunk));\n });\n socket.once(\"close\", () => markClosed());\n socket.once(\"end\", () => markClosed());\n socket.on(\"error\", (error) => {\n markClosed();\n const message = `connecting to ${path} failed: ${error.message}`;\n reject(error.code === \"ENOENT\" || error.code === \"ECONNREFUSED\" ? new SandboxConnectionRefusedError(message) : new Error(message));\n });\n socket.once(\"connect\", () => resolve({\n write: (data) => void socket.write(data),\n onData: (listener) => void listeners.push(listener),\n closed,\n close: () => socket.end()\n }));\n socket.connect({ path: hostPath(path) });\n });\n}\nfunction createLocalService(ctx) {\n const fileOps = {\n readFile: readFile2,\n readFileRange,\n writeFile: writeFile2,\n writeFileExclusive,\n removeFile,\n createDirectory,\n removeDirectory,\n listDirectory,\n stat: stat2,\n homeDirectory\n };\n const cmdOps = {\n runCommand\n };\n const netOps = {\n fetch,\n connect\n };\n const machine = { ...fileOps, ...cmdOps, ...netOps };\n const storage = createSandboxStorage(machine);\n const lifecycleOps = {\n create: (spec) => createDescriptor(ctx, storage, ctx.id ?? spec.name, spec),\n open: () => openDescriptor(ctx, storage, ctx.id ?? \"this machine\"),\n get: () => getDescriptor(storage, \"this machine\"),\n state,\n start,\n destroy\n };\n const dataOps = createEndpoints(machine, storage, firewalledPorts(machine, storage));\n const expirationOps = {\n hasExpiration,\n getExpiration,\n setExpiration\n };\n const shellOps = {\n hasShell: () => hasShell(ctx),\n sshEndpoint: () => sshEndpoint(ctx)\n };\n return {\n ...fileOps,\n ...lifecycleOps,\n ...cmdOps,\n ...dataOps,\n ...expirationOps,\n ...shellOps,\n ...netOps\n };\n}\n\n// agent/session/claude/auth.ts\nimport { readFile as readFile3 } from \"fs/promises\";\nimport { join as join2 } from \"path\";\n\n// agent/session/login.ts\nfunction plain(text) {\n return text.replace(/\\x1b\\][^\\x07\\x1b]*(\\x07|\\x1b\\\\)/g, \"\").replace(/\\x1b\\[[0-9;?]*[a-zA-Z]/g, \"\");\n}\nfunction spawnLogin(cmd, { env, cwd }, parse) {\n const proc = Bun.spawn(cmd, { env, cwd, stdin: \"pipe\", stdout: \"pipe\", stderr: \"pipe\" });\n let output = \"\";\n let showPrompt;\n let noPrompt;\n const prompt = new Promise((resolve, reject) => {\n showPrompt = resolve;\n noPrompt = reject;\n });\n async function read(stream) {\n const decoder = new TextDecoder;\n for await (const chunk of stream) {\n output += plain(decoder.decode(chunk, { stream: true }));\n const found = parse(output);\n if (found)\n showPrompt(found);\n }\n }\n const reading = Promise.all([read(proc.stdout), read(proc.stderr)]);\n const done = reading.then(() => proc.exited).then((code) => {\n const tail = output.trim().split(`\n`).slice(-3).join(\" \").trim();\n noPrompt(new Error(`the login ended before showing where to sign in: ${tail}`));\n if (code !== 0)\n throw new Error(`the login failed (exit ${code}): ${tail}`);\n });\n prompt.catch(() => {});\n done.catch(() => {});\n return {\n prompt,\n done,\n submitCode(code) {\n proc.stdin.write(`${code}\n`);\n proc.stdin.flush();\n },\n cancel() {\n proc.kill();\n }\n };\n}\n\n// agent/session/claude/provision.ts\nimport { existsSync as existsSync2 } from \"fs\";\n\n// agent/session/install.ts\nimport { existsSync } from \"fs\";\n// agent/vendor-types/manifest.json\nvar manifest_default = {\n claude: {\n docs: \"https://code.claude.com/docs/en/agent-sdk/typescript\",\n sources: [\n {\n source: \"npm:@anthropic-ai/claude-agent-sdk@0.3.283\",\n into: \"agent-sdk\",\n files: 7\n }\n ]\n },\n codex: {\n docs: \"https://developers.openai.com/codex/app-server\",\n sources: [\n {\n source: \"npm:@openai/codex-sdk@0.157.1\",\n into: \"sdk\",\n files: 1\n },\n {\n source: \"github:openai/codex@rust-v0.157.1/codex-rs/app-server-protocol/schema/typescript\",\n into: \"app-server\",\n files: 740\n },\n {\n source: \"github:openai/codex@rust-v0.157.1/codex-rs/app-server-protocol/schema/json\",\n into: \"app-server-json\",\n files: 2\n }\n ]\n },\n pi: {\n docs: \"https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/rpc.md\",\n sources: [\n {\n source: \"npm:@mariozechner/pi-coding-agent@0.73.1\",\n into: \"coding-agent\",\n files: 137\n },\n {\n source: \"npm:@mariozechner/pi-agent-core@0.73.1\",\n into: \"agent-core\",\n files: 5\n },\n {\n source: \"npm:@mariozechner/pi-ai@0.73.1\",\n into: \"ai\",\n files: 44\n }\n ]\n },\n opencode: {\n docs: \"https://opencode.ai/docs/server\",\n sources: [\n {\n source: \"npm:@opencode-ai/sdk@1.18.32\",\n into: \"sdk\",\n files: 39\n },\n {\n source: \"github:anomalyco/opencode@v1.18.32/packages/sdk\",\n into: \"\",\n files: 1\n }\n ]\n },\n openclaw: {\n docs: \"https://docs.openclaw.ai/gateway/protocol\",\n sources: [\n {\n source: \"npm:@openclaw/gateway-protocol@2026.9.6\",\n into: \"gateway-protocol\",\n files: 17\n }\n ]\n },\n hermes: {\n docs: \"https://hermes-agent.nousresearch.com/docs\",\n sources: [\n {\n source: \"github:NousResearch/hermes-agent@v2026.9.24/apps/shared/src\",\n into: \"gateway\",\n files: 3\n }\n ]\n }\n};\n\n// agent/session/install.ts\nfunction pinnedVersion(vendor, pkg, sourcePackage = pkg) {\n const prefix = `npm:${sourcePackage}@`;\n const source = manifest_default[vendor].sources.find((entry) => entry.source.startsWith(prefix));\n if (!source)\n throw new Error(`agent/vendor-types/manifest.json has no ${sourcePackage} for ${vendor} (needed for ${pkg})`);\n return source.source.slice(prefix.length);\n}\nasync function installedPackage(installDir, pkg, version, executable) {\n const manifest = Bun.file(`${installDir}/node_modules/${pkg}/package.json`);\n const found = await manifest.exists() ? (await manifest.json()).version ?? null : null;\n return { version: found, ready: found !== null && existsSync(executable()), pinned: version };\n}\nasync function installPackage(installDir, pkg, version, executable) {\n const installed = await installedPackage(installDir, pkg, version, executable);\n if (installed.ready && installed.version === version)\n return;\n const force = installed.version === version ? [\"--force\"] : [];\n const proc = Bun.spawn([\"flock\", `${installDir}/.install.lock`, process.execPath, \"add\", \"--exact\", ...force, `${pkg}@${version}`], {\n cwd: installDir,\n stdin: \"ignore\",\n stdout: \"pipe\",\n stderr: \"pipe\"\n });\n const [stdout, stderr, exitCode] = await Promise.all([\n new Response(proc.stdout).text(),\n new Response(proc.stderr).text(),\n proc.exited\n ]);\n if (exitCode !== 0)\n throw new Error(`bun add ${pkg}@${version} failed (exit ${exitCode}): ${(stderr || stdout).trim()}`);\n if (!existsSync(executable()))\n throw new Error(`bun add ${pkg}@${version} left no ${executable()}: its platform package didn't install`);\n}\n\n// agent/session/claude/provision.ts\nvar CLAUDE_SDK_PACKAGE = \"@anthropic-ai/claude-agent-sdk\";\nfunction installClaude(installDir) {\n return installPackage(installDir, CLAUDE_SDK_PACKAGE, pinnedVersion(\"claude\", CLAUDE_SDK_PACKAGE), () => claudeExecutable(installDir));\n}\nfunction installedClaude(installDir) {\n return installedPackage(installDir, CLAUDE_SDK_PACKAGE, pinnedVersion(\"claude\", CLAUDE_SDK_PACKAGE), () => claudeExecutable(installDir));\n}\nfunction claudeExecutable(installDir) {\n const base = `${installDir}/node_modules/${CLAUDE_SDK_PACKAGE}-linux-${process.arch}`;\n const glibc = `${base}/claude`;\n return existsSync2(glibc) ? glibc : `${base}-musl/claude`;\n}\n\n// agent/session/claude/auth.ts\nvar GATEWAY_CODE = \"x#x\";\nvar CREDENTIAL_VARIABLES = [\"ANTHROPIC_API_KEY\", \"ANTHROPIC_AUTH_TOKEN\", \"CLAUDE_CODE_OAUTH_TOKEN\"];\nvar claudeAuth = {\n async signedIn({ env }) {\n if (CREDENTIAL_VARIABLES.some((name) => env[name]))\n return true;\n const config = env.CLAUDE_CONFIG_DIR ?? join2(env.HOME ?? \"\", \".claude\");\n const credentials = await readJson(join2(config, \".credentials.json\"));\n if (credentials?.claudeAiOauth?.accessToken)\n return true;\n const settings = await readJson(join2(config, \".claude.json\"));\n return Boolean(settings?.primaryApiKey);\n },\n login({ installDir, env, cwd }, gatewaySignedIn) {\n const login = spawnLogin([claudeExecutable(installDir), \"auth\", \"login\"], { env, cwd }, (output) => {\n const url = /https:\\/\\/\\S*oauth\\/authorize\\S*/.exec(output)?.[0];\n return url ? { url, needsCode: true } : null;\n });\n if (gatewaySignedIn)\n login.prompt.then(() => login.submitCode(GATEWAY_CODE), () => {});\n return login;\n }\n};\nasync function readJson(path) {\n try {\n return JSON.parse(await readFile3(path, \"utf8\"));\n } catch {\n return null;\n }\n}\n\n// agent/api.ts\nvar AGENT_BACKENDS = [\"claude\", \"codex\"];\nvar AGENT_PERMISSION_MODES = [\"ask\", \"auto\"];\n\nclass AgentInvalidError extends Error {\n name = \"AgentInvalidError\";\n}\n\nclass AgentNotFoundError extends Error {\n name = \"AgentNotFoundError\";\n}\n\nclass AgentConflictError extends Error {\n name = \"AgentConflictError\";\n}\n\nclass AgentBackendNotInstalledError extends AgentConflictError {\n name = \"AgentBackendNotInstalledError\";\n}\n\n// agent/session/claude/backgroundTasks.ts\nfunction claudeBackgroundTasks() {\n let tasks = [];\n return {\n observe(message) {\n if (message.type !== \"system\")\n return;\n if (message.subtype === \"background_tasks_changed\")\n tasks = message.tasks;\n if (message.subtype === \"task_notification\")\n tasks = tasks.filter((task) => task.task_id !== message.task_id);\n },\n list: async () => tasks\n };\n}\nfunction claudeStopTask(query, tasks) {\n return async (taskId) => {\n if (!(await tasks.list()).some((task) => task.task_id === taskId))\n throw new AgentNotFoundError(`no background task ${taskId}`);\n await query.stopTask(taskId);\n };\n}\n\n// agent/session/claude/home.ts\nfunction claudeHomeEnvironment(home) {\n return { CLAUDE_CONFIG_DIR: home };\n}\n\n// agent/session/claude/input.ts\nfunction createPromptQueue() {\n const queued = [];\n let wake;\n let closed = false;\n function push(message) {\n queued.push(message);\n wake?.();\n }\n function close() {\n closed = true;\n wake?.();\n }\n async function* iterate() {\n for (;; ) {\n while (queued.length > 0)\n yield queued.shift();\n if (closed)\n return;\n await new Promise((resolve) => wake = resolve);\n wake = undefined;\n }\n }\n return { push, close, [Symbol.asyncIterator]: iterate };\n}\nfunction claudeInput(prompts, queue) {\n return async (message) => {\n if (!message.uuid)\n throw new Error(\"a message sent to claude needs a uuid to be acknowledged by\");\n const received = queue.sent({ ...message, uuid: message.uuid });\n prompts.push(message);\n await received;\n };\n}\n\n// agent/session/claude/messageQueue.ts\nvar RECEIVE_TIMEOUT_MS = 60000;\nfunction claudeMessageQueue() {\n const commands = new Map;\n let taken = null;\n const receipts = new Map;\n function observe(message) {\n const event = message;\n if (event.type !== \"command_lifecycle\" || !event.command_uuid || !event.state)\n return;\n const command = commands.get(event.command_uuid);\n if (command) {\n command.state = event.state;\n if (event.state === \"started\")\n taken = command.message;\n if (event.state !== \"queued\")\n commands.delete(event.command_uuid);\n }\n const receipt = receipts.get(event.command_uuid);\n if (!receipt)\n return;\n receipts.delete(event.command_uuid);\n if (event.state === \"queued\" || event.state === \"started\")\n receipt.resolve();\n else\n receipt.reject(new Error(`claude ${event.state} the message`));\n }\n function sent(message) {\n commands.set(message.uuid, { message, state: null });\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n receipts.delete(message.uuid);\n commands.delete(message.uuid);\n reject(new Error(\"claude didn't confirm it received the message\"));\n }, RECEIVE_TIMEOUT_MS);\n receipts.set(message.uuid, {\n resolve: () => (clearTimeout(timer), resolve()),\n reject: (error) => (clearTimeout(timer), commands.delete(message.uuid), reject(error))\n });\n });\n }\n async function lastUserMessages() {\n const queued = [...commands.values()].filter((command) => command.state === \"queued\");\n return [...taken ? [{ message: taken, queued: false }] : [], ...queued.map((command) => ({ message: command.message, queued: true }))];\n }\n return { observe, sent, lastUserMessages };\n}\n\n// agent/session/claude/status.ts\nvar SESSION_STATE_EVENTS = { CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS: \"1\" };\nfunction claudeStatus(changed) {\n let active = false;\n return {\n observe(message) {\n if (message.type !== \"system\" || message.subtype !== \"session_state_changed\")\n return;\n const next = message.state !== \"idle\";\n if (next === active)\n return;\n active = next;\n changed();\n },\n active: () => active\n };\n}\n\n// agent/session/claude/interrupt.ts\nfunction claudeInterrupt(query) {\n return async () => {\n await query.interrupt();\n };\n}\n\n// agent/session/claude/model.ts\nfunction claudeModel(query) {\n return {\n listModels: () => query.supportedModels(),\n setModel: (model) => query.setModel(model ?? undefined)\n };\n}\n\n// agent/session/sdkMessages.ts\nvar ASK_USER_QUESTION = \"AskUserQuestion\";\nfunction resultUsage({ input = 0, output = 0, cacheRead = 0, cacheWrite = 0, thinking = 0 } = {}) {\n return {\n input_tokens: input,\n output_tokens: output,\n output_tokens_details: { thinking_tokens: thinking },\n cache_read_input_tokens: cacheRead,\n cache_creation_input_tokens: cacheWrite,\n cache_creation: { ephemeral_1h_input_tokens: 0, ephemeral_5m_input_tokens: 0 },\n server_tool_use: { web_fetch_requests: 0, web_search_requests: 0 },\n service_tier: \"standard\",\n fallback_credit: { status: { type: \"not_applied\", reason: \"not_enabled\" } },\n inference_geo: \"\",\n iterations: [],\n speed: \"standard\"\n };\n}\nfunction userMessageText(message) {\n const content = message.message.content;\n if (typeof content === \"string\")\n return content;\n if (!Array.isArray(content))\n return \"\";\n return content.map((block) => block.type === \"text\" && typeof block.text === \"string\" ? block.text : \"\").filter(Boolean).join(`\n`);\n}\n\n// agent/session/claude/permissions.ts\nfunction claudePermissionMode(mode) {\n return mode === \"auto\" ? \"bypassPermissions\" : \"default\";\n}\nfunction claudePermissions(context, questions) {\n let mode = context.permissionMode;\n const canUseTool = (toolName, input, options) => {\n if (toolName === ASK_USER_QUESTION)\n return questions.answer(input, options);\n const { signal, toolUseID, decisionReason } = options;\n return context.requestPermission({ tool_name: toolName, input, tool_use_id: toolUseID, decision_reason: decisionReason }, signal);\n };\n const preToolUse = [\n {\n hooks: [async () => mode === \"auto\" ? {} : { hookSpecificOutput: { hookEventName: \"PreToolUse\", permissionDecision: \"ask\" } }]\n }\n ];\n async function setMode(query, next) {\n mode = next;\n await query.setPermissionMode(claudePermissionMode(next));\n }\n return { canUseTool, preToolUse, mode: () => mode, setMode };\n}\n\n// agent/session/claude/questions.ts\nvar QUESTION_DIALOG_KINDS = [\"permission_ask_user_question\", \"ask_user_question\", \"user_question\"];\nfunction claudeQuestions(context) {\n async function ask(input, toolUseId, signal) {\n const questions = Array.isArray(input.questions) ? input.questions : [];\n const response = await context.askQuestions({ tool_use_id: toolUseId, questions }, signal);\n if (!response)\n return { behavior: \"deny\", message: \"The user dismissed the questions.\" };\n return { behavior: \"allow\", updatedInput: { ...input, answers: response.answers } };\n }\n const onUserDialog = async (request, { signal }) => {\n if (!QUESTION_DIALOG_KINDS.includes(request.dialogKind))\n return { behavior: \"cancelled\" };\n const result = await ask(request.payload, request.toolUseID ?? crypto.randomUUID(), signal);\n if (result.behavior !== \"allow\")\n return { behavior: \"cancelled\" };\n return { behavior: \"completed\", result };\n };\n return {\n answer: (input, { signal, toolUseID }) => ask(input, toolUseID, signal),\n onUserDialog,\n supportedDialogKinds: QUESTION_DIALOG_KINDS\n };\n}\n\n// agent/session/claude/stream.ts\nimport { spawn as spawn2 } from \"child_process\";\n\n// agent/session/claude/sdk.ts\nasync function loadClaudeSdk(installDir) {\n return await import(Bun.resolveSync(CLAUDE_SDK_PACKAGE, installDir));\n}\n\n// agent/session/claude/stream.ts\nasync function startClaudeStream(context, prompts, permissions, questions, backgroundTasks, status, queue) {\n const sdk = await loadClaudeSdk(context.installDir);\n let cli = null;\n const query = sdk.query({\n prompt: prompts,\n options: {\n cwd: context.cwd,\n env: { ...context.env, ...SESSION_STATE_EVENTS },\n model: context.model ?? undefined,\n resume: context.nativeId ?? undefined,\n includePartialMessages: true,\n permissionMode: claudePermissionMode(permissions.mode()),\n allowDangerouslySkipPermissions: true,\n settingSources: [],\n canUseTool: permissions.canUseTool,\n hooks: { PreToolUse: permissions.preToolUse },\n onUserDialog: questions.onUserDialog,\n supportedDialogKinds: questions.supportedDialogKinds,\n spawnClaudeCodeProcess: (options) => {\n cli = spawnCli(options, context.spec.name);\n return cli.process;\n }\n }\n });\n const exit = async () => {\n query.close();\n return cli ? cli.exit : null;\n };\n const subagents = {\n observe(message) {\n if (message.type === \"system\" && message.subtype === \"task_started\" && message.tool_use_id && message.subagent_type) {\n context.subagentStarted(message.tool_use_id, message.task_id);\n }\n }\n };\n pump(context, query, [backgroundTasks, status, queue, subagents]).then(async () => context.exited(await exit()), async (error) => context.exited(await exit(), error));\n return query;\n}\nfunction spawnCli({ command, args, cwd, env, signal }, sessionName) {\n const child = spawn2(command, args, { cwd, env, signal, stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n child.stderr.on(\"data\", (data) => process.stderr.write(`[claude ${sessionName}] ${data}`));\n const exit = new Promise((resolve) => {\n child.once(\"exit\", (code, signal) => resolve({ code, signal }));\n child.once(\"error\", () => {\n if (child.pid === undefined)\n resolve(null);\n });\n });\n return { process: child, exit };\n}\nasync function pump(context, query, observers) {\n for await (const message of query) {\n for (const observer of observers)\n observer.observe(message);\n if (\"session_id\" in message && typeof message.session_id === \"string\" && message.session_id) {\n context.setNativeId(message.session_id);\n }\n const streamed = toStreamingMessage(message);\n if (streamed)\n context.emit(streamed);\n }\n}\nfunction toStreamingMessage(message) {\n switch (message.type) {\n case \"assistant\":\n case \"stream_event\":\n case \"result\":\n return message;\n case \"user\":\n return { ...message, uuid: message.uuid ?? crypto.randomUUID(), session_id: message.session_id ?? \"\" };\n case \"system\":\n if (message.subtype === \"compact_boundary\")\n return { ...message, timestamp: new Date().toISOString() };\n return message.subtype === \"init\" ? message : null;\n default:\n return null;\n }\n}\n\n// agent/session/fileTranscript.ts\nfunction sandboxFiles(sandbox) {\n return {\n size: async (path) => (await sandbox.stat(path)).size,\n readFileRange: (path, start, end) => sandbox.readFileRange(path, start, end),\n listDirectory: (path) => sandbox.listDirectory(path)\n };\n}\nfunction locateTranscript(files, home, layout, nativeId) {\n const escape = (text) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const [before = \"\", after = \"\"] = layout.name.split(\"{id}\").map((part) => part.split(\"*\").map(escape).join(\".*\"));\n const name = new RegExp(`^${before}${escape(nativeId)}${after}$`);\n return findFile(files, `${home}/${layout.dir}`, layout.depth, (entry) => name.test(entry));\n}\nvar CHUNK_BYTES = 64 * 1024;\nvar TIME_SLACK_MS = 60000;\nvar PROBE_LINES = 3;\nvar PROBE_BYTES = 4 * CHUNK_BYTES;\nvar NEWLINE = 10;\nfunction lineTime(entry, field) {\n const value = entry?.[field];\n const time = typeof value === \"string\" ? Date.parse(value) : NaN;\n return Number.isNaN(time) ? null : time;\n}\nvar SCAN_BYTES = 1024 * 1024;\nvar searchedCopies = new Map;\nvar SEARCHED_FILES = 16;\nfunction createFileTranscript(files, locate, lineMessages, layout) {\n function parse(bytes, start) {\n try {\n const entry = JSON.parse(new TextDecoder().decode(bytes));\n return { start, time: lineTime(entry, layout.time), messages: lineMessages(entry) };\n } catch {\n return { start, time: null, messages: [] };\n }\n }\n function kept(from, to, skip) {\n const parts = [];\n let start = from;\n for (const stretch of [...skip].sort((a, b) => a.start - b.start)) {\n if (stretch.end <= start || stretch.start >= to)\n continue;\n if (stretch.start > start)\n parts.push({ start, end: stretch.start });\n start = Math.max(start, stretch.end);\n }\n if (start < to)\n parts.push({ start, end: to });\n return parts;\n }\n async function* backward(path, end, skip = []) {\n for (const part of kept(0, end, skip).reverse())\n yield* backwardIn(path, part.start, part.end);\n }\n async function* backwardIn(path, floor, end) {\n let position = end;\n let carry = new Uint8Array(0);\n while (position > floor) {\n const from = Math.max(floor, position - CHUNK_BYTES);\n const chunk = await files.readFileRange(path, from, position);\n const bytes = joined(chunk, carry);\n let lineEnd = bytes.length;\n for (let index = bytes.length - 1;index >= 0; index--) {\n if (bytes[index] !== NEWLINE)\n continue;\n if (lineEnd > index + 1)\n yield parse(bytes.subarray(index + 1, lineEnd), from + index + 1);\n lineEnd = index;\n }\n carry = bytes.slice(0, lineEnd);\n position = from;\n }\n if (carry.length > 0)\n yield parse(carry, floor);\n }\n async function* forward(path, start, end, skip = []) {\n for (const part of kept(start, end, skip))\n yield* forwardIn(path, part.start, part.end);\n }\n async function* forwardIn(path, start, end) {\n let position = start;\n let carry = new Uint8Array(0);\n let carryStart = start;\n while (position < end) {\n const to = Math.min(end, position + CHUNK_BYTES);\n const bytes = joined(carry, await files.readFileRange(path, position, to));\n let lineStart = 0;\n for (let index = 0;index < bytes.length; index++) {\n if (bytes[index] !== NEWLINE)\n continue;\n if (index > lineStart)\n yield parse(bytes.subarray(lineStart, index), carryStart + lineStart);\n lineStart = index + 1;\n }\n carry = bytes.slice(lineStart);\n carryStart += lineStart;\n position = to;\n }\n if (carry.length > 0)\n yield parse(carry, carryStart);\n }\n async function seek(path, low, high, target, skip) {\n return (await narrow(path, low, high, target, skip)).high;\n }\n async function narrow(path, low, high, target, skip) {\n while (high - low > CHUNK_BYTES) {\n let middle = Math.floor((low + high) / 2);\n const inside = skip.find((stretch) => stretch.start <= middle && middle < stretch.end);\n if (inside) {\n if (inside.end >= high) {\n high = Math.max(low, inside.start);\n continue;\n }\n middle = inside.end;\n }\n const probe = await probeTime(path, middle, high, skip);\n if (!probe)\n break;\n if (probe.time >= target)\n high = probe.start;\n else\n low = probe.start;\n }\n return { low, high };\n }\n async function probeTime(path, from, limit, skip) {\n let start = -1;\n let time = Infinity;\n let timed = 0;\n for await (const line of forward(path, from, Math.min(limit, from + PROBE_BYTES), skip)) {\n if (line.start === from && from > 0)\n continue;\n if (line.start >= limit)\n break;\n if (start < 0)\n start = line.start;\n if (line.time === null)\n continue;\n time = Math.min(time, line.time);\n if (++timed === PROBE_LINES)\n break;\n }\n return start < 0 || timed === 0 ? null : { start, time };\n }\n async function timeAt(path, from, size, skip) {\n for await (const line of forward(path, from, Math.min(size, from + PROBE_BYTES), skip))\n if (line.time !== null)\n return line.time;\n return null;\n }\n async function lineStart(path, at) {\n for await (const line of backward(path, at + 1))\n return line.start;\n return 0;\n }\n function copiesOf(path, size) {\n if (!layout.copies)\n return Promise.resolve([]);\n const key = `${path}\\x00${size}`;\n let copies = searchedCopies.get(key);\n if (!copies) {\n copies = findCopies(path, size, layout.copies);\n copies.catch(() => searchedCopies.delete(key));\n searchedCopies.set(key, copies);\n if (searchedCopies.size > SEARCHED_FILES)\n searchedCopies.delete(searchedCopies.keys().next().value);\n }\n return copies;\n }\n async function findCopies(path, size, marker) {\n const needle = Buffer.from(marker);\n const found = [];\n for (let position = 0;position < size; position += SCAN_BYTES) {\n const from = Math.max(0, position - needle.length + 1);\n const bytes = Buffer.from(await files.readFileRange(path, from, Math.min(size, position + SCAN_BYTES)));\n for (let index = bytes.indexOf(needle);index >= 0; index = bytes.indexOf(needle, index + 1)) {\n if (from + index >= position || position === 0)\n found.push(from + index);\n }\n }\n const copies = [];\n for (const [index, at] of found.entries()) {\n const start = await lineStart(path, at);\n let earlier = null;\n for await (const before of backward(path, start)) {\n if (before.time === null)\n continue;\n earlier = before.time;\n break;\n }\n if (earlier === null || copies.some((copy) => copy.start <= start && start < copy.end))\n continue;\n let time = null;\n for await (const line of forward(path, start, size)) {\n time = line.time;\n break;\n }\n if (time === null || time >= earlier)\n continue;\n const next = found[index + 1];\n const near = await narrow(path, start + 1, next === undefined ? size : await lineStart(path, next), earlier + 1, []);\n let end = near.high;\n for await (const line of forward(path, near.low, near.high)) {\n if (line.start <= start || line.time === null || line.time <= earlier)\n continue;\n end = line.start;\n break;\n }\n copies.push({ start, end });\n }\n return copies;\n }\n async function* pagedBackward(path, end, after, skip) {\n let next = after;\n let waiting = [];\n const settle = function* (before) {\n const [timed, ...untimed] = waiting;\n if (!timed)\n return;\n const own = timed.time;\n const late = before !== null && own < before - TIME_SLACK_MS && (next === null || own < next - TIME_SLACK_MS);\n const ahead = before !== null && own > before + TIME_SLACK_MS && (next === null || own > next + TIME_SLACK_MS);\n const time = late || ahead ? before : own;\n yield { ...timed, time };\n for (const line of untimed)\n yield { ...line, time };\n next = own;\n waiting = [];\n };\n for await (const line of backward(path, end, skip)) {\n if (line.time === null) {\n if (waiting.length === 0)\n yield { ...line, time: next ?? Infinity };\n else\n waiting.push(line);\n continue;\n }\n yield* settle(line.time);\n waiting = [line];\n }\n yield* settle(null);\n }\n async function page(before, limit, found) {\n const path = await locate();\n if (!path)\n return { messages: [], nextBefore: null };\n const size = await files.size(path);\n const copies = found?.copies ?? await copiesOf(path, size);\n const from = Math.min(found?.end ?? (before === null ? size : await seek(path, 0, size, before + TIME_SLACK_MS, copies)), size);\n const after = found ? found.after : from < size ? await timeAt(path, from, size, copies) : null;\n const taken = [];\n let count = 0;\n let oldest = Infinity;\n let older = false;\n for await (const line of pagedBackward(path, from, after, copies)) {\n if (count >= limit && line.time < oldest - TIME_SLACK_MS) {\n older = true;\n break;\n }\n if (line.messages.length === 0 || before !== null && !(line.time < before))\n continue;\n taken.push(line);\n count += line.messages.length;\n if (count >= limit)\n oldest = oldestOnPage(taken, limit);\n }\n const kept = count >= limit ? taken.filter((line) => line.time >= oldest) : taken;\n if (kept.length < taken.length)\n older = true;\n return {\n messages: kept.sort((a, b) => a.start - b.start).flatMap((line) => line.messages),\n nextBefore: older && Number.isFinite(oldest) ? new Date(oldest).toISOString() : null\n };\n }\n async function replay(afterId, emit) {\n const path = await locate();\n if (!path)\n return afterId === undefined;\n const end = await files.size(path);\n const copies = await copiesOf(path, end);\n let start = 0;\n if (afterId !== undefined) {\n let found = null;\n for await (const line of backward(path, end, copies)) {\n if (line.messages.some((message) => message.uuid === afterId)) {\n found = line;\n break;\n }\n }\n if (!found)\n return false;\n start = found.start;\n }\n let passed = afterId === undefined;\n for await (const line of forward(path, start, end, copies)) {\n for (const message of line.messages) {\n if (passed)\n emit(message);\n else if (message.uuid === afterId)\n passed = true;\n }\n }\n return true;\n }\n async function find(match) {\n const path = await locate();\n if (!path)\n return null;\n const end = await files.size(path);\n for await (const line of backward(path, end, await copiesOf(path, end))) {\n const found = line.messages.find(match);\n if (found)\n return found;\n }\n return null;\n }\n return { page, replay, find };\n}\nfunction oldestOnPage(lines, limit) {\n const newestFirst = [...lines].sort((a, b) => b.time - a.time);\n let count = 0;\n for (const line of newestFirst) {\n count += line.messages.length;\n if (count >= limit)\n return line.time;\n }\n return newestFirst[newestFirst.length - 1]?.time ?? Infinity;\n}\nfunction joined(first, second) {\n if (first.length === 0)\n return second;\n if (second.length === 0)\n return first;\n const bytes = new Uint8Array(first.length + second.length);\n bytes.set(first);\n bytes.set(second, first.length);\n return bytes;\n}\nvar DEFAULT_PAGE_SIZE = 50;\nvar MAX_PAGE_SIZE = 500;\nasync function transcriptPage(transcript, { limit, before, cursor } = {}, found) {\n const time = transcriptTime(before ?? cursor);\n if (!transcript)\n return { messages: [], nextBefore: null, nextCursor: null };\n const size = Math.min(Math.max(Math.floor(limit ?? DEFAULT_PAGE_SIZE), 1), MAX_PAGE_SIZE);\n const page = await transcript.page(time, size, found);\n return { ...page, nextCursor: page.nextBefore };\n}\nfunction transcriptTime(before) {\n if (before === undefined)\n return null;\n const time = Date.parse(before);\n if (Number.isNaN(time))\n throw new AgentInvalidError(`invalid time ${before}: give an ISO 8601 time`);\n return time;\n}\nasync function directoryNames(files, dir) {\n try {\n return await files.listDirectory(dir);\n } catch (error) {\n if (error instanceof SandboxNotFoundError)\n return null;\n throw error;\n }\n}\nasync function findFile(files, dir, depth, matches) {\n const names = await directoryNames(files, dir);\n if (!names)\n return null;\n if (depth <= 1) {\n const name = names.find(matches);\n return name === undefined ? null : `${dir}/${name}`;\n }\n const found = await Promise.all(names.sort().reverse().map((name) => findFile(files, `${dir}/${name}`, depth - 1, matches).catch(() => null)));\n return found.find((path) => path !== null) ?? null;\n}\n\n// agent/session/claude/transcript.ts\nfunction claudeTranscript({ nativeId, env, files }) {\n return createFileTranscript(files, () => locateTranscript(files, claudeHome(env), CLAUDE_TRANSCRIPT_LAYOUT, nativeId), claudeLineMessages, CLAUDE_TRANSCRIPT_LAYOUT);\n}\nfunction claudeSubagentTranscript(source, agentId, toolUseId) {\n return subagentFile(source, `agent-${agentId}.jsonl`, toolUseId);\n}\nasync function claudeFindSubagent(source, toolUseId) {\n const isResult = (message) => message.type === \"user\" && Array.isArray(message.message.content) && message.message.content.some((block) => block.type === \"tool_result\" && block.tool_use_id === toolUseId);\n let found = await claudeTranscript(source).find(isResult);\n const dir = found ? null : await subagentsDirectory(source);\n const names = dir ? await directoryNames(source.files, dir) ?? [] : [];\n for (const name of names.filter((entry) => SUBAGENT_FILE.test(entry))) {\n found = await subagentFile(source, name, toolUseId).find(isResult);\n if (found)\n break;\n }\n const agentId = found?.tool_use_result?.agentId;\n return typeof agentId === \"string\" ? agentId : null;\n}\nfunction subagentFile(source, name, toolUseId) {\n async function locate() {\n const dir = await subagentsDirectory(source);\n if (!dir)\n return null;\n return (await directoryNames(source.files, dir))?.includes(name) ? `${dir}/${name}` : null;\n }\n return createFileTranscript(source.files, locate, (line) => lineMessages(line, toolUseId), CLAUDE_TRANSCRIPT_LAYOUT);\n}\nasync function subagentsDirectory({ nativeId, env, files }) {\n const main = await locateTranscript(files, claudeHome(env), CLAUDE_TRANSCRIPT_LAYOUT, nativeId);\n return main ? `${main.slice(0, -\".jsonl\".length)}/subagents` : null;\n}\nfunction claudeHome(env) {\n return env.CLAUDE_CONFIG_DIR ?? `${env.HOME}/.claude`;\n}\nvar SUBAGENT_FILE = /^agent-.+\\.jsonl$/;\nvar CLAUDE_TRANSCRIPT_LAYOUT = {\n dir: \"projects\",\n depth: 2,\n name: \"{id}.jsonl\",\n time: \"timestamp\",\n copies: '\"subtype\":\"compact_boundary\"'\n};\nfunction claudeLineMessages(line) {\n return lineMessages(line, null);\n}\nfunction lineMessages(line, parentToolUseId) {\n const entry = line;\n if (entry.type === \"system\" && entry.subtype === \"compact_boundary\")\n return compaction(entry);\n if (entry.type !== \"user\" && entry.type !== \"assistant\")\n return [];\n if (entry.isSidechain && parentToolUseId === null || entry.isMeta || !entry.uuid || !entry.message)\n return [];\n const common = { parent_tool_use_id: parentToolUseId, uuid: entry.uuid, session_id: entry.sessionId ?? \"\", ...entry.timestamp ? { timestamp: entry.timestamp } : {} };\n if (entry.type === \"assistant\")\n return [{ type: \"assistant\", message: entry.message, ...common }];\n return [{ type: \"user\", message: entry.message, ...common, ...entry.toolUseResult !== undefined ? { tool_use_result: entry.toolUseResult } : {} }];\n}\nfunction compaction(entry) {\n const meta = entry.compactMetadata;\n if (!entry.uuid || !meta?.trigger)\n return [];\n const { preservedSegment: segment, preservedMessages: preserved } = meta;\n return [\n {\n type: \"system\",\n subtype: \"compact_boundary\",\n compact_metadata: {\n trigger: meta.trigger,\n pre_tokens: meta.preTokens ?? 0,\n ...meta.postTokens !== undefined ? { post_tokens: meta.postTokens } : {},\n ...meta.durationMs !== undefined ? { duration_ms: meta.durationMs } : {},\n ...segment ? { preserved_segment: { head_uuid: segment.headUuid, anchor_uuid: segment.anchorUuid, tail_uuid: segment.tailUuid } } : {},\n ...preserved ? { preserved_messages: { anchor_uuid: preserved.anchorUuid, uuids: preserved.uuids } } : {}\n },\n uuid: entry.uuid,\n session_id: entry.sessionId ?? \"\",\n ...entry.timestamp ? { timestamp: entry.timestamp } : {}\n }\n ];\n}\n\n// agent/session/claude/index.ts\nvar claudeBackend = {\n install: installClaude,\n installed: installedClaude,\n auth: claudeAuth,\n transcript: claudeTranscript,\n findSubagent: claudeFindSubagent,\n subagentTranscript: claudeSubagentTranscript,\n homeEnvironment: claudeHomeEnvironment,\n async start(context) {\n const prompts = createPromptQueue();\n const questions = claudeQuestions(context);\n const backgroundTasks = claudeBackgroundTasks();\n const status = claudeStatus(() => context.activeChanged());\n const queue = claudeMessageQueue();\n const permissions = claudePermissions(context, questions);\n const query = await startClaudeStream(context, prompts, permissions, questions, backgroundTasks, status, queue);\n return {\n sendMessage: claudeInput(prompts, queue),\n active: status.active,\n getLastUserMessages: queue.lastUserMessages,\n cancel: claudeInterrupt(query),\n ...claudeModel(query),\n setPermissionMode: (mode) => permissions.setMode(query, mode),\n listBackgroundTasks: backgroundTasks.list,\n stopTask: claudeStopTask(query, backgroundTasks),\n async close() {\n prompts.close();\n query.close();\n }\n };\n }\n};\n\n// agent/session/codex/provision.ts\nvar CODEX_PACKAGE = \"@openai/codex\";\nvar CODEX_VERSION = () => pinnedVersion(\"codex\", CODEX_PACKAGE, \"@openai/codex-sdk\");\nfunction installCodex(installDir) {\n return installPackage(installDir, CODEX_PACKAGE, CODEX_VERSION(), () => codexExecutable(installDir).path);\n}\nfunction installedCodex(installDir) {\n return installedPackage(installDir, CODEX_PACKAGE, CODEX_VERSION(), () => codexExecutable(installDir).path);\n}\nvar LINUX_TARGETS = {\n x64: { pkg: \"@openai/codex-linux-x64\", triple: \"x86_64-unknown-linux-musl\" },\n arm64: { pkg: \"@openai/codex-linux-arm64\", triple: \"aarch64-unknown-linux-musl\" }\n};\nfunction codexExecutable(installDir) {\n const target = LINUX_TARGETS[process.arch];\n if (process.platform !== \"linux\" || !target)\n throw new Error(`codex: unsupported platform ${process.platform}/${process.arch}`);\n return {\n path: `${installDir}/node_modules/${target.pkg}/vendor/${target.triple}/bin/codex`,\n env: { CODEX_MANAGED_PACKAGE_ROOT: `${installDir}/node_modules/${CODEX_PACKAGE}`, CODEX_MANAGED_BY_BUN: \"1\" }\n };\n}\n\n// agent/session/codex/auth.ts\nfunction run({ installDir, env, cwd }, args) {\n const executable = codexExecutable(installDir);\n return { cmd: [executable.path, ...args], env: { ...env, ...executable.env }, cwd };\n}\nvar codexAuth = {\n async signedIn(environment) {\n const { cmd, env, cwd } = run(environment, [\"login\", \"status\"]);\n const proc = Bun.spawn(cmd, { env, cwd, stdout: \"ignore\", stderr: \"ignore\" });\n return await proc.exited === 0;\n },\n login(environment) {\n const { cmd, env, cwd } = run(environment, [\"login\", \"--device-auth\"]);\n return spawnLogin(cmd, { env, cwd }, (output) => {\n const url = /https:\\/\\/\\S+\\/device\\S*/.exec(output)?.[0];\n const userCode = /^\\s*([A-Z0-9]{4}-[A-Z0-9]{4,})\\s*$/m.exec(output)?.[1];\n return url && userCode ? { url, userCode, needsCode: false } : null;\n });\n }\n};\n\n// agent/session/codex/backgroundTasks.ts\nimport { readdirSync, readFileSync } from \"fs\";\nvar STOP_GRACE_MS = 1000;\nfunction codexBackgroundTasks() {\n const running = new Map;\n const background = new Map;\n function observe(notification) {\n switch (notification.method) {\n case \"item/started\": {\n const { item } = notification.params;\n if (item.type === \"commandExecution\")\n running.set(item.id, { task_id: item.id, task_type: \"local_bash\", description: item.command });\n return;\n }\n case \"item/completed\": {\n const { item } = notification.params;\n if (item.type !== \"commandExecution\")\n return;\n running.delete(item.id);\n background.delete(item.id);\n return;\n }\n case \"turn/completed\":\n for (const [id, task] of running)\n background.set(id, task);\n running.clear();\n return;\n }\n }\n async function stop(taskId, appServerPid) {\n const task = background.get(taskId);\n if (!task)\n throw new AgentNotFoundError(`no background task ${taskId}`);\n const targets = commandProcesses(appServerPid, task.description);\n signal(targets, \"SIGTERM\");\n const deadline = Date.now() + STOP_GRACE_MS;\n while (targets.some(alive) && Date.now() < deadline)\n await Bun.sleep(50);\n signal(targets.filter(alive), \"SIGKILL\");\n background.delete(taskId);\n }\n return { observe, list: async () => [...background.values()], stop };\n}\nfunction processes() {\n const entries = [];\n for (const name of readdirSync(\"/proc\")) {\n if (!/^\\d+$/.test(name))\n continue;\n try {\n const stat = readFileSync(`/proc/${name}/stat`, \"utf8\");\n const ppid = Number(stat.slice(stat.lastIndexOf(\")\") + 2).split(\" \")[1]);\n const argv = readFileSync(`/proc/${name}/cmdline`, \"utf8\").split(\"\\x00\").filter(Boolean);\n entries.push({ pid: Number(name), ppid, argv });\n } catch {}\n }\n return entries;\n}\nfunction commandProcesses(root, command) {\n const all = processes();\n const children = new Map;\n for (const entry of all)\n children.set(entry.ppid, [...children.get(entry.ppid) ?? [], entry.pid]);\n const under = (pid) => (children.get(pid) ?? []).flatMap((child) => [child, ...under(child)]);\n const descendants = new Set(under(root));\n const shells = all.filter((entry) => descendants.has(entry.pid) && entry.argv.length >= 3 && [\"-c\", \"-lc\"].includes(entry.argv.at(-2)) && command.includes(entry.argv.at(-1)));\n return [...new Set(shells.flatMap((shell) => [shell.pid, ...under(shell.pid)]))];\n}\nfunction signal(pids, name) {\n for (const pid of pids) {\n try {\n process.kill(pid, name);\n } catch {}\n }\n}\nfunction alive(pid) {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n// agent/session/codex/input.ts\nfunction codexApprovalPolicy(mode) {\n return mode === \"auto\" ? \"never\" : \"untrusted\";\n}\nfunction createCodexInput(thread, queue, initialModel, initialMode) {\n let model = initialModel;\n let mode = initialMode;\n async function sendMessage(message) {\n if (!message.uuid)\n throw new Error(\"a message sent to codex needs a uuid to be known by\");\n const { rpc, threadId } = thread();\n await rpc.request(\"turn/start\", {\n threadId,\n input: [{ type: \"text\", text: userMessageText(message), text_elements: [] }],\n clientUserMessageId: message.uuid,\n model,\n approvalPolicy: codexApprovalPolicy(mode)\n });\n queue.sent(message, message.uuid);\n }\n function setModel(next) {\n model = next;\n }\n async function setPermissionMode(next) {\n mode = next;\n }\n return { sendMessage, setModel, setPermissionMode };\n}\n\n// agent/session/codex/messageQueue.ts\nfunction codexMessageQueue() {\n const queued = new Map;\n let taken = null;\n return {\n observe(notification) {\n if (notification.method !== \"item/started\")\n return;\n const { item } = notification.params;\n if (item.type !== \"userMessage\" || !item.clientId)\n return;\n const message = queued.get(item.clientId);\n if (!message)\n return;\n queued.delete(item.clientId);\n taken = message;\n },\n sent(message, clientId) {\n queued.set(clientId, message);\n },\n async lastUserMessages() {\n return [...taken ? [{ message: taken, queued: false }] : [], ...[...queued.values()].map((message) => ({ message, queued: true }))];\n }\n };\n}\n\n// agent/session/codex/status.ts\nfunction codexStatus(changed) {\n let active = false;\n return {\n observe(notification) {\n if (notification.method !== \"thread/status/changed\")\n return;\n const next = notification.params.status.type === \"active\";\n if (next === active)\n return;\n active = next;\n changed();\n },\n active: () => active\n };\n}\n\n// agent/session/codex/interrupt.ts\nfunction codexInterrupt(thread) {\n return async () => {\n const { rpc, threadId, activeTurn } = thread();\n const turnId = activeTurn();\n if (!turnId)\n return;\n await rpc.request(\"turn/interrupt\", { threadId, turnId });\n };\n}\n\n// agent/session/codex/model.ts\nfunction codexModel(thread, input) {\n async function listModels() {\n const { data } = await thread().rpc.request(\"model/list\", {});\n return data.map((model) => ({ value: model.model, displayName: model.displayName, description: model.description }));\n }\n async function setModel(model) {\n input.setModel(model);\n }\n return { listModels, setModel };\n}\n\n// agent/session/codex/permissions.ts\nfunction codexPermissions(context) {\n function ask(request) {\n return context.requestPermission(request);\n }\n async function answer(request, rpc) {\n switch (request.method) {\n case \"item/commandExecution/requestApproval\": {\n const { params } = request;\n const decision = await ask({\n tool_name: \"commandExecution\",\n input: { command: params.command ?? \"\", cwd: params.cwd },\n tool_use_id: params.itemId,\n decision_reason: params.reason ?? undefined\n });\n const response = { decision: decision.behavior === \"allow\" ? \"accept\" : \"decline\" };\n return rpc.respond(request.id, response);\n }\n case \"item/fileChange/requestApproval\": {\n const { params } = request;\n const decision = await ask({\n tool_name: \"fileChange\",\n input: { grantRoot: params.grantRoot ?? null },\n tool_use_id: params.itemId,\n decision_reason: params.reason ?? undefined\n });\n const response = { decision: decision.behavior === \"allow\" ? \"accept\" : \"decline\" };\n return rpc.respond(request.id, response);\n }\n case \"execCommandApproval\": {\n const { params } = request;\n const decision = await ask({\n tool_name: \"commandExecution\",\n input: { command: params.command.join(\" \"), cwd: params.cwd },\n tool_use_id: params.callId,\n decision_reason: params.reason ?? undefined\n });\n const response = {\n decision: decision.behavior === \"allow\" ? \"approved\" : { denied: { rejection: decision.message } }\n };\n return rpc.respond(request.id, response);\n }\n case \"applyPatchApproval\": {\n const { params } = request;\n const decision = await ask({\n tool_name: \"fileChange\",\n input: { changes: params.fileChanges, grantRoot: params.grantRoot },\n tool_use_id: params.callId,\n decision_reason: params.reason ?? undefined\n });\n const response = {\n decision: decision.behavior === \"allow\" ? \"approved\" : { denied: { rejection: decision.message } }\n };\n return rpc.respond(request.id, response);\n }\n default:\n return rpc.respondError(request.id, `${request.method} is not supported by hyperbrane`);\n }\n }\n return (request, rpc) => {\n answer(request, rpc).catch((error) => {\n console.error(`answering codex's ${request.method} failed:`, error);\n rpc.respondError(request.id, error.message);\n });\n };\n}\n\n// agent/session/codex/questions.ts\nfunction codexQuestions(context, thread) {\n return async (params) => {\n const questions = params.questions.map((question) => ({\n question: question.question,\n header: question.header,\n options: question.options ?? [],\n multiSelect: false\n }));\n thread().emitToolUse(params.itemId, ASK_USER_QUESTION, { questions });\n const response = await context.askQuestions({ tool_use_id: params.itemId, questions });\n if (!response)\n thread().emitToolResult(params.itemId, \"The user dismissed the questions.\", true);\n else\n thread().emitToolResult(params.itemId, answersText(response.answers), false);\n const answers = {};\n for (const question of params.questions) {\n const answer = response?.answers[question.question];\n answers[question.id] = { answers: answer === undefined ? [] : [answer] };\n }\n return { answers };\n };\n}\nfunction answersText(answers) {\n const pairs = Object.entries(answers).map(([question, answer]) => `\"${question}\"=\"${answer}\"`);\n return `User has answered your questions: ${pairs.join(\", \")}.`;\n}\n\n// agent/session/codex/home.ts\nfunction codexHomeEnvironment(home) {\n return { CODEX_HOME: home };\n}\n\n// lib/version.ts\nvar HYPERBRANE_VERSION = \"v1\";\n\n// agent/session/codex/messages.ts\nfunction backendId(id) {\n return id;\n}\nfunction toolResultId(itemId) {\n return `${itemId}:result`;\n}\nfunction assistantMessage(uuid, content, threadId, model, extras = {}) {\n const usage = {\n ...resultUsage(),\n cache_creation: null,\n server_tool_use: null,\n service_tier: null,\n fallback_credit: null,\n inference_geo: null,\n iterations: null,\n output_tokens_details: null,\n speed: null\n };\n return {\n type: \"assistant\",\n message: {\n id: uuid,\n type: \"message\",\n role: \"assistant\",\n model,\n content,\n container: null,\n context_management: null,\n stop_reason: null,\n stop_sequence: null,\n stop_details: null,\n diagnostics: null,\n usage\n },\n parent_tool_use_id: null,\n uuid: backendId(uuid),\n session_id: threadId,\n ...extras\n };\n}\nfunction toolResultMessage(itemId, content, isError, threadId, output) {\n return {\n type: \"user\",\n message: { role: \"user\", content: [{ type: \"tool_result\", tool_use_id: itemId, content, is_error: isError }] },\n parent_tool_use_id: null,\n uuid: backendId(toolResultId(itemId)),\n session_id: threadId,\n ...output === undefined ? {} : { tool_use_result: output }\n };\n}\nfunction eventMessage(id, event, threadId) {\n return { type: \"system\", subtype: \"session_event\", event, uuid: id, session_id: threadId };\n}\nfunction itemMessages(item, threadId, model, finished = true) {\n const call = toolCallOf(item);\n if (call) {\n const messages = [assistantMessage(item.id, [call], threadId, model)];\n const result = finished ? toolResultOf(item) : null;\n if (result)\n messages.push(toolResultMessage(item.id, result.content, result.isError, threadId, item));\n return messages;\n }\n switch (item.type) {\n case \"userMessage\":\n return [{ type: \"user\", message: { role: \"user\", content: userContent(item.content) }, parent_tool_use_id: null, uuid: backendId(item.clientId ?? item.id), session_id: threadId }];\n case \"hookPrompt\":\n return [\n {\n type: \"user\",\n message: { role: \"user\", content: item.fragments.map((fragment) => fragment.text).join(`\n`) },\n parent_tool_use_id: null,\n uuid: backendId(item.id),\n session_id: threadId\n }\n ];\n case \"agentMessage\": {\n const citations = item.memoryCitation?.entries.map((entry, index) => ({\n type: \"search_result_location\",\n source: entry.path,\n title: entry.note,\n cited_text: \"\",\n search_result_index: index,\n start_block_index: entry.lineStart,\n end_block_index: entry.lineEnd\n }));\n const content = [{ type: \"text\", text: item.text, citations: citations?.length ? citations : null }];\n if (item.questions?.length) {\n const questions = item.questions.map((question) => ({\n question: question.title,\n header: \"\",\n options: (question.options ?? []).map((label) => ({ label, description: \"\" })),\n multiSelect: false\n }));\n content.push({ type: \"tool_use\", id: `${item.id}:questions`, name: ASK_USER_QUESTION, input: { questions } });\n }\n const extras = {\n ...item.phase ? { phase: item.phase === \"final_answer\" ? \"final\" : \"commentary\" } : {},\n ...item.delivery === \"async\" ? { async: true } : {}\n };\n return [assistantMessage(item.id, content, threadId, model, extras)];\n }\n case \"reasoning\":\n return [assistantMessage(item.id, [{ type: \"thinking\", thinking: [...item.summary, ...item.content].join(`\n\n`), signature: \"\" }], threadId, model)];\n case \"contextCompaction\":\n return [{ type: \"system\", subtype: \"compact_boundary\", uuid: backendId(item.id), session_id: threadId }];\n case \"enteredReviewMode\":\n return [eventMessage(item.id, { kind: \"review\", state: \"entered\", review: item.review }, threadId)];\n case \"exitedReviewMode\":\n return [eventMessage(item.id, { kind: \"review\", state: \"exited\", review: item.review }, threadId)];\n case \"subAgentActivity\":\n return [eventMessage(item.id, { kind: \"subagent\", activity: item.kind, agent: item.agentThreadId, path: item.agentPath }, threadId)];\n default:\n return [];\n }\n}\nfunction entryMessages({ item, startedAtMs, completedAtMs }, threadId, model, parentToolUseId = null) {\n const started = startedAtMs ?? completedAtMs;\n const completed = completedAtMs ?? startedAtMs;\n const at = (time) => time === null ? {} : { timestamp: new Date(time).toISOString() };\n const messages = itemMessages(item, threadId, model, completedAtMs !== null).map((message) => underTool(message, parentToolUseId));\n return messages.map((message, index) => ({ ...message, ...at(index === 0 && toolCallOf(item) ? started : completed) }));\n}\nfunction underTool(message, parentToolUseId) {\n return message.type === \"system\" || parentToolUseId === null ? message : { ...message, parent_tool_use_id: parentToolUseId };\n}\nfunction toolCallOf(item) {\n const call = (input, name = item.type) => ({ type: \"tool_use\", id: item.id, name, input });\n switch (item.type) {\n case \"commandExecution\":\n return call({ command: item.command, cwd: item.cwd });\n case \"fileChange\":\n return call({ changes: item.changes });\n case \"mcpToolCall\":\n return { type: \"mcp_tool_use\", id: item.id, name: item.tool, server_name: item.server, input: item.arguments };\n case \"dynamicToolCall\":\n return call(item.arguments, item.tool);\n case \"functionCallOutput\":\n return call({}, item.name);\n case \"collabAgentToolCall\":\n return call({ prompt: item.prompt, model: item.model, reasoningEffort: item.reasoningEffort, receiverThreadIds: item.receiverThreadIds }, item.tool);\n case \"webSearch\":\n return call({ query: item.query, action: item.action });\n case \"imageView\":\n return call({ path: item.path });\n case \"imageGeneration\":\n return call({ revisedPrompt: item.revisedPrompt });\n case \"sleep\":\n return call({ durationMs: item.durationMs });\n case \"plan\":\n return call({ text: item.text });\n default:\n return null;\n }\n}\nfunction toolResultOf(item) {\n switch (item.type) {\n case \"commandExecution\":\n return { content: item.aggregatedOutput ?? \"\", isError: item.status !== \"completed\" || (item.exitCode ?? 0) !== 0 };\n case \"fileChange\":\n return { content: item.status, isError: item.status !== \"completed\" };\n case \"mcpToolCall\":\n return { content: item.error ? item.error.message : mcpContent(item.result?.content ?? []), isError: item.status !== \"completed\" };\n case \"dynamicToolCall\":\n return { content: inputContent(item.contentItems ?? []), isError: item.success === false };\n case \"functionCallOutput\":\n return { content: typeof item.output === \"string\" ? item.output : functionOutputContent(item.output), isError: false };\n case \"collabAgentToolCall\":\n return { content: JSON.stringify(item.agentsStates), isError: item.status === \"failed\" };\n case \"webSearch\":\n return { content: item.results ? JSON.stringify(item.results) : \"\", isError: false };\n case \"imageView\":\n case \"sleep\":\n return { content: \"\", isError: false };\n case \"imageGeneration\": {\n const image = base64Image(item.result);\n return { content: image ? [image] : \"\", isError: item.failure !== null };\n }\n case \"plan\":\n return { content: item.text, isError: false };\n default:\n return null;\n }\n}\nfunction userContent(inputs) {\n if (inputs.every((input) => input.type === \"text\"))\n return inputs.map((input) => input.text).join(\"\");\n return inputs.flatMap((input) => {\n if (input.type === \"text\")\n return [{ type: \"text\", text: input.text }];\n if (input.type === \"image\" && \"url\" in input)\n return [imageBlock(input.url)];\n return [];\n });\n}\nfunction mcpContent(content) {\n const texts = content.map((block) => block.type === \"text\" ? block.text : null);\n if (texts.every((text) => text !== null))\n return texts.join(`\n`);\n return JSON.stringify(content);\n}\nfunction inputContent(items) {\n return items.flatMap((item) => {\n if (item.type === \"inputText\")\n return [{ type: \"text\", text: item.text }];\n if (item.type === \"inputImage\")\n return [imageBlock(item.imageUrl)];\n return [];\n });\n}\nfunction functionOutputContent(items) {\n return items.flatMap((item) => {\n if (item.type === \"input_text\")\n return [{ type: \"text\", text: item.text }];\n if (item.type === \"input_image\" && \"image_url\" in item)\n return [imageBlock(item.image_url)];\n return [];\n });\n}\nfunction imageBlock(url) {\n const data = /^data:(image\\/(?:png|jpeg|gif|webp));base64,(.*)$/s.exec(url);\n if (data)\n return { type: \"image\", source: { type: \"base64\", media_type: data[1], data: data[2] } };\n return { type: \"image\", source: { type: \"url\", url } };\n}\nvar IMAGE_SIGNATURES = [\n [\"iVBORw0KGgo\", \"image/png\"],\n [\"/9j/\", \"image/jpeg\"],\n [\"R0lGOD\", \"image/gif\"],\n [\"UklGR\", \"image/webp\"]\n];\nfunction base64Image(data) {\n const media = IMAGE_SIGNATURES.find(([signature]) => data.startsWith(signature))?.[1];\n return media ? { type: \"image\", source: { type: \"base64\", media_type: media, data } } : null;\n}\n\n// agent/session/codex/rpc.ts\nvar METHOD_NOT_FOUND = -32601;\nfunction spawnCodexAppServer(executable, { cwd, env }, handlers) {\n const args = [\"app-server\", \"-c\", \"tools.experimental_request_user_input.enabled=true\", \"--enable\", \"default_mode_request_user_input\"];\n const proc = Bun.spawn([executable.path, ...args], {\n cwd,\n env: { ...env, ...executable.env },\n stdin: \"pipe\",\n stdout: \"pipe\",\n stderr: \"pipe\"\n });\n const pending = new Map;\n let nextId = 1;\n function send(message) {\n proc.stdin.write(JSON.stringify(message) + `\n`);\n proc.stdin.flush();\n }\n function dispatch(message) {\n if (message.id !== undefined && message.method === undefined) {\n const waiting = pending.get(message.id);\n pending.delete(message.id);\n if (message.error)\n waiting?.reject(new Error(`codex: ${message.error.message ?? JSON.stringify(message.error)}`));\n else\n waiting?.resolve(message.result);\n return;\n }\n if (message.id !== undefined)\n handlers.onRequest(message);\n else\n handlers.onNotification(message);\n }\n async function readLines() {\n const decoder = new TextDecoder;\n let buffered = \"\";\n for await (const chunk of proc.stdout) {\n buffered += decoder.decode(chunk, { stream: true });\n let newline;\n while ((newline = buffered.indexOf(`\n`)) >= 0) {\n const line = buffered.slice(0, newline).trim();\n buffered = buffered.slice(newline + 1);\n if (line)\n dispatch(JSON.parse(line));\n }\n }\n }\n async function forwardStderr() {\n for await (const chunk of proc.stderr)\n process.stderr.write(chunk);\n }\n forwardStderr().catch(() => {});\n readLines().then(() => proc.exited).then(() => {\n const exit = { code: proc.exitCode, signal: proc.signalCode === null ? null : String(proc.signalCode) };\n finish(exit, exit.code === 0 ? undefined : new Error(`codex app-server exited with ${exit.signal ?? `code ${exit.code}`}`));\n }, (error) => finish(null, error));\n function finish(exit, error) {\n for (const waiting of pending.values())\n waiting.reject(error ?? new Error(\"codex app-server exited\"));\n pending.clear();\n handlers.onExit(exit, error);\n }\n function request(method, params) {\n const id = nextId++;\n return new Promise((resolve, reject) => {\n pending.set(id, { resolve: (result) => resolve(result), reject });\n send({ id, method, params });\n });\n }\n function notify(method) {\n send({ method });\n }\n function respond(id, result) {\n send({ id, result });\n }\n function respondError(id, message) {\n send({ id, error: { code: METHOD_NOT_FOUND, message } });\n }\n function close() {\n proc.stdin.end();\n proc.kill();\n }\n return { pid: proc.pid, request, notify, respond, respondError, close };\n}\n\n// agent/session/codex/transcript.ts\nvar ITEMS_PAGE = 100;\nvar CURSORS_KEPT = 1000;\nvar THREADS_SEARCHED = 50;\nvar cursors = new Map;\nfunction codexSubagentTranscript(source, subagentThreadId, toolUseId) {\n return codexTranscript({ ...source, nativeId: subagentThreadId }, toolUseId);\n}\nasync function codexFindSubagent(source, toolUseId) {\n const threads = [source.nativeId];\n const seen = new Set(threads);\n for (let thread = threads.shift();thread !== undefined && seen.size <= THREADS_SEARCHED; thread = threads.shift()) {\n const found = await codexReading({ ...source, nativeId: thread }, async (list) => {\n for (let cursor = null;; ) {\n const response = await list(cursor);\n for (const { item } of response.data) {\n if (item.type !== \"collabAgentToolCall\")\n continue;\n if (item.id === toolUseId)\n return item.receiverThreadIds[0] ?? null;\n for (const receiver of item.receiverThreadIds) {\n if (seen.has(receiver))\n continue;\n seen.add(receiver);\n threads.push(receiver);\n }\n }\n if (!response.nextCursor)\n return;\n cursor = response.nextCursor;\n }\n });\n if (found !== undefined)\n return found;\n }\n return null;\n}\nasync function codexReading({ nativeId: threadId, env, installDir }, read) {\n let rpc = null;\n rpc = spawnCodexAppServer(codexExecutable(installDir), { env }, {\n onNotification: () => {},\n onRequest: (request) => rpc?.respondError(request.id, \"this app-server only reads thread history\"),\n onExit: () => {}\n });\n const reader = rpc;\n try {\n await reader.request(\"initialize\", {\n clientInfo: { name: \"hyperbrane\", title: \"hyperbrane\", version: HYPERBRANE_VERSION },\n capabilities: { experimentalApi: true, requestAttestation: false }\n });\n reader.notify(\"initialized\");\n return await read((cursor) => reader.request(\"thread/items/list\", { threadId, cursor, limit: ITEMS_PAGE, sortDirection: \"desc\" }));\n } finally {\n reader.close();\n }\n}\nfunction codexTranscript(source, parentToolUseId = null) {\n const threadId = source.nativeId;\n const reading = (read) => codexReading(source, read);\n const messagesOf = (entry) => entryMessages(entry, threadId, \"\", parentToolUseId);\n const timeOf = (entry) => entry.startedAtMs ?? entry.completedAtMs ?? 0;\n const cursorKey = (time) => `${threadId} ${time}`;\n function keepCursor(time, cursor) {\n cursors.delete(cursorKey(time));\n cursors.set(cursorKey(time), cursor);\n if (cursors.size > CURSORS_KEPT)\n cursors.delete(cursors.keys().next().value);\n }\n const page = (before, limit) => reading((list) => pageOf(list, before, limit));\n const replay = (afterId, emit) => reading((list) => replayOf(list, afterId, emit));\n async function pageOf(list, before, limit) {\n let cursor = before === null ? null : cursors.get(cursorKey(before)) ?? null;\n const taken = [];\n let count = 0;\n let boundary = Infinity;\n let boundaryCursor = null;\n let older = false;\n pages:\n for (;; ) {\n const response = await list(cursor);\n for (const entry of response.data) {\n const time = timeOf(entry);\n if (before !== null && time >= before)\n continue;\n if (count >= limit && time < boundary) {\n older = true;\n break pages;\n }\n const messages = messagesOf(entry);\n if (messages.length === 0)\n continue;\n taken.push(messages);\n count += messages.length;\n if (count >= limit && boundary === Infinity) {\n boundary = time;\n boundaryCursor = cursor;\n }\n }\n if (!response.nextCursor)\n break;\n cursor = response.nextCursor;\n }\n if (older)\n keepCursor(boundary, boundaryCursor);\n return { messages: taken.reverse().flat(), nextBefore: older ? new Date(boundary).toISOString() : null };\n }\n async function replayOf(list, afterId, emit) {\n const newestFirst = [];\n let found = afterId === undefined;\n let cursor = null;\n scan:\n for (;; ) {\n const response = await list(cursor);\n for (const entry of response.data) {\n const messages = messagesOf(entry);\n newestFirst.push(messages);\n if (messages.some((message) => message.uuid === afterId)) {\n found = true;\n break scan;\n }\n }\n if (!response.nextCursor)\n break;\n cursor = response.nextCursor;\n }\n if (!found)\n return false;\n let passed = afterId === undefined;\n for (const messages of newestFirst.reverse()) {\n for (const message of messages) {\n if (passed)\n emit(message);\n else if (message.uuid === afterId)\n passed = true;\n }\n }\n return true;\n }\n return { page, replay };\n}\n\n// agent/session/codex/stream.ts\nasync function startCodexStream(context, hooks) {\n let threadId = context.nativeId ?? \"\";\n let model = context.model ?? \"\";\n let turn = null;\n const rpc = spawnCodexAppServer(codexExecutable(context.installDir), { cwd: context.cwd, env: context.env }, {\n onNotification,\n onRequest: (request) => hooks.onServerRequest(request, rpc),\n onExit: (exit, error) => context.exited(exit, error)\n });\n function base(parent = null, session = threadId) {\n return { parent_tool_use_id: parent, uuid: crypto.randomUUID(), session_id: session };\n }\n const subagentThreads = new Map;\n function noteSubagents(item) {\n if (item.type !== \"collabAgentToolCall\")\n return;\n for (const thread of item.receiverThreadIds) {\n if (subagentThreads.has(thread))\n continue;\n subagentThreads.set(thread, item.id);\n context.subagentStarted(item.id, thread);\n }\n }\n function delta(notification, parent, session = threadId) {\n switch (notification.method) {\n case \"item/agentMessage/delta\":\n return { type: \"stream_event\", event: { type: \"content_block_delta\", index: 0, delta: { type: \"text_delta\", text: notification.params.delta } }, ...base(parent, session) };\n case \"item/reasoning/summaryTextDelta\":\n case \"item/reasoning/textDelta\":\n return {\n type: \"stream_event\",\n event: { type: \"content_block_delta\", index: 0, delta: { type: \"thinking_delta\", thinking: notification.params.delta, estimated_tokens: null } },\n ...base(parent, session)\n };\n default:\n return null;\n }\n }\n function onSubagentNotification(notification, thread) {\n const parent = subagentThreads.get(thread);\n if (!parent)\n return;\n const streamed = delta(notification, parent, thread);\n if (streamed)\n return context.emit(streamed);\n if (notification.method === \"item/started\") {\n const { item } = notification.params;\n noteSubagents(item);\n const call = toolCallOf(item);\n if (call)\n context.emit({ ...underTool(assistantMessage(item.id, [call], thread, model), parent), timestamp: new Date(notification.params.startedAtMs).toISOString() });\n }\n if (notification.method === \"item/completed\") {\n const { item } = notification.params;\n noteSubagents(item);\n const messages = itemMessages(item, thread, model).map((message) => underTool(message, parent));\n const timestamp = new Date(notification.params.completedAtMs).toISOString();\n for (const message of toolCallOf(item) ? messages.slice(1) : messages)\n context.emit({ ...message, timestamp });\n }\n }\n function currentTurn(id) {\n turn ??= { id, startedAt: Date.now(), lastText: \"\", usage: null, errors: [] };\n turn.id ??= id;\n return turn;\n }\n function onNotification(notification) {\n const thread = notification.params?.threadId;\n if (typeof thread === \"string\" && threadId !== \"\" && thread !== threadId)\n return onSubagentNotification(notification, thread);\n for (const observer of hooks.observers)\n observer.observe(notification);\n const streamed = delta(notification, null);\n if (streamed)\n return context.emit(streamed);\n switch (notification.method) {\n case \"turn/started\":\n currentTurn(notification.params.turn.id);\n context.setNativeId(threadId);\n return;\n case \"item/started\": {\n const { item } = notification.params;\n noteSubagents(item);\n const call = toolCallOf(item);\n if (call)\n context.emit({ ...assistantMessage(item.id, [call], threadId, model), timestamp: new Date(notification.params.startedAtMs).toISOString() });\n return;\n }\n case \"item/completed\": {\n const { item } = notification.params;\n noteSubagents(item);\n if (item.type === \"agentMessage\")\n currentTurn(notification.params.turnId).lastText = item.text;\n if (item.type === \"userMessage\")\n return;\n const messages = itemMessages(item, threadId, model);\n const timestamp = new Date(notification.params.completedAtMs).toISOString();\n for (const message of toolCallOf(item) ? messages.slice(1) : messages)\n context.emit({ ...message, timestamp });\n return;\n }\n case \"thread/tokenUsage/updated\":\n currentTurn(notification.params.turnId).usage = notification.params.tokenUsage.last;\n return;\n case \"error\":\n if (!notification.params.willRetry)\n currentTurn(notification.params.turnId).errors.push(notification.params.error.message);\n return;\n case \"turn/completed\":\n context.emit(resultOf(currentTurn(notification.params.turn.id), notification.params.turn));\n turn = null;\n return;\n }\n }\n function resultOf(state, finished) {\n const duration = finished.durationMs ?? Date.now() - state.startedAt;\n const usage = resultUsage({\n input: state.usage?.inputTokens,\n output: state.usage?.outputTokens,\n cacheRead: state.usage?.cachedInputTokens,\n cacheWrite: state.usage?.cacheWriteInputTokens,\n thinking: state.usage?.reasoningOutputTokens\n });\n const common = {\n duration_ms: duration,\n duration_api_ms: duration,\n num_turns: 1,\n total_cost_usd: 0,\n usage,\n modelUsage: {},\n permission_denials: [],\n uuid: crypto.randomUUID(),\n session_id: threadId\n };\n if (finished.status === \"completed\") {\n return { type: \"result\", subtype: \"success\", is_error: false, result: state.lastText, stop_reason: \"end_turn\", ...common };\n }\n const errors = [...state.errors];\n if (finished.error)\n errors.push(finished.error.message);\n if (errors.length === 0)\n errors.push(`turn ${finished.status}`);\n return { type: \"result\", subtype: \"error_during_execution\", is_error: true, stop_reason: null, errors, ...common };\n }\n await rpc.request(\"initialize\", {\n clientInfo: { name: \"hyperbrane\", title: \"hyperbrane\", version: HYPERBRANE_VERSION },\n capabilities: { experimentalApi: true, requestAttestation: false }\n });\n rpc.notify(\"initialized\");\n const settings = {\n cwd: context.spec.cwd ?? null,\n model: context.model,\n approvalPolicy: codexApprovalPolicy(context.permissionMode),\n sandbox: \"danger-full-access\"\n };\n const started = threadId ? await rpc.request(\"thread/resume\", { threadId, ...settings }) : await rpc.request(\"thread/start\", settings);\n threadId = started.thread.id;\n model = started.model;\n return {\n rpc,\n threadId,\n model,\n activeTurn: () => turn?.id ?? null,\n emitToolUse: (id, name, input) => context.emit(assistantMessage(id, [{ type: \"tool_use\", id, name, input }], threadId, model)),\n emitToolResult: (id, content, isError) => context.emit(toolResultMessage(id, content, isError, threadId))\n };\n}\n\n// agent/session/codex/index.ts\nvar codexBackend = {\n install: installCodex,\n installed: installedCodex,\n auth: codexAuth,\n transcript: (source) => codexTranscript(source),\n findSubagent: codexFindSubagent,\n subagentTranscript: codexSubagentTranscript,\n homeEnvironment: codexHomeEnvironment,\n async start(context) {\n let thread;\n const current = () => thread;\n const queue = codexMessageQueue();\n const status = codexStatus(() => context.activeChanged());\n const input = createCodexInput(current, queue, context.model, context.permissionMode);\n const permissions = codexPermissions(context);\n const questions = codexQuestions(context, current);\n function onServerRequest(request, rpc) {\n if (request.method !== \"item/tool/requestUserInput\")\n return permissions(request, rpc);\n questions(request.params).then((response) => rpc.respond(request.id, response), (error) => rpc.respondError(request.id, error.message));\n }\n const backgroundTasks = codexBackgroundTasks();\n thread = await startCodexStream(context, { onServerRequest, observers: [backgroundTasks, status, queue] });\n return {\n sendMessage: input.sendMessage,\n active: status.active,\n getLastUserMessages: queue.lastUserMessages,\n cancel: codexInterrupt(current),\n ...codexModel(current, input),\n setPermissionMode: input.setPermissionMode,\n listBackgroundTasks: backgroundTasks.list,\n stopTask: (taskId) => backgroundTasks.stop(taskId, current().rpc.pid),\n async close() {\n current().rpc.close();\n }\n };\n }\n};\n\n// agent/session/backends.ts\nvar SESSION_BACKENDS = {\n claude: claudeBackend,\n codex: codexBackend\n};\n\n// dimension/paths.ts\nfunction containerName(dimensionName) {\n return `hyb-${dimensionName}`;\n}\nvar WORKSPACE_PATH = \"/workspace\";\nvar RUN_PATH = \"/run/hyperbrane\";\nvar STATE_PATH = \"/var/lib/hyperbrane\";\nvar CA_BUNDLE_PATH = \"/etc/ssl/certs/ca-certificates.crt\";\nvar TRANSPORT_SOCKET = \".transport.sock\";\nfunction dimensionRunDirectory(dataDirectory, dimensionName) {\n return `${dataDirectory}/dimension-run/${dimensionName}`;\n}\nfunction dimensionStateDirectory(dataDirectory, dimensionName) {\n return `${dataDirectory}/dimension-state/${dimensionName}`;\n}\nvar dimensionLayout = {\n containerName,\n workspacePath: () => WORKSPACE_PATH,\n runPath: () => RUN_PATH,\n statePath: () => STATE_PATH,\n caBundlePath: () => CA_BUNDLE_PATH,\n runDirectory: dimensionRunDirectory,\n stateDirectory: dimensionStateDirectory,\n transportSocketPath: (dataDirectory, dimensionName) => `${dimensionRunDirectory(dataDirectory, dimensionName)}/${TRANSPORT_SOCKET}`\n};\n\n// dimension/api.ts\nvar MEMORY_UNITS = {\n \"\": 1,\n K: 1024,\n M: 1024 ** 2,\n G: 1024 ** 3,\n T: 1024 ** 4\n};\n\n// agent/session/sessionStorage.ts\nfunction workerSocketPath(runDirectory, agentName) {\n return `${runDirectory}/${agentName}.sock`;\n}\nfunction workerReadyPath(runDirectory, agentName) {\n return `${runDirectory}/${agentName}.ready`;\n}\nfunction sessionsDirectory(stateDirectory, agentName) {\n return `${stateDirectory}/agents/${agentName}/sessions`;\n}\nfunction backendHomePath(stateDirectory, backend) {\n return `${stateDirectory}/backends/${backend}`;\n}\nfunction createSessionStorage(sandbox, agentName, { stateDirectory = dimensionLayout.statePath(), runDirectory = dimensionLayout.runPath() } = {}) {\n const sessionsDir = sessionsDirectory(stateDirectory, agentName);\n const sessions = createJsonDirectory(sandbox, async () => sessionsDir);\n const sessionDir = (sessionName) => `${sessionsDir}/${checkName(sessionName)}`;\n const permissionRequests = (sessionName) => createJsonDirectory(sandbox, async () => `${sessionDir(sessionName)}/permission-requests`);\n const questionRequests = (sessionName) => createJsonDirectory(sandbox, async () => `${sessionDir(sessionName)}/question-requests`);\n const sessionFiles = (sessionName) => createJsonDirectory(sandbox, async () => sessionDir(sessionName));\n const statusFiles = (sessionName) => createJsonDirectory(sandbox, async () => sessionDir(sessionName));\n async function oldestFirst(stored) {\n return (await stored).sort((a, b) => a.at - b.at).map((entry) => entry.request);\n }\n async function getSessions() {\n return sessions.list();\n }\n async function insertSession(record) {\n return sessions.insert(record.spec.name, record);\n }\n async function saveSession(record) {\n await sessions.save(record.spec.name, record);\n }\n async function removeSession(sessionName) {\n await sandbox.removeDirectory(sessionDir(sessionName));\n await sessions.remove(sessionName);\n }\n async function getPermissionRequests(sessionName) {\n return oldestFirst(permissionRequests(sessionName).list());\n }\n async function savePermissionRequest(sessionName, request) {\n await permissionRequests(sessionName).save(request.id, { request, at: Date.now() });\n }\n async function removePermissionRequest(sessionName, requestId) {\n await permissionRequests(sessionName).remove(requestId);\n }\n async function getQuestionRequests(sessionName) {\n return oldestFirst(questionRequests(sessionName).list());\n }\n async function saveQuestionRequest(sessionName, request) {\n await questionRequests(sessionName).save(request.id, { request, at: Date.now() });\n }\n async function removeQuestionRequest(sessionName, requestId) {\n await questionRequests(sessionName).remove(requestId);\n }\n async function readAuth(sessionName) {\n return sessionFiles(sessionName).read(\"auth\");\n }\n async function saveAuth(sessionName, record) {\n await sessionFiles(sessionName).save(\"auth\", record);\n }\n async function saveStatus(sessionName, status) {\n await statusFiles(sessionName).save(\"status\", { status });\n }\n async function socketPath() {\n return workerSocketPath(runDirectory, agentName);\n }\n async function backendHome(backend) {\n const path = backendHomePath(stateDirectory, backend);\n await sandbox.createDirectory(path);\n return path;\n }\n return {\n getSessions,\n insertSession,\n saveSession,\n removeSession,\n getPermissionRequests,\n savePermissionRequest,\n removePermissionRequest,\n getQuestionRequests,\n saveQuestionRequest,\n removeQuestionRequest,\n readAuth,\n saveAuth,\n saveStatus,\n socketPath,\n backendHome\n };\n}\n\n// agent/session/worker/protocol.ts\nvar WORKER_PROTOCOL = 3;\nvar WorkerErrorCode = {\n methodNotFound: -32601,\n invalidParams: -32602,\n internalError: -32603,\n notFound: -32004,\n conflict: -32009,\n backendNotInstalled: -32010,\n protocolMismatch: -32011\n};\n\nclass WorkerMethodNotFoundError extends Error {\n name = \"WorkerMethodNotFoundError\";\n}\n\nclass WorkerProtocolMismatchError extends Error {\n name = \"WorkerProtocolMismatchError\";\n}\nfunction encodeWorkerError(error) {\n const message = error instanceof Error ? error.message : String(error);\n if (error instanceof AgentInvalidError)\n return { code: WorkerErrorCode.invalidParams, message };\n if (error instanceof AgentNotFoundError)\n return { code: WorkerErrorCode.notFound, message };\n if (error instanceof AgentBackendNotInstalledError)\n return { code: WorkerErrorCode.backendNotInstalled, message };\n if (error instanceof AgentConflictError)\n return { code: WorkerErrorCode.conflict, message };\n if (error instanceof WorkerMethodNotFoundError)\n return { code: WorkerErrorCode.methodNotFound, message };\n if (error instanceof WorkerProtocolMismatchError)\n return { code: WorkerErrorCode.protocolMismatch, message };\n return { code: WorkerErrorCode.internalError, message };\n}\nfunction decodeWorkerError({ code, message }) {\n if (code === WorkerErrorCode.invalidParams)\n return new AgentInvalidError(message);\n if (code === WorkerErrorCode.notFound)\n return new AgentNotFoundError(message);\n if (code === WorkerErrorCode.conflict)\n return new AgentConflictError(message);\n if (code === WorkerErrorCode.backendNotInstalled)\n return new AgentBackendNotInstalledError(message);\n if (code === WorkerErrorCode.methodNotFound)\n return new WorkerMethodNotFoundError(`worker: ${message}`);\n if (code === WorkerErrorCode.protocolMismatch)\n return new WorkerProtocolMismatchError(`worker: ${message}`);\n return new Error(`worker: ${message}`);\n}\n\n// agent/session/worker/workerServer.ts\nimport { rm as rm2 } from \"fs/promises\";\n\n// agent/session/worker/jsonRpc.ts\nvar PARSE_ERROR = -32700;\nfunction createJsonRpcPeer(write, options) {\n const pending = new Map;\n let nextId = 1;\n let buffered = \"\";\n let closedWith;\n function send(message) {\n if (closedWith)\n return;\n write(JSON.stringify({ jsonrpc: \"2.0\", ...message }) + `\n`);\n }\n function handle(message) {\n if (message.method === undefined) {\n if (message.id === undefined || message.id === null)\n return;\n const waiting = pending.get(message.id);\n pending.delete(message.id);\n if (message.error)\n waiting?.reject(options.decodeError(message.error));\n else\n waiting?.resolve(message.result);\n return;\n }\n if (message.id === undefined || message.id === null) {\n options.onNotification?.(message.method, message.params);\n return;\n }\n const id = message.id;\n options.onRequest(message.method, message.params, message.protocol).then((result) => send({ id, result: result ?? null }), (error) => send({ id, error: options.encodeError(error) }));\n }\n function receive(text) {\n buffered += text;\n let newline;\n while ((newline = buffered.indexOf(`\n`)) >= 0) {\n const line = buffered.slice(0, newline).trim();\n buffered = buffered.slice(newline + 1);\n if (!line)\n continue;\n let message;\n try {\n message = JSON.parse(line);\n } catch {\n send({ id: null, error: { code: PARSE_ERROR, message: \"not JSON\" } });\n continue;\n }\n handle(message);\n }\n }\n function request(method, params) {\n if (closedWith)\n return Promise.reject(closedWith);\n const id = nextId++;\n return new Promise((resolve, reject) => {\n pending.set(id, { resolve, reject });\n send({ id, method, params, ...options.protocol === undefined ? {} : { protocol: options.protocol } });\n });\n }\n function notify(method, params) {\n send({ method, params });\n }\n function close(error) {\n closedWith ??= error;\n for (const waiting of pending.values())\n waiting.reject(error);\n pending.clear();\n }\n return { receive, request, notify, close };\n}\nfunction createSocketWriter(socket) {\n const encoder = new TextEncoder;\n let backlog = null;\n function write(text) {\n const bytes = encoder.encode(text);\n if (backlog) {\n const joined = new Uint8Array(backlog.length + bytes.length);\n joined.set(backlog);\n joined.set(bytes, backlog.length);\n backlog = joined;\n return;\n }\n const written = Math.max(socket.write(bytes), 0);\n if (written < bytes.length)\n backlog = bytes.subarray(written);\n }\n function drain() {\n if (!backlog)\n return;\n const written = Math.max(socket.write(backlog), 0);\n backlog = written < backlog.length ? backlog.subarray(written) : null;\n }\n return { write, drain };\n}\n\n// agent/session/worker/workerParams.ts\nvar WORKER_PARAMS = {\n \"worker/health\": () => ({}),\n \"worker/shutdown\": () => ({}),\n \"backend/install\": (params) => ({ backend: backend(params.backend) }),\n \"backend/list\": () => ({}),\n \"session/create\": (params) => ({ session: session(object(params.session, \"session\")) }),\n \"session/list\": () => ({}),\n \"session/update\": (params) => ({ session: name(params), limits: limits(object(params.limits, \"limits\")) }),\n \"session/stop\": (params) => ({ session: name(params) }),\n \"session/send\": (params) => ({ session: name(params), message: userMessage(params.message) }),\n \"session/interrupt\": (params) => ({ session: name(params) }),\n \"session/status\": (params) => ({ session: name(params) }),\n \"session/lastUserMessages\": (params) => ({ session: name(params) }),\n \"session/subscribe\": (params) => ({\n session: name(params),\n subscription: text(params.subscription, \"subscription\"),\n after_id: optional(params.after_id, (value) => text(value, \"after_id\"))\n }),\n \"session/unsubscribe\": (params) => ({ subscription: text(params.subscription, \"subscription\") }),\n \"session/transcript\": (params) => ({\n session: name(params),\n limit: optional(params.limit, (value) => number(value, \"limit\")),\n before: params.before === undefined ? optional(params.cursor, (value) => text(value, \"cursor\")) : text(params.before, \"before\")\n }),\n \"session/subagentTranscript\": (params) => ({\n session: name(params),\n toolUseId: text(params.toolUseId, \"toolUseId\"),\n limit: optional(params.limit, (value) => number(value, \"limit\")),\n before: optional(params.before, (value) => text(value, \"before\"))\n }),\n \"permission/list\": (params) => ({ session: name(params) }),\n \"permission/respond\": (params) => ({\n session: name(params),\n request: text(params.request, \"request\"),\n result: permissionResult(params.result)\n }),\n \"question/list\": (params) => ({ session: name(params) }),\n \"question/respond\": (params) => ({\n session: name(params),\n request: text(params.request, \"request\"),\n response: questionResponse(params.response)\n }),\n \"model/list\": (params) => ({ session: name(params) }),\n \"model/set\": (params) => ({\n session: name(params),\n model: params.model === null ? null : text(params.model, \"the model (a model name, or null for the backend's default)\")\n }),\n \"permission/mode\": (params) => ({ session: name(params), mode: permissionMode(params.mode) }),\n \"task/list\": (params) => ({ session: name(params) }),\n \"task/stop\": (params) => ({ session: name(params), task: text(params.task, \"task\") }),\n \"auth/status\": (params) => ({ session: name(params) }),\n \"auth/login\": (params) => ({\n session: name(params),\n gatewaySignedIn: optional(params.gatewaySignedIn, (value) => boolean(value, \"gatewaySignedIn\")) ?? false\n }),\n \"auth/code\": (params) => ({ session: name(params), code: text(params.code, \"the code (the one the sign-in page showed)\").trim() })\n};\nfunction invalid2(message) {\n throw new AgentInvalidError(message);\n}\nfunction name(params) {\n return text(params.session, \"session\");\n}\nfunction object(value, what) {\n if (value === null || typeof value !== \"object\" || Array.isArray(value))\n invalid2(`${what} must be an object`);\n return value;\n}\nfunction text(value, what) {\n if (typeof value !== \"string\" || value.trim() === \"\")\n invalid2(`${what} must be a non-empty string`);\n return value;\n}\nfunction number(value, what) {\n if (typeof value !== \"number\" || !Number.isFinite(value))\n invalid2(`${what} must be a number`);\n return value;\n}\nfunction boolean(value, what) {\n if (typeof value !== \"boolean\")\n invalid2(`${what} must be true or false`);\n return value;\n}\nfunction optional(value, parse) {\n return value === undefined ? undefined : parse(value);\n}\nfunction permissionMode(value) {\n const known = AGENT_PERMISSION_MODES.find((mode) => mode === value);\n if (!known)\n invalid2(`unknown permission mode \"${String(value)}\"; known: ${AGENT_PERMISSION_MODES.join(\", \")}`);\n return known;\n}\nfunction backend(value) {\n const known = AGENT_BACKENDS.find((name) => name === value);\n if (!known)\n invalid2(`unknown backend \"${String(value)}\"; known: ${AGENT_BACKENDS.join(\", \")}`);\n return known;\n}\nfunction memoryLimit(value) {\n const bytes = number(value, \"memoryLimit\");\n if (!(bytes > 0))\n invalid2(\"memoryLimit must be positive\");\n return bytes;\n}\nfunction cpuLimit(value) {\n const cores = number(value, \"cpuLimit\");\n if (!(cores > 0))\n invalid2(\"cpuLimit must be positive\");\n return cores;\n}\nfunction environment(value) {\n const fields = object(value, \"env\");\n for (const [key, entry] of Object.entries(fields))\n if (typeof entry !== \"string\")\n invalid2(`env.${key} must be a string`);\n return fields;\n}\nfunction session(fields) {\n return {\n name: text(fields.name, \"the session's name\"),\n backend: backend(fields.backend),\n memoryLimit: memoryLimit(fields.memoryLimit),\n cpuLimit: cpuLimit(fields.cpuLimit),\n cwd: optional(fields.cwd, (value) => text(value, \"cwd\")),\n env: optional(fields.env, environment)\n };\n}\nfunction limits(fields) {\n return { memoryLimit: optional(fields.memoryLimit, memoryLimit), cpuLimit: optional(fields.cpuLimit, cpuLimit) };\n}\nfunction userMessage(value) {\n const fields = object(value, 'a message (an SDK user message: {type: \"user\", message})');\n if (fields.type !== \"user\")\n invalid2('a message is an SDK user message: {type: \"user\", message}');\n const message = object(fields.message, \"message.message\");\n if (message.role !== \"user\")\n invalid2('message.message.role must be \"user\"');\n if (typeof message.content !== \"string\" && !Array.isArray(message.content))\n invalid2(\"message.message.content must be a string or an array of content blocks\");\n return value;\n}\nfunction permissionResult(value) {\n const fields = object(value, \"result\");\n if (fields.behavior === \"allow\") {\n return { behavior: \"allow\", updatedInput: optional(fields.updatedInput, (input) => object(input, \"updatedInput\")) };\n }\n if (fields.behavior === \"deny\") {\n return {\n behavior: \"deny\",\n message: typeof fields.message === \"string\" ? fields.message : invalid2(\"a denial needs a message\"),\n interrupt: optional(fields.interrupt, (interrupt) => boolean(interrupt, \"interrupt\"))\n };\n }\n return invalid2('a permission result is {behavior: \"allow\"} or {behavior: \"deny\", message}');\n}\nfunction questionResponse(value) {\n const answers = object(object(value, \"response\").answers, \"a question response's answers ({<question>: <answer>})\");\n for (const [question, answer] of Object.entries(answers))\n if (typeof answer !== \"string\")\n invalid2(`the answer to \"${question}\" must be a string`);\n return { answers };\n}\n\n// agent/session/worker/workerServer.ts\nvar ANY_PROTOCOL = new Set([\"worker/health\", \"worker/shutdown\"]);\nfunction createWorkerServer(service, options) {\n let listener;\n function ref(session) {\n return { agentName: options.health.agentName, sessionName: session };\n }\n const handlers = {\n \"worker/health\": async () => options.health,\n \"worker/shutdown\": async () => {\n setTimeout(options.onShutdown, 0);\n return null;\n },\n \"backend/install\": async ({ backend }) => nothing(await service.install(options.health.agentName, backend)),\n \"backend/list\": () => service.listBackends(options.health.agentName),\n \"session/create\": ({ session }) => service.createSession(options.health.agentName, session),\n \"session/list\": () => service.listSessions(),\n \"session/update\": ({ session, limits }) => service.updateSession(ref(session), limits),\n \"session/stop\": async ({ session }) => nothing(await service.stopSession(ref(session))),\n \"session/send\": async ({ session, message }) => nothing(await service.sendMessage(ref(session), message)),\n \"session/interrupt\": async ({ session }) => nothing(await service.cancel(ref(session))),\n \"session/status\": ({ session }) => service.getSessionStatus(ref(session)),\n \"session/lastUserMessages\": ({ session }) => service.getLastUserMessages(ref(session)),\n \"session/subscribe\": async ({ session, subscription, after_id }, connection) => {\n if (connection.subscriptions.has(subscription))\n throw new AgentConflictError(`subscription ${subscription} already exists`);\n let unsubscribe = null;\n let cancelled = false;\n connection.subscriptions.set(subscription, () => {\n cancelled = true;\n unsubscribe?.();\n });\n try {\n unsubscribe = await service.subscribe(ref(session), after_id, (message) => {\n const notification = { subscription, message };\n connection.peer.notify(\"session/message\", notification);\n });\n } catch (error) {\n connection.subscriptions.delete(subscription);\n throw error;\n }\n if (cancelled)\n unsubscribe();\n return null;\n },\n \"session/unsubscribe\": async ({ subscription }, connection) => {\n connection.subscriptions.get(subscription)?.();\n connection.subscriptions.delete(subscription);\n return null;\n },\n \"session/transcript\": ({ session, limit, before }) => service.getTranscript(ref(session), { limit, before }),\n \"session/subagentTranscript\": ({ session, toolUseId, limit, before }) => service.getTranscript(ref(session), { limit, before, toolUseId }),\n \"permission/list\": ({ session }) => service.listPermissionRequests(ref(session)),\n \"permission/respond\": async ({ session, request, result }) => nothing(await service.respondPermissionRequest(ref(session), request, result)),\n \"question/list\": ({ session }) => service.listQuestionRequests(ref(session)),\n \"question/respond\": async ({ session, request, response }) => nothing(await service.respondQuestionRequest(ref(session), request, response)),\n \"model/list\": ({ session }) => service.listModels(ref(session)),\n \"model/set\": async ({ session, model }) => nothing(await service.setModel(ref(session), model)),\n \"permission/mode\": async ({ session, mode }) => nothing(await service.setPermissionMode(ref(session), mode)),\n \"task/list\": ({ session }) => service.listBackgroundTasks(ref(session)),\n \"task/stop\": async ({ session, task }) => nothing(await service.stopTask(ref(session), task)),\n \"auth/status\": ({ session }) => service.getAuthStatus(ref(session)),\n \"auth/login\": ({ session, gatewaySignedIn }) => service.login(ref(session), gatewaySignedIn),\n \"auth/code\": ({ session, code }) => service.submitAuthCode(ref(session), code)\n };\n function handle(method, params, connection) {\n return handlers[method](WORKER_PARAMS[method](params), connection);\n }\n async function dispatch(method, params, connection) {\n if (!isWorkerMethod(method))\n throw new WorkerMethodNotFoundError(`no method ${method}`);\n const fields = params ?? {};\n if (typeof fields !== \"object\" || Array.isArray(fields))\n throw new AgentInvalidError(`${method} takes an object of params`);\n return handle(method, fields, connection);\n }\n function openConnection(socket) {\n const writer = createSocketWriter(socket);\n const peer = createJsonRpcPeer(writer.write, {\n onRequest: (method, params, protocol) => {\n if (protocol !== undefined && protocol !== options.health.protocol && !ANY_PROTOCOL.has(method)) {\n return Promise.reject(new WorkerProtocolMismatchError(`this worker speaks protocol ${options.health.protocol}, not ${protocol}`));\n }\n return dispatch(method, params, connection);\n },\n encodeError: (error) => {\n const encoded = encodeWorkerError(error);\n if (encoded.code === WorkerErrorCode.internalError)\n console.error(error);\n return encoded;\n },\n decodeError: decodeWorkerError\n });\n const connection = { peer, writer, subscriptions: new Map, decoder: new TextDecoder };\n return connection;\n }\n async function start() {\n await rm2(options.socketPath, { force: true });\n listener = Bun.listen({\n unix: options.socketPath,\n socket: {\n open(socket) {\n socket.data = openConnection(socket);\n },\n data(socket, data) {\n socket.data.peer.receive(socket.data.decoder.decode(data, { stream: true }));\n },\n drain(socket) {\n socket.data.writer.drain();\n },\n close(socket) {\n for (const unsubscribe of socket.data.subscriptions.values())\n unsubscribe();\n socket.data.subscriptions.clear();\n socket.data.peer.close(new Error(\"the connection closed\"));\n },\n error(_socket, error) {\n console.error(\"worker connection failed:\", error);\n }\n }\n });\n }\n function stop() {\n listener?.stop(true);\n listener = undefined;\n }\n return { start, stop };\n}\nfunction isWorkerMethod(method) {\n return Object.hasOwn(WORKER_PARAMS, method);\n}\nfunction nothing(_done) {\n return null;\n}\n\n// agent/session/workerService.ts\nimport { statSync } from \"fs\";\n\n// agent/session/pending.ts\nfunction createPendingRequests(store, fallback) {\n const waiting = new Map;\n function add(request, signal) {\n const id = crypto.randomUUID();\n return new Promise((resolve) => {\n let settled = false;\n const settle = (response) => {\n if (settled)\n return;\n settled = true;\n waiting.delete(id);\n signal?.removeEventListener(\"abort\", aborted);\n store.remove(id).catch((error) => console.error(`removing request ${id} failed:`, error));\n resolve(response);\n };\n const aborted = () => settle(fallback(\"the request was cancelled\"));\n if (signal?.aborted)\n return aborted();\n signal?.addEventListener(\"abort\", aborted);\n waiting.set(id, settle);\n store.save({ ...request, id }).catch((error) => {\n console.error(`keeping request ${id} failed:`, error);\n settle(fallback(\"the request couldn't be kept\"));\n });\n });\n }\n async function list() {\n return (await store.list()).filter((request) => waiting.has(request.id));\n }\n async function answer(id, response) {\n const settle = waiting.get(id);\n if (!settle)\n return false;\n settle(response);\n return true;\n }\n async function dropAll(reason) {\n for (const settle of [...waiting.values()])\n settle(fallback(reason));\n for (const request of await store.list())\n if (!waiting.has(request.id))\n await store.remove(request.id);\n }\n return { add, list, count: () => waiting.size, answer, dropAll };\n}\n\n// agent/session/workerService.ts\nfunction createWorkerService(config) {\n const sessions = new Map;\n let installing = Promise.resolve();\n const installs = new Map;\n async function loadSessions() {\n for (const record of await config.storage.getSessions()) {\n const state = await newState(record);\n sessions.set(record.spec.name, state);\n noteStatus(state);\n await dropRequests(state, \"the worker restarted\");\n const auth = await config.storage.readAuth(record.spec.name);\n if (auth?.login)\n await config.storage.saveAuth(record.spec.name, { login: null, error: auth.error });\n }\n }\n async function newState(record) {\n return {\n ...record,\n listeners: new Set,\n running: null,\n startedAt: 0,\n generation: 0,\n session: null,\n login: null,\n accepted: null,\n sending: new Map,\n pending: [],\n draining: null,\n handing: null,\n exit: null,\n failure: null,\n tools: new Map,\n subagents: new Map,\n permissionMode: record.permissionMode ?? \"ask\",\n turnOpen: false,\n status: null,\n statusSaving: Promise.resolve(),\n permissionRequests: createPendingRequests({\n list: () => config.storage.getPermissionRequests(record.spec.name),\n save: (request) => config.storage.savePermissionRequest(record.spec.name, request),\n remove: (id) => config.storage.removePermissionRequest(record.spec.name, id)\n }, (message) => ({ behavior: \"deny\", message })),\n questionRequests: createPendingRequests({\n list: () => config.storage.getQuestionRequests(record.spec.name),\n save: (request) => config.storage.saveQuestionRequest(record.spec.name, request),\n remove: (id) => config.storage.removeQuestionRequest(record.spec.name, id)\n }, () => null)\n };\n }\n async function saveRecord(state) {\n await config.storage.saveSession({ spec: state.spec, nativeId: state.nativeId, model: state.model, permissionMode: state.permissionMode });\n }\n async function info(state) {\n return { ...state.spec, agentName: config.agentName, status: currentStatus(state), nativeId: state.nativeId, model: state.model, permissionMode: state.permissionMode };\n }\n function statusOf(state) {\n if (!state.running) {\n const exited = { state: \"exited\", code: state.exit?.code ?? null, signal: state.exit?.signal ?? null };\n return state.failure ? { ...exited, error: state.failure } : exited;\n }\n if (!state.session) {\n const install = installs.get(state.spec.backend);\n return install ? { state: \"installing\", backend: state.spec.backend, version: install.version } : { state: \"starting\" };\n }\n if (state.permissionRequests.count() + state.questionRequests.count() > 0)\n return { state: \"waiting\" };\n if (!state.session.active())\n return { state: \"idle\" };\n if (state.tools.size > 0)\n return { state: \"tool\", tools: [...state.tools.values()] };\n return { state: \"thinking\" };\n }\n async function lastStoredPrompt(state) {\n const transcript = await transcriptOf(state);\n let before = null;\n while (transcript) {\n const page = await transcript.page(before, LAST_PROMPT_PAGE_SIZE);\n const prompt = page.messages.findLast((message) => message.type === \"user\" && isPrompt(message.message.content));\n if (prompt?.type === \"user\")\n return { message: prompt, queued: false };\n if (!page.nextBefore)\n return null;\n before = Date.parse(page.nextBefore);\n }\n return null;\n }\n function trustChangedSince(time) {\n if (!config.trustBundle)\n return false;\n try {\n return statSync(config.trustBundle).mtimeMs > time;\n } catch {\n return false;\n }\n }\n function requireAgent(agentName) {\n if (agentName !== config.agentName)\n throw new AgentNotFoundError(`this worker runs agent ${config.agentName}, not ${agentName}`);\n }\n function requireSession({ agentName, sessionName }) {\n requireAgent(agentName);\n const state = sessions.get(sessionName);\n if (!state)\n throw new AgentNotFoundError(`session ${sessionName} does not exist`);\n return state;\n }\n async function dropRequests(state, reason) {\n await Promise.all([state.permissionRequests.dropAll(reason), state.questionRequests.dropAll(reason)]);\n }\n function broadcast(state, message) {\n if (message.type === \"result\")\n state.turnOpen = false;\n else if (message.type === \"user\" || message.type === \"assistant\")\n state.turnOpen = true;\n noteToolCalls(state, message);\n for (const listener of state.listeners)\n listener(message);\n if (message.type !== \"stream_event\")\n noteStatus(state);\n }\n function noteToolCalls(state, message) {\n if (message.type === \"assistant\" && message.parent_tool_use_id === null) {\n for (const block of message.message.content)\n if (block.type === \"tool_use\" || block.type === \"mcp_tool_use\")\n state.tools.set(block.id, block.name);\n }\n if (message.type === \"user\" && Array.isArray(message.message.content)) {\n for (const block of message.message.content)\n if (block.type === \"tool_result\")\n state.tools.delete(block.tool_use_id);\n }\n }\n function noteStatus(state) {\n const computed = statusOf(state);\n if (state.status && sameStatus(computed, state.status))\n return;\n const status = { ...computed, since: Date.now() };\n state.status = status;\n for (const listener of state.listeners)\n listener({ type: \"session_status\", status });\n state.statusSaving = state.statusSaving.then(async () => {\n if (sessions.get(state.spec.name) !== state)\n return;\n await config.storage.saveStatus(state.spec.name, status);\n }).catch((error) => console.error(`saving session ${state.spec.name}'s status failed:`, error));\n }\n function currentStatus(state) {\n noteStatus(state);\n return state.status;\n }\n async function transcriptSource(state) {\n if (!state.nativeId)\n return null;\n return { nativeId: state.nativeId, env: await environmentOf(state), files: config.files, installDir: config.installDir };\n }\n async function transcriptOf(state) {\n const source = await transcriptSource(state);\n return source && config.backends[state.spec.backend].transcript(source);\n }\n async function subagentTranscriptOf(state, toolUseId) {\n const source = await transcriptSource(state);\n if (!source)\n return null;\n const backend = config.backends[state.spec.backend];\n const subagent = state.subagents.get(toolUseId) ?? await backend.findSubagent(source, toolUseId);\n return subagent ? backend.subagentTranscript(source, subagent, toolUseId) : null;\n }\n async function environmentOf(state) {\n const env = {};\n for (const [key, value] of Object.entries(process.env))\n if (value !== undefined)\n env[key] = value;\n const backend = config.backends[state.spec.backend];\n return { ...env, ...backend.homeEnvironment(await config.storage.backendHome(state.spec.backend)), ...state.spec.env };\n }\n async function authEnvironment(state) {\n return { installDir: config.installDir, env: await environmentOf(state), cwd: state.spec.cwd ?? process.cwd() };\n }\n function runInstall(backend, version) {\n const underway = installs.get(backend);\n if (underway)\n return underway.done;\n const run = installing.then(() => config.backends[backend].install(config.installDir));\n installing = run.catch(() => {});\n const done = run.finally(() => {\n installs.delete(backend);\n noteBackendStatus(backend);\n });\n installs.set(backend, { version, done });\n noteBackendStatus(backend);\n return done;\n }\n function noteBackendStatus(backend) {\n for (const state of sessions.values())\n if (state.spec.backend === backend)\n noteStatus(state);\n }\n async function ensureBackend(state) {\n const backend = state.spec.backend;\n const installed = await config.backends[backend].installed(config.installDir);\n if (installed.ready && installed.version === installed.pinned)\n return;\n try {\n await runInstall(backend, installed.pinned);\n } catch (error) {\n const reason = error.message;\n if (!installed.ready)\n throw new AgentBackendNotInstalledError(`installing ${backend} ${installed.pinned} failed: ${reason}`);\n console.error(`updating ${backend} to ${installed.pinned} failed:`, error);\n broadcast(state, { type: \"session_notice\", text: `updating ${backend} from ${installed.version} to ${installed.pinned} failed, so ${installed.version} runs: ${reason}` });\n }\n }\n async function authStatus(state) {\n const auth = await config.storage.readAuth(state.spec.name);\n if (state.login && auth?.login)\n return auth.login;\n const backend = state.spec.backend;\n if (!(await config.backends[backend].installed(config.installDir)).ready) {\n return { state: \"signedOut\", error: `${backend} isn't installed yet: it installs when the session's CLI first starts` };\n }\n if (await config.backends[state.spec.backend].auth.signedIn(await authEnvironment(state)))\n return { state: \"signedIn\" };\n return auth?.error ? { state: \"signedOut\", error: auth.error } : { state: \"signedOut\" };\n }\n function saveAuth(state, record) {\n return config.storage.saveAuth(state.spec.name, record);\n }\n function watchLogin(state, login) {\n return login.done.then(async () => {\n if (state.login?.process !== login)\n return;\n state.login = null;\n await saveAuth(state, { login: null, error: null });\n if (statusOf(state).state === \"idle\")\n await closeSession(state);\n }, async (error) => {\n if (state.login?.process !== login)\n return;\n state.login = null;\n await saveAuth(state, { login: null, error: error.message });\n });\n }\n async function settleLogin(state, settled, timeoutMs) {\n await Promise.race([settled, Bun.sleep(timeoutMs)]);\n return authStatus(state);\n }\n function contextFor(state, generation, env) {\n const current = () => state.generation === generation;\n return {\n spec: state.spec,\n nativeId: state.nativeId,\n cwd: state.spec.cwd ?? process.cwd(),\n model: state.model,\n permissionMode: state.permissionMode,\n installDir: config.installDir,\n env,\n emit(message) {\n if (!current())\n return;\n broadcast(state, message);\n },\n setNativeId(id) {\n if (!current() || state.nativeId === id)\n return;\n state.nativeId = id;\n saveRecord(state).catch((error) => console.error(`saving session ${state.spec.name} failed:`, error));\n },\n async requestPermission(request, signal) {\n if (!current())\n return { behavior: \"deny\", message: \"the session stopped\" };\n if (state.permissionMode === \"auto\")\n return { behavior: \"allow\", updatedInput: request.input };\n const answer = state.permissionRequests.add(request, signal);\n noteStatus(state);\n return answer.finally(() => noteStatus(state));\n },\n async askQuestions(request, signal) {\n if (!current())\n return null;\n const answer = state.questionRequests.add(request, signal);\n noteStatus(state);\n return answer.finally(() => noteStatus(state));\n },\n subagentStarted(toolUseId, subagentId) {\n if (!current())\n return;\n state.subagents.set(toolUseId, subagentId);\n if (state.subagents.size > SUBAGENTS_KEPT)\n state.subagents.delete(state.subagents.keys().next().value);\n },\n activeChanged() {\n if (!current())\n return;\n if (!state.session?.active())\n state.tools.clear();\n noteStatus(state);\n },\n exited(exit, error) {\n if (!current())\n return;\n state.running = null;\n state.session = null;\n state.exit = exit;\n state.failure = error ? `${state.spec.backend} exited: ${error.message}` : null;\n state.tools.clear();\n if (error)\n console.error(`session ${state.spec.name} exited:`, error);\n if (state.turnOpen) {\n broadcast(state, {\n type: \"result\",\n subtype: \"error_during_execution\",\n duration_ms: 0,\n duration_api_ms: 0,\n is_error: true,\n num_turns: 0,\n stop_reason: null,\n total_cost_usd: 0,\n usage: resultUsage(),\n modelUsage: {},\n permission_denials: [],\n errors: [`the agent exited${error ? `: ${error.message}` : \"\"}`],\n uuid: crypto.randomUUID(),\n session_id: state.nativeId ?? \"\"\n });\n }\n noteStatus(state);\n dropRequests(state, \"the agent exited\").catch((error) => console.error(`dropping ${state.spec.name}'s requests failed:`, error));\n }\n };\n }\n function ensureRunning(state) {\n if (state.running)\n return state.running;\n const generation = ++state.generation;\n state.startedAt = Date.now();\n state.failure = null;\n const backend = state.spec.backend;\n const starting = ensureBackend(state).then(() => environmentOf(state)).then((env) => config.backends[backend].start(contextFor(state, generation, env)).catch((error) => {\n throw new Error(`${backend} didn't start: ${error.message}`);\n }));\n state.running = starting;\n noteStatus(state);\n starting.then((session) => {\n if (state.generation === generation)\n state.session = session;\n noteStatus(state);\n }, (error) => {\n if (state.generation === generation) {\n state.running = null;\n state.failure = error.message;\n }\n noteStatus(state);\n });\n return starting;\n }\n async function closeSession(state) {\n const running = state.running;\n state.generation++;\n state.running = null;\n state.session = null;\n state.exit = null;\n state.failure = null;\n state.tools.clear();\n state.turnOpen = false;\n noteStatus(state);\n await dropRequests(state, \"the session stopped\");\n if (running)\n await (await running.catch(() => null))?.close();\n }\n async function cancelLogin(state) {\n if (!state.login)\n return;\n state.login.process.cancel();\n state.login = null;\n const auth = await config.storage.readAuth(state.spec.name);\n await saveAuth(state, { login: null, error: auth?.error ?? null });\n }\n async function listBackends(agentName) {\n requireAgent(agentName);\n return Promise.all(AGENT_BACKENDS.map(async (backend) => ({ backend, ...await config.backends[backend].installed(config.installDir) })));\n }\n async function install(agentName, backend) {\n requireAgent(agentName);\n await runInstall(backend, (await config.backends[backend].installed(config.installDir)).pinned);\n }\n async function createSession(agentName, session) {\n requireAgent(agentName);\n const spec = validSession(session);\n const existing = sessions.get(spec.name);\n if (existing) {\n if (!sameSpec(existing.spec, spec))\n throw new AgentConflictError(`session ${spec.name} already exists with a different spec`);\n return info(existing);\n }\n const record = await config.storage.insertSession({ spec, nativeId: null, model: null });\n const state = await newState(record);\n sessions.set(spec.name, state);\n noteStatus(state);\n return info(state);\n }\n async function listSessions() {\n return Promise.all([...sessions.values()].map(info));\n }\n async function updateSession(ref, limits) {\n const state = requireSession(ref);\n const { memoryLimit, cpuLimit } = limits;\n state.spec = { ...state.spec, memoryLimit: memoryLimit ?? state.spec.memoryLimit, cpuLimit: cpuLimit ?? state.spec.cpuLimit };\n await saveRecord(state);\n return info(state);\n }\n async function stopSession(ref) {\n requireAgent(ref.agentName);\n const state = sessions.get(ref.sessionName);\n if (!state)\n return;\n failPending(state, \"the session stopped\");\n sessions.delete(ref.sessionName);\n await closeSession(state);\n await state.statusSaving;\n state.login?.process.cancel();\n state.login = null;\n state.listeners.clear();\n await config.storage.removeSession(state.spec.name);\n }\n async function sendMessage(ref, sent) {\n const state = requireSession(ref);\n if (!sent.uuid)\n return deliver(state, sent);\n const uuid = sent.uuid;\n const underway = state.sending.get(uuid);\n if (underway)\n return underway;\n const sending = (async () => {\n const accepted = await acceptedOf(state);\n if (accepted.has(uuid))\n return;\n await deliver(state, sent);\n accepted.add(uuid);\n if (accepted.size > ACCEPTED_KEPT)\n accepted.delete(accepted.values().next().value);\n })();\n state.sending.set(uuid, sending);\n try {\n await sending;\n } finally {\n state.sending.delete(uuid);\n }\n }\n async function acceptedOf(state) {\n if (state.accepted)\n return state.accepted;\n const seeded = new Set;\n const transcript = await transcriptOf(state);\n const page = transcript ? await transcript.page(null, ACCEPTED_SEED_PAGE_SIZE) : null;\n for (const message of page?.messages ?? [])\n if (message.type === \"user\" && isPrompt(message.message.content))\n seeded.add(message.uuid);\n state.accepted ??= seeded;\n return state.accepted;\n }\n async function deliver(state, sent) {\n if (statusOf(state).state === \"idle\" && trustChangedSince(state.startedAt))\n await closeSession(state);\n state.pending.push({ ...sent, uuid: sent.uuid ?? crypto.randomUUID(), session_id: state.nativeId ?? \"\" });\n drain(state);\n }\n function drain(state) {\n if (state.draining)\n return;\n const current = () => sessions.get(state.spec.name) === state;\n state.draining = (async () => {\n while (state.pending.length > 0 && current()) {\n let running;\n try {\n running = await ensureRunning(state);\n } catch (error) {\n failPending(state, error.message);\n return;\n }\n if (state.session !== running)\n continue;\n const message = state.pending.shift();\n if (!message)\n return;\n state.handing = message;\n try {\n await running.sendMessage(message);\n } catch (error) {\n failPrompt(state, message, error.message);\n continue;\n } finally {\n state.handing = null;\n }\n broadcast(state, { ...message, timestamp: message.timestamp ?? new Date().toISOString() });\n }\n })().finally(() => {\n state.draining = null;\n if (state.pending.length > 0 && current())\n drain(state);\n });\n }\n function failPrompt(state, message, error) {\n state.accepted?.delete(message.uuid);\n broadcast(state, { type: \"prompt_failed\", uuid: message.uuid, error });\n }\n function failPending(state, error) {\n for (const message of state.pending.splice(0))\n failPrompt(state, message, error);\n }\n async function cancel(ref) {\n const state = requireSession(ref);\n failPending(state, \"the turn was interrupted\");\n await dropRequests(state, \"the turn was interrupted\");\n if (state.session)\n await state.session.cancel();\n }\n async function getSessionStatus(ref) {\n return currentStatus(requireSession(ref));\n }\n async function getLastUserMessages(ref) {\n const state = requireSession(ref);\n const waiting = [...state.handing ? [state.handing] : [], ...state.pending];\n const listed = state.session ? await state.session.getLastUserMessages() : await lastStoredPrompt(state).then((stored) => stored ? [stored] : []);\n const known = new Set(listed.map(({ message }) => message.uuid));\n return [...listed, ...waiting.filter((message) => !known.has(message.uuid)).map((message) => ({ message, queued: true }))];\n }\n async function subscribe(ref, afterId, emit) {\n const state = requireSession(ref);\n let held = [];\n const listener = (message) => held ? held.push(message) : emit(message);\n const status = currentStatus(state);\n state.listeners.add(listener);\n emit({ type: \"session_status\", status });\n const unsubscribe = () => void state.listeners.delete(listener);\n try {\n const replayed = new Set;\n const transcript = await transcriptOf(state);\n const found = transcript ? await transcript.replay(afterId, (message) => {\n replayed.add(message.uuid);\n emit(message);\n }) : afterId === undefined;\n if (!found) {\n throw new AgentInvalidError(`no message ${afterId} in session ${ref.sessionName}: after_id is the uuid of a user or assistant message (partial output and results aren't stored)`);\n }\n for (const message of held)\n if (!(\"uuid\" in message) || !replayed.has(message.uuid))\n emit(message);\n held = null;\n } catch (error) {\n unsubscribe();\n throw error;\n }\n return unsubscribe;\n }\n async function getTranscript(ref, request = {}) {\n const state = requireSession(ref);\n if (request.toolUseId !== undefined)\n return transcriptPage(await subagentTranscriptOf(state, request.toolUseId), request);\n return transcriptPage(await transcriptOf(state), request);\n }\n async function listPermissionRequests(ref) {\n return requireSession(ref).permissionRequests.list();\n }\n async function respondPermissionRequest(ref, requestId, result) {\n const state = requireSession(ref);\n if (!await state.permissionRequests.answer(requestId, result))\n throw new AgentNotFoundError(`no pending permission request ${requestId}`);\n }\n async function listQuestionRequests(ref) {\n return requireSession(ref).questionRequests.list();\n }\n async function respondQuestionRequest(ref, requestId, response) {\n const state = requireSession(ref);\n if (!await state.questionRequests.answer(requestId, response)) {\n throw new AgentNotFoundError(`no pending question request ${requestId}`);\n }\n }\n async function listModels(ref) {\n return (await ensureRunning(requireSession(ref))).listModels();\n }\n async function setModel(ref, model) {\n const state = requireSession(ref);\n state.model = model;\n await saveRecord(state);\n if (state.running)\n await (await state.running).setModel(model);\n }\n async function setPermissionMode(ref, mode) {\n const state = requireSession(ref);\n state.permissionMode = mode;\n await saveRecord(state);\n if (state.running)\n await (await state.running).setPermissionMode(mode);\n if (mode !== \"auto\")\n return;\n for (const request of await state.permissionRequests.list())\n await state.permissionRequests.answer(request.id, { behavior: \"allow\", updatedInput: request.input });\n }\n async function listBackgroundTasks(ref) {\n const state = requireSession(ref);\n return state.running ? (await state.running).listBackgroundTasks() : [];\n }\n async function stopTask(ref, taskId) {\n const state = requireSession(ref);\n if (!state.running)\n throw new AgentNotFoundError(`no background task ${taskId}: session ${ref.sessionName}'s CLI isn't running`);\n await (await state.running).stopTask(taskId);\n }\n async function login(ref, gatewaySignedIn) {\n const state = requireSession(ref);\n if (state.login)\n return authStatus(state);\n await ensureBackend(state);\n if (await config.backends[state.spec.backend].auth.signedIn(await authEnvironment(state)))\n return { state: \"signedIn\" };\n const login = config.backends[state.spec.backend].auth.login(await authEnvironment(state), gatewaySignedIn);\n const entry = { process: login, settled: Promise.resolve() };\n state.login = entry;\n await saveAuth(state, { login: null, error: null });\n entry.settled = watchLogin(state, login);\n try {\n const prompt = await Promise.race([\n login.prompt,\n Bun.sleep(LOGIN_PROMPT_TIMEOUT_MS).then(() => Promise.reject(new Error(\"the login didn't show where to sign in\")))\n ]);\n if (state.login === entry)\n await saveAuth(state, { login: { state: \"pending\", ...prompt }, error: null });\n } catch (error) {\n if (state.login === entry)\n await cancelLogin(state);\n await saveAuth(state, { login: null, error: error.message });\n return authStatus(state);\n }\n return settleLogin(state, entry.settled, gatewaySignedIn ? LOGIN_COMPLETION_TIMEOUT_MS : 0);\n }\n async function submitAuthCode(ref, code) {\n const state = requireSession(ref);\n const login = state.login;\n const prompt = (await config.storage.readAuth(ref.sessionName))?.login;\n if (!login || !prompt)\n throw new AgentInvalidError(`session ${ref.sessionName} has no sign-in waiting`);\n if (!prompt.needsCode)\n throw new AgentInvalidError(`session ${ref.sessionName}'s sign-in takes no code: finish it at ${prompt.url}`);\n login.process.submitCode(code);\n return settleLogin(state, login.settled, LOGIN_COMPLETION_TIMEOUT_MS);\n }\n async function close() {\n for (const state of sessions.values())\n failPending(state, \"the agent's worker stopped\");\n await Promise.all([...sessions.values()].map(closeSession));\n }\n return {\n load: loadSessions,\n close,\n install,\n listBackends,\n createSession,\n listSessions,\n updateSession,\n stopSession,\n sendMessage,\n cancel,\n getSessionStatus,\n getLastUserMessages,\n subscribe,\n getTranscript,\n listPermissionRequests,\n respondPermissionRequest,\n listQuestionRequests,\n respondQuestionRequest,\n listModels,\n setModel,\n setPermissionMode,\n listBackgroundTasks,\n stopTask,\n getAuthStatus: async (ref) => authStatus(requireSession(ref)),\n login,\n submitAuthCode\n };\n}\nvar LAST_PROMPT_PAGE_SIZE = 50;\nvar SUBAGENTS_KEPT = 1000;\nvar ACCEPTED_KEPT = 500;\nvar ACCEPTED_SEED_PAGE_SIZE = 100;\nfunction isPrompt(content) {\n if (typeof content === \"string\")\n return true;\n return Array.isArray(content) && content.some((block) => block.type === \"text\");\n}\nvar LOGIN_PROMPT_TIMEOUT_MS = 30000;\nvar LOGIN_COMPLETION_TIMEOUT_MS = 60000;\nfunction sameStatus(a, b) {\n const { since: _a, ...first } = a;\n const { since: _b, ...second } = b;\n return JSON.stringify(first) === JSON.stringify(second);\n}\nfunction validSession(spec) {\n try {\n checkName(spec.name);\n } catch (error) {\n if (error instanceof InvalidNameError)\n throw new AgentInvalidError(`invalid session name: ${error.message}`);\n throw error;\n }\n return { ...spec, cwd: spec.cwd ?? dimensionLayout.workspacePath() };\n}\nfunction sameSpec(a, b) {\n return a.backend === b.backend && a.memoryLimit === b.memoryLimit && a.cpuLimit === b.cpuLimit && a.cwd === b.cwd && JSON.stringify(a.env ?? {}) === JSON.stringify(b.env ?? {});\n}\n\n// agent/session/main.ts\nfunction required(name) {\n const value = process.env[name];\n if (!value)\n throw new Error(`${name} is not set`);\n return value;\n}\nvar agentName = required(\"HYB_AGENT\");\nvar build = required(\"HYB_WORKER_BUILD\");\nvar protocol = Number(process.env.HYB_WORKER_PROTOCOL ?? WORKER_PROTOCOL);\nvar files = createLocalService({ kind: \"local\" });\nvar storage = createSessionStorage(files, agentName);\nvar service = createWorkerService({\n agentName,\n storage,\n installDir: required(\"HYB_INSTALL_DIR\"),\n files: sandboxFiles(files),\n trustBundle: process.env.NODE_EXTRA_CA_CERTS,\n backends: SESSION_BACKENDS\n});\nvar socketPath = await storage.socketPath();\nvar readyPath = workerReadyPath(dirname2(socketPath), agentName);\nvar server = createWorkerServer(service, { socketPath, health: { agentName, build, protocol }, onShutdown: shutdown });\nfunction shutdown() {\n server.stop();\n rm3(readyPath, { force: true }).catch(() => {});\n service.close().catch((error) => console.error(\"stopping the sessions failed:\", error)).finally(() => process.exit(0));\n}\nfor (const signal of [\"SIGTERM\", \"SIGINT\"])\n process.once(signal, shutdown);\nawait service.load();\nawait server.start();\nvar socketOwner = process.env.HYB_SOCKET_OWNER;\nif (socketOwner) {\n const [uid, gid] = socketOwner.split(\":\").map(Number);\n await chown(socketPath, uid, gid);\n}\nawait writeFile3(readyPath, `${process.pid} ${build} ${protocol}\n`);\nconsole.log(`session worker for agent ${agentName} (build ${build}) listening on ${socketPath}`);\n";
|
|
2
|
+
export const WORKER_SOURCE = "// @bun\n// agent/session/main.ts\nimport { chown, rm as rm3, writeFile as writeFile3 } from \"fs/promises\";\nimport { dirname as dirname2 } from \"path\";\n\n// sandbox/providers/local/localService.ts\nimport { spawn } from \"child_process\";\nimport { randomUUID } from \"crypto\";\nimport * as fs from \"fs/promises\";\nimport { connect as netConnect } from \"net\";\nimport { homedir, userInfo } from \"os\";\nimport { basename, dirname, join, resolve } from \"path\";\n\n// sandbox/api.ts\nclass SandboxNotFoundError extends Error {\n name = \"SandboxNotFoundError\";\n}\nclass SandboxConnectionRefusedError extends Error {\n name = \"SandboxConnectionRefusedError\";\n}\n\n// sandbox/storage/descriptors.ts\nfunction makeDescriptor(ctx, id, spec) {\n return {\n id,\n kind: ctx.kind,\n name: spec.name,\n ...ctx.sshHost === undefined ? {} : { sshHost: ctx.sshHost },\n ...ctx.sshUser === undefined ? {} : { sshUser: ctx.sshUser },\n ...ctx.sshPort === undefined ? {} : { sshPort: ctx.sshPort },\n ...spec.dataEndpointOptions === undefined ? {} : { dataEndpointOptions: spec.dataEndpointOptions }\n };\n}\nasync function createDescriptor(ctx, storage, id, spec) {\n const descriptor = makeDescriptor(ctx, id, spec);\n await storage.saveDescriptor(descriptor);\n return [{ ...ctx, id }, descriptor];\n}\nasync function openDescriptor(ctx, storage, id) {\n return await storage.readDescriptor() ?? storage.insertDescriptor(makeDescriptor(ctx, id, { name: id }));\n}\nasync function getDescriptor(storage, where) {\n const descriptor = await storage.readDescriptor();\n if (!descriptor)\n throw new Error(`${where} has no descriptor: opening it saves one`);\n return descriptor;\n}\n\n// sandbox/data-endpoints/dataEndpoints.ts\nimport { randomBytes } from \"crypto\";\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/buffer_utils.js\nvar encoder = new TextEncoder;\nvar decoder = new TextDecoder;\nvar strictDecoder = new TextDecoder(\"utf-8\", { fatal: true });\nvar UINT32_RANGE = 2 ** 32;\nfunction concat(...buffers) {\n const size = buffers.reduce((acc, { length }) => acc + length, 0), buf = new Uint8Array(size);\n let i = 0;\n for (const buffer of buffers)\n buf.set(buffer, i), i += buffer.length;\n return buf;\n}\nvar NON_ASCII = /[^\\x00-\\x7f]/;\nfunction encode(string) {\n if (typeof string == \"string\" && string.length >= 128) {\n if (NON_ASCII.test(string))\n throw new TypeError(\"non-ASCII string encountered in encode()\");\n return encoder.encode(string);\n }\n const bytes = new Uint8Array(string.length);\n for (let i = 0;i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code > 127)\n throw new TypeError(\"non-ASCII string encountered in encode()\");\n bytes[i] = code;\n }\n return bytes;\n}\nfunction encodeBase64(input, url = false) {\n if (Uint8Array.prototype.toBase64)\n return input.toBase64({ alphabet: url ? \"base64url\" : \"base64\", omitPadding: url });\n const CHUNK_SIZE = 32768, arr = [];\n for (let i = 0;i < input.length; i += CHUNK_SIZE)\n arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));\n const encoded = btoa(arr.join(\"\"));\n return url ? encoded.replace(/=/g, \"\").replace(/\\+/g, \"-\").replace(/\\//g, \"_\") : encoded;\n}\nfunction decodeBase64(encoded, url = false) {\n if (Uint8Array.fromBase64)\n return Uint8Array.fromBase64(encoded, { alphabet: url ? \"base64url\" : \"base64\" });\n if (url) {\n if (encoded.includes(\"+\") || encoded.includes(\"/\"))\n throw new TypeError(\"Invalid base64url\");\n encoded = encoded.replace(/-/g, \"+\").replace(/_/g, \"/\");\n }\n const binary = atob(encoded), bytes = new Uint8Array(binary.length);\n for (let i = 0;i < binary.length; i++)\n bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/util/errors.js\nclass JOSEError extends Error {\n static code = \"ERR_JOSE_GENERIC\";\n code = \"ERR_JOSE_GENERIC\";\n constructor(message, options) {\n super(message, options), this.name = this.constructor.name, Error.captureStackTrace?.(this, this.constructor);\n }\n}\nclass JOSENotSupported extends JOSEError {\n static code = \"ERR_JOSE_NOT_SUPPORTED\";\n code = \"ERR_JOSE_NOT_SUPPORTED\";\n}\nclass JWSInvalid extends JOSEError {\n static code = \"ERR_JWS_INVALID\";\n code = \"ERR_JWS_INVALID\";\n}\n\nclass JWTInvalid extends JOSEError {\n static code = \"ERR_JWT_INVALID\";\n code = \"ERR_JWT_INVALID\";\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/util/base64url.js\nvar invalid = \"The input to be decoded is not correctly encoded.\";\nfunction decode(input) {\n try {\n return decodeBase64(typeof input == \"string\" ? input : decoder.decode(input), true);\n } catch (cause) {\n throw new TypeError(invalid, { cause });\n }\n}\nfunction encode2(input) {\n return encodeBase64(typeof input == \"string\" ? encoder.encode(input) : input, true);\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/validate.js\nfunction isObject(input) {\n if (typeof input != \"object\" || input === null || Object.prototype.toString.call(input) !== \"[object Object]\")\n return false;\n const prototype = Object.getPrototypeOf(input);\n return prototype === null || Object.getPrototypeOf(prototype) === null;\n}\nfunction isDisjoint(...headers) {\n const parameters = /* @__PURE__ */ new Set;\n for (const header of headers)\n if (header)\n for (const parameter of Object.keys(header)) {\n if (parameters.has(parameter))\n return false;\n parameters.add(parameter);\n }\n return true;\n}\nfunction assertNotSet(value, name) {\n if (value !== undefined)\n throw new TypeError(`${name} can only be called once`);\n}\nvar JWS_RECOGNIZED = { __proto__: null, b64: true };\nfunction validateCritDuplicates(Err, protectedHeader) {\n const { crit } = protectedHeader ?? {};\n if (Array.isArray(crit) && new Set(crit).size !== crit.length)\n throw new Err('\"crit\" (Critical) Header Parameter MUST NOT contain duplicate values');\n}\nfunction validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {\n if (joseHeader.crit !== undefined && protectedHeader?.crit === undefined)\n throw new Err('\"crit\" (Critical) Header Parameter MUST be integrity protected');\n if (!protectedHeader || protectedHeader.crit === undefined)\n return [];\n if (!Array.isArray(protectedHeader.crit) || protectedHeader.crit.length === 0 || protectedHeader.crit.some((input) => typeof input != \"string\" || input.length === 0))\n throw new Err('\"crit\" (Critical) Header Parameter MUST be an array of non-empty strings when present');\n const recognized = recognizedOption === undefined ? recognizedDefault : { __proto__: null, ...recognizedOption, ...recognizedDefault };\n for (const parameter of protectedHeader.crit) {\n if (!(parameter in recognized))\n throw new JOSENotSupported(`Extension Header Parameter \"${parameter}\" is not recognized`);\n if (!Object.hasOwn(joseHeader, parameter) || joseHeader[parameter] === undefined)\n throw new Err(`Extension Header Parameter \"${parameter}\" is missing`);\n if (recognized[parameter] && (!Object.hasOwn(protectedHeader, parameter) || protectedHeader[parameter] === undefined))\n throw new Err(`Extension Header Parameter \"${parameter}\" MUST be integrity protected`);\n }\n return protectedHeader.crit;\n}\nfunction validateB64(protectedHeader, extensions) {\n if (extensions.includes(\"b64\")) {\n const b64 = protectedHeader.b64;\n if (typeof b64 != \"boolean\")\n throw new JWSInvalid('The \"b64\" (base64url-encode payload) Header Parameter must be a boolean');\n return b64;\n }\n return true;\n}\nfunction serializeJoseHeader(Err, header) {\n let serialized, parsed;\n try {\n serialized = JSON.stringify(header), parsed = JSON.parse(serialized);\n } catch (cause) {\n throw new Err(\"JOSE Header is not valid JSON\", { cause });\n }\n if (!isObject(parsed))\n throw new Err(\"JOSE Header is not a JSON object\");\n return [parsed, serialized];\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/key.js\nvar tag = (key) => key[Symbol.toStringTag];\nvar jwkMatchesOp = (entry, key, usage) => {\n const { alg } = entry;\n if (key.use !== undefined) {\n const expected = usage === \"sign\" || usage === \"verify\" ? \"sig\" : \"enc\";\n if (key.use !== expected)\n throw new TypeError(`Invalid key for this operation, its \"use\" must be \"${expected}\" when present`);\n }\n if (key.alg !== undefined && key.alg !== alg)\n throw new TypeError(`Invalid key for this operation, its \"alg\" must be \"${alg}\" when present`);\n if (Array.isArray(key.key_ops)) {\n const expectedKeyOp = usage === \"encrypt\" || usage === \"decrypt\" ? entry.ops?.[usage === \"encrypt\" ? 0 : 1] : usage;\n if (expectedKeyOp && !key.key_ops.includes(expectedKeyOp))\n throw new TypeError(`Invalid key for this operation, its \"key_ops\" must include \"${expectedKeyOp}\" when present`);\n }\n};\nasync function prepareKey(entry, key, usage) {\n const { alg, secret } = entry, requiresPrivateKey = usage === \"decrypt\" || usage === \"sign\";\n if (secret && key instanceof Uint8Array)\n return key;\n let normalized, keyObject;\n if (isObject(key)) {\n if (normalized = normalizeJwk(key), typeof normalized.kty != \"string\")\n throw invalidKeyType(alg, key, secret);\n if (!(secret ? normalized.kty === \"oct\" && typeof normalized.k == \"string\" : normalized.kty !== \"oct\" && (requiresPrivateKey ? normalized.kty === \"AKP\" && typeof normalized.priv == \"string\" || typeof normalized.d == \"string\" : normalized.d === undefined && normalized.priv === undefined)))\n throw new TypeError(secret ? 'JSON Web Key for symmetric algorithms must have JWK \"kty\" (Key Type) equal to \"oct\" and the JWK \"k\" (Key Value) present' : `JSON Web Key for this operation must be a ${requiresPrivateKey ? \"private\" : \"public\"} JWK`);\n if (jwkMatchesOp(entry, normalized, usage), normalized.kty === \"oct\")\n return decode(normalized.k);\n if (!Object.isFrozen(key)) {\n const { key_ops } = key;\n Array.isArray(key_ops) && Object.freeze(key_ops), Object.freeze(key);\n }\n } else {\n if (!isKeyLike(key))\n throw invalidKeyType(alg, key, secret);\n const expectedType = secret ? \"secret\" : requiresPrivateKey ? \"private\" : \"public\";\n if (key.type !== expectedType && (secret || [\"secret\", \"public\", \"private\"].includes(key.type)))\n throw new TypeError(`${tag(key)} instances must be of type \"${expectedType}\" for the ${alg} algorithm`);\n if (isCryptoKey(key))\n return key;\n if (keyObject = key, keyObject.type === \"secret\")\n return keyObject.export();\n }\n cache ||= /* @__PURE__ */ new WeakMap;\n const cacheKey = key;\n let cached = cache.get(cacheKey);\n if (cached?.[alg])\n return cached[alg];\n if (cached || cache.set(cacheKey, cached = {}), keyObject && typeof keyObject.toCryptoKey == \"function\") {\n const isPublic = keyObject.type === \"public\", crv = nist[keyObject.asymmetricKeyDetails?.namedCurve], params = entry.resolve?.({ crv, asymmetricKeyType: keyObject.asymmetricKeyType }) ?? entry.subtle;\n return cached[alg] = keyObject.toCryptoKey(params, isPublic, entry.usages[isPublic ? 0 : 1]);\n }\n return normalized ??= keyObject.export({ format: \"jwk\" }), normalized.alg = alg, cached[alg] = await jwkToKey(entry, normalized);\n}\nvar cache;\nvar nist = {\n __proto__: null,\n prime256v1: \"P-256\",\n secp384r1: \"P-384\",\n secp521r1: \"P-521\"\n};\nvar isCryptoKey = (key) => {\n if (key?.[Symbol.toStringTag] === \"CryptoKey\")\n return true;\n try {\n return key instanceof CryptoKey;\n } catch {\n return false;\n }\n};\nvar isKeyObject = (key) => key?.[Symbol.toStringTag] === \"KeyObject\";\nvar isKeyLike = (key) => isCryptoKey(key) || isKeyObject(key);\nfunction message(msg, actual, ...types) {\n if (types.length > 2) {\n const last = types.pop();\n msg += `one of type ${types.join(\", \")}, or ${last}.`;\n } else\n types.length === 2 ? msg += `one of type ${types[0]} or ${types[1]}.` : msg += `of type ${types[0]}.`;\n return actual == null ? msg += ` Received ${actual}` : typeof actual == \"function\" && actual.name ? msg += ` Received function ${actual.name}` : typeof actual == \"object\" && actual != null && actual.constructor?.name && (msg += ` Received an instance of ${actual.constructor.name}`), msg;\n}\nfunction invalidKeyType(alg, actual, secret) {\n const types = [\"CryptoKey\", \"KeyObject\", \"JSON Web Key\"];\n return secret && types.push(\"Uint8Array\"), new TypeError(message(`Key for the ${alg} algorithm must be `, actual, ...types));\n}\nvar unusable = (name, prop = \"algorithm.name\") => new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);\nfunction checkUsage(key, usage) {\n if (usage && !key.usages.includes(usage))\n throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);\n}\nfunction checkModulusLength(alg, key) {\n const { modulusLength } = key.algorithm;\n if (typeof modulusLength != \"number\" || modulusLength < 2048)\n throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);\n}\nfunction checkCryptoKey(key, expected, usage) {\n const algorithm = key.algorithm;\n if (algorithm.name !== expected.name)\n throw unusable(expected.name);\n if (expected.hash && algorithm.hash?.name !== expected.hash)\n throw unusable(expected.hash, \"algorithm.hash\");\n if (expected.namedCurve && algorithm.namedCurve !== expected.namedCurve)\n throw unusable(expected.namedCurve, \"algorithm.namedCurve\");\n if (expected.length !== undefined && algorithm.length !== expected.length)\n throw unusable(expected.length, \"algorithm.length\");\n checkUsage(key, usage);\n}\nfunction snapshotJwk(jwk) {\n return { __proto__: null, ...jwk };\n}\nfunction normalizeJwk(jwk) {\n const normalized = snapshotJwk(jwk);\n if (normalized.ext !== undefined && typeof normalized.ext != \"boolean\")\n throw new TypeError('\"ext\" (Extractable) Parameter must be a boolean');\n if (normalized.key_ops !== undefined) {\n const value = normalized.key_ops, keyOps = Array.isArray(value) ? [...value] : undefined;\n if (!keyOps || keyOps.some((operation) => typeof operation != \"string\") || new Set(keyOps).size !== keyOps.length)\n throw new TypeError('\"key_ops\" (Key Operations) Parameter must be an array of unique strings');\n normalized.key_ops = keyOps;\n }\n return normalized;\n}\nasync function jwkToKey(entry, jwk, extractable) {\n if (!entry.kty.includes(jwk.kty))\n throw new JOSENotSupported('Invalid or unsupported JWK \"alg\" (Algorithm) Parameter value');\n const algorithm = entry.resolve?.({ kty: jwk.kty, crv: jwk.crv }) ?? entry.subtle, isPrivate = !!(jwk.d || jwk.priv), keyData = { ...jwk, ext: extractable ?? jwk.ext };\n return keyData.kty !== \"AKP\" && delete keyData.alg, delete keyData.use, crypto.subtle.importKey(\"jwk\", keyData, algorithm, keyData.ext ?? !isPrivate, jwk.key_ops ?? entry.usages[isPrivate ? 1 : 0]);\n}\nasync function rawKey(key, expected, usage, extractable = false) {\n return key instanceof Uint8Array && (key = await crypto.subtle.importKey(\"raw\", key, expected, extractable, [usage])), checkCryptoKey(key, expected, usage), key;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/key_descriptor.js\nfunction table(entries) {\n const out = { __proto__: null };\n for (const alg in entries)\n out[alg] = { ...entries[alg], alg };\n return out;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/jws_algorithms.js\nvar sig = [[\"verify\"], [\"sign\"]];\nfunction hmac(bits) {\n const subtle = { name: \"HMAC\", hash: `SHA-${bits}` };\n return { kty: [\"oct\"], secret: true, subtle, signing: subtle, usages: sig };\n}\nfunction rsa(bits, saltLength) {\n const subtle = { name: saltLength ? \"RSA-PSS\" : \"RSASSA-PKCS1-v1_5\", hash: `SHA-${bits}` };\n return {\n kty: [\"RSA\"],\n subtle,\n signing: saltLength ? { ...subtle, saltLength } : subtle,\n usages: sig,\n minRsaBits: 2048\n };\n}\nfunction ecdsa(crv, bits) {\n return {\n kty: [\"EC\"],\n crv,\n subtle: { name: \"ECDSA\", namedCurve: crv },\n signing: { name: \"ECDSA\", hash: `SHA-${bits}` },\n usages: sig\n };\n}\nfunction eddsa() {\n const subtle = { name: \"Ed25519\" };\n return {\n kty: [\"OKP\"],\n crv: \"Ed25519\",\n subtle,\n signing: subtle,\n usages: sig\n };\n}\nfunction mldsa(parameterSet) {\n const subtle = { name: `ML-DSA-${parameterSet}` };\n return {\n kty: [\"AKP\"],\n subtle,\n signing: subtle,\n usages: sig\n };\n}\nvar JWS = table({\n HS256: hmac(256),\n HS384: hmac(384),\n HS512: hmac(512),\n RS256: rsa(256),\n RS384: rsa(384),\n RS512: rsa(512),\n PS256: rsa(256, 32),\n PS384: rsa(384, 48),\n PS512: rsa(512, 64),\n ES256: ecdsa(\"P-256\", 256),\n ES384: ecdsa(\"P-384\", 384),\n ES512: ecdsa(\"P-521\", 512),\n EdDSA: eddsa(),\n Ed25519: eddsa(),\n \"ML-DSA-44\": mldsa(44),\n \"ML-DSA-65\": mldsa(65),\n \"ML-DSA-87\": mldsa(87)\n});\nfunction jwsAlgorithm(alg) {\n const entry = typeof alg == \"string\" ? JWS[alg] : undefined;\n if (!entry)\n throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);\n return entry;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/jwt_claims_set.js\nvar epoch = (date) => Math.floor(date.getTime() / 1000);\nvar multipliers = {\n s: 1,\n m: 60,\n h: 3600,\n d: 86400,\n w: 604800,\n y: 31557600\n};\nvar REGEX = /^(\\+|\\-)? ?(\\d+|\\d+\\.\\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;\nfunction invalidDuration() {\n throw new TypeError(\"Invalid time period format\");\n}\nfunction secs(str) {\n typeof str != \"string\" && invalidDuration();\n const matched = REGEX.exec(str);\n (!matched || matched[4] && matched[1]) && invalidDuration();\n const value = parseFloat(matched[2]), durationSeconds = Math.round(value * multipliers[matched[3][0].toLowerCase()]);\n return Number.isFinite(durationSeconds) || invalidDuration(), matched[1] === \"-\" || matched[4] === \"ago\" ? -durationSeconds : durationSeconds;\n}\nfunction validateInput(label, input) {\n if (!Number.isFinite(input))\n throw new TypeError(`Invalid ${label} input`);\n return input;\n}\nfunction validateStringClaim(claim, value) {\n if (typeof value != \"string\")\n throw new TypeError(`\"${claim}\" claim must be a string`);\n}\nfunction validateAudienceClaim(value) {\n if (typeof value != \"string\" && (!Array.isArray(value) || Array.from(value).some((member) => typeof member != \"string\")))\n throw new TypeError('\"aud\" claim must be a string or an array of strings');\n}\nfunction numericDate(value, label) {\n return typeof value == \"number\" ? validateInput(label, value) : value instanceof Date ? validateInput(label, epoch(value)) : epoch(/* @__PURE__ */ new Date) + secs(value);\n}\nvar producerClaimsSets;\nfunction producerClaimsSet(producer) {\n return producerClaimsSets.get(producer);\n}\nfunction jwtClaimsSetBytes(producer) {\n const claimsSet = producerClaimsSet(producer);\n for (const claim of [\"iat\", \"nbf\", \"exp\"]) {\n const value = claimsSet[claim];\n if (typeof value == \"number\" && !Number.isFinite(value))\n throw new TypeError(`\"${claim}\" claim must be a finite number`);\n }\n return encoder.encode(JSON.stringify(claimsSet));\n}\nclass JWTClaimsBuilder {\n constructor(claimsSet = {}) {\n if (!isObject(claimsSet))\n throw new TypeError(\"JWT Claims Set MUST be an object\");\n (producerClaimsSets ||= /* @__PURE__ */ new WeakMap).set(this, structuredClone(claimsSet));\n }\n setIssuer(value) {\n return validateStringClaim(\"iss\", value), producerClaimsSet(this).iss = value, this;\n }\n setSubject(value) {\n return validateStringClaim(\"sub\", value), producerClaimsSet(this).sub = value, this;\n }\n setAudience(value) {\n return validateAudienceClaim(value), producerClaimsSet(this).aud = value, this;\n }\n setJti(value) {\n return validateStringClaim(\"jti\", value), producerClaimsSet(this).jti = value, this;\n }\n setNotBefore(value) {\n return producerClaimsSet(this).nbf = numericDate(value, \"setNotBefore\"), this;\n }\n setExpirationTime(value) {\n return producerClaimsSet(this).exp = numericDate(value, \"setExpirationTime\"), this;\n }\n setIssuedAt(value) {\n const claimsSet = producerClaimsSet(this);\n return value === undefined ? claimsSet.iat = epoch(/* @__PURE__ */ new Date) : typeof value == \"string\" ? claimsSet.iat = validateInput(\"setIssuedAt\", epoch(/* @__PURE__ */ new Date) + secs(value)) : claimsSet.iat = numericDate(value, \"setIssuedAt\"), this;\n }\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/lib/jws_sign.js\nasync function createSignature(input, key, rejectUnencoded) {\n let [payload, protectedHeader, unprotectedHeader, crit] = input, encodedProtectedHeader = \"\";\n if (protectedHeader !== undefined) {\n const normalized = serializeJoseHeader(JWSInvalid, protectedHeader);\n protectedHeader = normalized[0], encodedProtectedHeader = encode2(normalized[1]);\n }\n if (unprotectedHeader !== undefined && (unprotectedHeader = serializeJoseHeader(JWSInvalid, unprotectedHeader)[0]), !protectedHeader && !unprotectedHeader)\n throw new JWSInvalid(\"either setProtectedHeader or setUnprotectedHeader must be called before #sign()\");\n if (!isDisjoint(protectedHeader, unprotectedHeader))\n throw new JWSInvalid(\"JWS Protected and JWS Unprotected Header Parameter names must be disjoint\");\n const joseHeader = { ...protectedHeader, ...unprotectedHeader };\n validateCritDuplicates(JWSInvalid, protectedHeader);\n const b64 = validateB64(protectedHeader, validateCrit(JWSInvalid, JWS_RECOGNIZED, crit, protectedHeader, joseHeader));\n b64 || rejectUnencoded?.();\n const { alg } = joseHeader;\n if (typeof alg != \"string\" || !alg)\n throw new JWSInvalid('JWS \"alg\" (Algorithm) Header Parameter missing or invalid');\n const entry = jwsAlgorithm(alg);\n let encodedPayload = \"\", signingPayload = payload, signingInput;\n if (b64) {\n const encodedPayloadCache = input[4];\n encodedPayloadCache ? (encodedPayload = encodedPayloadCache[0] ??= encode2(payload), signingPayload = encodedPayloadCache[1] ??= encode(encodedPayload)) : (encodedPayload = encode2(payload), signingInput = encoder.encode(`${encodedProtectedHeader}.${encodedPayload}`));\n }\n signingInput ??= concat(encode(encodedProtectedHeader), encode(\".\"), signingPayload);\n const signingKey = await rawKey(await prepareKey(entry, key, \"sign\"), entry.subtle, \"sign\");\n entry.minRsaBits && checkModulusLength(entry.alg, signingKey);\n const jws = {\n signature: encode2(new Uint8Array(await crypto.subtle.sign(entry.signing, signingKey, signingInput))),\n payload: encodedPayload\n };\n return protectedHeader && (jws.protected = encodedProtectedHeader), unprotectedHeader && (jws.header = unprotectedHeader), [jws, b64];\n}\nasync function createCompactSignature(payload, protectedHeader, crit, key, rejectUnencoded) {\n const [jws] = await createSignature([payload, protectedHeader, undefined, crit], key, rejectUnencoded);\n return `${jws.protected}.${jws.payload}.${jws.signature}`;\n}\n\n// ../node_modules/.bun/jose@6.2.13/node_modules/jose/dist/webapi/jwt/sign.js\nvar SignJWT_base = JWTClaimsBuilder;\n\nclass SignJWT extends SignJWT_base {\n #protectedHeader;\n setProtectedHeader(protectedHeader) {\n return assertNotSet(this.#protectedHeader, \"setProtectedHeader\"), this.#protectedHeader = protectedHeader, this;\n }\n async sign(key, options) {\n return createCompactSignature(jwtClaimsSetBytes(this), this.#protectedHeader, options?.crit, key, () => {\n throw new JWTInvalid(\"JWTs MUST NOT use unencoded payload\");\n });\n }\n}\n\n// lib/sh.ts\nfunction sh(strings, ...values) {\n if (values.length > 0)\n throw new Error(\"sh`\\u2026` takes no interpolations: pass inputs as environment variables or arguments\");\n return scriptText(strings.raw[0]);\n}\nfunction scriptText(raw) {\n return raw.replace(/\\\\(\\$\\{|`)/g, \"$1\").replace(/^\\n/, \"\");\n}\n\n// sandbox/data-endpoints/dataEndpoints.ts\nvar DEFAULT_TTL_S = 12 * 3600;\nfunction signToken(secret, ttlSeconds) {\n return new SignJWT().setProtectedHeader({ alg: \"HS256\" }).setExpirationTime(`${ttlSeconds}s`).sign(new TextEncoder().encode(secret));\n}\nasync function listeningPorts(sandbox) {\n const listed = await sandbox.runCommand([\"sh\", \"-c\", LISTENING_PORTS]);\n if (listed.exitCode !== 0)\n throw new Error(`listing the ports in use failed: ${listed.stderr.trim()}`);\n return listed.stdout.split(`\n`).filter(Boolean).map(Number);\n}\nasync function withLock(sandbox, body) {\n const lock = `${await sandbox.homeDirectory()}/.local/state/hyperbrane/endpoints.lock`;\n const taken = await sandbox.runCommand([\"sh\", \"-c\", TAKE_LOCK, \"sh\", lock]);\n if (taken.exitCode !== 0)\n throw new Error(`${lock} stayed locked: ${taken.stderr.trim()}`);\n try {\n return await body();\n } finally {\n await sandbox.runCommand([\"rm\", \"-rf\", lock]);\n }\n}\nfunction under(base, endpoint) {\n if (base === undefined || new URL(base).pathname === \"/\")\n return endpoint;\n return { ...endpoint, url: `${base.replace(/\\/+$/, \"\")}/${endpoint.name}` };\n}\nfunction createEndpoints(sandbox, storage, ports) {\n const located = async (endpoints) => {\n const base = (await storage.readDescriptor())?.dataEndpointOptions?.url;\n return endpoints.map((endpoint) => under(base, endpoint));\n };\n async function openEndpoint(name) {\n const [endpoint] = await located([await openStored(name)]);\n return endpoint;\n }\n async function openStored(name) {\n return withLock(sandbox, async () => {\n const existing = await storage.readEndpoint(name);\n if (existing)\n return existing;\n const { from, to } = await ports.ports();\n const used = new Set([...(await storage.listEndpoints()).map((endpoint) => endpoint.port), ...await listeningPorts(sandbox)]);\n let port = from;\n while (port <= to && used.has(port))\n port++;\n if (port > to)\n throw new Error(`no data endpoint ports are left in ${from}-${to}`);\n const endpoint = { name, url: await ports.expose(port), port, secret: randomBytes(32).toString(\"base64url\") };\n await storage.saveEndpoint(endpoint);\n return endpoint;\n });\n }\n async function closeEndpoint(name) {\n await withLock(sandbox, async () => {\n const endpoint = await storage.readEndpoint(name);\n if (!endpoint)\n return;\n await ports.conceal(endpoint.port);\n await storage.removeEndpoint(name);\n });\n }\n async function credentials(name, {\n ttlSeconds = DEFAULT_TTL_S\n } = {}) {\n const endpoint = await storage.readEndpoint(name);\n if (!endpoint)\n throw new SandboxNotFoundError(`there is no data endpoint ${name}`);\n return { token: await signToken(endpoint.secret, ttlSeconds), ...await ports.credentials?.(endpoint, ttlSeconds) };\n }\n return {\n openEndpoint,\n closeEndpoint,\n listEndpoints: async () => located(await storage.listEndpoints()),\n credentials\n };\n}\nvar LISTENING_PORTS = sh`\nawk 'FNR > 1 && $4 == \"0A\" { n = split($2, local, \":\"); print local[n] }' /proc/net/tcp /proc/net/tcp6 2> /dev/null | while read -r hex; do\n printf '%d\\n' \"0x$hex\"\ndone\n`;\nvar TAKE_LOCK = sh`\nmkdir -p \"$(dirname \"$1\")\"\nwaited=0\nuntil mkdir \"$1\" 2> /dev/null; do\n if [ -n \"$(find \"$1\" -maxdepth 0 -mmin +2 2> /dev/null)\" ]; then\n rm -rf \"$1\"\n continue\n fi\n [ \"$waited\" -lt 1200 ] || exit 1\n sleep 0.1\n waited=$((waited + 1))\ndone\n`;\n\n// sandbox/data-endpoints/firewalledPorts.ts\nfunction firewalledPorts(sandbox, storage) {\n const options = async () => (await storage.readDescriptor())?.dataEndpointOptions;\n async function firewall(rule) {\n const result = await sandbox.runCommand([\"sudo\", \"-n\", \"ufw\", ...rule]);\n if (result.exitCode !== 0)\n throw new Error(`ufw ${rule.join(\" \")} failed: ${result.stderr.trim()}`);\n }\n return {\n async ports() {\n const ports = (await options())?.ports;\n if (!ports)\n throw new Error(\"this machine has no data endpoints: it's created with dataEndpointOptions.ports\");\n return ports;\n },\n async expose(port) {\n const found = await options();\n if (found?.needsFirewall)\n await firewall([\"allow\", `${port}/tcp`]);\n const url = new URL(found?.url ?? \"http://127.0.0.1\");\n url.port = String(port);\n return url.toString().replace(/\\/$/, \"\");\n },\n async conceal(port) {\n if ((await options())?.needsFirewall)\n await firewall([\"delete\", \"allow\", `${port}/tcp`]);\n }\n };\n}\n\n// sandbox/lib/httpOverStream.ts\nimport { request as httpRequest } from \"http\";\nimport { Readable } from \"stream\";\nfunction httpOver(url, connection, { method = \"GET\", headers = {}, body, signal, timeout }) {\n return new Promise((resolve, reject) => {\n const target = new URL(url);\n const request = httpRequest({ createConnection: connection, path: `${target.pathname}${target.search}`, method, headers: { host: target.host, ...headers }, signal }, (response) => {\n const responseHeaders = new Headers;\n for (let i = 0;i + 1 < response.rawHeaders.length; i += 2)\n responseHeaders.append(response.rawHeaders[i], response.rawHeaders[i + 1]);\n const status = response.statusCode ?? 502;\n const nullBody = status === 204 || status === 304 || method === \"HEAD\";\n if (nullBody)\n response.resume();\n resolve(new Response(nullBody ? null : Readable.toWeb(response), { status, headers: responseHeaders }));\n });\n request.once(\"error\", reject);\n if (typeof timeout === \"number\")\n request.setTimeout(timeout, () => request.destroy(new Error(`fetch ${url}: no data for ${timeout}ms`)));\n request.end(body === undefined ? undefined : typeof body === \"string\" ? body : Buffer.from(body));\n });\n}\n\n// lib/unixSocket.ts\nimport { Socket } from \"net\";\nfunction connectUnixSocket(path, label = path) {\n return new Promise((resolve, reject) => {\n const socket = new Socket;\n let markClosed;\n const closed = new Promise((done) => markClosed = done);\n const listeners = [];\n socket.on(\"data\", (chunk) => {\n for (const listener of listeners)\n listener(new Uint8Array(chunk));\n });\n socket.once(\"close\", () => markClosed());\n socket.once(\"end\", () => markClosed());\n socket.on(\"error\", (error) => {\n markClosed();\n const message = `connecting to ${label} failed: ${error.message}`;\n reject(error.code === \"ENOENT\" || error.code === \"ECONNREFUSED\" ? new SandboxConnectionRefusedError(message) : new Error(message));\n });\n socket.once(\"connect\", () => resolve({\n write: (data) => void socket.write(data),\n onData: (listener) => void listeners.push(listener),\n closed,\n close: () => socket.end()\n }));\n socket.connect({ path });\n });\n}\n\n// lib/jsonDirectory.ts\nvar EXTENSION = \".json\";\nvar NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}$/;\n\nclass InvalidNameError extends Error {\n name = \"InvalidNameError\";\n}\n\nclass NotARecordError extends Error {\n name = \"NotARecordError\";\n}\nfunction parseRecord(path, text) {\n let record;\n try {\n record = JSON.parse(text);\n } catch (error) {\n throw new NotARecordError(`${path} isn't JSON: ${error.message}`);\n }\n if (record === null || typeof record !== \"object\" || Array.isArray(record))\n throw new NotARecordError(`${path} isn't a JSON record (an object)`);\n return record;\n}\nfunction checkName(name) {\n if (!NAME_PATTERN.test(name)) {\n throw new InvalidNameError(`Invalid name \"${name}\": use letters, digits, \"_\", \".\" and \"-\", up to 63 characters`);\n }\n return name;\n}\nfunction createJsonDirectory(sandbox, dir) {\n async function pathFor(name) {\n return `${await dir()}/${checkName(name)}${EXTENSION}`;\n }\n function serialize(record) {\n return JSON.stringify(record, null, 2) + `\n`;\n }\n async function readPath(path) {\n try {\n return parseRecord(path, new TextDecoder().decode(await sandbox.readFile(path)));\n } catch (error) {\n if (error instanceof SandboxNotFoundError)\n return null;\n throw error;\n }\n }\n async function read(name) {\n return readPath(await pathFor(name));\n }\n async function insert(name, record) {\n const path = await pathFor(name);\n if (await sandbox.writeFileExclusive(path, serialize(record)))\n return record;\n return await readPath(path) ?? insert(name, record);\n }\n async function save(name, record) {\n await sandbox.writeFile(await pathFor(name), serialize(record));\n }\n async function remove(name) {\n await sandbox.removeFile(await pathFor(name));\n }\n async function entries() {\n const path = await dir();\n const all = await sandbox.runCommand([\"sh\", \"-c\", LIST_SCRIPT, \"sh\", path], { env: { MISSING: String(MISSING), EXTENSION } }).catch(() => null);\n if (all?.exitCode === MISSING)\n return [];\n if (all?.exitCode === 0) {\n return all.stdout.split(RECORD_END).filter((text) => text.trim()).flatMap((text) => {\n const newline = text.indexOf(`\n`);\n const file = text.slice(0, newline);\n try {\n return [[file.slice(0, -EXTENSION.length), parseRecord(`${path}/${file}`, text.slice(newline + 1))]];\n } catch {\n return [];\n }\n });\n }\n let files;\n try {\n files = await sandbox.listDirectory(path);\n } catch (error) {\n if (error instanceof SandboxNotFoundError)\n return [];\n throw error;\n }\n const records = await Promise.all(files.filter((file) => file.endsWith(EXTENSION)).map(async (file) => [file.slice(0, -EXTENSION.length), await readPath(`${path}/${file}`).catch(skipNotARecord)]));\n return records.filter((entry) => entry[1] !== null);\n }\n async function list() {\n return (await entries()).map(([, record]) => record);\n }\n return { read, insert, save, remove, list, entries };\n}\nvar MISSING = 44;\nvar RECORD_END = `\n\\x1E\n`;\nvar LIST_SCRIPT = sh`\ncd \"$1\" 2>/dev/null || exit \"$MISSING\"\nfor file in *\"$EXTENSION\"; do\n [ -f \"$file\" ] || continue\n printf '%s\\n' \"$file\"\n cat \"$file\"\n printf '\\n\\036\\n'\ndone\n`;\nfunction skipNotARecord(error) {\n if (error instanceof NotARecordError)\n return null;\n throw error;\n}\n\n// sandbox/storage/sandboxStorage.ts\nvar DESCRIPTOR = \"sandbox\";\nfunction createSandboxStorage(sandbox) {\n const stateDirectory = async () => `${await sandbox.homeDirectory()}/.local/state/hyperbrane`;\n const endpointsDirectory = async () => `${await stateDirectory()}/endpoints`;\n const descriptors = createJsonDirectory(sandbox, stateDirectory);\n const endpoints = createJsonDirectory(sandbox, endpointsDirectory);\n async function readDescriptor() {\n return descriptors.read(DESCRIPTOR);\n }\n async function insertDescriptor(descriptor) {\n return descriptors.insert(DESCRIPTOR, descriptor);\n }\n async function saveDescriptor(descriptor) {\n await descriptors.save(DESCRIPTOR, descriptor);\n }\n async function readEndpoint(name) {\n return endpoints.read(name);\n }\n async function saveEndpoint(endpoint) {\n const directory = await endpointsDirectory();\n const made = await sandbox.runCommand([\"sh\", \"-c\", 'mkdir -p \"$1\" && chmod 700 \"$1\"', \"sh\", directory]);\n if (made.exitCode !== 0)\n throw new Error(`making ${directory} private failed: ${made.stderr.trim()}`);\n await endpoints.save(endpoint.name, endpoint);\n }\n async function removeEndpoint(name) {\n await endpoints.remove(name);\n }\n async function listEndpoints() {\n return endpoints.list();\n }\n return {\n readDescriptor,\n insertDescriptor,\n saveDescriptor,\n readEndpoint,\n saveEndpoint,\n removeEndpoint,\n listEndpoints\n };\n}\n\n// sandbox/providers/local/localService.ts\nfunction hostPath(path) {\n return resolve(\"/\", path);\n}\nasync function notFoundAs(path, operation) {\n try {\n return await operation;\n } catch (error) {\n if (error.code === \"ENOENT\")\n throw new SandboxNotFoundError(`Not found: \"${path}\"`);\n throw error;\n }\n}\nasync function writeTemp(full, data, mode = 438) {\n await fs.mkdir(dirname(full), { recursive: true });\n const tmp = join(dirname(full), `.${basename(full)}.${randomUUID()}.tmp`);\n await fs.writeFile(tmp, data, { mode });\n return tmp;\n}\nasync function readFile2(path) {\n return new Uint8Array(await notFoundAs(path, fs.readFile(hostPath(path))));\n}\nasync function readFileRange(path, start, end) {\n const file = await notFoundAs(path, fs.open(hostPath(path), \"r\"));\n try {\n const buffer = new Uint8Array(Math.max(0, end - start));\n let filled = 0;\n while (filled < buffer.length) {\n const { bytesRead } = await file.read(buffer, filled, buffer.length - filled, start + filled);\n if (bytesRead === 0)\n break;\n filled += bytesRead;\n }\n return buffer.subarray(0, filled);\n } finally {\n await file.close();\n }\n}\nasync function writeFile2(path, data) {\n const full = hostPath(path);\n await fs.rename(await writeTemp(full, data), full);\n}\nasync function writeFileExclusive(path, data) {\n const full = hostPath(path);\n const tmp = await writeTemp(full, data);\n try {\n await fs.link(tmp, full);\n return true;\n } catch (error) {\n if (error.code === \"EEXIST\")\n return false;\n throw error;\n } finally {\n await fs.unlink(tmp);\n }\n}\nasync function removeFile(path) {\n await fs.rm(hostPath(path), { force: true });\n}\nasync function createDirectory(path) {\n await fs.mkdir(hostPath(path), { recursive: true });\n}\nasync function removeDirectory(path) {\n await fs.rm(hostPath(path), { recursive: true, force: true });\n}\nasync function listDirectory(path) {\n return notFoundAs(path, fs.readdir(hostPath(path)));\n}\nasync function stat2(path) {\n const stats = await notFoundAs(path, fs.stat(hostPath(path)));\n const type = stats.isFile() ? \"file\" : stats.isDirectory() ? \"directory\" : \"other\";\n return { type, uid: stats.uid, gid: stats.gid, size: stats.size };\n}\nasync function homeDirectory() {\n return process.env.HOME || homedir();\n}\nfunction runCommand(cmd, { cwd, env = {}, stdin } = {}) {\n return new Promise((resolve, reject) => {\n const [command, ...args] = cmd;\n if (!command)\n return reject(new Error(\"no command to run\"));\n const child = spawn(command, args, {\n cwd: hostPath(cwd ?? \".\"),\n env: { ...process.env, ...env },\n stdio: [stdin === undefined ? \"ignore\" : \"pipe\", \"pipe\", \"pipe\"]\n });\n const stdout = [];\n const stderr = [];\n child.stdout.on(\"data\", (chunk) => stdout.push(chunk));\n child.stderr.on(\"data\", (chunk) => stderr.push(chunk));\n child.once(\"error\", reject);\n child.once(\"close\", (code) => resolve({ exitCode: code ?? -1, stdout: Buffer.concat(stdout).toString(), stderr: Buffer.concat(stderr).toString() }));\n if (stdin !== undefined)\n child.stdin.end(stdin);\n });\n}\nasync function state() {\n return \"running\";\n}\nasync function start() {}\nasync function destroy() {}\nfunction hasExpiration() {\n return false;\n}\nasync function getExpiration() {\n return null;\n}\nasync function setExpiration() {\n return null;\n}\nfunction hasShell(ctx) {\n return ctx.sshKey !== undefined;\n}\nasync function sshEndpoint(ctx) {\n if (ctx.sshKey === undefined)\n throw new Error(\"this machine has no shell: its context has no SSH key (sshKey)\");\n return {\n host: ctx.sshHost ?? \"127.0.0.1\",\n port: ctx.sshPort ?? 22,\n user: ctx.sshUser ?? userInfo().username,\n privateKey: ctx.sshKey\n };\n}\nasync function fetch(url, { unix, ...init } = {}) {\n if (unix === undefined || \"Bun\" in globalThis)\n return globalThis.fetch(url, { ...init, unix: unix === undefined ? undefined : hostPath(unix) });\n return httpOver(url, () => netConnect(hostPath(unix)), init);\n}\nfunction connect(path) {\n return connectUnixSocket(hostPath(path), path);\n}\nfunction createLocalService(ctx) {\n const fileOps = {\n readFile: readFile2,\n readFileRange,\n writeFile: writeFile2,\n writeFileExclusive,\n removeFile,\n createDirectory,\n removeDirectory,\n listDirectory,\n stat: stat2,\n homeDirectory\n };\n const cmdOps = {\n runCommand\n };\n const netOps = {\n fetch,\n connect\n };\n const machine = { ...fileOps, ...cmdOps, ...netOps };\n const storage = createSandboxStorage(machine);\n const lifecycleOps = {\n create: (spec) => createDescriptor(ctx, storage, ctx.id ?? spec.name, spec),\n open: () => openDescriptor(ctx, storage, ctx.id ?? \"this machine\"),\n get: () => getDescriptor(storage, \"this machine\"),\n state,\n start,\n destroy\n };\n const dataOps = createEndpoints(machine, storage, firewalledPorts(machine, storage));\n const expirationOps = {\n hasExpiration,\n getExpiration,\n setExpiration\n };\n const shellOps = {\n hasShell: () => hasShell(ctx),\n sshEndpoint: () => sshEndpoint(ctx)\n };\n return {\n ...fileOps,\n ...lifecycleOps,\n ...cmdOps,\n ...dataOps,\n ...expirationOps,\n ...shellOps,\n ...netOps\n };\n}\n\n// agent/session/claude/auth.ts\nimport { readFile as readFile3 } from \"fs/promises\";\nimport { join as join2 } from \"path\";\n\n// agent/session/login.ts\nfunction plain(text) {\n return text.replace(/\\x1b\\][^\\x07\\x1b]*(\\x07|\\x1b\\\\)/g, \"\").replace(/\\x1b\\[[0-9;?]*[a-zA-Z]/g, \"\");\n}\nfunction spawnLogin(cmd, { env, cwd }, parse) {\n const proc = Bun.spawn(cmd, { env, cwd, stdin: \"pipe\", stdout: \"pipe\", stderr: \"pipe\" });\n let output = \"\";\n let showPrompt;\n let noPrompt;\n const prompt = new Promise((resolve, reject) => {\n showPrompt = resolve;\n noPrompt = reject;\n });\n async function read(stream) {\n const decoder = new TextDecoder;\n for await (const chunk of stream) {\n output += plain(decoder.decode(chunk, { stream: true }));\n const found = parse(output);\n if (found)\n showPrompt(found);\n }\n }\n const reading = Promise.all([read(proc.stdout), read(proc.stderr)]);\n const done = reading.then(() => proc.exited).then((code) => {\n const tail = output.trim().split(`\n`).slice(-3).join(\" \").trim();\n noPrompt(new Error(`the login ended before showing where to sign in: ${tail}`));\n if (code !== 0)\n throw new Error(`the login failed (exit ${code}): ${tail}`);\n });\n prompt.catch(() => {});\n done.catch(() => {});\n return {\n prompt,\n done,\n submitCode(code) {\n proc.stdin.write(`${code}\n`);\n proc.stdin.flush();\n },\n cancel() {\n proc.kill();\n }\n };\n}\n\n// agent/session/claude/provision.ts\nimport { existsSync as existsSync2 } from \"fs\";\n\n// agent/session/install.ts\nimport { existsSync } from \"fs\";\n// agent/vendor-types/manifest.json\nvar manifest_default = {\n claude: {\n docs: \"https://code.claude.com/docs/en/agent-sdk/typescript\",\n sources: [\n {\n source: \"npm:@anthropic-ai/claude-agent-sdk@0.3.310\",\n into: \"agent-sdk\",\n files: 7\n }\n ]\n },\n codex: {\n docs: \"https://developers.openai.com/codex/app-server\",\n sources: [\n {\n source: \"npm:@openai/codex-sdk@0.164.0\",\n into: \"sdk\",\n files: 1\n },\n {\n source: \"github:openai/codex@rust-v0.164.0/codex-rs/app-server-protocol/schema/typescript\",\n into: \"app-server\",\n files: 751\n },\n {\n source: \"github:openai/codex@rust-v0.164.0/codex-rs/app-server-protocol/schema/json\",\n into: \"app-server-json\",\n files: 2\n }\n ]\n }\n};\n\n// agent/session/install.ts\nfunction pinnedVersion(vendor, pkg, sourcePackage = pkg) {\n const prefix = `npm:${sourcePackage}@`;\n const source = manifest_default[vendor].sources.find((entry) => entry.source.startsWith(prefix));\n if (!source)\n throw new Error(`agent/vendor-types/manifest.json has no ${sourcePackage} for ${vendor} (needed for ${pkg})`);\n return source.source.slice(prefix.length);\n}\nasync function installedPackage(installDir, pkg, version, executable) {\n const manifest = Bun.file(`${installDir}/node_modules/${pkg}/package.json`);\n const found = await manifest.exists() ? (await manifest.json()).version ?? null : null;\n return { version: found, ready: found !== null && existsSync(executable()), pinned: version };\n}\nasync function installPackage(installDir, pkg, version, executable) {\n const installed = await installedPackage(installDir, pkg, version, executable);\n if (installed.ready && installed.version === version)\n return;\n const force = installed.version === version ? [\"--force\"] : [];\n const proc = Bun.spawn([\"flock\", `${installDir}/.install.lock`, process.execPath, \"add\", \"--exact\", ...force, `${pkg}@${version}`], {\n cwd: installDir,\n stdin: \"ignore\",\n stdout: \"pipe\",\n stderr: \"pipe\"\n });\n const [stdout, stderr, exitCode] = await Promise.all([\n new Response(proc.stdout).text(),\n new Response(proc.stderr).text(),\n proc.exited\n ]);\n if (exitCode !== 0)\n throw new Error(`bun add ${pkg}@${version} failed (exit ${exitCode}): ${(stderr || stdout).trim()}`);\n if (!existsSync(executable()))\n throw new Error(`bun add ${pkg}@${version} left no ${executable()}: its platform package didn't install`);\n}\n\n// agent/session/claude/provision.ts\nvar CLAUDE_SDK_PACKAGE = \"@anthropic-ai/claude-agent-sdk\";\nfunction installClaude(installDir) {\n return installPackage(installDir, CLAUDE_SDK_PACKAGE, pinnedVersion(\"claude\", CLAUDE_SDK_PACKAGE), () => claudeExecutable(installDir));\n}\nfunction installedClaude(installDir) {\n return installedPackage(installDir, CLAUDE_SDK_PACKAGE, pinnedVersion(\"claude\", CLAUDE_SDK_PACKAGE), () => claudeExecutable(installDir));\n}\nfunction claudeExecutable(installDir) {\n const base = `${installDir}/node_modules/${CLAUDE_SDK_PACKAGE}-linux-${process.arch}`;\n const glibc = `${base}/claude`;\n return existsSync2(glibc) ? glibc : `${base}-musl/claude`;\n}\n\n// agent/session/claude/auth.ts\nvar GATEWAY_CODE = \"x#x\";\nvar CREDENTIAL_VARIABLES = [\"ANTHROPIC_API_KEY\", \"ANTHROPIC_AUTH_TOKEN\", \"CLAUDE_CODE_OAUTH_TOKEN\"];\nvar claudeAuth = {\n async signedIn({ env }) {\n if (CREDENTIAL_VARIABLES.some((name) => env[name]))\n return true;\n const config = env.CLAUDE_CONFIG_DIR ?? join2(env.HOME ?? \"\", \".claude\");\n const credentials = await readJson(join2(config, \".credentials.json\"));\n if (credentials?.claudeAiOauth?.accessToken)\n return true;\n const settings = await readJson(join2(config, \".claude.json\"));\n return Boolean(settings?.primaryApiKey);\n },\n login({ installDir, env, cwd }, gatewaySignedIn) {\n const login = spawnLogin([claudeExecutable(installDir), \"auth\", \"login\"], { env, cwd }, (output) => {\n const url = /https:\\/\\/\\S*oauth\\/authorize\\S*/.exec(output)?.[0];\n return url ? { url, needsCode: true } : null;\n });\n if (gatewaySignedIn)\n login.prompt.then(() => login.submitCode(GATEWAY_CODE), () => {});\n return login;\n }\n};\nasync function readJson(path) {\n try {\n return JSON.parse(await readFile3(path, \"utf8\"));\n } catch {\n return null;\n }\n}\n\n// agent/api.ts\nvar AGENT_BACKENDS = [\"claude\", \"codex\"];\nvar AGENT_PERMISSION_MODES = [\"ask\", \"auto\"];\n\nclass AgentInvalidError extends Error {\n name = \"AgentInvalidError\";\n}\n\nclass AgentNotFoundError extends Error {\n name = \"AgentNotFoundError\";\n}\n\nclass AgentConflictError extends Error {\n name = \"AgentConflictError\";\n}\n\nclass AgentBackendNotInstalledError extends AgentConflictError {\n name = \"AgentBackendNotInstalledError\";\n}\n\n// agent/session/claude/backgroundTasks.ts\nfunction claudeBackgroundTasks() {\n let tasks = [];\n return {\n observe(message) {\n if (message.type !== \"system\")\n return;\n if (message.subtype === \"background_tasks_changed\")\n tasks = message.tasks;\n if (message.subtype === \"task_notification\")\n tasks = tasks.filter((task) => task.task_id !== message.task_id);\n },\n list: async () => tasks\n };\n}\nfunction claudeStopTask(query, tasks) {\n return async (taskId) => {\n if (!(await tasks.list()).some((task) => task.task_id === taskId))\n throw new AgentNotFoundError(`no background task ${taskId}`);\n await query.stopTask(taskId);\n };\n}\n\n// agent/session/claude/home.ts\nfunction claudeHomeEnvironment(home) {\n return { CLAUDE_CONFIG_DIR: home };\n}\n\n// agent/session/claude/input.ts\nfunction createPromptQueue() {\n const queued = [];\n let wake;\n let closed = false;\n function push(message) {\n queued.push(message);\n wake?.();\n }\n function close() {\n closed = true;\n wake?.();\n }\n async function* iterate() {\n for (;; ) {\n while (queued.length > 0)\n yield queued.shift();\n if (closed)\n return;\n await new Promise((resolve) => wake = resolve);\n wake = undefined;\n }\n }\n return { push, close, [Symbol.asyncIterator]: iterate };\n}\nfunction claudeInput(prompts, queue) {\n return async (message) => {\n if (!message.uuid)\n throw new Error(\"a message sent to claude needs a uuid to be acknowledged by\");\n const received = queue.sent({ ...message, uuid: message.uuid });\n prompts.push(message);\n await received;\n };\n}\n\n// agent/session/claude/messageQueue.ts\nvar RECEIVE_TIMEOUT_MS = 60000;\nfunction claudeMessageQueue() {\n const commands = new Map;\n let taken = null;\n const receipts = new Map;\n function observe(message) {\n const event = message;\n if (event.type !== \"command_lifecycle\" || !event.command_uuid || !event.state)\n return;\n const command = commands.get(event.command_uuid);\n if (command) {\n command.state = event.state;\n if (event.state === \"started\")\n taken = command.message;\n if (event.state !== \"queued\")\n commands.delete(event.command_uuid);\n }\n const receipt = receipts.get(event.command_uuid);\n if (!receipt)\n return;\n receipts.delete(event.command_uuid);\n if (event.state === \"queued\" || event.state === \"started\")\n receipt.resolve();\n else\n receipt.reject(new Error(`claude ${event.state} the message`));\n }\n function sent(message) {\n commands.set(message.uuid, { message, state: null });\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n receipts.delete(message.uuid);\n commands.delete(message.uuid);\n reject(new Error(\"claude didn't confirm it received the message\"));\n }, RECEIVE_TIMEOUT_MS);\n receipts.set(message.uuid, {\n resolve: () => (clearTimeout(timer), resolve()),\n reject: (error) => (clearTimeout(timer), commands.delete(message.uuid), reject(error))\n });\n });\n }\n async function lastUserMessages() {\n const queued = [...commands.values()].filter((command) => command.state === \"queued\");\n return [...taken ? [{ message: taken, queued: false }] : [], ...queued.map((command) => ({ message: command.message, queued: true }))];\n }\n return { observe, sent, lastUserMessages };\n}\n\n// agent/session/claude/status.ts\nvar SESSION_STATE_EVENTS = { CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS: \"1\" };\nfunction claudeStatus(changed) {\n let active = false;\n return {\n observe(message) {\n if (message.type !== \"system\" || message.subtype !== \"session_state_changed\")\n return;\n const next = message.state !== \"idle\";\n if (next === active)\n return;\n active = next;\n changed();\n },\n active: () => active\n };\n}\n\n// agent/session/claude/interrupt.ts\nfunction claudeInterrupt(query) {\n return async () => {\n await query.interrupt();\n };\n}\n\n// agent/session/claude/model.ts\nfunction claudeModel(query) {\n return {\n listModels: () => query.supportedModels(),\n setModel: (model) => query.setModel(model ?? undefined)\n };\n}\n\n// agent/session/sdkMessages.ts\nvar ASK_USER_QUESTION = \"AskUserQuestion\";\nfunction resultUsage({ input = 0, output = 0, cacheRead = 0, cacheWrite = 0, thinking = 0 } = {}) {\n return {\n input_tokens: input,\n output_tokens: output,\n output_tokens_details: { thinking_tokens: thinking },\n cache_read_input_tokens: cacheRead,\n cache_creation_input_tokens: cacheWrite,\n cache_creation: { ephemeral_1h_input_tokens: 0, ephemeral_5m_input_tokens: 0 },\n server_tool_use: { web_fetch_requests: 0, web_search_requests: 0 },\n service_tier: \"standard\",\n fallback_credit: { status: { type: \"not_applied\", reason: \"not_enabled\" } },\n inference_geo: \"\",\n iterations: [],\n speed: \"standard\"\n };\n}\nfunction userMessageText(message) {\n const content = message.message.content;\n if (typeof content === \"string\")\n return content;\n if (!Array.isArray(content))\n return \"\";\n return content.map((block) => block.type === \"text\" && typeof block.text === \"string\" ? block.text : \"\").filter(Boolean).join(`\n`);\n}\n\n// agent/session/claude/permissions.ts\nfunction claudePermissionMode(mode) {\n return mode === \"auto\" ? \"bypassPermissions\" : \"default\";\n}\nfunction claudePermissions(context, questions) {\n let mode = context.permissionMode;\n const canUseTool = (toolName, input, options) => {\n if (toolName === ASK_USER_QUESTION)\n return questions.answer(input, options);\n const { signal, toolUseID, decisionReason } = options;\n return context.requestPermission({ tool_name: toolName, input, tool_use_id: toolUseID, decision_reason: decisionReason }, signal);\n };\n const preToolUse = [\n {\n hooks: [async () => mode === \"auto\" ? {} : { hookSpecificOutput: { hookEventName: \"PreToolUse\", permissionDecision: \"ask\" } }]\n }\n ];\n async function setMode(query, next) {\n mode = next;\n await query.setPermissionMode(claudePermissionMode(next));\n }\n return { canUseTool, preToolUse, mode: () => mode, setMode };\n}\n\n// agent/session/claude/questions.ts\nvar QUESTION_DIALOG_KINDS = [\"permission_ask_user_question\", \"ask_user_question\", \"user_question\"];\nfunction claudeQuestions(context) {\n async function ask(input, toolUseId, signal) {\n const questions = Array.isArray(input.questions) ? input.questions : [];\n const response = await context.askQuestions({ tool_use_id: toolUseId, questions }, signal);\n if (!response)\n return { behavior: \"deny\", message: \"The user dismissed the questions.\" };\n return { behavior: \"allow\", updatedInput: { ...input, answers: response.answers } };\n }\n const onUserDialog = async (request, { signal }) => {\n if (!QUESTION_DIALOG_KINDS.includes(request.dialogKind))\n return { behavior: \"cancelled\" };\n const result = await ask(request.payload, request.toolUseID ?? crypto.randomUUID(), signal);\n if (result.behavior !== \"allow\")\n return { behavior: \"cancelled\" };\n return { behavior: \"completed\", result };\n };\n return {\n answer: (input, { signal, toolUseID }) => ask(input, toolUseID, signal),\n onUserDialog,\n supportedDialogKinds: QUESTION_DIALOG_KINDS\n };\n}\n\n// agent/session/claude/stream.ts\nimport { spawn as spawn2 } from \"child_process\";\n\n// agent/session/claude/sdk.ts\nasync function loadClaudeSdk(installDir) {\n return await import(Bun.resolveSync(CLAUDE_SDK_PACKAGE, installDir));\n}\n\n// agent/session/claude/stream.ts\nasync function startClaudeStream(context, prompts, permissions, questions, backgroundTasks, status, queue) {\n const sdk = await loadClaudeSdk(context.installDir);\n let cli = null;\n const query = sdk.query({\n prompt: prompts,\n options: {\n cwd: context.cwd,\n env: { ...context.env, ...SESSION_STATE_EVENTS },\n model: context.model ?? undefined,\n resume: context.nativeId ?? undefined,\n includePartialMessages: true,\n permissionMode: claudePermissionMode(permissions.mode()),\n allowDangerouslySkipPermissions: true,\n settingSources: [],\n canUseTool: permissions.canUseTool,\n hooks: { PreToolUse: permissions.preToolUse },\n onUserDialog: questions.onUserDialog,\n supportedDialogKinds: questions.supportedDialogKinds,\n spawnClaudeCodeProcess: (options) => {\n cli = spawnCli(options, context.spec.name);\n return cli.process;\n }\n }\n });\n const exit = async () => {\n query.close();\n return cli ? cli.exit : null;\n };\n const subagents = {\n observe(message) {\n if (message.type === \"system\" && message.subtype === \"task_started\" && message.tool_use_id && message.subagent_type) {\n context.subagentStarted(message.tool_use_id, message.task_id);\n }\n }\n };\n pump(context, query, [backgroundTasks, status, queue, subagents]).then(async () => context.exited(await exit()), async (error) => context.exited(await exit(), error));\n return query;\n}\nfunction spawnCli({ command, args, cwd, env, signal }, sessionName) {\n const child = spawn2(command, args, { cwd, env, signal, stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n child.stderr.on(\"data\", (data) => process.stderr.write(`[claude ${sessionName}] ${data}`));\n const exit = new Promise((resolve) => {\n child.once(\"exit\", (code, signal) => resolve({ code, signal }));\n child.once(\"error\", () => {\n if (child.pid === undefined)\n resolve(null);\n });\n });\n return { process: child, exit };\n}\nasync function pump(context, query, observers) {\n for await (const message of query) {\n for (const observer of observers)\n observer.observe(message);\n if (\"session_id\" in message && typeof message.session_id === \"string\" && message.session_id) {\n context.setNativeId(message.session_id);\n }\n const streamed = toStreamingMessage(message);\n if (streamed)\n context.emit(streamed);\n }\n}\nfunction toStreamingMessage(message) {\n switch (message.type) {\n case \"assistant\":\n case \"stream_event\":\n case \"result\":\n return message;\n case \"user\":\n return { ...message, uuid: message.uuid ?? crypto.randomUUID(), session_id: message.session_id ?? \"\" };\n case \"system\":\n if (message.subtype === \"compact_boundary\")\n return { ...message, timestamp: new Date().toISOString() };\n return message.subtype === \"init\" ? message : null;\n default:\n return null;\n }\n}\n\n// agent/session/fileTranscript.ts\nfunction sandboxFiles(sandbox) {\n return {\n size: async (path) => (await sandbox.stat(path)).size,\n readFileRange: (path, start, end) => sandbox.readFileRange(path, start, end),\n listDirectory: (path) => sandbox.listDirectory(path)\n };\n}\nfunction locateTranscript(files, home, layout, nativeId) {\n const escape = (text) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const [before = \"\", after = \"\"] = layout.name.split(\"{id}\").map((part) => part.split(\"*\").map(escape).join(\".*\"));\n const name = new RegExp(`^${before}${escape(nativeId)}${after}$`);\n return findFile(files, `${home}/${layout.dir}`, layout.depth, (entry) => name.test(entry));\n}\nvar CHUNK_BYTES = 64 * 1024;\nvar TIME_SLACK_MS = 60000;\nvar PROBE_LINES = 3;\nvar PROBE_BYTES = 4 * CHUNK_BYTES;\nvar NEWLINE = 10;\nfunction lineTime(entry, field) {\n const value = entry?.[field];\n const time = typeof value === \"string\" ? Date.parse(value) : NaN;\n return Number.isNaN(time) ? null : time;\n}\nvar SCAN_BYTES = 1024 * 1024;\nvar searchedCopies = new Map;\nvar SEARCHED_FILES = 16;\nfunction createFileTranscript(files, locate, lineMessages, layout) {\n function parse(bytes, start) {\n try {\n const entry = JSON.parse(new TextDecoder().decode(bytes));\n return { start, time: lineTime(entry, layout.time), messages: lineMessages(entry) };\n } catch {\n return { start, time: null, messages: [] };\n }\n }\n function kept(from, to, skip) {\n const parts = [];\n let start = from;\n for (const stretch of [...skip].sort((a, b) => a.start - b.start)) {\n if (stretch.end <= start || stretch.start >= to)\n continue;\n if (stretch.start > start)\n parts.push({ start, end: stretch.start });\n start = Math.max(start, stretch.end);\n }\n if (start < to)\n parts.push({ start, end: to });\n return parts;\n }\n async function* backward(path, end, skip = []) {\n for (const part of kept(0, end, skip).reverse())\n yield* backwardIn(path, part.start, part.end);\n }\n async function* backwardIn(path, floor, end) {\n let position = end;\n let carry = new Uint8Array(0);\n while (position > floor) {\n const from = Math.max(floor, position - CHUNK_BYTES);\n const chunk = await files.readFileRange(path, from, position);\n const bytes = joined(chunk, carry);\n let lineEnd = bytes.length;\n for (let index = bytes.length - 1;index >= 0; index--) {\n if (bytes[index] !== NEWLINE)\n continue;\n if (lineEnd > index + 1)\n yield parse(bytes.subarray(index + 1, lineEnd), from + index + 1);\n lineEnd = index;\n }\n carry = bytes.slice(0, lineEnd);\n position = from;\n }\n if (carry.length > 0)\n yield parse(carry, floor);\n }\n async function* forward(path, start, end, skip = []) {\n for (const part of kept(start, end, skip))\n yield* forwardIn(path, part.start, part.end);\n }\n async function* forwardIn(path, start, end) {\n let position = start;\n let carry = new Uint8Array(0);\n let carryStart = start;\n while (position < end) {\n const to = Math.min(end, position + CHUNK_BYTES);\n const bytes = joined(carry, await files.readFileRange(path, position, to));\n let lineStart = 0;\n for (let index = 0;index < bytes.length; index++) {\n if (bytes[index] !== NEWLINE)\n continue;\n if (index > lineStart)\n yield parse(bytes.subarray(lineStart, index), carryStart + lineStart);\n lineStart = index + 1;\n }\n carry = bytes.slice(lineStart);\n carryStart += lineStart;\n position = to;\n }\n if (carry.length > 0)\n yield parse(carry, carryStart);\n }\n async function seek(path, low, high, target, skip) {\n return (await narrow(path, low, high, target, skip)).high;\n }\n async function narrow(path, low, high, target, skip) {\n while (high - low > CHUNK_BYTES) {\n let middle = Math.floor((low + high) / 2);\n const inside = skip.find((stretch) => stretch.start <= middle && middle < stretch.end);\n if (inside) {\n if (inside.end >= high) {\n high = Math.max(low, inside.start);\n continue;\n }\n middle = inside.end;\n }\n const probe = await probeTime(path, middle, high, skip);\n if (!probe)\n break;\n if (probe.time >= target)\n high = probe.start;\n else\n low = probe.start;\n }\n return { low, high };\n }\n async function probeTime(path, from, limit, skip) {\n let start = -1;\n let time = Infinity;\n let timed = 0;\n for await (const line of forward(path, from, Math.min(limit, from + PROBE_BYTES), skip)) {\n if (line.start === from && from > 0)\n continue;\n if (line.start >= limit)\n break;\n if (start < 0)\n start = line.start;\n if (line.time === null)\n continue;\n time = Math.min(time, line.time);\n if (++timed === PROBE_LINES)\n break;\n }\n return start < 0 || timed === 0 ? null : { start, time };\n }\n async function timeAt(path, from, size, skip) {\n for await (const line of forward(path, from, Math.min(size, from + PROBE_BYTES), skip))\n if (line.time !== null)\n return line.time;\n return null;\n }\n async function lineStart(path, at) {\n for await (const line of backward(path, at + 1))\n return line.start;\n return 0;\n }\n function copiesOf(path, size) {\n if (!layout.copies)\n return Promise.resolve([]);\n const key = `${path}\\x00${size}`;\n let copies = searchedCopies.get(key);\n if (!copies) {\n copies = findCopies(path, size, layout.copies);\n copies.catch(() => searchedCopies.delete(key));\n searchedCopies.set(key, copies);\n if (searchedCopies.size > SEARCHED_FILES)\n searchedCopies.delete(searchedCopies.keys().next().value);\n }\n return copies;\n }\n async function findCopies(path, size, marker) {\n const needle = Buffer.from(marker);\n const found = [];\n for (let position = 0;position < size; position += SCAN_BYTES) {\n const from = Math.max(0, position - needle.length + 1);\n const bytes = Buffer.from(await files.readFileRange(path, from, Math.min(size, position + SCAN_BYTES)));\n for (let index = bytes.indexOf(needle);index >= 0; index = bytes.indexOf(needle, index + 1)) {\n if (from + index >= position || position === 0)\n found.push(from + index);\n }\n }\n const copies = [];\n for (const [index, at] of found.entries()) {\n const start = await lineStart(path, at);\n let earlier = null;\n for await (const before of backward(path, start)) {\n if (before.time === null)\n continue;\n earlier = before.time;\n break;\n }\n if (earlier === null || copies.some((copy) => copy.start <= start && start < copy.end))\n continue;\n let time = null;\n for await (const line of forward(path, start, size)) {\n time = line.time;\n break;\n }\n if (time === null || time >= earlier)\n continue;\n const next = found[index + 1];\n const near = await narrow(path, start + 1, next === undefined ? size : await lineStart(path, next), earlier + 1, []);\n let end = near.high;\n for await (const line of forward(path, near.low, near.high)) {\n if (line.start <= start || line.time === null || line.time <= earlier)\n continue;\n end = line.start;\n break;\n }\n copies.push({ start, end });\n }\n return copies;\n }\n async function* pagedBackward(path, end, after, skip) {\n let next = after;\n let waiting = [];\n const settle = function* (before) {\n const [timed, ...untimed] = waiting;\n if (!timed)\n return;\n const own = timed.time;\n const late = before !== null && own < before - TIME_SLACK_MS && (next === null || own < next - TIME_SLACK_MS);\n const ahead = before !== null && own > before + TIME_SLACK_MS && (next === null || own > next + TIME_SLACK_MS);\n const time = late || ahead ? before : own;\n yield { ...timed, time };\n for (const line of untimed)\n yield { ...line, time };\n next = own;\n waiting = [];\n };\n for await (const line of backward(path, end, skip)) {\n if (line.time === null) {\n if (waiting.length === 0)\n yield { ...line, time: next ?? Infinity };\n else\n waiting.push(line);\n continue;\n }\n yield* settle(line.time);\n waiting = [line];\n }\n yield* settle(null);\n }\n async function page(before, limit, found) {\n const path = await locate();\n if (!path)\n return { messages: [], nextBefore: null };\n const size = await files.size(path);\n const copies = found?.copies ?? await copiesOf(path, size);\n const from = Math.min(found?.end ?? (before === null ? size : await seek(path, 0, size, before + TIME_SLACK_MS, copies)), size);\n const after = found ? found.after : from < size ? await timeAt(path, from, size, copies) : null;\n const taken = [];\n let count = 0;\n let oldest = Infinity;\n let older = false;\n for await (const line of pagedBackward(path, from, after, copies)) {\n if (count >= limit && line.time < oldest - TIME_SLACK_MS) {\n older = true;\n break;\n }\n if (line.messages.length === 0 || before !== null && !(line.time < before))\n continue;\n taken.push(line);\n count += line.messages.length;\n if (count >= limit)\n oldest = oldestOnPage(taken, limit);\n }\n const kept = count >= limit ? taken.filter((line) => line.time >= oldest) : taken;\n if (kept.length < taken.length)\n older = true;\n return {\n messages: kept.sort((a, b) => a.start - b.start).flatMap((line) => line.messages),\n nextBefore: older && Number.isFinite(oldest) ? new Date(oldest).toISOString() : null\n };\n }\n async function replay(afterId, emit) {\n const path = await locate();\n if (!path)\n return afterId === undefined;\n const end = await files.size(path);\n const copies = await copiesOf(path, end);\n let start = 0;\n if (afterId !== undefined) {\n let found = null;\n for await (const line of backward(path, end, copies)) {\n if (line.messages.some((message) => message.uuid === afterId)) {\n found = line;\n break;\n }\n }\n if (!found)\n return false;\n start = found.start;\n }\n let passed = afterId === undefined;\n for await (const line of forward(path, start, end, copies)) {\n for (const message of line.messages) {\n if (passed)\n emit(message);\n else if (message.uuid === afterId)\n passed = true;\n }\n }\n return true;\n }\n async function find(match) {\n const path = await locate();\n if (!path)\n return null;\n const end = await files.size(path);\n for await (const line of backward(path, end, await copiesOf(path, end))) {\n const found = line.messages.find(match);\n if (found)\n return found;\n }\n return null;\n }\n return { page, replay, find };\n}\nfunction oldestOnPage(lines, limit) {\n const newestFirst = [...lines].sort((a, b) => b.time - a.time);\n let count = 0;\n for (const line of newestFirst) {\n count += line.messages.length;\n if (count >= limit)\n return line.time;\n }\n return newestFirst[newestFirst.length - 1]?.time ?? Infinity;\n}\nfunction joined(first, second) {\n if (first.length === 0)\n return second;\n if (second.length === 0)\n return first;\n const bytes = new Uint8Array(first.length + second.length);\n bytes.set(first);\n bytes.set(second, first.length);\n return bytes;\n}\nvar DEFAULT_PAGE_SIZE = 50;\nvar MAX_PAGE_SIZE = 500;\nasync function transcriptPage(transcript, { limit, before, cursor } = {}, found) {\n const time = transcriptTime(before ?? cursor);\n if (!transcript)\n return { messages: [], nextBefore: null, nextCursor: null };\n const size = Math.min(Math.max(Math.floor(limit ?? DEFAULT_PAGE_SIZE), 1), MAX_PAGE_SIZE);\n const page = await transcript.page(time, size, found);\n return { ...page, nextCursor: page.nextBefore };\n}\nfunction transcriptTime(before) {\n if (before === undefined)\n return null;\n const time = Date.parse(before);\n if (Number.isNaN(time))\n throw new AgentInvalidError(`invalid time ${before}: give an ISO 8601 time`);\n return time;\n}\nasync function directoryNames(files, dir) {\n try {\n return await files.listDirectory(dir);\n } catch (error) {\n if (error instanceof SandboxNotFoundError)\n return null;\n throw error;\n }\n}\nasync function findFile(files, dir, depth, matches) {\n const names = await directoryNames(files, dir);\n if (!names)\n return null;\n if (depth <= 1) {\n const name = names.find(matches);\n return name === undefined ? null : `${dir}/${name}`;\n }\n const found = await Promise.all(names.sort().reverse().map((name) => findFile(files, `${dir}/${name}`, depth - 1, matches).catch(() => null)));\n return found.find((path) => path !== null) ?? null;\n}\n\n// agent/session/claude/transcript.ts\nfunction claudeTranscript({ nativeId, env, files }) {\n return createFileTranscript(files, () => locateTranscript(files, claudeHome(env), CLAUDE_TRANSCRIPT_LAYOUT, nativeId), claudeLineMessages, CLAUDE_TRANSCRIPT_LAYOUT);\n}\nfunction claudeSubagentTranscript(source, agentId, toolUseId) {\n return subagentFile(source, `agent-${agentId}.jsonl`, toolUseId);\n}\nasync function claudeFindSubagent(source, toolUseId) {\n const isResult = (message) => message.type === \"user\" && Array.isArray(message.message.content) && message.message.content.some((block) => block.type === \"tool_result\" && block.tool_use_id === toolUseId);\n let found = await claudeTranscript(source).find(isResult);\n const dir = found ? null : await subagentsDirectory(source);\n const names = dir ? await directoryNames(source.files, dir) ?? [] : [];\n for (const name of names.filter((entry) => SUBAGENT_FILE.test(entry))) {\n found = await subagentFile(source, name, toolUseId).find(isResult);\n if (found)\n break;\n }\n const agentId = found?.tool_use_result?.agentId;\n return typeof agentId === \"string\" ? agentId : null;\n}\nfunction subagentFile(source, name, toolUseId) {\n async function locate() {\n const dir = await subagentsDirectory(source);\n if (!dir)\n return null;\n return (await directoryNames(source.files, dir))?.includes(name) ? `${dir}/${name}` : null;\n }\n return createFileTranscript(source.files, locate, (line) => lineMessages(line, toolUseId), CLAUDE_TRANSCRIPT_LAYOUT);\n}\nasync function subagentsDirectory({ nativeId, env, files }) {\n const main = await locateTranscript(files, claudeHome(env), CLAUDE_TRANSCRIPT_LAYOUT, nativeId);\n return main ? `${main.slice(0, -\".jsonl\".length)}/subagents` : null;\n}\nfunction claudeHome(env) {\n return env.CLAUDE_CONFIG_DIR ?? `${env.HOME}/.claude`;\n}\nvar SUBAGENT_FILE = /^agent-.+\\.jsonl$/;\nvar CLAUDE_TRANSCRIPT_LAYOUT = {\n dir: \"projects\",\n depth: 2,\n name: \"{id}.jsonl\",\n time: \"timestamp\",\n copies: '\"subtype\":\"compact_boundary\"'\n};\nfunction claudeLineMessages(line) {\n return lineMessages(line, null);\n}\nfunction lineMessages(line, parentToolUseId) {\n const entry = line;\n if (entry.type === \"system\" && entry.subtype === \"compact_boundary\")\n return compaction(entry);\n if (entry.type !== \"user\" && entry.type !== \"assistant\")\n return [];\n if (entry.isSidechain && parentToolUseId === null || entry.isMeta || !entry.uuid || !entry.message)\n return [];\n const common = { parent_tool_use_id: parentToolUseId, uuid: entry.uuid, session_id: entry.sessionId ?? \"\", ...entry.timestamp ? { timestamp: entry.timestamp } : {} };\n if (entry.type === \"assistant\")\n return [{ type: \"assistant\", message: entry.message, ...common }];\n return [{ type: \"user\", message: entry.message, ...common, ...entry.toolUseResult !== undefined ? { tool_use_result: entry.toolUseResult } : {} }];\n}\nfunction compaction(entry) {\n const meta = entry.compactMetadata;\n if (!entry.uuid || !meta?.trigger)\n return [];\n const { preservedSegment: segment, preservedMessages: preserved } = meta;\n return [\n {\n type: \"system\",\n subtype: \"compact_boundary\",\n compact_metadata: {\n trigger: meta.trigger,\n pre_tokens: meta.preTokens ?? 0,\n ...meta.postTokens !== undefined ? { post_tokens: meta.postTokens } : {},\n ...meta.durationMs !== undefined ? { duration_ms: meta.durationMs } : {},\n ...segment ? { preserved_segment: { head_uuid: segment.headUuid, anchor_uuid: segment.anchorUuid, tail_uuid: segment.tailUuid } } : {},\n ...preserved ? { preserved_messages: { anchor_uuid: preserved.anchorUuid, uuids: preserved.uuids } } : {}\n },\n uuid: entry.uuid,\n session_id: entry.sessionId ?? \"\",\n ...entry.timestamp ? { timestamp: entry.timestamp } : {}\n }\n ];\n}\n\n// ../node_modules/.bun/@pydantic+genai-prices@0.1.13/node_modules/@pydantic/genai-prices/dist/index.js\nvar W = [\n {\n id: \"anthropic\",\n name: \"Anthropic\",\n pricing_urls: [\"https://www.anthropic.com/pricing#api\"],\n api_pattern: \"https://api\\\\.anthropic\\\\.com\",\n price_comments: \"Message Batches API usage is charged at 50% of the standard rates. Prompt caching multipliers stack with the batch discount, so batch cache writes and cache reads are also 50% of standard; on Claude Haiku 5.5 the over-100k-token prompt tier is halved too. Web search ($10 per 1,000 searches) has no published batch rate, so it stays at the standard price. Batch variants cover the active models listed in Anthropic's batch price table as of 2026-10-14; earlier batch history is not modelled. Retired models are skipped. Refs: https://platform.claude.com/docs/en/build-with-claude/batch-processing#pricing, https://platform.claude.com/docs/en/about-claude/pricing#batch-processing\",\n model_match: {\n contains: \"claude\"\n },\n provider_match: {\n contains: \"anthropic\"\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"cache_creation_input_tokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"cache_read_input_tokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"cache_creation_input_tokens\",\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"cache_creation\", \"ephemeral_5m_input_tokens\"],\n dest: \"cache_write_5m_tokens\",\n required: false\n },\n {\n path: [\"cache_creation\", \"ephemeral_1h_input_tokens\"],\n dest: \"cache_write_1h_tokens\",\n required: false\n },\n {\n path: \"cache_read_input_tokens\",\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"server_tool_use\", \"web_search_requests\"],\n dest: \"web_searches\",\n required: false\n },\n {\n path: [\"output_tokens_details\", \"thinking_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"cached_tokens\",\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"claude-2\",\n name: \"Claude 2.0 / 2.1\",\n description: `Claude 2 is Anthropic's previous generation model, offering reliable performance for various tasks. This includes Claude 2.0 and Claude 2.1.\n`,\n match: {\n or: [\n {\n starts_with: \"claude-2\"\n },\n {\n contains: \"claude-v2\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 8,\n output_mtok: 24\n }\n },\n {\n id: \"claude-3-5-haiku-latest\",\n name: \"Claude Haiku 3.5\",\n description: \"Fastest, most cost-effective model\",\n match: {\n or: [\n {\n starts_with: \"claude-3-5-haiku\"\n },\n {\n starts_with: \"claude-3.5-haiku\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"One-hour cache writes cost 2x the base input price. Ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 0.8,\n cache_write_mtok: 1,\n cache_read_mtok: 0.08,\n output_mtok: 4,\n cache_write_1h_mtok: 1.6,\n web_searches_kcount: 10\n }\n },\n {\n id: \"claude-3-5-sonnet\",\n name: \"Claude Sonnet 3.5\",\n description: \"Claude 3.5 Sonnet is an ideal balance of intelligence and speed for enterprise workloads. Maximum utility at a lower price, dependable, balanced for scaled deployments.\",\n match: {\n or: [\n {\n starts_with: \"claude-3-5-sonnet\"\n },\n {\n starts_with: \"claude-3.5-sonnet\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"One-hour cache writes cost 2x the base input price. Ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15,\n cache_write_1h_mtok: 6,\n web_searches_kcount: 10\n }\n },\n {\n id: \"claude-3-7-sonnet-latest\",\n name: \"Claude Sonnet 3.7\",\n description: \"Claude 3.7 Sonnet is an advanced large language model with improved reasoning, coding, and problem-solving capabilities.\",\n match: {\n or: [\n {\n starts_with: \"claude-3-7-sonnet\"\n },\n {\n starts_with: \"claude-3.7-sonnet\"\n },\n {\n starts_with: \"claude-sonnet-3.7\"\n },\n {\n starts_with: \"claude-sonnet-3-7\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"One-hour cache writes cost 2x the base input price. Ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15,\n cache_write_1h_mtok: 6,\n web_searches_kcount: 10\n }\n },\n {\n id: \"claude-3-haiku\",\n name: \"Claude Haiku 3\",\n description: \"Fastest, most cost-effective model\",\n match: {\n starts_with: \"claude-3-haiku\"\n },\n context_window: 200000,\n price_comments: \"One-hour cache writes cost 2x the base input price. Ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 0.25,\n cache_write_mtok: 0.3,\n cache_read_mtok: 0.03,\n output_mtok: 1.25,\n cache_write_1h_mtok: 0.5\n }\n },\n {\n id: \"claude-3-opus-latest\",\n name: \"Claude Opus 3\",\n description: \"Claude 3 Opus was Anthropic's most powerful model for highly complex tasks. It boasts top-level performance, intelligence, fluency, and understanding.\",\n match: {\n starts_with: \"claude-3-opus\"\n },\n context_window: 200000,\n price_comments: \"One-hour cache writes cost 2x the base input price. Ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 15,\n cache_write_mtok: 18.75,\n cache_read_mtok: 1.5,\n output_mtok: 75,\n cache_write_1h_mtok: 30\n }\n },\n {\n id: \"claude-3-sonnet\",\n name: \"Claude 3 Sonnet\",\n description: \"Claude 3 Sonnet is an ideal balance of intelligence and speed for enterprise workloads. Maximum utility at a lower price, dependable, balanced for scaled deployments.\",\n match: {\n starts_with: \"claude-3-sonnet\"\n },\n context_window: 200000,\n price_comments: \"One-hour cache writes cost 2x the base input price. Ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15,\n cache_write_1h_mtok: 6\n }\n },\n {\n id: \"claude-fable-5\",\n name: \"Claude Fable 5\",\n description: \"For demanding reasoning and long-horizon agentic work\",\n match: {\n or: [\n {\n equals: \"claude-fable-5\"\n },\n {\n regex: \"^claude-fable-5-\\\\d{8}$\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window (no tiered pricing). Ref: https://platform.claude.com/docs/en/about-claude/pricing#long-context-pricing Prompt caching ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 10,\n cache_write_mtok: 12.5,\n cache_read_mtok: 1,\n output_mtok: 50,\n cache_write_1h_mtok: 20,\n web_searches_kcount: 10\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"claude-fable-5-1\",\n name: \"Claude Fable 5.1\",\n description: \"Anthropic's most capable widely released model for demanding reasoning and long-horizon agentic work\",\n match: {\n or: [\n {\n equals: \"claude-fable-5-1\"\n },\n {\n regex: \"^claude-fable-5-1-\\\\d{8}$\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window (no tiered pricing). Cache hits are 0.025x base input (not the usual 0.1x), unique to Fable 5.1 and Mythos 5.1. Ref: https://platform.claude.com/docs/en/about-claude/pricing#model-pricing Prompt caching ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 10,\n cache_write_mtok: 12.5,\n cache_read_mtok: 0.25,\n output_mtok: 50,\n cache_write_1h_mtok: 20,\n web_searches_kcount: 10\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.125,\n output_mtok: 25,\n cache_write_1h_mtok: 10\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"claude-haiku-4-5\",\n name: \"Claude Haiku 4.5\",\n description: \"Fastest and most intelligent Haiku model\",\n match: {\n or: [\n {\n starts_with: \"claude-haiku-4-5\"\n },\n {\n starts_with: \"claude-haiku-4.5\"\n },\n {\n starts_with: \"claude-4-5-haiku\"\n },\n {\n starts_with: \"claude-4.5-haiku\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"One-hour cache writes cost 2x the base input price. Ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 1,\n cache_write_mtok: 1.25,\n cache_read_mtok: 0.1,\n output_mtok: 5,\n cache_write_1h_mtok: 2,\n web_searches_kcount: 10\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.5,\n cache_write_mtok: 0.625,\n cache_read_mtok: 0.05,\n output_mtok: 2.5,\n cache_write_1h_mtok: 1\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"claude-haiku-5-5\",\n name: \"Claude Haiku 5.5\",\n description: \"For high-volume, latency-sensitive tasks such as classification, extraction, and routing\",\n match: {\n or: [\n {\n equals: \"claude-haiku-5-5\"\n },\n {\n regex: \"^claude-haiku-5-5-\\\\d{8}$\"\n },\n {\n starts_with: \"claude-haiku-5-5@\"\n },\n {\n starts_with: \"claude-haiku-5.5\"\n },\n {\n starts_with: \"claude-5-5-haiku\"\n },\n {\n starts_with: \"claude-5.5-haiku\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Priced by prompt length: every token in a request whose prompt exceeds 100,000 tokens is billed at the higher rate (5x). Cache reads are the standard 0.1x of base input. Ref: https://platform.claude.com/docs/en/about-claude/pricing#long-context-pricing Model ID ref: https://platform.claude.com/docs/en/about-claude/models/overview\",\n prices: {\n input_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 1e5,\n price: 0.5\n }\n ]\n },\n cache_write_mtok: {\n base: 0.125,\n tiers: [\n {\n start: 1e5,\n price: 0.625\n }\n ]\n },\n cache_read_mtok: {\n base: 0.01,\n tiers: [\n {\n start: 1e5,\n price: 0.05\n }\n ]\n },\n output_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 1e5,\n price: 2.5\n }\n ]\n },\n cache_write_1h_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 1e5,\n price: 1\n }\n ]\n },\n web_searches_kcount: 10\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 0.05,\n tiers: [\n {\n start: 1e5,\n price: 0.25\n }\n ]\n },\n cache_write_mtok: {\n base: 0.0625,\n tiers: [\n {\n start: 1e5,\n price: 0.3125\n }\n ]\n },\n cache_read_mtok: {\n base: 0.005,\n tiers: [\n {\n start: 1e5,\n price: 0.025\n }\n ]\n },\n output_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 1e5,\n price: 1.25\n }\n ]\n },\n cache_write_1h_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 1e5,\n price: 0.5\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"claude-opus-4-0\",\n name: \"Claude Opus 4\",\n description: \"Most intelligent model for complex tasks\",\n match: {\n or: [\n {\n starts_with: \"claude-opus-4-0\"\n },\n {\n starts_with: \"claude-4-opus\"\n },\n {\n equals: \"claude-opus-4\"\n },\n {\n equals: \"claude-opus-4-20250514\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"One-hour cache writes cost 2x the base input price. Ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 15,\n cache_write_mtok: 18.75,\n cache_read_mtok: 1.5,\n output_mtok: 75,\n cache_write_1h_mtok: 30,\n web_searches_kcount: 10\n }\n },\n {\n id: \"claude-opus-4-1\",\n name: \"Claude Opus 4.1\",\n description: \"Most intelligent model for complex tasks\",\n match: {\n or: [\n {\n starts_with: \"claude-opus-4-1\"\n },\n {\n starts_with: \"claude-opus-4.1\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"One-hour cache writes cost 2x the base input price. Ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 15,\n cache_write_mtok: 18.75,\n cache_read_mtok: 1.5,\n output_mtok: 75,\n cache_write_1h_mtok: 30,\n web_searches_kcount: 10\n }\n },\n {\n id: \"claude-opus-4-5\",\n name: \"Claude Opus 4.5\",\n description: \"Premium model combining maximum intelligence with practical performance\",\n match: {\n or: [\n {\n starts_with: \"claude-opus-4-5\"\n },\n {\n starts_with: \"claude-opus-4.5\"\n },\n {\n starts_with: \"claude-4-5-opus\"\n },\n {\n starts_with: \"claude-4.5-opus\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"One-hour cache writes cost 2x the base input price. Ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10,\n web_searches_kcount: 10\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 2.5,\n cache_write_mtok: 3.125,\n cache_read_mtok: 0.25,\n output_mtok: 12.5,\n cache_write_1h_mtok: 5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"claude-opus-4-6\",\n name: \"Claude Opus 4.6\",\n description: \"Our most intelligent model for building agents and coding\",\n match: {\n or: [\n {\n starts_with: \"claude-opus-4-6\"\n },\n {\n starts_with: \"claude-opus-4.6\"\n },\n {\n starts_with: \"claude-4-6-opus\"\n },\n {\n starts_with: \"claude-4.6-opus\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"One-hour cache writes cost 2x the base input price. The 1M context window is generally available. Refs: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing, https://platform.claude.com/docs/en/build-with-claude/context-windows#context-window-sizes-by-model\",\n prices: [\n {\n prices: {\n input_mtok: {\n base: 5,\n tiers: [\n {\n start: 200000,\n price: 10\n }\n ]\n },\n cache_write_mtok: {\n base: 6.25,\n tiers: [\n {\n start: 200000,\n price: 12.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 200000,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 25,\n tiers: [\n {\n start: 200000,\n price: 37.5\n }\n ]\n },\n cache_write_1h_mtok: {\n base: 10,\n tiers: [\n {\n start: 200000,\n price: 20\n }\n ]\n },\n web_searches_kcount: 10\n }\n },\n {\n constraint: {\n start_date: \"2026-03-13\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10,\n web_searches_kcount: 10\n }\n }\n ],\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 2.5,\n cache_write_mtok: 3.125,\n cache_read_mtok: 0.25,\n output_mtok: 12.5,\n cache_write_1h_mtok: 5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"claude-opus-4-7\",\n name: \"Claude Opus 4.7\",\n description: \"Our most capable model for complex reasoning and agentic coding\",\n match: {\n or: [\n {\n starts_with: \"claude-opus-4-7\"\n },\n {\n starts_with: \"claude-opus-4.7\"\n },\n {\n starts_with: \"claude-4-7-opus\"\n },\n {\n starts_with: \"claude-4.7-opus\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window (no tiered pricing). Ref: https://platform.claude.com/docs/en/about-claude/pricing#long-context-pricing Prompt caching ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10,\n web_searches_kcount: 10\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 2.5,\n cache_write_mtok: 3.125,\n cache_read_mtok: 0.25,\n output_mtok: 12.5,\n cache_write_1h_mtok: 5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"claude-opus-4-8\",\n name: \"Claude Opus 4.8\",\n description: \"Our most capable model for complex reasoning and agentic coding\",\n match: {\n or: [\n {\n starts_with: \"claude-opus-4-8\"\n },\n {\n starts_with: \"claude-opus-4.8\"\n },\n {\n starts_with: \"claude-4-8-opus\"\n },\n {\n starts_with: \"claude-4.8-opus\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window (no tiered pricing). Ref: https://platform.claude.com/docs/en/about-claude/pricing#long-context-pricing Prompt caching ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10,\n web_searches_kcount: 10\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 2.5,\n cache_write_mtok: 3.125,\n cache_read_mtok: 0.25,\n output_mtok: 12.5,\n cache_write_1h_mtok: 5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"claude-opus-5\",\n name: \"Claude Opus 5\",\n description: \"For complex agentic coding and enterprise work\",\n match: {\n or: [\n {\n equals: \"claude-opus-5\"\n },\n {\n equals: \"claude-opus-5-latest\"\n },\n {\n regex: \"^claude-opus-5[-@]\\\\d{8}$\"\n },\n {\n regex: \"^claude-opus-5-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n },\n {\n starts_with: \"claude-opus-5.0\"\n },\n {\n starts_with: \"claude-5-opus\"\n },\n {\n starts_with: \"claude-5.0-opus\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window (no tiered pricing). Refs: https://platform.claude.com/docs/en/about-claude/pricing#long-context-pricing and https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool Prompt caching ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10,\n web_searches_kcount: 10\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 2.5,\n cache_write_mtok: 3.125,\n cache_read_mtok: 0.25,\n output_mtok: 12.5,\n cache_write_1h_mtok: 5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"claude-opus-5-5\",\n name: \"Claude Opus 5.5\",\n description: \"For long-running agentic coding and knowledge work\",\n match: {\n or: [\n {\n equals: \"claude-opus-5-5\"\n },\n {\n regex: \"^claude-opus-5-5-\\\\d{8}$\"\n },\n {\n starts_with: \"claude-opus-5.5\"\n },\n {\n starts_with: \"claude-5-5-opus\"\n },\n {\n starts_with: \"claude-5.5-opus\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window (no tiered pricing). Cache hits are 0.05x base input (not the usual 0.1x), as on Sonnet 5.5. Ref: https://platform.claude.com/docs/en/about-claude/pricing#model-pricing Prompt caching ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 4,\n cache_write_mtok: 5,\n cache_read_mtok: 0.2,\n output_mtok: 20,\n cache_write_1h_mtok: 8,\n web_searches_kcount: 10\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 2,\n cache_write_mtok: 2.5,\n cache_read_mtok: 0.1,\n output_mtok: 10,\n cache_write_1h_mtok: 4\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"claude-sonnet-4-0\",\n name: \"Claude Sonnet 4\",\n description: \"Optimal balance of intelligence, cost, and speed\",\n match: {\n or: [\n {\n starts_with: \"claude-sonnet-4-2025\"\n },\n {\n starts_with: \"claude-sonnet-4-0\"\n },\n {\n starts_with: \"claude-sonnet-4@\"\n },\n {\n equals: \"claude-sonnet-4\"\n },\n {\n starts_with: \"claude-4-sonnet\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"One-hour cache writes cost 2x the base input price. Ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15,\n cache_write_1h_mtok: 6,\n web_searches_kcount: 10\n }\n },\n {\n id: \"claude-sonnet-4-5\",\n name: \"Claude Sonnet 4.5\",\n description: \"Our best combination of speed and intelligence\",\n match: {\n or: [\n {\n starts_with: \"claude-sonnet-4-5\"\n },\n {\n starts_with: \"claude-sonnet-4.5\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"One-hour cache writes cost 2x the base input price. The 1M context beta was retired on 2026-04-30; requests over 200k now error. The >200k tiers describe historical usage from before retirement. Refs: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing, https://platform.claude.com/docs/en/release-notes/overview\",\n prices: {\n input_mtok: {\n base: 3,\n tiers: [\n {\n start: 200000,\n price: 6\n }\n ]\n },\n cache_write_mtok: {\n base: 3.75,\n tiers: [\n {\n start: 200000,\n price: 7.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.3,\n tiers: [\n {\n start: 200000,\n price: 0.6\n }\n ]\n },\n output_mtok: {\n base: 15,\n tiers: [\n {\n start: 200000,\n price: 22.5\n }\n ]\n },\n cache_write_1h_mtok: {\n base: 6,\n tiers: [\n {\n start: 200000,\n price: 12\n }\n ]\n },\n web_searches_kcount: 10\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 1.5,\n tiers: [\n {\n start: 200000,\n price: 3\n }\n ]\n },\n cache_write_mtok: {\n base: 1.875,\n tiers: [\n {\n start: 200000,\n price: 3.75\n }\n ]\n },\n cache_read_mtok: {\n base: 0.15,\n tiers: [\n {\n start: 200000,\n price: 0.3\n }\n ]\n },\n output_mtok: {\n base: 7.5,\n tiers: [\n {\n start: 200000,\n price: 11.25\n }\n ]\n },\n cache_write_1h_mtok: {\n base: 3,\n tiers: [\n {\n start: 200000,\n price: 6\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"claude-sonnet-4-6\",\n name: \"Claude Sonnet 4.6\",\n description: \"Our best combination of speed and intelligence\",\n match: {\n or: [\n {\n starts_with: \"claude-sonnet-4-6\"\n },\n {\n starts_with: \"claude-sonnet-4.6\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"One-hour cache writes cost 2x the base input price. Ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: [\n {\n prices: {\n input_mtok: {\n base: 3,\n tiers: [\n {\n start: 200000,\n price: 6\n }\n ]\n },\n cache_write_mtok: {\n base: 3.75,\n tiers: [\n {\n start: 200000,\n price: 7.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.3,\n tiers: [\n {\n start: 200000,\n price: 0.6\n }\n ]\n },\n output_mtok: {\n base: 15,\n tiers: [\n {\n start: 200000,\n price: 22.5\n }\n ]\n },\n cache_write_1h_mtok: {\n base: 6,\n tiers: [\n {\n start: 200000,\n price: 12\n }\n ]\n },\n web_searches_kcount: 10\n }\n },\n {\n constraint: {\n start_date: \"2026-03-13\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15,\n cache_write_1h_mtok: 6,\n web_searches_kcount: 10\n }\n }\n ],\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 1.5,\n cache_write_mtok: 1.875,\n cache_read_mtok: 0.15,\n output_mtok: 7.5,\n cache_write_1h_mtok: 3\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"claude-sonnet-5\",\n name: \"Claude Sonnet 5\",\n description: \"Our most agentic Sonnet model, approaching Opus 4.8 capability at lower cost\",\n match: {\n or: [\n {\n equals: \"claude-sonnet-5\"\n },\n {\n regex: \"^claude-sonnet-5-\\\\d{8}$\"\n },\n {\n starts_with: \"claude-sonnet-5@\"\n },\n {\n starts_with: \"claude-sonnet-5.0\"\n },\n {\n starts_with: \"claude-5-sonnet\"\n },\n {\n starts_with: \"claude-5.0-sonnet\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across the full 1M context window (no tiered pricing). Anthropic made the introductory $2/$10 per MTok rates permanent and cancelled the previously scheduled 2026-09-01 increase. Ref: https://platform.claude.com/docs/en/about-claude/pricing Prompt caching ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing\",\n prices: {\n input_mtok: 2,\n cache_write_mtok: 2.5,\n cache_read_mtok: 0.2,\n output_mtok: 10,\n cache_write_1h_mtok: 4,\n web_searches_kcount: 10\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 1,\n cache_write_mtok: 1.25,\n cache_read_mtok: 0.1,\n output_mtok: 5,\n cache_write_1h_mtok: 2\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"claude-sonnet-5-5\",\n name: \"Claude Sonnet 5.5\",\n description: \"The best combination of speed and intelligence\",\n match: {\n or: [\n {\n equals: \"claude-sonnet-5-5\"\n },\n {\n regex: \"^claude-sonnet-5-5-\\\\d{8}$\"\n },\n {\n starts_with: \"claude-sonnet-5-5@\"\n },\n {\n starts_with: \"claude-sonnet-5.5\"\n },\n {\n starts_with: \"claude-5-5-sonnet\"\n },\n {\n starts_with: \"claude-5.5-sonnet\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across the full 1M context window (no tiered pricing). Cache hits are 0.05x base input (not the usual 0.1x), as on Opus 5.5. Ref: https://platform.claude.com/docs/en/about-claude/pricing#model-pricing Prompt caching ref: https://platform.claude.com/docs/en/about-claude/pricing#prompt-caching Model ID ref: https://platform.claude.com/docs/en/about-claude/models/overview\",\n prices: {\n input_mtok: 2,\n cache_write_mtok: 2.5,\n cache_read_mtok: 0.1,\n output_mtok: 10,\n cache_write_1h_mtok: 4,\n web_searches_kcount: 10\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 1,\n cache_write_mtok: 1.25,\n cache_read_mtok: 0.05,\n output_mtok: 5,\n cache_write_1h_mtok: 2\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"claude-v1\",\n description: \"Retired, here to match price sources\",\n match: {\n equals: \"claude-v1\"\n },\n prices: {\n input_mtok: 8,\n output_mtok: 24\n }\n }\n ]\n },\n {\n id: \"arcee\",\n name: \"Arcee\",\n pricing_urls: [\"https://docs.arcee.ai/get-started/pricing\", \"https://api.arcee.ai/api/v1/models\"],\n api_pattern: \"https://api\\\\.arcee\\\\.ai(?:/|$)\",\n description: \"OpenAI-compatible hosted inference through the Arcee Platform API.\",\n price_comments: \"Covers the complete public Text Models pricing table. The table publishes input, cached-input, and output token rates. Context windows and rates for images, requests, and cache writes are omitted because the public pricing page does not publish them. Model IDs use third-party namespaces, so they do not infer the Arcee provider without an Arcee provider ID or API URL.\",\n provider_match: {\n contains: \"arcee\"\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"deepseek/deepseek-v4-flash-latest\",\n name: \"DeepSeek V4 Flash\",\n match: {\n equals: \"deepseek/deepseek-v4-flash-latest\"\n },\n prices: {\n input_mtok: 0.14,\n cache_read_mtok: 0.028,\n output_mtok: 0.28\n }\n },\n {\n id: \"deepseek/deepseek-v4-pro\",\n name: \"DeepSeek V4 Pro\",\n match: {\n equals: \"deepseek/deepseek-v4-pro\"\n },\n prices: {\n input_mtok: 1.74,\n cache_read_mtok: 0.2,\n output_mtok: 3.48\n }\n },\n {\n id: \"deepseek/deepseek-v4-pro-0813\",\n name: \"DeepSeek V4 Pro 0813\",\n match: {\n equals: \"deepseek/deepseek-v4-pro-0813\"\n },\n prices: {\n input_mtok: 1.32,\n cache_read_mtok: 0.044,\n output_mtok: 3.96\n }\n },\n {\n id: \"deepseek/deepseek-v4.1-flash\",\n name: \"DeepSeek V4.1 Flash\",\n match: {\n equals: \"deepseek/deepseek-v4.1-flash\"\n },\n prices: {\n input_mtok: 0.22,\n cache_read_mtok: 0.007,\n output_mtok: 0.66\n }\n },\n {\n id: \"moonshotai/kimi-k3\",\n name: \"Kimi K3\",\n match: {\n equals: \"moonshotai/kimi-k3\"\n },\n prices: {\n input_mtok: 3,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"thinkingmachines/inkling-small\",\n name: \"Inkling Small\",\n match: {\n equals: \"thinkingmachines/inkling-small\"\n },\n prices: {\n input_mtok: 0.5,\n cache_read_mtok: 0.1,\n output_mtok: 1.2\n }\n },\n {\n id: \"trinity-large-thinking\",\n name: \"Trinity Large Thinking\",\n match: {\n equals: \"trinity-large-thinking\"\n },\n prices: {\n input_mtok: 0.25,\n cache_read_mtok: 0.06,\n output_mtok: 0.8\n }\n },\n {\n id: \"zai-org/glm-5.2\",\n name: \"GLM 5.2\",\n match: {\n equals: \"zai-org/glm-5.2\"\n },\n prices: {\n input_mtok: 1.4,\n cache_read_mtok: 0.26,\n output_mtok: 4.4\n }\n },\n {\n id: \"zai-org/glm-5.3\",\n name: \"GLM 5.3\",\n match: {\n equals: \"zai-org/glm-5.3\"\n },\n prices: {\n input_mtok: 1.4,\n cache_read_mtok: 0.26,\n output_mtok: 4.4\n }\n },\n {\n id: \"zai-org/glm-5.3-flash\",\n name: \"GLM 5.3 Flash\",\n match: {\n equals: \"zai-org/glm-5.3-flash\"\n },\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.03,\n output_mtok: 0.5\n }\n }\n ]\n },\n {\n id: \"avian\",\n name: \"Avian\",\n pricing_urls: [\"https://avian.io/pricing/\"],\n api_pattern: \"https://api\\\\.avian\\\\.io\",\n price_comments: \"Prices and model IDs checked against https://avian.io/docs/ on 2026-08-25. Cache prices use Avian's documented cache-read rates. The previously listed Meta Llama models are no longer in Avian's catalog and are deprecated.\",\n models: [\n {\n id: \"Meta-Llama-3.1-405B-Instruct\",\n match: {\n equals: \"Meta-Llama-3.1-405B-Instruct\"\n },\n price_comments: \"No longer listed in Avian's model catalog as of 2026-08-25.\",\n prices: {\n input_mtok: 1.5,\n output_mtok: 1.5\n },\n deprecated: true\n },\n {\n id: \"Meta-Llama-3.1-70B-Instruct\",\n match: {\n equals: \"Meta-Llama-3.1-70B-Instruct\"\n },\n price_comments: \"No longer listed in Avian's model catalog as of 2026-08-25.\",\n prices: {\n input_mtok: 0.45,\n output_mtok: 0.45\n },\n deprecated: true\n },\n {\n id: \"Meta-Llama-3.1-8B-Instruct\",\n match: {\n equals: \"Meta-Llama-3.1-8B-Instruct\"\n },\n price_comments: \"No longer listed in Avian's model catalog as of 2026-08-25.\",\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n },\n deprecated: true\n },\n {\n id: \"Meta-Llama-3.3-70B-Instruct\",\n match: {\n equals: \"Meta-Llama-3.3-70B-Instruct\"\n },\n price_comments: \"No longer listed in Avian's model catalog as of 2026-08-25.\",\n prices: {\n input_mtok: 0.45,\n output_mtok: 0.45\n },\n deprecated: true\n },\n {\n id: \"deepseek/deepseek-v3.2\",\n name: \"DeepSeek V3.2 (Legacy)\",\n match: {\n equals: \"deepseek/deepseek-v3.2\"\n },\n context_window: 163000,\n price_comments: \"No longer listed in Avian's model catalog (checked 2026-10-09); kept for historical usage.\",\n prices: {\n input_mtok: 0.23,\n cache_read_mtok: 0.012,\n output_mtok: 0.33\n },\n deprecated: true\n },\n {\n id: \"deepseek/deepseek-v4-flash\",\n name: \"DeepSeek V4 Flash\",\n match: {\n equals: \"deepseek/deepseek-v4-flash\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.0805,\n cache_read_mtok: 0.0165,\n output_mtok: 0.161\n }\n },\n {\n id: \"deepseek/deepseek-v4-pro\",\n name: \"DeepSeek V4 Pro\",\n match: {\n equals: \"deepseek/deepseek-v4-pro\"\n },\n context_window: 1e6,\n price_comments: \"No longer listed in Avian's model catalog (checked 2026-10-09); the dated V4 Pro 0813 entry took its place.\",\n prices: {\n input_mtok: 1.305,\n cache_read_mtok: 0.10875,\n output_mtok: 2.61\n },\n deprecated: true\n },\n {\n id: \"deepseek/deepseek-v4-pro-0813\",\n name: \"DeepSeek V4 Pro 0813\",\n match: {\n equals: \"deepseek/deepseek-v4-pro-0813\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.594,\n cache_read_mtok: 0.0198,\n output_mtok: 1.782\n }\n },\n {\n id: \"deepseek/deepseek-v4.1-flash\",\n name: \"DeepSeek V4.1 Flash\",\n match: {\n equals: \"deepseek/deepseek-v4.1-flash\"\n },\n context_window: 1e6,\n price_comments: \"Rates and context window from the model table at https://avian.io/docs.\",\n prices: {\n input_mtok: 0.1275,\n cache_read_mtok: 0.00255,\n output_mtok: 0.51\n }\n },\n {\n id: \"minimax/minimax-m2.5\",\n name: \"MiniMax M2.5\",\n match: {\n equals: \"minimax/minimax-m2.5\"\n },\n context_window: 196000,\n price_comments: \"No longer listed in Avian's model catalog (checked 2026-10-09); kept for historical usage.\",\n prices: {\n input_mtok: 0.27,\n cache_read_mtok: 0.15,\n output_mtok: 1.08\n },\n deprecated: true\n },\n {\n id: \"minimax/minimax-m3\",\n name: \"MiniMax M3\",\n match: {\n equals: \"minimax/minimax-m3\"\n },\n context_window: 512000,\n price_comments: \"Rates and context window from the model table at https://avian.io/docs.\",\n prices: {\n input_mtok: 0.255,\n cache_read_mtok: 0.051,\n output_mtok: 1.02\n }\n },\n {\n id: \"moonshotai/kimi-k2.5\",\n name: \"Kimi K2.5\",\n match: {\n equals: \"moonshotai/kimi-k2.5\"\n },\n context_window: 262000,\n price_comments: \"No longer listed in Avian's model catalog (checked 2026-10-09); Moonshot discontinued the kimi-k2.5 family on 2026-08-31 (https://platform.kimi.ai/docs/models.md).\",\n prices: {\n input_mtok: 0.45,\n cache_read_mtok: 0.225,\n output_mtok: 2.2\n },\n deprecated: true\n },\n {\n id: \"moonshotai/kimi-k2.6\",\n name: \"Kimi K2.6\",\n match: {\n equals: \"moonshotai/kimi-k2.6\"\n },\n context_window: 262000,\n price_comments: \"No longer listed in Avian's model catalog (checked 2026-10-09); kept for historical usage.\",\n prices: {\n input_mtok: 0.95,\n cache_read_mtok: 0.16,\n output_mtok: 4\n },\n deprecated: true\n },\n {\n id: \"moonshotai/kimi-k3\",\n name: \"Kimi K3\",\n match: {\n equals: \"moonshotai/kimi-k3\"\n },\n context_window: 1e6,\n price_comments: \"Rates and context window from the model table at https://avian.io/docs.\",\n prices: {\n input_mtok: 2.55,\n cache_read_mtok: 0.255,\n output_mtok: 12.75\n }\n },\n {\n id: \"xiaomi/mimo-v2.5\",\n name: \"MiMo-V2.5 Small\",\n match: {\n equals: \"xiaomi/mimo-v2.5\"\n },\n context_window: 1e6,\n price_comments: \"No longer listed in Avian's model catalog (checked 2026-10-09); kept for historical usage.\",\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.05,\n output_mtok: 0.4\n },\n deprecated: true\n },\n {\n id: \"xiaomi/mimo-v2.5-pro\",\n name: \"MiMo-V2.5 Pro\",\n match: {\n equals: \"xiaomi/mimo-v2.5-pro\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.435,\n cache_read_mtok: 0.0036,\n output_mtok: 0.87\n }\n },\n {\n id: \"xiaomi/mimo-v2.6-flash\",\n name: \"MiMo-V2.6 Flash\",\n match: {\n equals: \"xiaomi/mimo-v2.6-flash\"\n },\n context_window: 1e6,\n price_comments: \"Rates and context window from the model table at https://avian.io/docs.\",\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.05,\n output_mtok: 0.4\n }\n },\n {\n id: \"xiaomi/mimo-v2.6-pro\",\n name: \"MiMo-V2.6 Pro\",\n match: {\n equals: \"xiaomi/mimo-v2.6-pro\"\n },\n context_window: 1e6,\n price_comments: \"Rates and context window from the model table at https://avian.io/docs.\",\n prices: {\n input_mtok: 0.435,\n cache_read_mtok: 0.0036,\n output_mtok: 0.87\n }\n },\n {\n id: \"z-ai/glm-4.7\",\n name: \"GLM-4.7\",\n match: {\n equals: \"z-ai/glm-4.7\"\n },\n context_window: 202000,\n price_comments: \"No longer listed in Avian's model catalog (checked 2026-10-09); kept for historical usage.\",\n prices: {\n input_mtok: 0.388,\n cache_read_mtok: 0.097,\n output_mtok: 1.806\n },\n deprecated: true\n },\n {\n id: \"z-ai/glm-5\",\n name: \"GLM-5\",\n match: {\n equals: \"z-ai/glm-5\"\n },\n context_window: 205000,\n price_comments: \"No longer listed in Avian's model catalog (checked 2026-10-09); kept for historical usage.\",\n prices: {\n input_mtok: 0.516,\n cache_read_mtok: 0.129,\n output_mtok: 2.322\n },\n deprecated: true\n },\n {\n id: \"z-ai/glm-5.1\",\n name: \"GLM-5.1\",\n match: {\n equals: \"z-ai/glm-5.1\"\n },\n context_window: 202000,\n price_comments: \"No longer listed in Avian's model catalog (checked 2026-10-09); kept for historical usage.\",\n prices: {\n input_mtok: 0.743,\n cache_read_mtok: 0.186,\n output_mtok: 2.971\n },\n deprecated: true\n },\n {\n id: \"z-ai/glm-5.2\",\n name: \"GLM-5.2\",\n match: {\n equals: \"z-ai/glm-5.2\"\n },\n context_window: 1e6,\n price_comments: \"No longer listed in Avian's model catalog (checked 2026-10-09); kept for historical usage.\",\n prices: {\n input_mtok: 0.495,\n cache_read_mtok: 0.124,\n output_mtok: 1.733\n },\n deprecated: true\n },\n {\n id: \"z-ai/glm-5.3\",\n name: \"GLM-5.3\",\n match: {\n equals: \"z-ai/glm-5.3\"\n },\n context_window: 1e6,\n price_comments: \"Rates and context window from the model table at https://avian.io/docs.\",\n prices: {\n input_mtok: 1.19,\n cache_read_mtok: 0.221,\n output_mtok: 3.74\n }\n },\n {\n id: \"z-ai/glm-5.3-flash\",\n name: \"GLM-5.3 Flash\",\n match: {\n equals: \"z-ai/glm-5.3-flash\"\n },\n context_window: 1e6,\n price_comments: \"Rates and context window from the model table at https://avian.io/docs.\",\n prices: {\n input_mtok: 0.1275,\n cache_read_mtok: 0.0255,\n output_mtok: 0.425\n }\n }\n ]\n },\n {\n id: \"aws\",\n name: \"AWS Bedrock\",\n pricing_urls: [\"https://aws.amazon.com/bedrock/pricing/\"],\n api_pattern: \"https://bedrock-runtime\\\\.[a-z0-9-]+\\\\.amazonaws\\\\.com(/|$)\",\n provider_match: {\n or: [\n {\n contains: \"bedrock\"\n },\n {\n contains: \"amazon\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"inputTokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"cacheReadInputTokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"cacheWriteInputTokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"cacheReadInputTokens\",\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: \"cacheWriteInputTokens\",\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\n \"cacheDetails\",\n {\n type: \"array-match\",\n field: \"ttl\",\n match: {\n equals: \"5m\"\n }\n },\n \"inputTokens\"\n ],\n dest: \"cache_write_5m_tokens\",\n required: false\n },\n {\n path: [\n \"cacheDetails\",\n {\n type: \"array-match\",\n field: \"ttl\",\n match: {\n equals: \"1h\"\n }\n },\n \"inputTokens\"\n ],\n dest: \"cache_write_1h_tokens\",\n required: false\n },\n {\n path: \"outputTokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"anthropic\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"cache_creation_input_tokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"cache_read_input_tokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"cache_creation_input_tokens\",\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"cache_creation\", \"ephemeral_5m_input_tokens\"],\n dest: \"cache_write_5m_tokens\",\n required: false\n },\n {\n path: [\"cache_creation\", \"ephemeral_1h_input_tokens\"],\n dest: \"cache_write_1h_tokens\",\n required: false\n },\n {\n path: \"cache_read_input_tokens\",\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"amazon.nova-2-sonic-v1:0\",\n name: \"Nova 2 Sonic\",\n match: {\n contains: \"amazon.nova-2-sonic\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.33,\n output_mtok: 2.75,\n input_audio_mtok: 3,\n output_audio_mtok: 12\n }\n },\n {\n id: \"amazon.nova-lite-v1:0\",\n name: \"Nova Lite\",\n description: \"Amazon Nova Lite 1.0 is a very low-cost multimodal model from Amazon that focused on fast processing of image, video, and text inputs to generate text output. Amazon Nova Lite can handle real-time customer interactions, document analysis, and visual question-answering tasks with high accuracy.\",\n match: {\n contains: \"amazon.nova-lite\"\n },\n context_window: 300000,\n prices: {\n input_mtok: 0.06,\n cache_read_mtok: 0.015,\n output_mtok: 0.24\n }\n },\n {\n id: \"amazon.nova-micro-v1:0\",\n name: \"Nova Micro\",\n description: \"Amazon Nova Micro 1.0 is a text-only model that delivers the lowest latency responses in the Amazon Nova family of models at a very low cost. With a context length of 128K tokens and optimized for speed and cost, Amazon Nova Micro excels at tasks such as text summarization, translation, content classification, interactive chat, and brainstorming. It has simple mathematical reasoning and coding abilities.\",\n match: {\n contains: \"amazon.nova-micro\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.035,\n cache_read_mtok: 0.00875,\n output_mtok: 0.14\n }\n },\n {\n id: \"amazon.nova-premier-v1:0\",\n name: \"Nova Premier\",\n match: {\n contains: \"amazon.nova-premier\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 2.5,\n cache_read_mtok: 0.625,\n output_mtok: 12.5\n }\n },\n {\n id: \"amazon.nova-pro-v1:0\",\n name: \"Nova Pro\",\n description: \"Amazon Nova Pro 1.0 is a capable multimodal model from Amazon focused on providing a combination of accuracy, speed, and cost for a wide range of tasks. As of December 2024, it achieves state-of-the-art performance on key benchmarks including visual question answering (TextVQA) and video understanding (VATEX).\",\n match: {\n contains: \"amazon.nova-pro\"\n },\n context_window: 300000,\n prices: {\n input_mtok: 0.8,\n cache_read_mtok: 0.2,\n output_mtok: 3.2\n }\n },\n {\n id: \"amazon.nova-sonic-v1:0\",\n name: \"Nova Sonic\",\n match: {\n contains: \"amazon.nova-sonic\"\n },\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.24,\n input_audio_mtok: 3.4,\n output_audio_mtok: 13.6\n }\n },\n {\n id: \"amazon.titan-embed-text-v1\",\n name: \"Titan Embeddings G1 - Text\",\n match: {\n or: [\n {\n ends_with: \"amazon.titan-embed-text\"\n },\n {\n contains: \"amazon.titan-embed-text-v1\"\n }\n ]\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.1\n }\n },\n {\n id: \"amazon.titan-embed-text-v2:0\",\n name: \"Titan Text Embeddings V2\",\n match: {\n contains: \"amazon.titan-embed-text-v2\"\n },\n context_window: 8192,\n price_comments: \"On-demand input token price from the AWS price list API, AmazonBedrock (us-east-1), usage type TitanEmbeddingV2-Text-input-tokens: $0.00002 per 1K tokens. Ref: https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-amazon-titan-text-embeddings-v2.html\",\n prices: {\n input_mtok: 0.02\n }\n },\n {\n id: \"amazon.titan-text-express-v1\",\n name: \"Titan Text G1 - Express\",\n match: {\n contains: \"titan-text-express\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.6\n }\n },\n {\n id: \"amazon.titan-text-lite-v1\",\n name: \"Titan Text G1 - Lite\",\n match: {\n contains: \"titan-text-lite\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.2\n }\n },\n {\n id: \"deepseek.r1-v1:0\",\n name: \"DeepSeek-R1\",\n match: {\n contains: \"deepseek.r1\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 1.35,\n output_mtok: 5.4\n }\n },\n {\n id: \"global.amazon.nova-2-lite-v1:0\",\n name: \"Nova 2 Lite\",\n match: {\n contains: \"global.amazon.nova-2-lite\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.075,\n output_mtok: 2.5\n }\n },\n {\n id: \"global.anthropic.claude-fable-5-1-v1:0\",\n match: {\n or: [\n {\n ends_with: \"global.anthropic.claude-fable-5-1\"\n },\n {\n contains: \"global.anthropic.claude-fable-5-1-v1\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Global endpoint (no premium). Cache hits are 0.025x base input (not the usual 0.1x), unique to Fable 5.1 and Mythos 5.1. Ref: https://platform.claude.com/docs/en/about-claude/pricing#model-pricing Model ID ref: https://platform.claude.com/docs/en/build-with-claude/claude-in-amazon-bedrock One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 10,\n cache_write_mtok: 12.5,\n cache_read_mtok: 0.25,\n output_mtok: 50,\n cache_write_1h_mtok: 20\n }\n },\n {\n id: \"global.anthropic.claude-fable-5-v1:0\",\n match: {\n or: [\n {\n ends_with: \"global.anthropic.claude-fable-5\"\n },\n {\n contains: \"global.anthropic.claude-fable-5-v1\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 10,\n cache_write_mtok: 12.5,\n cache_read_mtok: 1,\n output_mtok: 50,\n cache_write_1h_mtok: 20\n }\n },\n {\n id: \"global.anthropic.claude-haiku-4-5-20251001-v1:0\",\n match: {\n contains: \"global.anthropic.claude-haiku-4-5-20251001\"\n },\n context_window: 200000,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 1,\n cache_write_mtok: 1.25,\n cache_read_mtok: 0.1,\n output_mtok: 5,\n cache_write_1h_mtok: 2\n }\n },\n {\n id: \"global.anthropic.claude-haiku-5-5\",\n match: {\n or: [\n {\n ends_with: \"global.anthropic.claude-haiku-5-5\"\n },\n {\n contains: \"global.anthropic.claude-haiku-5-5-v1\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: 'Global endpoint (no premium). Prompts over 100,000 tokens bill every token at the long-context rate. Ref: AWS price list API, AmazonBedrockFoundationModels \"Claude Haiku 5.5 (Amazon Bedrock Edition)\" (https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonBedrockFoundationModels/current/us-east-1/index.json) Model ID ref: https://platform.claude.com/docs/en/about-claude/models/overview',\n prices: {\n input_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 1e5,\n price: 0.5\n }\n ]\n },\n cache_write_mtok: {\n base: 0.125,\n tiers: [\n {\n start: 1e5,\n price: 0.625\n }\n ]\n },\n cache_read_mtok: {\n base: 0.01,\n tiers: [\n {\n start: 1e5,\n price: 0.05\n }\n ]\n },\n output_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 1e5,\n price: 2.5\n }\n ]\n },\n cache_write_1h_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 1e5,\n price: 1\n }\n ]\n }\n }\n },\n {\n id: \"global.anthropic.claude-opus-4-5-v1:0\",\n match: {\n contains: \"global.anthropic.claude-opus-4-5\"\n },\n context_window: 200000,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10\n }\n },\n {\n id: \"global.anthropic.claude-opus-4-6-v1:0\",\n match: {\n contains: \"global.anthropic.claude-opus-4-6\"\n },\n context_window: 1e6,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10\n }\n },\n {\n id: \"global.anthropic.claude-opus-4-7-v1:0\",\n match: {\n contains: \"global.anthropic.claude-opus-4-7\"\n },\n context_window: 1e6,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10\n }\n },\n {\n id: \"global.anthropic.claude-opus-4-8-v1:0\",\n match: {\n contains: \"global.anthropic.claude-opus-4-8\"\n },\n context_window: 1e6,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10\n }\n },\n {\n id: \"global.anthropic.claude-opus-5\",\n match: {\n or: [\n {\n ends_with: \"global.anthropic.claude-opus-5\"\n },\n {\n contains: \"global.anthropic.claude-opus-5-v1\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10\n }\n },\n {\n id: \"global.anthropic.claude-opus-5-5\",\n match: {\n or: [\n {\n ends_with: \"global.anthropic.claude-opus-5-5\"\n },\n {\n contains: \"global.anthropic.claude-opus-5-5-v1\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: 'Global endpoint (no premium). Cache hits are 0.05x base input (not the usual 0.1x), as on Sonnet 5.5. Ref: AWS price list API, AmazonBedrockFoundationModels \"Claude Opus 5.5 (Amazon Bedrock Edition)\" (https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonBedrockFoundationModels/current/us-east-1/index.json) Model ID ref: https://platform.claude.com/docs/en/build-with-claude/claude-in-amazon-bedrock',\n prices: {\n input_mtok: 4,\n cache_write_mtok: 5,\n cache_read_mtok: 0.2,\n output_mtok: 20,\n cache_write_1h_mtok: 8\n }\n },\n {\n id: \"global.anthropic.claude-sonnet-4-20250514-v1:0\",\n match: {\n contains: \"global.anthropic.claude-sonnet-4-20250514\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"global.anthropic.claude-sonnet-4-5-20250929-v1:0\",\n match: {\n contains: \"global.anthropic.claude-sonnet-4-5-20250929\"\n },\n context_window: 200000,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15,\n cache_write_1h_mtok: 6\n }\n },\n {\n id: \"global.anthropic.claude-sonnet-4-6-v1:0\",\n match: {\n contains: \"global.anthropic.claude-sonnet-4-6\"\n },\n context_window: 1e6,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15,\n cache_write_1h_mtok: 6\n }\n },\n {\n id: \"global.anthropic.claude-sonnet-5-5\",\n match: {\n or: [\n {\n ends_with: \"global.anthropic.claude-sonnet-5-5\"\n },\n {\n contains: \"global.anthropic.claude-sonnet-5-5-v1\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: 'Global endpoint (no premium), flat across the full 1M context window. Cache hits are 0.05x base input (not the usual 0.1x), as on Opus 5.5. Ref: AWS price list API, AmazonBedrockFoundationModels \"Claude Sonnet 5.5 (Amazon Bedrock Edition)\" (https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonBedrockFoundationModels/current/us-east-1/index.json) Model ID ref: https://platform.claude.com/docs/en/about-claude/models/overview',\n prices: {\n input_mtok: 2,\n cache_write_mtok: 2.5,\n cache_read_mtok: 0.1,\n output_mtok: 10,\n cache_write_1h_mtok: 4\n }\n },\n {\n id: \"global.anthropic.claude-sonnet-5-v1:0\",\n match: {\n or: [\n {\n ends_with: \"global.anthropic.claude-sonnet-5\"\n },\n {\n contains: \"global.anthropic.claude-sonnet-5-v1\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across the full 1M context window (no tiered pricing). The $2/$10 per MTok launch rates are now permanent, with no 2026-09-01 increase. Refs: https://aws.amazon.com/bedrock/pricing/, https://platform.claude.com/docs/en/about-claude/pricing One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 2,\n cache_write_mtok: 2.5,\n cache_read_mtok: 0.2,\n output_mtok: 10,\n cache_write_1h_mtok: 4\n }\n },\n {\n id: \"global.openai.gpt-5.6-luna\",\n name: \"GPT-5.6 Luna (global)\",\n match: {\n contains: \"global.openai.gpt-5.6-luna\"\n },\n context_window: 1e6,\n price_comments: \"Global cross-Region inference, priced 10% below in-Region and Geo, and identical to OpenAI's own list price. Cross-Region inference for the GPT-5.6 models launched on 2026-08-17. Cache writes (30m TTL) are billed at 1.25x the input rate. Above 272K input tokens, input and cache are 2x and output 1.5x. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. The 80% Luna price cut of 2026-07-30 predates global availability, so there is no earlier global rate to record. Refs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-luna.html, https://aws.amazon.com/about-aws/whats-new/2026/08/amazon-bedrock-cross-region-openai-v2/, https://aws.amazon.com/about-aws/whats-new/2026/08/gpt-sol-terra-luna-long-context-bedrock/\",\n prices: {\n input_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n cache_write_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 272000,\n price: 0.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.02,\n tiers: [\n {\n start: 272000,\n price: 0.04\n }\n ]\n },\n output_mtok: {\n base: 1.2,\n tiers: [\n {\n start: 272000,\n price: 1.8\n }\n ]\n }\n }\n },\n {\n id: \"global.openai.gpt-5.6-sol\",\n name: \"GPT-5.6 Sol (global)\",\n match: {\n contains: \"global.openai.gpt-5.6-sol\"\n },\n context_window: 1e6,\n price_comments: \"Global cross-Region inference, priced 10% below in-Region and Geo, and identical to OpenAI's own list price. Cross-Region inference launched 2026-08-17. Cache writes (30m TTL) are billed at 1.25x the input rate. Above 272K input tokens, input and cache are 2x and output 1.5x. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. AWS cut Sol input prices by 20% and output prices by 33.3% on 2026-08-21, quoting the global rates ($4 input, $20 output); the unconstrained entry keeps the four days of rates between global availability and the cut. Refs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-sol.html, https://aws.amazon.com/about-aws/whats-new/2026/08/bedrock-openai-gpt-56-sol-reduced-pricing/, https://aws.amazon.com/about-aws/whats-new/2026/08/amazon-bedrock-cross-region-openai-v2/, https://aws.amazon.com/about-aws/whats-new/2026/08/gpt-sol-terra-luna-long-context-bedrock/\",\n prices: [\n {\n prices: {\n input_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_write_mtok: {\n base: 6.25,\n tiers: [\n {\n start: 272000,\n price: 12.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 272000,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 30,\n tiers: [\n {\n start: 272000,\n price: 45\n }\n ]\n }\n }\n },\n {\n constraint: {\n start_date: \"2026-08-21\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: {\n base: 4,\n tiers: [\n {\n start: 272000,\n price: 8\n }\n ]\n },\n cache_write_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_read_mtok: {\n base: 0.4,\n tiers: [\n {\n start: 272000,\n price: 0.8\n }\n ]\n },\n output_mtok: {\n base: 20,\n tiers: [\n {\n start: 272000,\n price: 30\n }\n ]\n }\n }\n }\n ]\n },\n {\n id: \"global.openai.gpt-5.6-terra\",\n name: \"GPT-5.6 Terra (global)\",\n match: {\n contains: \"global.openai.gpt-5.6-terra\"\n },\n context_window: 1e6,\n price_comments: \"Global cross-Region inference, priced 10% below in-Region and Geo, and identical to OpenAI's own list price. Cross-Region inference for the GPT-5.6 models launched on 2026-08-17. Cache writes (30m TTL) are billed at 1.25x the input rate. Above 272K input tokens, input and cache are 2x and output 1.5x. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. The 20% Terra price cut of 2026-07-30 predates global availability, so there is no earlier global rate to record. Refs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-terra.html, https://aws.amazon.com/about-aws/whats-new/2026/08/amazon-bedrock-cross-region-openai-v2/, https://aws.amazon.com/about-aws/whats-new/2026/08/gpt-sol-terra-luna-long-context-bedrock/\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 12,\n tiers: [\n {\n start: 272000,\n price: 18\n }\n ]\n }\n }\n },\n {\n id: \"global.openai.gpt-6-astra\",\n name: \"GPT-6 Astra (global)\",\n match: {\n contains: \"global.openai.gpt-6-astra\"\n },\n context_window: 1050000,\n price_comments: \"Global cross-Region inference at OpenAI's own list price; In-Region and Geo add 10% on top of it. Launched on Bedrock 2026-09-08. Cache writes (30m TTL) are billed at 1.25x the input rate. Above 272K input tokens, input and cache are 2x and output 1.5x; AWS bills 272K or fewer at the short-context rate, so tier starts are 272000. Ref: https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-6-astra.html\",\n prices: {\n input_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 20\n }\n ]\n },\n cache_write_mtok: {\n base: 12.5,\n tiers: [\n {\n start: 272000,\n price: 25\n }\n ]\n },\n cache_read_mtok: {\n base: 1,\n tiers: [\n {\n start: 272000,\n price: 2\n }\n ]\n },\n output_mtok: {\n base: 50,\n tiers: [\n {\n start: 272000,\n price: 75\n }\n ]\n }\n }\n },\n {\n id: \"global.openai.gpt-6-luna\",\n name: \"GPT-6 Luna (global)\",\n match: {\n contains: \"global.openai.gpt-6-luna\"\n },\n context_window: 1e6,\n price_comments: \"Launched on Bedrock 2026-09-22. AWS had not yet published a model card or rate table for Luna, so these rates follow the rule every Bedrock OpenAI model card states: Global CRIS is OpenAI's list price, and In-Region/Geo add 10%. GPT-6 Astra's card confirms the rule for the GPT-6 family. Cache writes (30m TTL) are 1.25x input. Above 272K input tokens, input and cache are 2x and output 1.5x (tier start 272000). Refs: https://aws.amazon.com/about-aws/whats-new/2026/09/openai-gpt-6-sol-luna-on-amazon-bedrock/, https://developers.openai.com/api/docs/models/gpt-6-luna\",\n prices: {\n input_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n cache_write_mtok: {\n base: 0.125,\n tiers: [\n {\n start: 272000,\n price: 0.25\n }\n ]\n },\n cache_read_mtok: {\n base: 0.01,\n tiers: [\n {\n start: 272000,\n price: 0.02\n }\n ]\n },\n output_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 272000,\n price: 0.75\n }\n ]\n }\n }\n },\n {\n id: \"global.openai.gpt-6-sol\",\n name: \"GPT-6 Sol (global)\",\n match: {\n contains: \"global.openai.gpt-6-sol\"\n },\n context_window: 1e6,\n price_comments: \"Launched on Bedrock 2026-09-22. AWS had not yet published a model card or rate table for Sol, so these rates follow the rule every Bedrock OpenAI model card states: Global CRIS is OpenAI's list price, and In-Region/Geo add 10%. GPT-6 Astra's card confirms the rule for the GPT-6 family. Cache writes (30m TTL) are 1.25x input. Above 272K input tokens, input and cache are 2x and output 1.5x (tier start 272000). Refs: https://aws.amazon.com/about-aws/whats-new/2026/09/openai-gpt-6-sol-luna-on-amazon-bedrock/, https://developers.openai.com/api/docs/models/gpt-6-sol\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 15\n }\n ]\n }\n }\n },\n {\n id: \"global.openai.gpt-6.1-sol\",\n name: \"GPT-6.1 Sol (global)\",\n match: {\n contains: \"global.openai.gpt-6.1-sol\"\n },\n context_window: 1e6,\n price_comments: \"Launched on Bedrock 2026-09-29. Global cross-Region inference, identical to OpenAI's own list price and 10% below In-Region and Geo. Cache reads are 5% of input and cache writes 1.25x. AWS bills the whole request at long-context rates above 272K input tokens: input and cache are 2x and output 1.5x. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. Refs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-6-1-sol.html, https://developers.openai.com/api/docs/models/gpt-6.1-sol\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n output_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 15\n }\n ]\n }\n }\n },\n {\n id: \"google.gemma-3-12b-it\",\n name: \"Gemma 3 12B IT\",\n match: {\n contains: \"google.gemma-3-12b-it\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.09,\n output_mtok: 0.29\n }\n },\n {\n id: \"google.gemma-3-27b-it\",\n name: \"Gemma 3 27B IT\",\n match: {\n contains: \"google.gemma-3-27b-it\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.23,\n output_mtok: 0.38\n }\n },\n {\n id: \"google.gemma-3-4b-it\",\n name: \"Gemma 3 4B IT\",\n match: {\n contains: \"google.gemma-3-4b-it\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.08\n }\n },\n {\n id: \"meta.llama3-1-70b-instruct-v1:0\",\n name: \"Llama 3.1 70B Instruct\",\n match: {\n contains: \"meta.llama3-1-70b-instruct\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.72,\n output_mtok: 0.72\n }\n },\n {\n id: \"meta.llama3-1-8b-instruct-v1:0\",\n name: \"Llama 3.1 8B Instruct\",\n match: {\n contains: \"meta.llama3-1-8b-instruct\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.22,\n output_mtok: 0.22\n }\n },\n {\n id: \"meta.llama3-2-11b-instruct-v1:0\",\n name: \"Llama 3.2 11B Instruct\",\n match: {\n contains: \"meta.llama3-2-11b-instruct\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.16,\n output_mtok: 0.16\n }\n },\n {\n id: \"meta.llama3-2-1b-instruct-v1:0\",\n name: \"Llama 3.2 1B Instruct\",\n match: {\n contains: \"meta.llama3-2-1b-instruct\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"meta.llama3-2-3b-instruct-v1:0\",\n name: \"Llama 3.2 3B Instruct\",\n match: {\n contains: \"meta.llama3-2-3b-instruct\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.15\n }\n },\n {\n id: \"meta.llama3-2-90b-instruct-v1:0\",\n name: \"Llama 3.2 90B Instruct\",\n match: {\n contains: \"meta.llama3-2-90b-instruct\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.72,\n output_mtok: 0.72\n }\n },\n {\n id: \"meta.llama3-3-70b-instruct-v1:0\",\n name: \"Llama 3.3 70B Instruct\",\n match: {\n contains: \"meta.llama3-3-70b-instruct\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.72,\n output_mtok: 0.72\n }\n },\n {\n id: \"meta.llama3-70b-instruct-v1:0\",\n name: \"Llama 3 70B Instruct\",\n match: {\n contains: \"meta.llama3-70b-instruct\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 2.65,\n output_mtok: 3.5\n }\n },\n {\n id: \"meta.llama3-8b-instruct-v1:0\",\n name: \"Llama 3 8B Instruct\",\n match: {\n contains: \"meta.llama3-8b-instruct\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.6\n }\n },\n {\n id: \"meta.llama4-maverick-17b-instruct-v1:0\",\n name: \"Llama 4 Maverick 17B Instruct\",\n match: {\n contains: \"meta.llama4-maverick-17b-instruct\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.24,\n output_mtok: 0.97\n }\n },\n {\n id: \"meta.llama4-scout-17b-instruct-v1:0\",\n name: \"Llama 4 Scout 17B Instruct\",\n match: {\n contains: \"meta.llama4-scout-17b-instruct\"\n },\n context_window: 1e7,\n prices: {\n input_mtok: 0.17,\n output_mtok: 0.66\n }\n },\n {\n id: \"mistral.devstral-2-123b\",\n name: \"Devstral 2 123B\",\n match: {\n contains: \"mistral.devstral-2-123b\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.4,\n output_mtok: 2\n }\n },\n {\n id: \"mistral.magistral-small-2509\",\n name: \"Magistral Small 2509\",\n match: {\n contains: \"mistral.magistral-small-2509\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.5\n }\n },\n {\n id: \"mistral.ministral-3-14b-instruct\",\n name: \"Ministral 14B 3.0\",\n match: {\n contains: \"mistral.ministral-3-14b-instruct\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"mistral.ministral-3-3b-instruct\",\n name: \"Ministral 3B 3.0\",\n match: {\n contains: \"mistral.ministral-3-3b-instruct\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"mistral.ministral-3-8b-instruct\",\n name: \"Ministral 8B 3.0\",\n match: {\n contains: \"mistral.ministral-3-8b-instruct\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.15\n }\n },\n {\n id: \"mistral.mistral-7b-instruct-v0:2\",\n name: \"Mistral 7B Instruct\",\n match: {\n contains: \"mistral.mistral-7b-instruct-v0\"\n },\n context_window: 32000,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.2\n }\n },\n {\n id: \"mistral.mistral-large-2402-v1:0\",\n name: \"Mistral Large (24.02)\",\n match: {\n contains: \"mistral.mistral-large-2402\"\n },\n context_window: 32000,\n prices: {\n input_mtok: 4,\n output_mtok: 12\n }\n },\n {\n id: \"mistral.mistral-large-3-675b-instruct\",\n name: \"Mistral Large 3\",\n match: {\n contains: \"mistral.mistral-large-3-675b-instruct\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.5\n }\n },\n {\n id: \"mistral.mistral-small-2402-v1:0\",\n name: \"Mistral Small (24.02)\",\n match: {\n contains: \"mistral.mistral-small-2402\"\n },\n context_window: 32000,\n prices: {\n input_mtok: 1,\n output_mtok: 3\n }\n },\n {\n id: \"mistral.mixtral-8x7b-instruct-v0:1\",\n name: \"Mixtral 8x7B Instruct\",\n match: {\n contains: \"mistral.mixtral-8x7b-instruct-v0\"\n },\n context_window: 32000,\n prices: {\n input_mtok: 0.45,\n output_mtok: 0.7\n }\n },\n {\n id: \"mistral.pixtral-large-2502-v1:0\",\n name: \"Pixtral Large (25.02)\",\n match: {\n contains: \"mistral.pixtral-large-2502\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 2,\n output_mtok: 6\n }\n },\n {\n id: \"mistral.voxtral-mini-3b-2507\",\n name: \"Voxtral Mini 3B 2507\",\n match: {\n contains: \"mistral.voxtral-mini-3b-2507\"\n },\n context_window: 32000,\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.04\n }\n },\n {\n id: \"mistral.voxtral-small-24b-2507\",\n name: \"Voxtral Small 24B 2507\",\n match: {\n contains: \"mistral.voxtral-small-24b-2507\"\n },\n context_window: 32000,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.3\n }\n },\n {\n id: \"nvidia.nemotron-nano-3-30b:0\",\n name: \"Nemotron 3 Nano 30B\",\n match: {\n contains: \"nvidia.nemotron-nano-3-30b\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.24\n }\n },\n {\n id: \"nvidia.nemotron-nano-9b-v2:0\",\n name: \"Nemotron 2 Nano 9B\",\n match: {\n contains: \"nvidia.nemotron-nano-9b-v2\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.23\n }\n },\n {\n id: \"nvidia.nemotron-super-3-120b:0\",\n name: \"Nemotron 3 Super 120B\",\n match: {\n contains: \"nvidia.nemotron-super-3-120b\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.65\n }\n },\n {\n id: \"openai.gpt-5.4\",\n name: \"GPT-5.4\",\n match: {\n equals: \"openai.gpt-5.4\"\n },\n prices: {\n input_mtok: 2.75,\n cache_read_mtok: 0.275,\n output_mtok: 16.5\n }\n },\n {\n id: \"openai.gpt-5.5\",\n name: \"GPT-5.5\",\n match: {\n equals: \"openai.gpt-5.5\"\n },\n prices: {\n input_mtok: 5.5,\n cache_read_mtok: 0.55,\n output_mtok: 33\n }\n },\n {\n id: \"openai.gpt-oss-120b-1:0\",\n name: \"gpt-oss-120b\",\n match: {\n contains: \"openai.gpt-oss-120b-1\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n }\n },\n {\n id: \"openai.gpt-oss-20b-1:0\",\n name: \"gpt-oss-20b\",\n match: {\n contains: \"openai.gpt-oss-20b-1\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.3\n }\n },\n {\n id: \"qwen.qwen3-32b-v1:0\",\n name: \"Qwen3 32B (dense)\",\n match: {\n contains: \"qwen.qwen3-32b\"\n },\n context_window: 32000,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n }\n },\n {\n id: \"qwen.qwen3-coder-30b-a3b-v1:0\",\n name: \"Qwen3-Coder-30B-A3B-Instruct\",\n match: {\n contains: \"qwen.qwen3-coder-30b-a3b\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n }\n },\n {\n id: \"qwen.qwen3-coder-480b-a35b-v1:0\",\n name: \"Qwen3-Coder-480B-A35B-Instruct\",\n match: {\n contains: \"qwen.qwen3-coder-480b-a35b\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.45,\n output_mtok: 1.8\n }\n },\n {\n id: \"qwen.qwen3-vl-235b-a22b-v1:0\",\n name: \"Qwen3-VL-235B-A22B-Instruct\",\n match: {\n contains: \"qwen.qwen3-vl-235b-a22b\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.53,\n output_mtok: 2.66\n }\n },\n {\n id: \"regional.amazon.nova-2-lite-v1:0\",\n name: \"Nova 2 Lite\",\n description: \"Amazon Nova 2 Lite is an advanced multimodal reasoning model that intelligently balances performance and efficiency by dynamically adjusting reasoning depth based on task complexity. With flexible controls for developers to adjust the reasoning process, Nova 2 Lite delivers superior results for agentic workflows across software development, consumer experiences and enterprise application.\",\n match: {\n or: [\n {\n contains: \"us.amazon.nova-2-lite\"\n },\n {\n contains: \"eu.amazon.nova-2-lite\"\n },\n {\n contains: \"jp.amazon.nova-2-lite\"\n }\n ]\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.33,\n cache_read_mtok: 0.0825,\n output_mtok: 2.75\n }\n },\n {\n id: \"regional.anthropic.claude-3-5-haiku-20241022-v1:0\",\n match: {\n contains: \"claude-3-5-haiku-20241022\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 0.8,\n cache_write_mtok: 1,\n cache_read_mtok: 0.08,\n output_mtok: 4\n }\n },\n {\n id: \"regional.anthropic.claude-3-5-sonnet-20240620-v1:0\",\n match: {\n contains: \"claude-3-5-sonnet-20240620\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"regional.anthropic.claude-3-5-sonnet-20241022-v2:0\",\n match: {\n contains: \"claude-3-5-sonnet-20241022\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"regional.anthropic.claude-3-7-sonnet-20250219-v1:0\",\n match: {\n contains: \"claude-3-7-sonnet-20250219\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"regional.anthropic.claude-3-haiku-20240307-v1:0\",\n match: {\n contains: \"claude-3-haiku-20240307\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 0.25,\n output_mtok: 1.25\n }\n },\n {\n id: \"regional.anthropic.claude-3-opus-20240229-v1:0\",\n match: {\n contains: \"claude-3-opus-20240229\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 15,\n output_mtok: 75\n }\n },\n {\n id: \"regional.anthropic.claude-3-sonnet-20240229-v1:0\",\n match: {\n contains: \"claude-3-sonnet-20240229\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"regional.anthropic.claude-fable-5-1-v1:0\",\n match: {\n or: [\n {\n equals: \"anthropic.claude-fable-5-1\"\n },\n {\n equals: \"claude-fable-5-1\"\n },\n {\n starts_with: \"anthropic.claude-fable-5-1-v1\"\n },\n {\n starts_with: \"claude-fable-5-1-v1\"\n },\n {\n equals: \"us.anthropic.claude-fable-5-1\"\n },\n {\n contains: \"us.anthropic.claude-fable-5-1-v1\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Regional endpoint: 10% premium over the global endpoint. Fable 5.1 regional is currently the us. inference profile only; the AWS price list has no EU, AU or JP regional rate. Cache hits are 0.025x base input (not the usual 0.1x), unique to Fable 5.1 and Mythos 5.1. Ref: https://platform.claude.com/docs/en/build-with-claude/claude-in-amazon-bedrock#regions One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 11,\n cache_write_mtok: 13.75,\n cache_read_mtok: 0.275,\n output_mtok: 55,\n cache_write_1h_mtok: 22\n }\n },\n {\n id: \"regional.anthropic.claude-fable-5-v1:0\",\n match: {\n or: [\n {\n equals: \"anthropic.claude-fable-5\"\n },\n {\n equals: \"claude-fable-5\"\n },\n {\n starts_with: \"anthropic.claude-fable-5-v1\"\n },\n {\n starts_with: \"claude-fable-5-v1\"\n },\n {\n equals: \"us.anthropic.claude-fable-5\"\n },\n {\n equals: \"au.anthropic.claude-fable-5\"\n },\n {\n equals: \"eu.anthropic.claude-fable-5\"\n },\n {\n contains: \"us.anthropic.claude-fable-5-v1\"\n },\n {\n contains: \"au.anthropic.claude-fable-5-v1\"\n },\n {\n contains: \"eu.anthropic.claude-fable-5-v1\"\n },\n {\n contains: \"jp.anthropic.claude-fable-5-v1\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1). The price list carries regional Fable 5 rates for US, EU and AU regions, but none for JP.\",\n prices: {\n input_mtok: 11,\n cache_write_mtok: 13.75,\n cache_read_mtok: 1.1,\n output_mtok: 55,\n cache_write_1h_mtok: 22\n }\n },\n {\n id: \"regional.anthropic.claude-haiku-4-5-20251001-v1:0\",\n match: {\n or: [\n {\n starts_with: \"anthropic.claude-haiku-4-5-20251001\"\n },\n {\n starts_with: \"claude-haiku-4-5-20251001\"\n },\n {\n contains: \"us.anthropic.claude-haiku-4-5-20251001\"\n },\n {\n contains: \"au.anthropic.claude-haiku-4-5-20251001\"\n },\n {\n contains: \"apac.anthropic.claude-haiku-4-5-20251001\"\n },\n {\n contains: \"eu.anthropic.claude-haiku-4-5-20251001\"\n },\n {\n contains: \"us-gov.anthropic.claude-haiku-4-5-20251001\"\n },\n {\n contains: \"jp.anthropic.claude-haiku-4-5-20251001\"\n },\n {\n contains: \"in.anthropic.claude-haiku-4-5-20251001\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 1.1,\n cache_write_mtok: 1.375,\n cache_read_mtok: 0.11,\n output_mtok: 5.5,\n cache_write_1h_mtok: 2.2\n }\n },\n {\n id: \"regional.anthropic.claude-haiku-5-5\",\n match: {\n or: [\n {\n equals: \"anthropic.claude-haiku-5-5\"\n },\n {\n equals: \"claude-haiku-5-5\"\n },\n {\n starts_with: \"anthropic.claude-haiku-5-5-v1\"\n },\n {\n starts_with: \"claude-haiku-5-5-v1\"\n },\n {\n equals: \"us.anthropic.claude-haiku-5-5\"\n },\n {\n equals: \"au.anthropic.claude-haiku-5-5\"\n },\n {\n equals: \"apac.anthropic.claude-haiku-5-5\"\n },\n {\n equals: \"eu.anthropic.claude-haiku-5-5\"\n },\n {\n equals: \"us-gov.anthropic.claude-haiku-5-5\"\n },\n {\n equals: \"jp.anthropic.claude-haiku-5-5\"\n },\n {\n contains: \"us.anthropic.claude-haiku-5-5-v1\"\n },\n {\n contains: \"au.anthropic.claude-haiku-5-5-v1\"\n },\n {\n contains: \"apac.anthropic.claude-haiku-5-5-v1\"\n },\n {\n contains: \"eu.anthropic.claude-haiku-5-5-v1\"\n },\n {\n contains: \"us-gov.anthropic.claude-haiku-5-5-v1\"\n },\n {\n contains: \"jp.anthropic.claude-haiku-5-5-v1\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: 'Regional endpoints and inference profiles carry a 10% premium over the global endpoint. Prompts over 100,000 tokens bill every token at the long-context rate. Ref: AWS price list API, AmazonBedrockFoundationModels \"Claude Haiku 5.5 (Amazon Bedrock Edition)\" (https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonBedrockFoundationModels/current/us-east-1/index.json)',\n prices: {\n input_mtok: {\n base: 0.11,\n tiers: [\n {\n start: 1e5,\n price: 0.55\n }\n ]\n },\n cache_write_mtok: {\n base: 0.1375,\n tiers: [\n {\n start: 1e5,\n price: 0.6875\n }\n ]\n },\n cache_read_mtok: {\n base: 0.011,\n tiers: [\n {\n start: 1e5,\n price: 0.055\n }\n ]\n },\n output_mtok: {\n base: 0.55,\n tiers: [\n {\n start: 1e5,\n price: 2.75\n }\n ]\n },\n cache_write_1h_mtok: {\n base: 0.22,\n tiers: [\n {\n start: 1e5,\n price: 1.1\n }\n ]\n }\n }\n },\n {\n id: \"regional.anthropic.claude-opus-4-1-20250805-v1:0\",\n match: {\n or: [\n {\n starts_with: \"anthropic.claude-opus-4-1-20250805\"\n },\n {\n starts_with: \"claude-opus-4-1-20250805\"\n },\n {\n contains: \"us.anthropic.claude-opus-4-1-20250805\"\n },\n {\n contains: \"au.anthropic.claude-opus-4-1-20250805\"\n },\n {\n contains: \"apac.anthropic.claude-opus-4-1-20250805\"\n },\n {\n contains: \"eu.anthropic.claude-opus-4-1-20250805\"\n },\n {\n contains: \"us-gov.anthropic.claude-opus-4-1-20250805\"\n },\n {\n contains: \"jp.anthropic.claude-opus-4-1-20250805\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 15,\n cache_write_mtok: 18.75,\n cache_read_mtok: 1.5,\n output_mtok: 75\n }\n },\n {\n id: \"regional.anthropic.claude-opus-4-20250514-v1:0\",\n match: {\n or: [\n {\n starts_with: \"anthropic.claude-opus-4-20250514\"\n },\n {\n starts_with: \"claude-opus-4-20250514\"\n },\n {\n contains: \"us.anthropic.claude-opus-4-20250514\"\n },\n {\n contains: \"au.anthropic.claude-opus-4-20250514\"\n },\n {\n contains: \"apac.anthropic.claude-opus-4-20250514\"\n },\n {\n contains: \"eu.anthropic.claude-opus-4-20250514\"\n },\n {\n contains: \"us-gov.anthropic.claude-opus-4-20250514\"\n },\n {\n contains: \"jp.anthropic.claude-opus-4-20250514\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 15,\n cache_write_mtok: 18.75,\n cache_read_mtok: 1.5,\n output_mtok: 75\n }\n },\n {\n id: \"regional.anthropic.claude-opus-4-5-v1:0\",\n match: {\n or: [\n {\n starts_with: \"anthropic.claude-opus-4-5\"\n },\n {\n starts_with: \"claude-opus-4-5\"\n },\n {\n contains: \"us.anthropic.claude-opus-4-5\"\n },\n {\n contains: \"au.anthropic.claude-opus-4-5\"\n },\n {\n contains: \"apac.anthropic.claude-opus-4-5\"\n },\n {\n contains: \"eu.anthropic.claude-opus-4-5\"\n },\n {\n contains: \"us-gov.anthropic.claude-opus-4-5\"\n },\n {\n contains: \"jp.anthropic.claude-opus-4-5\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 5.5,\n cache_write_mtok: 6.875,\n cache_read_mtok: 0.55,\n output_mtok: 27.5,\n cache_write_1h_mtok: 11\n }\n },\n {\n id: \"regional.anthropic.claude-opus-4-6-v1:0\",\n match: {\n or: [\n {\n starts_with: \"anthropic.claude-opus-4-6\"\n },\n {\n starts_with: \"claude-opus-4-6\"\n },\n {\n contains: \"us.anthropic.claude-opus-4-6\"\n },\n {\n contains: \"au.anthropic.claude-opus-4-6\"\n },\n {\n contains: \"apac.anthropic.claude-opus-4-6\"\n },\n {\n contains: \"eu.anthropic.claude-opus-4-6\"\n },\n {\n contains: \"us-gov.anthropic.claude-opus-4-6\"\n },\n {\n contains: \"jp.anthropic.claude-opus-4-6\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 5.5,\n cache_write_mtok: 6.875,\n cache_read_mtok: 0.55,\n output_mtok: 27.5,\n cache_write_1h_mtok: 11\n }\n },\n {\n id: \"regional.anthropic.claude-opus-4-7-v1:0\",\n match: {\n or: [\n {\n starts_with: \"anthropic.claude-opus-4-7\"\n },\n {\n starts_with: \"claude-opus-4-7\"\n },\n {\n contains: \"us.anthropic.claude-opus-4-7\"\n },\n {\n contains: \"au.anthropic.claude-opus-4-7\"\n },\n {\n contains: \"apac.anthropic.claude-opus-4-7\"\n },\n {\n contains: \"eu.anthropic.claude-opus-4-7\"\n },\n {\n contains: \"us-gov.anthropic.claude-opus-4-7\"\n },\n {\n contains: \"jp.anthropic.claude-opus-4-7\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 5.5,\n cache_write_mtok: 6.875,\n cache_read_mtok: 0.55,\n output_mtok: 27.5,\n cache_write_1h_mtok: 11\n }\n },\n {\n id: \"regional.anthropic.claude-opus-4-8-v1:0\",\n match: {\n or: [\n {\n starts_with: \"anthropic.claude-opus-4-8\"\n },\n {\n starts_with: \"claude-opus-4-8\"\n },\n {\n contains: \"us.anthropic.claude-opus-4-8\"\n },\n {\n contains: \"au.anthropic.claude-opus-4-8\"\n },\n {\n contains: \"eu.anthropic.claude-opus-4-8\"\n },\n {\n contains: \"jp.anthropic.claude-opus-4-8\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 5.5,\n cache_write_mtok: 6.875,\n cache_read_mtok: 0.55,\n output_mtok: 27.5,\n cache_write_1h_mtok: 11\n }\n },\n {\n id: \"regional.anthropic.claude-opus-5\",\n match: {\n or: [\n {\n equals: \"anthropic.claude-opus-5\"\n },\n {\n equals: \"claude-opus-5\"\n },\n {\n starts_with: \"anthropic.claude-opus-5-v1\"\n },\n {\n starts_with: \"claude-opus-5-v1\"\n },\n {\n equals: \"us.anthropic.claude-opus-5\"\n },\n {\n equals: \"au.anthropic.claude-opus-5\"\n },\n {\n equals: \"eu.anthropic.claude-opus-5\"\n },\n {\n equals: \"jp.anthropic.claude-opus-5\"\n },\n {\n equals: \"in.anthropic.claude-opus-5\"\n },\n {\n contains: \"us.anthropic.claude-opus-5-v1\"\n },\n {\n contains: \"au.anthropic.claude-opus-5-v1\"\n },\n {\n contains: \"eu.anthropic.claude-opus-5-v1\"\n },\n {\n contains: \"jp.anthropic.claude-opus-5-v1\"\n },\n {\n contains: \"in.anthropic.claude-opus-5-v1\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Regional endpoints and US/EU/JP/AU/IN inference profiles carry a 10% premium over the global endpoint. Ref: https://platform.claude.com/docs/en/build-with-claude/claude-in-amazon-bedrock#regions IN rates from the AWS price list API, AmazonBedrockFoundationModels (ap-south-1, ap-south-2). One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 5.5,\n cache_write_mtok: 6.875,\n cache_read_mtok: 0.55,\n output_mtok: 27.5,\n cache_write_1h_mtok: 11\n }\n },\n {\n id: \"regional.anthropic.claude-opus-5-5\",\n match: {\n or: [\n {\n equals: \"anthropic.claude-opus-5-5\"\n },\n {\n equals: \"claude-opus-5-5\"\n },\n {\n starts_with: \"anthropic.claude-opus-5-5-v1\"\n },\n {\n starts_with: \"claude-opus-5-5-v1\"\n },\n {\n equals: \"us.anthropic.claude-opus-5-5\"\n },\n {\n equals: \"au.anthropic.claude-opus-5-5\"\n },\n {\n equals: \"eu.anthropic.claude-opus-5-5\"\n },\n {\n equals: \"jp.anthropic.claude-opus-5-5\"\n },\n {\n contains: \"us.anthropic.claude-opus-5-5-v1\"\n },\n {\n contains: \"au.anthropic.claude-opus-5-5-v1\"\n },\n {\n contains: \"eu.anthropic.claude-opus-5-5-v1\"\n },\n {\n contains: \"jp.anthropic.claude-opus-5-5-v1\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: 'Regional endpoints and US/EU/JP/AU inference profiles carry a 10% premium over the global endpoint. Cache hits are 0.05x base input (not the usual 0.1x), as on Sonnet 5.5. Ref: AWS price list API, AmazonBedrockFoundationModels \"Claude Opus 5.5 (Amazon Bedrock Edition)\" (https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonBedrockFoundationModels/current/us-east-1/index.json)',\n prices: {\n input_mtok: 4.4,\n cache_write_mtok: 5.5,\n cache_read_mtok: 0.22,\n output_mtok: 22,\n cache_write_1h_mtok: 8.8\n }\n },\n {\n id: \"regional.anthropic.claude-sonnet-4-20250514-v1:0\",\n match: {\n or: [\n {\n starts_with: \"anthropic.claude-sonnet-4-20250514\"\n },\n {\n starts_with: \"claude-sonnet-4-20250514\"\n },\n {\n contains: \"us.anthropic.claude-sonnet-4-20250514\"\n },\n {\n contains: \"au.anthropic.claude-sonnet-4-20250514\"\n },\n {\n contains: \"apac.anthropic.claude-sonnet-4-20250514\"\n },\n {\n contains: \"eu.anthropic.claude-sonnet-4-20250514\"\n },\n {\n contains: \"us-gov.anthropic.claude-sonnet-4-20250514\"\n },\n {\n contains: \"jp.anthropic.claude-sonnet-4-20250514\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"regional.anthropic.claude-sonnet-4-5-20250929-v1:0\",\n match: {\n or: [\n {\n starts_with: \"anthropic.claude-sonnet-4-5-20250929\"\n },\n {\n starts_with: \"claude-sonnet-4-5-20250929\"\n },\n {\n contains: \"us.anthropic.claude-sonnet-4-5-20250929\"\n },\n {\n contains: \"au.anthropic.claude-sonnet-4-5-20250929\"\n },\n {\n contains: \"apac.anthropic.claude-sonnet-4-5-20250929\"\n },\n {\n contains: \"eu.anthropic.claude-sonnet-4-5-20250929\"\n },\n {\n contains: \"us-gov.anthropic.claude-sonnet-4-5-20250929\"\n },\n {\n contains: \"jp.anthropic.claude-sonnet-4-5-20250929\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 3.3,\n cache_write_mtok: 4.125,\n cache_read_mtok: 0.33,\n output_mtok: 16.5,\n cache_write_1h_mtok: 6.6\n }\n },\n {\n id: \"regional.anthropic.claude-sonnet-4-6-v1:0\",\n match: {\n or: [\n {\n starts_with: \"anthropic.claude-sonnet-4-6\"\n },\n {\n starts_with: \"claude-sonnet-4-6\"\n },\n {\n contains: \"us.anthropic.claude-sonnet-4-6\"\n },\n {\n contains: \"au.anthropic.claude-sonnet-4-6\"\n },\n {\n contains: \"apac.anthropic.claude-sonnet-4-6\"\n },\n {\n contains: \"eu.anthropic.claude-sonnet-4-6\"\n },\n {\n contains: \"us-gov.anthropic.claude-sonnet-4-6\"\n },\n {\n contains: \"jp.anthropic.claude-sonnet-4-6\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 3.3,\n cache_write_mtok: 4.125,\n cache_read_mtok: 0.33,\n output_mtok: 16.5,\n cache_write_1h_mtok: 6.6\n }\n },\n {\n id: \"regional.anthropic.claude-sonnet-5-5\",\n match: {\n or: [\n {\n equals: \"anthropic.claude-sonnet-5-5\"\n },\n {\n equals: \"claude-sonnet-5-5\"\n },\n {\n starts_with: \"anthropic.claude-sonnet-5-5-v1\"\n },\n {\n starts_with: \"claude-sonnet-5-5-v1\"\n },\n {\n equals: \"us.anthropic.claude-sonnet-5-5\"\n },\n {\n equals: \"au.anthropic.claude-sonnet-5-5\"\n },\n {\n equals: \"apac.anthropic.claude-sonnet-5-5\"\n },\n {\n equals: \"eu.anthropic.claude-sonnet-5-5\"\n },\n {\n equals: \"us-gov.anthropic.claude-sonnet-5-5\"\n },\n {\n equals: \"jp.anthropic.claude-sonnet-5-5\"\n },\n {\n contains: \"us.anthropic.claude-sonnet-5-5-v1\"\n },\n {\n contains: \"au.anthropic.claude-sonnet-5-5-v1\"\n },\n {\n contains: \"apac.anthropic.claude-sonnet-5-5-v1\"\n },\n {\n contains: \"eu.anthropic.claude-sonnet-5-5-v1\"\n },\n {\n contains: \"us-gov.anthropic.claude-sonnet-5-5-v1\"\n },\n {\n contains: \"jp.anthropic.claude-sonnet-5-5-v1\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: 'Regional endpoints and inference profiles carry a 10% premium over the global endpoint. Cache hits are 0.05x base input (not the usual 0.1x), as on Opus 5.5. Ref: AWS price list API, AmazonBedrockFoundationModels \"Claude Sonnet 5.5 (Amazon Bedrock Edition)\" (https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonBedrockFoundationModels/current/us-east-1/index.json)',\n prices: {\n input_mtok: 2.2,\n cache_write_mtok: 2.75,\n cache_read_mtok: 0.11,\n output_mtok: 11,\n cache_write_1h_mtok: 4.4\n }\n },\n {\n id: \"regional.anthropic.claude-sonnet-5-v1:0\",\n match: {\n or: [\n {\n equals: \"anthropic.claude-sonnet-5\"\n },\n {\n equals: \"claude-sonnet-5\"\n },\n {\n starts_with: \"anthropic.claude-sonnet-5-v1\"\n },\n {\n starts_with: \"claude-sonnet-5-v1\"\n },\n {\n equals: \"us.anthropic.claude-sonnet-5\"\n },\n {\n equals: \"au.anthropic.claude-sonnet-5\"\n },\n {\n equals: \"apac.anthropic.claude-sonnet-5\"\n },\n {\n equals: \"eu.anthropic.claude-sonnet-5\"\n },\n {\n equals: \"us-gov.anthropic.claude-sonnet-5\"\n },\n {\n equals: \"jp.anthropic.claude-sonnet-5\"\n },\n {\n equals: \"in.anthropic.claude-sonnet-5\"\n },\n {\n contains: \"us.anthropic.claude-sonnet-5-v1\"\n },\n {\n contains: \"au.anthropic.claude-sonnet-5-v1\"\n },\n {\n contains: \"apac.anthropic.claude-sonnet-5-v1\"\n },\n {\n contains: \"eu.anthropic.claude-sonnet-5-v1\"\n },\n {\n contains: \"us-gov.anthropic.claude-sonnet-5-v1\"\n },\n {\n contains: \"jp.anthropic.claude-sonnet-5-v1\"\n },\n {\n contains: \"in.anthropic.claude-sonnet-5-v1\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Regional/cross-region endpoints carry a 10% premium over global. The launch rates are now permanent, with no 2026-09-01 increase. Refs: https://aws.amazon.com/bedrock/pricing/, https://platform.claude.com/docs/en/about-claude/pricing One-hour cache write price from the AWS price list API, AmazonBedrockFoundationModels (us-east-1).\",\n prices: {\n input_mtok: 2.2,\n cache_write_mtok: 2.75,\n cache_read_mtok: 0.22,\n output_mtok: 11,\n cache_write_1h_mtok: 4.4\n }\n },\n {\n id: \"regional.openai.gpt-5.6-luna\",\n name: \"GPT-5.6 Luna (regional)\",\n match: {\n or: [\n {\n starts_with: \"openai.gpt-5.6-luna\"\n },\n {\n starts_with: \"gpt-5.6-luna\"\n },\n {\n contains: \"us.openai.gpt-5.6-luna\"\n },\n {\n contains: \"in.openai.gpt-5.6-luna\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"In-Region and Geo (`us.`/`in.`) inference, 10% above global. Cache writes (30m TTL) are billed at 1.25x input; above 272K input tokens, input and cache are 2x and output 1.5x. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. AWS cut Luna prices by 80% on 2026-07-30; the unconstrained entry preserves the launch (2026-07-13) rates. The long-context tier arrived with the 1M context window on 2026-08-03, so the launch entry stays flat. Refs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-luna.html, https://aws.amazon.com/about-aws/whats-new/2026/07/openai-gpt-terra-luna-pricing-bedrock/, https://aws.amazon.com/about-aws/whats-new/2026/08/gpt-sol-terra-luna-long-context-bedrock/\",\n prices: [\n {\n prices: {\n input_mtok: 1.1,\n cache_write_mtok: 1.375,\n cache_read_mtok: 0.11,\n output_mtok: 6.6\n }\n },\n {\n constraint: {\n start_date: \"2026-07-30\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: {\n base: 0.22,\n tiers: [\n {\n start: 272000,\n price: 0.44\n }\n ]\n },\n cache_write_mtok: {\n base: 0.275,\n tiers: [\n {\n start: 272000,\n price: 0.55\n }\n ]\n },\n cache_read_mtok: {\n base: 0.022,\n tiers: [\n {\n start: 272000,\n price: 0.044\n }\n ]\n },\n output_mtok: {\n base: 1.32,\n tiers: [\n {\n start: 272000,\n price: 1.98\n }\n ]\n }\n }\n }\n ]\n },\n {\n id: \"regional.openai.gpt-5.6-sol\",\n name: \"GPT-5.6 Sol (regional)\",\n match: {\n or: [\n {\n starts_with: \"openai.gpt-5.6-sol\"\n },\n {\n starts_with: \"gpt-5.6-sol\"\n },\n {\n contains: \"us.openai.gpt-5.6-sol\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"In-Region and Geo (`us.`) inference, 10% above global. Cache writes (30m TTL) are billed at 1.25x input; above 272K input tokens, input and cache are 2x and output 1.5x. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. Sol input prices fell 20% and output prices 33.3% on 2026-08-21; the announcement quotes the global rates, and the in-Region and Geo rates moved with them. Refs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-sol.html, https://aws.amazon.com/about-aws/whats-new/2026/08/bedrock-openai-gpt-56-sol-reduced-pricing/, https://aws.amazon.com/about-aws/whats-new/2026/08/gpt-sol-terra-luna-long-context-bedrock/\",\n prices: [\n {\n prices: {\n input_mtok: {\n base: 5.5,\n tiers: [\n {\n start: 272000,\n price: 11\n }\n ]\n },\n cache_write_mtok: {\n base: 6.875,\n tiers: [\n {\n start: 272000,\n price: 13.75\n }\n ]\n },\n cache_read_mtok: {\n base: 0.55,\n tiers: [\n {\n start: 272000,\n price: 1.1\n }\n ]\n },\n output_mtok: {\n base: 33,\n tiers: [\n {\n start: 272000,\n price: 49.5\n }\n ]\n }\n }\n },\n {\n constraint: {\n start_date: \"2026-08-21\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: {\n base: 4.4,\n tiers: [\n {\n start: 272000,\n price: 8.8\n }\n ]\n },\n cache_write_mtok: {\n base: 5.5,\n tiers: [\n {\n start: 272000,\n price: 11\n }\n ]\n },\n cache_read_mtok: {\n base: 0.44,\n tiers: [\n {\n start: 272000,\n price: 0.88\n }\n ]\n },\n output_mtok: {\n base: 22,\n tiers: [\n {\n start: 272000,\n price: 33\n }\n ]\n }\n }\n }\n ]\n },\n {\n id: \"regional.openai.gpt-5.6-terra\",\n name: \"GPT-5.6 Terra (regional)\",\n match: {\n or: [\n {\n starts_with: \"openai.gpt-5.6-terra\"\n },\n {\n starts_with: \"gpt-5.6-terra\"\n },\n {\n contains: \"us.openai.gpt-5.6-terra\"\n },\n {\n contains: \"in.openai.gpt-5.6-terra\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"In-Region and Geo (`us.`/`in.`) inference, 10% above global. Cache writes (30m TTL) are billed at 1.25x input; above 272K input tokens, input and cache are 2x and output 1.5x. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. AWS cut Terra prices by 20% on 2026-07-30; the unconstrained entry preserves the launch (2026-07-13) rates. The long-context tier arrived with the 1M context window on 2026-08-03, so the launch entry stays flat. Refs: https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-56-terra.html, https://aws.amazon.com/about-aws/whats-new/2026/07/openai-gpt-terra-luna-pricing-bedrock/, https://aws.amazon.com/about-aws/whats-new/2026/08/gpt-sol-terra-luna-long-context-bedrock/\",\n prices: [\n {\n prices: {\n input_mtok: 2.75,\n cache_write_mtok: 3.4375,\n cache_read_mtok: 0.275,\n output_mtok: 16.5\n }\n },\n {\n constraint: {\n start_date: \"2026-07-30\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: {\n base: 2.2,\n tiers: [\n {\n start: 272000,\n price: 4.4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.75,\n tiers: [\n {\n start: 272000,\n price: 5.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.22,\n tiers: [\n {\n start: 272000,\n price: 0.44\n }\n ]\n },\n output_mtok: {\n base: 13.2,\n tiers: [\n {\n start: 272000,\n price: 19.8\n }\n ]\n }\n }\n }\n ]\n },\n {\n id: \"regional.openai.gpt-6-astra\",\n name: \"GPT-6 Astra (regional)\",\n match: {\n or: [\n {\n starts_with: \"openai.gpt-6-astra\"\n },\n {\n starts_with: \"gpt-6-astra\"\n },\n {\n contains: \"us.openai.gpt-6-astra\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"In-Region and Geo (`us.`) inference, 10% above global. Cache writes (30m TTL) are billed at 1.25x input. Above 272K input tokens, input and cache are 2x and output 1.5x; AWS bills 272K or fewer at the short-context rate, so tier starts are 272000. Ref: https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-6-astra.html\",\n prices: {\n input_mtok: {\n base: 11,\n tiers: [\n {\n start: 272000,\n price: 22\n }\n ]\n },\n cache_write_mtok: {\n base: 13.75,\n tiers: [\n {\n start: 272000,\n price: 27.5\n }\n ]\n },\n cache_read_mtok: {\n base: 1.1,\n tiers: [\n {\n start: 272000,\n price: 2.2\n }\n ]\n },\n output_mtok: {\n base: 55,\n tiers: [\n {\n start: 272000,\n price: 82.5\n }\n ]\n }\n }\n },\n {\n id: \"regional.openai.gpt-6-luna\",\n name: \"GPT-6 Luna (regional)\",\n match: {\n or: [\n {\n starts_with: \"openai.gpt-6-luna\"\n },\n {\n starts_with: \"gpt-6-luna\"\n },\n {\n contains: \"us.openai.gpt-6-luna\"\n },\n {\n contains: \"in.openai.gpt-6-luna\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"In-Region and Geo (`us.`/`in.`) inference, 10% above global. AWS had not yet published a model card or rate table for Luna; see the global entry for how these rates were derived. Ref: https://aws.amazon.com/about-aws/whats-new/2026/09/openai-gpt-6-sol-luna-on-amazon-bedrock/\",\n prices: {\n input_mtok: {\n base: 0.11,\n tiers: [\n {\n start: 272000,\n price: 0.22\n }\n ]\n },\n cache_write_mtok: {\n base: 0.1375,\n tiers: [\n {\n start: 272000,\n price: 0.275\n }\n ]\n },\n cache_read_mtok: {\n base: 0.011,\n tiers: [\n {\n start: 272000,\n price: 0.022\n }\n ]\n },\n output_mtok: {\n base: 0.55,\n tiers: [\n {\n start: 272000,\n price: 0.825\n }\n ]\n }\n }\n },\n {\n id: \"regional.openai.gpt-6-sol\",\n name: \"GPT-6 Sol (regional)\",\n match: {\n or: [\n {\n starts_with: \"openai.gpt-6-sol\"\n },\n {\n starts_with: \"gpt-6-sol\"\n },\n {\n contains: \"us.openai.gpt-6-sol\"\n },\n {\n contains: \"in.openai.gpt-6-sol\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"In-Region and Geo (`us.`/`in.`) inference, 10% above global. AWS had not yet published a model card or rate table for Sol; see the global entry for how these rates were derived. Ref: https://aws.amazon.com/about-aws/whats-new/2026/09/openai-gpt-6-sol-luna-on-amazon-bedrock/\",\n prices: {\n input_mtok: {\n base: 2.2,\n tiers: [\n {\n start: 272000,\n price: 4.4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.75,\n tiers: [\n {\n start: 272000,\n price: 5.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.22,\n tiers: [\n {\n start: 272000,\n price: 0.44\n }\n ]\n },\n output_mtok: {\n base: 11,\n tiers: [\n {\n start: 272000,\n price: 16.5\n }\n ]\n }\n }\n },\n {\n id: \"regional.openai.gpt-6.1-sol\",\n name: \"GPT-6.1 Sol (regional)\",\n match: {\n or: [\n {\n starts_with: \"openai.gpt-6.1-sol\"\n },\n {\n starts_with: \"gpt-6.1-sol\"\n },\n {\n contains: \"us.openai.gpt-6.1-sol\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"In-Region (`bedrock-mantle` in us-east-1) and US Geo (`us.`) inference, 10% above global. AWS offers no `in.` profile for 6.1 Sol. See the global entry for tiering and cache notes. Ref: https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-6-1-sol.html\",\n prices: {\n input_mtok: {\n base: 2.2,\n tiers: [\n {\n start: 272000,\n price: 4.4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.75,\n tiers: [\n {\n start: 272000,\n price: 5.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.11,\n tiers: [\n {\n start: 272000,\n price: 0.22\n }\n ]\n },\n output_mtok: {\n base: 11,\n tiers: [\n {\n start: 272000,\n price: 16.5\n }\n ]\n }\n }\n },\n {\n id: \"writer.palmyra-x4-v1:0\",\n name: \"Palmyra X4\",\n match: {\n contains: \"writer.palmyra-x4\"\n },\n context_window: 128000,\n price_comments: \"Bedrock serves Palmyra X4 through cross-region inference profiles, so the model reference arrives prefixed, e.g. 'us.writer.palmyra-x4-v1:0'. Pricing is flat across regions. Ref: https://aws.amazon.com/bedrock/pricing/\",\n prices: {\n input_mtok: 2.5,\n output_mtok: 10\n }\n },\n {\n id: \"writer.palmyra-x5-v1:0\",\n name: \"Palmyra X5\",\n match: {\n contains: \"writer.palmyra-x5\"\n },\n context_window: 128000,\n price_comments: \"Bedrock serves Palmyra X5 through cross-region inference profiles, so the model reference arrives prefixed, e.g. 'us.writer.palmyra-x5-v1:0'. Pricing is flat across regions. Ref: https://aws.amazon.com/bedrock/pricing/\",\n prices: {\n input_mtok: 0.6,\n output_mtok: 6\n }\n }\n ]\n },\n {\n id: \"azure\",\n name: \"Microsoft Azure\",\n pricing_urls: [\"https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/#pricing\"],\n api_pattern: \"(https?://)?([^.]*\\\\.)?(?:openai\\\\.azure\\\\.com|azure-api\\\\.net|cognitiveservices\\\\.azure\\\\.com|services\\\\.ai\\\\.azure\\\\.com)\",\n price_comments: 'These are prices for \"*-Global\" models, prices for \"Regional\" models are often slightly higher. Retired models are listed at https://learn.microsoft.com/th-th/azure/ai-foundry/openai/concepts/legacy-models',\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"responses\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"input_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"input_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"output_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"realtime\",\n root: [\"response\", \"usage\"],\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"text_tokens\"],\n dest: \"input_text_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"image_tokens\"],\n dest: \"input_image_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"cached_tokens_details\", \"text_tokens\"],\n dest: \"cache_text_read_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"cached_tokens_details\", \"audio_tokens\"],\n dest: \"cache_audio_read_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"cached_tokens_details\", \"image_tokens\"],\n dest: \"cache_image_read_tokens\",\n required: false\n },\n {\n path: [\"output_token_details\", \"text_tokens\"],\n dest: \"output_text_tokens\",\n required: false\n },\n {\n path: [\"output_token_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: false\n }\n ]\n },\n {\n api_flavor: \"embeddings\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"anthropic\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"cache_creation_input_tokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"cache_read_input_tokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"cache_creation_input_tokens\",\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"cache_creation\", \"ephemeral_5m_input_tokens\"],\n dest: \"cache_write_5m_tokens\",\n required: false\n },\n {\n path: [\"cache_creation\", \"ephemeral_1h_input_tokens\"],\n dest: \"cache_write_1h_tokens\",\n required: false\n },\n {\n path: \"cache_read_input_tokens\",\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n fallback_model_providers: [\"openai\", \"anthropic\", \"deepseek\", \"x-ai\", \"moonshotai\"],\n models: [\n {\n id: \"ada\",\n match: {\n or: [\n {\n equals: \"ada\"\n },\n {\n equals: \"text-embedding-ada\"\n },\n {\n equals: \"text-embedding-ada-002\"\n },\n {\n equals: \"text-embedding-ada-002-v2\"\n }\n ]\n },\n prices: {\n input_mtok: 0.1\n }\n },\n {\n id: \"babbage\",\n match: {\n or: [\n {\n equals: \"babbage\"\n },\n {\n equals: \"babbage-002\"\n }\n ]\n },\n prices: {\n input_mtok: 0.4\n }\n },\n {\n id: \"curie\",\n match: {\n or: [\n {\n equals: \"curie\"\n },\n {\n equals: \"text-curie\"\n },\n {\n equals: \"text-curie-001\"\n }\n ]\n },\n prices: {\n input_mtok: 2\n }\n },\n {\n id: \"davinci\",\n match: {\n or: [\n {\n equals: \"davinci\"\n },\n {\n equals: \"davinci-002\"\n },\n {\n equals: \"text-davinci\"\n },\n {\n equals: \"text-davinci-002\"\n }\n ]\n },\n prices: {\n input_mtok: 2\n }\n },\n {\n id: \"mai-ds-r1:free\",\n name: \"MAI DS R1 (free)\",\n description: \"MAI-DS-R1 is a post-trained variant of DeepSeek-R1 developed by the Microsoft AI team to improve the model's responsiveness on previously blocked topics while enhancing its safety profile. Built on top of DeepSeek-R1's reasoning foundation, it integrates 110k examples from the Tulu-3 SFT dataset and 350k internally curated multilingual safety-alignment samples. The model retains strong reasoning, coding, and problem-solving capabilities, while unblocking a wide range of prompts previously restricted in R1.\",\n match: {\n equals: \"mai-ds-r1:free\"\n },\n context_window: 163840,\n prices: {}\n },\n {\n id: \"o1\",\n match: {\n or: [\n {\n equals: \"o1\"\n },\n {\n equals: \"o1-2024-12-17\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 15,\n cache_read_mtok: 7.5,\n output_mtok: 60\n }\n },\n {\n id: \"o1-mini\",\n match: {\n or: [\n {\n equals: \"o1-mini\"\n },\n {\n equals: \"o1-mini-2024-09-12\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 1.1,\n cache_read_mtok: 0.55,\n output_mtok: 4.4\n }\n },\n {\n id: \"o1-preview\",\n match: {\n or: [\n {\n equals: \"o1-preview\"\n },\n {\n equals: \"o1-preview-2024-09-12\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 15,\n cache_read_mtok: 7.5,\n output_mtok: 60\n }\n },\n {\n id: \"o3-2025-04-16\",\n match: {\n or: [\n {\n equals: \"o3\"\n },\n {\n equals: \"o3-2025-04-16\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.5,\n output_mtok: 8\n }\n },\n {\n id: \"o3-mini\",\n match: {\n or: [\n {\n equals: \"o3-mini\"\n },\n {\n equals: \"o3-mini-2025-01-31\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 1.1,\n cache_read_mtok: 0.55,\n output_mtok: 4.4\n }\n },\n {\n id: \"o4-mini\",\n match: {\n or: [\n {\n contains: \"o4-mini\"\n },\n {\n contains: \"o4-mini-2025-04-16\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 1.1,\n cache_read_mtok: 0.28,\n output_mtok: 4.4\n }\n },\n {\n id: \"phi-3-medium-128k-instruct\",\n name: \"Phi-3 Medium 128K Instruct\",\n description: \"Phi-3 128K Medium is a powerful 14-billion parameter model designed for advanced language understanding, reasoning, and instruction following. Optimized through supervised fine-tuning and preference adjustments, it excels in tasks involving common sense, mathematics, logical reasoning, and code processing.\",\n match: {\n equals: \"phi-3-medium-128k-instruct\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 1,\n output_mtok: 1\n }\n },\n {\n id: \"phi-3-mini-128k-instruct\",\n name: \"Phi-3 Mini 128K Instruct\",\n description: \"Phi-3 Mini is a powerful 3.8B parameter model designed for advanced language understanding, reasoning, and instruction following. Optimized through supervised fine-tuning and preference adjustments, it excels in tasks involving common sense, mathematics, logical reasoning, and code processing.\",\n match: {\n equals: \"phi-3-mini-128k-instruct\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"phi-3.5-mini-128k-instruct\",\n name: \"Phi-3.5 Mini 128K Instruct\",\n description: \"Phi-3.5 models are lightweight, state-of-the-art open models. These models were trained with Phi-3 datasets that include both synthetic data and the filtered, publicly available websites data, with a focus on high quality and reasoning-dense properties. Phi-3.5 Mini uses 3.8B parameters, and is a dense decoder-only transformer model using the same tokenizer as Phi-3 Mini.\",\n match: {\n equals: \"phi-3.5-mini-128k-instruct\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"phi-4\",\n name: \"Phi 4\",\n description: \"Microsoft Research Phi-4 is designed to perform well in complex reasoning tasks and can operate efficiently in situations with limited memory or where quick responses are needed.\",\n match: {\n equals: \"phi-4\"\n },\n context_window: 16384,\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.14\n }\n },\n {\n id: \"phi-4-mini-instruct\",\n name: \"Phi 4 Mini Instruct\",\n description: \"Phi-4-mini-instruct is a lightweight open model built upon synthetic data and filtered publicly available websites, with a focus on high-quality, reasoning-dense data.\",\n match: {\n equals: \"phi-4-mini-instruct\"\n },\n context_window: 131072,\n price_comments: \"Imported from OpenRouter pricing; verify against Azure AI Foundry when native pricing is published.\",\n prices: {\n input_mtok: 0.08,\n output_mtok: 0.35\n }\n },\n {\n id: \"phi-4-multimodal-instruct\",\n name: \"Phi 4 Multimodal Instruct\",\n description: \"Phi-4 Multimodal Instruct is a versatile 5.6B parameter foundation model that combines advanced reasoning and instruction-following capabilities across both text and visual inputs, providing accurate text outputs. The unified architecture enables efficient, low-latency inference, suitable for edge and mobile deployments. Phi-4 Multimodal Instruct supports text inputs in multiple languages including Arabic, Chinese, English, French, German, Japanese, Spanish, and more, with visual input optimized primarily for English. It delivers impressive performance on multimodal tasks involving mathematical, scientific, and document reasoning, providing developers and enterprises a powerful yet compact model for sophisticated interactive applications. For more information, see the Phi-4 Multimodal blog post.\",\n match: {\n equals: \"phi-4-multimodal-instruct\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.1\n }\n },\n {\n id: \"phi-4-reasoning-plus\",\n name: \"Phi 4 Reasoning Plus\",\n description: \"Phi-4-reasoning-plus is an enhanced 14B parameter model from Microsoft, fine-tuned from Phi-4 with additional reinforcement learning to boost accuracy on math, science, and code reasoning tasks. It uses the same dense decoder-only transformer architecture as Phi-4, but generates longer, more comprehensive outputs structured into a step-by-step reasoning trace and final answer.\",\n match: {\n equals: \"phi-4-reasoning-plus\"\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.35\n }\n },\n {\n id: \"phi-4-reasoning-plus:free\",\n name: \"Phi 4 Reasoning Plus (free)\",\n description: \"Phi-4-reasoning-plus is an enhanced 14B parameter model from Microsoft, fine-tuned from Phi-4 with additional reinforcement learning to boost accuracy on math, science, and code reasoning tasks. It uses the same dense decoder-only transformer architecture as Phi-4, but generates longer, more comprehensive outputs structured into a step-by-step reasoning trace and final answer.\",\n match: {\n equals: \"phi-4-reasoning-plus:free\"\n },\n context_window: 32768,\n prices: {}\n },\n {\n id: \"phi-4-reasoning:free\",\n name: \"Phi 4 Reasoning (free)\",\n description: \"Phi-4-reasoning is a 14B parameter dense decoder-only transformer developed by Microsoft, fine-tuned from Phi-4 to enhance complex reasoning capabilities. It uses a combination of supervised fine-tuning on chain-of-thought traces and reinforcement learning, targeting math, science, and code reasoning tasks. With a 32k context window and high inference efficiency, it is optimized for structured responses in a two-part format: reasoning trace followed by a final solution.\",\n match: {\n equals: \"phi-4-reasoning:free\"\n },\n context_window: 32768,\n prices: {}\n },\n {\n id: \"text-embedding-3-large\",\n match: {\n equals: \"text-embedding-3-large\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.13\n }\n },\n {\n id: \"text-embedding-3-small\",\n match: {\n equals: \"text-embedding-3-small\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.02\n }\n },\n {\n id: \"wizardlm-2-8x22b\",\n name: \"WizardLM-2 8x22B\",\n description: \"WizardLM-2 8x22B is Microsoft AI's most advanced Wizard model. It demonstrates highly competitive performance compared to leading proprietary models, and it consistently outperforms all existing state-of-the-art opensource models.\",\n match: {\n equals: \"wizardlm-2-8x22b\"\n },\n prices: {\n input_mtok: 0.48,\n output_mtok: 0.48\n }\n }\n ]\n },\n {\n id: \"baseten\",\n name: \"Baseten\",\n pricing_urls: [\"https://www.baseten.co/pricing/\", \"https://docs.baseten.co/inference/model-apis/overview\"],\n api_pattern: \"https://inference\\\\.baseten\\\\.co(?:/|$)\",\n description: \"OpenAI- and Anthropic-compatible hosted inference through Baseten Model APIs.\",\n price_comments: \"Covers the complete public Model APIs pricing table. Baseten automatically caches prompt prefixes and bills cached input at the published cache-input rate. Context windows use the catalog's displayed values in thousands of tokens. Model IDs use third-party namespaces, so they do not infer the Baseten provider without a Baseten provider ID or API URL.\",\n provider_match: {\n contains: \"baseten\"\n },\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"anthropic\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"cache_read_input_tokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"cache_read_input_tokens\",\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"deepseek-ai/DeepSeek-V4-Flash-0731\",\n name: \"DeepSeek V4 Flash 0731\",\n match: {\n equals: \"deepseek-ai/DeepSeek-V4-Flash-0731\"\n },\n context_window: 1048000,\n prices: {\n input_mtok: 0.13,\n cache_read_mtok: 0.028,\n output_mtok: 0.26\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V4-Pro\",\n name: \"DeepSeek V4 Pro\",\n match: {\n equals: \"deepseek-ai/DeepSeek-V4-Pro\"\n },\n context_window: 1048000,\n price_comments: \"No longer listed in the Baseten Model APIs catalog (https://docs.baseten.co/inference/model-apis/overview, checked 2026-10-09); the dated V4 Pro 0813 endpoint took its place.\",\n prices: {\n input_mtok: 1.74,\n cache_read_mtok: 0.145,\n output_mtok: 3.48\n },\n deprecated: true\n },\n {\n id: \"deepseek-ai/DeepSeek-V4-Pro-0813\",\n name: \"DeepSeek V4 Pro 0813\",\n match: {\n equals: \"deepseek-ai/DeepSeek-V4-Pro-0813\"\n },\n context_window: 1048000,\n prices: {\n input_mtok: 1.32,\n cache_read_mtok: 0.132,\n output_mtok: 3.96\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V4.1-Flash\",\n name: \"DeepSeek V4.1 Flash\",\n match: {\n equals: \"deepseek-ai/DeepSeek-V4.1-Flash\"\n },\n context_window: 1048000,\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.007,\n output_mtok: 1.2\n }\n },\n {\n id: \"moonshotai/Kimi-K2.6\",\n name: \"Kimi K2.6\",\n match: {\n equals: \"moonshotai/Kimi-K2.6\"\n },\n context_window: 262000,\n price_comments: \"No longer listed in the Baseten Model APIs catalog (https://docs.baseten.co/inference/model-apis/overview, checked 2026-10-09).\",\n prices: {\n input_mtok: 0.95,\n cache_read_mtok: 0.16,\n output_mtok: 4\n },\n deprecated: true\n },\n {\n id: \"moonshotai/Kimi-K2.7-Code\",\n name: \"Kimi K2.7 Code\",\n match: {\n equals: \"moonshotai/Kimi-K2.7-Code\"\n },\n context_window: 262000,\n price_comments: \"No longer listed in the Baseten Model APIs catalog (https://docs.baseten.co/inference/model-apis/overview, checked 2026-10-09).\",\n prices: {\n input_mtok: 0.95,\n cache_read_mtok: 0.16,\n output_mtok: 4\n },\n deprecated: true\n },\n {\n id: \"moonshotai/Kimi-K3\",\n name: \"Kimi K3\",\n match: {\n equals: \"moonshotai/Kimi-K3\"\n },\n context_window: 1048000,\n prices: {\n input_mtok: 3,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B\",\n name: \"NVIDIA Nemotron 3 Ultra\",\n match: {\n equals: \"nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B\"\n },\n context_window: 202000,\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.12,\n output_mtok: 2.4\n }\n },\n {\n id: \"openai/gpt-oss-120b\",\n name: \"GPT OSS 120B\",\n match: {\n equals: \"openai/gpt-oss-120b\"\n },\n context_window: 128000,\n price_comments: \"The pricing table does not publish a separate cached-input rate for this model.\",\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.5\n }\n },\n {\n id: \"thinkingmachines/inkling\",\n name: \"Inkling\",\n match: {\n equals: \"thinkingmachines/inkling\"\n },\n context_window: 1048000,\n price_comments: \"No longer listed in the Baseten Model APIs catalog (https://docs.baseten.co/inference/model-apis/overview, checked 2026-10-09).\",\n prices: {\n input_mtok: 1,\n cache_read_mtok: 0.17,\n output_mtok: 4.05\n },\n deprecated: true\n },\n {\n id: \"thinkingmachines/inkling-small\",\n name: \"Inkling Small\",\n match: {\n equals: \"thinkingmachines/inkling-small\"\n },\n context_window: 1048000,\n price_comments: \"No longer listed in the Baseten Model APIs catalog (https://docs.baseten.co/inference/model-apis/overview, checked 2026-10-09).\",\n prices: {\n input_mtok: 0.5,\n cache_read_mtok: 0.1,\n output_mtok: 1.2\n },\n deprecated: true\n },\n {\n id: \"zai-org/GLM-4.7\",\n name: \"GLM 4.7\",\n match: {\n equals: \"zai-org/GLM-4.7\"\n },\n context_window: 200000,\n price_comments: \"No longer listed in the Baseten Model APIs catalog (https://docs.baseten.co/inference/model-apis/overview, checked 2026-10-09).\",\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.12,\n output_mtok: 2.2\n },\n deprecated: true\n },\n {\n id: \"zai-org/GLM-5.2\",\n name: \"GLM 5.2\",\n match: {\n equals: \"zai-org/GLM-5.2\"\n },\n context_window: 1048000,\n prices: {\n input_mtok: 1.4,\n cache_read_mtok: 0.14,\n output_mtok: 4.4\n }\n },\n {\n id: \"zai-org/GLM-5.2-Fast\",\n name: \"GLM 5.2 Fast\",\n match: {\n equals: \"zai-org/GLM-5.2-Fast\"\n },\n context_window: 1048000,\n prices: {\n input_mtok: 2.1,\n cache_read_mtok: 0.21,\n output_mtok: 6.6\n }\n },\n {\n id: \"zai-org/GLM-5.3\",\n name: \"GLM 5.3\",\n match: {\n equals: \"zai-org/GLM-5.3\"\n },\n context_window: 1048000,\n prices: {\n input_mtok: 1.4,\n cache_read_mtok: 0.14,\n output_mtok: 4.4\n }\n },\n {\n id: \"zai-org/GLM-5.3-Fast\",\n name: \"GLM 5.3 Fast\",\n match: {\n equals: \"zai-org/GLM-5.3-Fast\"\n },\n context_window: 1048000,\n prices: {\n input_mtok: 2.1,\n cache_read_mtok: 0.21,\n output_mtok: 6.6\n }\n },\n {\n id: \"zai-org/GLM-5.3-Flash\",\n name: \"GLM 5.3 Flash\",\n match: {\n equals: \"zai-org/GLM-5.3-Flash\"\n },\n context_window: 1048000,\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.03,\n output_mtok: 0.5\n }\n }\n ]\n },\n {\n id: \"cerebras\",\n name: \"Cerebras\",\n pricing_urls: [\n \"https://www.cerebras.ai/pricing#pricing\",\n \"https://inference-docs.cerebras.ai/models/openai-oss\",\n \"https://inference-docs.cerebras.ai/models/gemma-4-31b\",\n \"https://inference-docs.cerebras.ai/support/change-log\"\n ],\n api_pattern: \"https://api\\\\.cerebras\\\\.ai\",\n model_match: {\n contains: \"cerebras\"\n },\n provider_match: {\n contains: \"cerebras\"\n },\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"gemma-4-31b\",\n name: \"Gemma 4 31B\",\n description: \"Google's Gemma 4 31B open model for chat, instruction following, and multilingual tasks, served with fast inference on Cerebras hardware (~1,850 tokens/second).\",\n match: {\n or: [\n {\n equals: \"gemma-4-31b\"\n },\n {\n starts_with: \"cerebras/gemma-4-31b\"\n },\n {\n starts_with: \"cerebras:gemma-4-31b\"\n }\n ]\n },\n context_window: 131072,\n price_comments: \"Developer tier pricing. Free tier: 65k context, Paid tier: 131k context.\",\n prices: {\n input_mtok: 0.99,\n output_mtok: 1.49\n }\n },\n {\n id: \"gpt-oss-120b\",\n name: \"GPT-OSS 120B\",\n description: \"OpenAI's flagship open source model, built on a Mixture-of-Experts (MoE) architecture with 120 billion parameters and 128 experts. Delivers frontier reasoning capabilities with record-breaking inference speeds on Cerebras hardware (~3,000 tokens/second).\",\n match: {\n or: [\n {\n equals: \"gpt-oss-120b\"\n },\n {\n starts_with: \"cerebras/gpt-oss-120b\"\n },\n {\n starts_with: \"cerebras:gpt-oss-120b\"\n }\n ]\n },\n context_window: 131072,\n price_comments: \"Developer tier pricing. Free tier: 65k context, Paid tier: 131k context.\",\n prices: {\n input_mtok: 0.35,\n output_mtok: 0.75\n }\n },\n {\n id: \"llama-3.3-70b\",\n name: \"Llama 3.3 70B\",\n description: \"Meta's enhanced 70B model delivering 405B-level accuracy. Optimized for chat, coding, instruction following, mathematics, and reasoning with high-speed inference on Cerebras hardware (~2,100 tokens/second).\",\n match: {\n or: [\n {\n equals: \"llama-3.3-70b\"\n },\n {\n starts_with: \"cerebras/llama-3.3-70b\"\n },\n {\n starts_with: \"cerebras:llama-3.3-70b\"\n }\n ]\n },\n context_window: 128000,\n price_comments: \"No longer listed in the Cerebras Inference docs (checked 2026-07-17 and 2026-08-27); last known rates kept for historical usage.\",\n prices: {\n input_mtok: 0.85,\n output_mtok: 1.2\n },\n deprecated: true\n },\n {\n id: \"llama3.1-8b\",\n name: \"Llama 3.1 8B\",\n description: \"Meta's Llama 3.1 8B model for general-purpose tasks including chat, coding, and instruction following. Optimized for fast inference on Cerebras hardware (~2,200 tokens/second).\",\n match: {\n or: [\n {\n equals: \"llama3.1-8b\"\n },\n {\n starts_with: \"cerebras/llama3.1-8b\"\n },\n {\n starts_with: \"cerebras:llama3.1-8b\"\n }\n ]\n },\n context_window: 32768,\n price_comments: \"No longer listed in the Cerebras Inference docs (checked 2026-07-17 and 2026-08-27); last known rates kept for historical usage.\",\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n },\n deprecated: true\n },\n {\n id: \"qwen-3-32b\",\n name: \"Qwen 3 32B\",\n description: \"Qwen's 32B parameter model with enhanced reasoning and coding capabilities. Supports both standard and reasoning modes for complex tasks, with fast inference speeds on Cerebras hardware (~2,600 tokens/second).\",\n match: {\n or: [\n {\n equals: \"qwen-3-32b\"\n },\n {\n starts_with: \"cerebras/qwen-3-32b\"\n },\n {\n starts_with: \"cerebras:qwen-3-32b\"\n }\n ]\n },\n context_window: 131072,\n price_comments: \"No longer listed in the Cerebras Inference docs (checked 2026-07-17 and 2026-08-27); last known rates kept for historical usage.\",\n prices: {\n input_mtok: 0.4,\n output_mtok: 0.8\n },\n deprecated: true\n },\n {\n id: \"qwen-3-coder-480b\",\n name: \"qwen-3-coder-480b\",\n match: {\n equals: \"qwen-3-coder-480b\"\n },\n price_comments: \"No longer listed in the Cerebras Inference models API (https://api.cerebras.ai/public/v1/models, last checked 2026-10-09); kept for historical usage.\",\n prices: {},\n deprecated: true\n },\n {\n id: \"qwen-3.8-27b\",\n name: \"Qwen 3.8 27B\",\n description: \"Qwen's 27B vision-language model for coding, research, and long-horizon agentic tasks.\",\n match: {\n or: [\n {\n equals: \"qwen-3.8-27b\"\n },\n {\n starts_with: \"cerebras/qwen-3.8-27b\"\n },\n {\n starts_with: \"cerebras:qwen-3.8-27b\"\n }\n ]\n },\n context_window: 65536,\n price_comments: \"Rates and context length from https://api.cerebras.ai/public/v1/models.\",\n prices: {\n input_mtok: 0.99,\n output_mtok: 1.49\n }\n },\n {\n id: \"zai-glm-4.7\",\n name: \"Z.ai GLM 4.7\",\n description: \"Z.ai's GLM 4.7, a 355 billion parameter model with reasoning enabled by default, served with fast inference on Cerebras hardware (~1,000 tokens/second).\",\n match: {\n or: [\n {\n equals: \"zai-glm-4.7\"\n },\n {\n starts_with: \"cerebras/zai-glm-4.7\"\n },\n {\n starts_with: \"cerebras:zai-glm-4.7\"\n }\n ]\n },\n context_window: 131072,\n price_comments: \"Deprecated by Cerebras on 2026-08-17 (see https://inference-docs.cerebras.ai/support/change-log) and no longer in the model catalog; last published developer tier rates kept for historical usage.\",\n prices: {\n input_mtok: 2.25,\n output_mtok: 2.75\n },\n deprecated: true\n }\n ]\n },\n {\n id: \"cloudflare\",\n name: \"Cloudflare Workers AI\",\n pricing_urls: [\n \"https://developers.cloudflare.com/workers-ai/platform/pricing/\",\n \"https://developers.cloudflare.com/workers-ai/models/\"\n ],\n api_pattern: \"https://api\\\\.cloudflare\\\\.com/client/v4/accounts/[^/]+/ai(?:/|$)\",\n price_comments: \"Paid Workers plan rates, expressed using Cloudflare's token-equivalent prices. Calculations do not apply the account-wide free allocation of 10,000 Neurons per day. This provider entry deliberately covers only models with token-equivalent prices. Image and audio models are omitted.\",\n model_match: {\n starts_with: \"@cf/\"\n },\n provider_match: {\n contains: \"cloudflare\"\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: [\"result\", \"usage\"],\n model_path: [\"result\", \"model\"],\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"responses\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"input_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"output_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"embeddings\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"decisions\",\n root: [\"result\", \"usage\"],\n model_path: [\"result\", \"model\"],\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"@cf/ai4bharat/indictrans2-en-indic-1B\",\n name: \"IndicTrans2 EN-Indic 1B\",\n match: {\n equals: \"@cf/ai4bharat/indictrans2-en-indic-1B\"\n },\n prices: {\n input_mtok: 0.342,\n output_mtok: 0.342\n }\n },\n {\n id: \"@cf/aisingapore/gemma-sea-lion-v4-27b-it\",\n name: \"Gemma SEA-LION v4 27B IT\",\n match: {\n equals: \"@cf/aisingapore/gemma-sea-lion-v4-27b-it\"\n },\n prices: {\n input_mtok: 0.351,\n output_mtok: 0.555\n }\n },\n {\n id: \"@cf/baai/bge-base-en-v1.5\",\n name: \"BGE Base English v1.5\",\n match: {\n equals: \"@cf/baai/bge-base-en-v1.5\"\n },\n prices: {\n input_mtok: 0.067\n }\n },\n {\n id: \"@cf/baai/bge-large-en-v1.5\",\n name: \"BGE Large English v1.5\",\n match: {\n equals: \"@cf/baai/bge-large-en-v1.5\"\n },\n prices: {\n input_mtok: 0.204\n }\n },\n {\n id: \"@cf/baai/bge-m3\",\n name: \"BGE-M3\",\n match: {\n equals: \"@cf/baai/bge-m3\"\n },\n prices: {\n input_mtok: 0.012\n }\n },\n {\n id: \"@cf/baai/bge-reranker-base\",\n name: \"BGE Reranker Base\",\n match: {\n equals: \"@cf/baai/bge-reranker-base\"\n },\n prices: {\n input_mtok: 0.003\n }\n },\n {\n id: \"@cf/baai/bge-small-en-v1.5\",\n name: \"BGE Small English v1.5\",\n match: {\n equals: \"@cf/baai/bge-small-en-v1.5\"\n },\n prices: {\n input_mtok: 0.02\n }\n },\n {\n id: \"@cf/cloudflare/clef\",\n name: \"Clef\",\n match: {\n or: [\n {\n equals: \"@cf/cloudflare/clef\"\n },\n {\n equals: \"clef\"\n }\n ]\n },\n context_window: 65536,\n price_comments: \"Input only, $0.24 per million tokens. https://developers.cloudflare.com/workers-ai/models/clef/\",\n prices: {\n input_mtok: 0.24\n }\n },\n {\n id: \"@cf/cloudflare/clef-flash\",\n name: \"Clef Flash\",\n match: {\n or: [\n {\n equals: \"@cf/cloudflare/clef-flash\"\n },\n {\n equals: \"clef-flash\"\n }\n ]\n },\n context_window: 65536,\n price_comments: \"Input only, $0.09 per million tokens. https://developers.cloudflare.com/workers-ai/models/clef-flash/\",\n prices: {\n input_mtok: 0.09\n }\n },\n {\n id: \"@cf/cloudflare/clef-omni\",\n name: \"Clef Omni\",\n match: {\n equals: \"@cf/cloudflare/clef-omni\"\n },\n context_window: 64000,\n price_comments: \"Input only, $0.15 per million tokens. https://developers.cloudflare.com/workers-ai/models/clef-omni/\",\n prices: {\n input_mtok: 0.15\n }\n },\n {\n id: \"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b\",\n name: \"DeepSeek R1 Distill Qwen 32B\",\n match: {\n equals: \"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b\"\n },\n prices: {\n input_mtok: 0.497,\n output_mtok: 4.881\n }\n },\n {\n id: \"@cf/deepseek-ai/deepseek-v4-flash-0731\",\n name: \"DeepSeek V4 Flash 0731\",\n match: {\n equals: \"@cf/deepseek-ai/deepseek-v4-flash-0731\"\n },\n prices: {\n input_mtok: 0.44,\n cache_read_mtok: 0.014,\n output_mtok: 1.32\n }\n },\n {\n id: \"@cf/deepseek-ai/deepseek-v4-pro-0813\",\n name: \"DeepSeek V4 Pro 0813\",\n match: {\n equals: \"@cf/deepseek-ai/deepseek-v4-pro-0813\"\n },\n prices: {\n input_mtok: 1.32,\n cache_read_mtok: 0.044,\n output_mtok: 3.96\n }\n },\n {\n id: \"@cf/google/gemma-3-12b-it\",\n name: \"Gemma 3 12B IT\",\n match: {\n equals: \"@cf/google/gemma-3-12b-it\"\n },\n prices: {\n input_mtok: 0.345,\n output_mtok: 0.556\n },\n deprecated: true\n },\n {\n id: \"@cf/google/gemma-4-26b-a4b-it\",\n name: \"Gemma 4 26B A4B IT\",\n match: {\n equals: \"@cf/google/gemma-4-26b-a4b-it\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.3\n }\n },\n {\n id: \"@cf/huggingface/distilbert-sst-2-int8\",\n name: \"DistilBERT SST-2 INT8\",\n match: {\n equals: \"@cf/huggingface/distilbert-sst-2-int8\"\n },\n prices: {\n input_mtok: 0.026\n }\n },\n {\n id: \"@cf/ibm-granite/granite-4.0-h-micro\",\n name: \"Granite 4.0 H Micro\",\n match: {\n equals: \"@cf/ibm-granite/granite-4.0-h-micro\"\n },\n prices: {\n input_mtok: 0.017,\n output_mtok: 0.112\n }\n },\n {\n id: \"@cf/meta/llama-2-7b-chat-fp16\",\n name: \"Llama 2 7B Chat FP16\",\n match: {\n equals: \"@cf/meta/llama-2-7b-chat-fp16\"\n },\n prices: {\n input_mtok: 0.556,\n output_mtok: 6.667\n },\n deprecated: true\n },\n {\n id: \"@cf/meta/llama-3-8b-instruct\",\n name: \"Llama 3 8B Instruct\",\n match: {\n equals: \"@cf/meta/llama-3-8b-instruct\"\n },\n prices: {\n input_mtok: 0.282,\n output_mtok: 0.827\n },\n deprecated: true\n },\n {\n id: \"@cf/meta/llama-3-8b-instruct-awq\",\n name: \"Llama 3 8B Instruct AWQ\",\n match: {\n equals: \"@cf/meta/llama-3-8b-instruct-awq\"\n },\n prices: {\n input_mtok: 0.123,\n output_mtok: 0.266\n },\n deprecated: true\n },\n {\n id: \"@cf/meta/llama-3.1-70b-instruct-fp8-fast\",\n name: \"Llama 3.1 70B Instruct FP8 Fast\",\n match: {\n equals: \"@cf/meta/llama-3.1-70b-instruct-fp8-fast\"\n },\n prices: {\n input_mtok: 0.293,\n output_mtok: 2.253\n }\n },\n {\n id: \"@cf/meta/llama-3.1-8b-instruct\",\n name: \"Llama 3.1 8B Instruct\",\n match: {\n equals: \"@cf/meta/llama-3.1-8b-instruct\"\n },\n prices: {\n input_mtok: 0.282,\n output_mtok: 0.827\n },\n deprecated: true\n },\n {\n id: \"@cf/meta/llama-3.1-8b-instruct-awq\",\n name: \"Llama 3.1 8B Instruct AWQ\",\n match: {\n equals: \"@cf/meta/llama-3.1-8b-instruct-awq\"\n },\n prices: {\n input_mtok: 0.123,\n output_mtok: 0.266\n },\n deprecated: true\n },\n {\n id: \"@cf/meta/llama-3.1-8b-instruct-fp8\",\n name: \"Llama 3.1 8B Instruct FP8\",\n match: {\n equals: \"@cf/meta/llama-3.1-8b-instruct-fp8\"\n },\n prices: {\n input_mtok: 0.152,\n output_mtok: 0.287\n }\n },\n {\n id: \"@cf/meta/llama-3.1-8b-instruct-fp8-fast\",\n name: \"Llama 3.1 8B Instruct FP8 Fast\",\n match: {\n or: [\n {\n equals: \"@cf/meta/llama-3.1-8b-instruct-fp8-fast\"\n },\n {\n equals: \"@cf/meta/llama-3.1-8b-instruct-fast\"\n }\n ]\n },\n price_comments: \"Cloudflare's pricing table uses the fp8-fast identifier while the model catalog documents the fast identifier.\",\n prices: {\n input_mtok: 0.045,\n output_mtok: 0.384\n }\n },\n {\n id: \"@cf/meta/llama-3.2-11b-vision-instruct\",\n name: \"Llama 3.2 11B Vision Instruct\",\n match: {\n equals: \"@cf/meta/llama-3.2-11b-vision-instruct\"\n },\n prices: {\n input_mtok: 0.049,\n output_mtok: 0.676\n }\n },\n {\n id: \"@cf/meta/llama-3.2-1b-instruct\",\n name: \"Llama 3.2 1B Instruct\",\n match: {\n equals: \"@cf/meta/llama-3.2-1b-instruct\"\n },\n prices: {\n input_mtok: 0.027,\n output_mtok: 0.201\n }\n },\n {\n id: \"@cf/meta/llama-3.2-3b-instruct\",\n name: \"Llama 3.2 3B Instruct\",\n match: {\n equals: \"@cf/meta/llama-3.2-3b-instruct\"\n },\n prices: {\n input_mtok: 0.051,\n output_mtok: 0.335\n }\n },\n {\n id: \"@cf/meta/llama-3.3-70b-instruct-fp8-fast\",\n name: \"Llama 3.3 70B Instruct FP8 Fast\",\n match: {\n equals: \"@cf/meta/llama-3.3-70b-instruct-fp8-fast\"\n },\n context_window: 24000,\n prices: {\n input_mtok: 0.293,\n output_mtok: 2.253\n }\n },\n {\n id: \"@cf/meta/llama-4-scout-17b-16e-instruct\",\n name: \"Llama 4 Scout 17B 16E Instruct\",\n match: {\n equals: \"@cf/meta/llama-4-scout-17b-16e-instruct\"\n },\n prices: {\n input_mtok: 0.27,\n output_mtok: 0.85\n }\n },\n {\n id: \"@cf/meta/llama-guard-3-8b\",\n name: \"Llama Guard 3 8B\",\n match: {\n equals: \"@cf/meta/llama-guard-3-8b\"\n },\n prices: {\n input_mtok: 0.484,\n output_mtok: 0.03\n }\n },\n {\n id: \"@cf/meta/m2m100-1.2b\",\n name: \"M2M100 1.2B\",\n match: {\n equals: \"@cf/meta/m2m100-1.2b\"\n },\n prices: {\n input_mtok: 0.342,\n output_mtok: 0.342\n }\n },\n {\n id: \"@cf/mistral/mistral-7b-instruct-v0.1\",\n name: \"Mistral 7B Instruct v0.1\",\n match: {\n equals: \"@cf/mistral/mistral-7b-instruct-v0.1\"\n },\n prices: {\n input_mtok: 0.11,\n output_mtok: 0.19\n },\n deprecated: true\n },\n {\n id: \"@cf/mistralai/mistral-small-3.1-24b-instruct\",\n name: \"Mistral Small 3.1 24B Instruct\",\n match: {\n equals: \"@cf/mistralai/mistral-small-3.1-24b-instruct\"\n },\n prices: {\n input_mtok: 0.351,\n output_mtok: 0.555\n }\n },\n {\n id: \"@cf/moondream/moondream3.1-9B-A2B\",\n name: \"Moondream 3.1 9B A2B\",\n match: {\n equals: \"@cf/moondream/moondream3.1-9B-A2B\"\n },\n prices: {\n input_mtok: 0.3,\n output_mtok: 1\n }\n },\n {\n id: \"@cf/moonshotai/kimi-k2.5\",\n name: \"Kimi K2.5\",\n match: {\n equals: \"@cf/moonshotai/kimi-k2.5\"\n },\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.1,\n output_mtok: 3\n },\n deprecated: true\n },\n {\n id: \"@cf/moonshotai/kimi-k2.6\",\n name: \"Kimi K2.6\",\n match: {\n equals: \"@cf/moonshotai/kimi-k2.6\"\n },\n prices: {\n input_mtok: 0.95,\n cache_read_mtok: 0.16,\n output_mtok: 4\n }\n },\n {\n id: \"@cf/moonshotai/kimi-k2.7-code\",\n name: \"Kimi K2.7 Code\",\n match: {\n equals: \"@cf/moonshotai/kimi-k2.7-code\"\n },\n prices: {\n input_mtok: 0.95,\n cache_read_mtok: 0.19,\n output_mtok: 4\n }\n },\n {\n id: \"@cf/nvidia/nemotron-3-120b-a12b\",\n name: \"Nemotron 3 120B A12B\",\n match: {\n equals: \"@cf/nvidia/nemotron-3-120b-a12b\"\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.5\n }\n },\n {\n id: \"@cf/openai/gpt-oss-120b\",\n name: \"GPT-OSS 120B\",\n match: {\n equals: \"@cf/openai/gpt-oss-120b\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.35,\n output_mtok: 0.75\n }\n },\n {\n id: \"@cf/openai/gpt-oss-20b\",\n name: \"GPT-OSS 20B\",\n match: {\n equals: \"@cf/openai/gpt-oss-20b\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.3\n }\n },\n {\n id: \"@cf/pfnet/plamo-embedding-1b\",\n name: \"PLaMo Embedding 1B\",\n match: {\n equals: \"@cf/pfnet/plamo-embedding-1b\"\n },\n prices: {\n input_mtok: 0.019\n }\n },\n {\n id: \"@cf/qwen/qwen2.5-coder-32b-instruct\",\n name: \"Qwen2.5 Coder 32B Instruct\",\n match: {\n equals: \"@cf/qwen/qwen2.5-coder-32b-instruct\"\n },\n prices: {\n input_mtok: 0.66,\n output_mtok: 1\n }\n },\n {\n id: \"@cf/qwen/qwen3-30b-a3b-fp8\",\n name: \"Qwen3 30B A3B FP8\",\n match: {\n equals: \"@cf/qwen/qwen3-30b-a3b-fp8\"\n },\n prices: {\n input_mtok: 0.051,\n output_mtok: 0.335\n }\n },\n {\n id: \"@cf/qwen/qwen3-embedding-0.6b\",\n name: \"Qwen3 Embedding 0.6B\",\n match: {\n equals: \"@cf/qwen/qwen3-embedding-0.6b\"\n },\n prices: {\n input_mtok: 0.012\n }\n },\n {\n id: \"@cf/qwen/qwen3.8-27b\",\n name: \"Qwen3.8 27B\",\n match: {\n equals: \"@cf/qwen/qwen3.8-27b\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.45,\n output_mtok: 3.2\n }\n },\n {\n id: \"@cf/qwen/qwq-32b\",\n name: \"QwQ 32B\",\n match: {\n equals: \"@cf/qwen/qwq-32b\"\n },\n prices: {\n input_mtok: 0.66,\n output_mtok: 1\n }\n },\n {\n id: \"@cf/zai-org/glm-4.7-flash\",\n name: \"GLM 4.7 Flash\",\n match: {\n equals: \"@cf/zai-org/glm-4.7-flash\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.4\n }\n },\n {\n id: \"@cf/zai-org/glm-5.2\",\n name: \"GLM 5.2\",\n match: {\n equals: \"@cf/zai-org/glm-5.2\"\n },\n prices: {\n input_mtok: 1.4,\n cache_read_mtok: 0.26,\n output_mtok: 4.4\n }\n },\n {\n id: \"@cf/zai-org/glm-5.3\",\n name: \"GLM 5.3\",\n match: {\n equals: \"@cf/zai-org/glm-5.3\"\n },\n price_comments: \"https://developers.cloudflare.com/workers-ai/platform/pricing/\",\n prices: {\n input_mtok: 1.4,\n cache_read_mtok: 0.26,\n output_mtok: 4.4\n }\n },\n {\n id: \"@cf/zai-org/glm-5.3-flash\",\n name: \"GLM 5.3 Flash\",\n match: {\n equals: \"@cf/zai-org/glm-5.3-flash\"\n },\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.03,\n output_mtok: 0.5\n }\n }\n ]\n },\n {\n id: \"cohere\",\n name: \"Cohere\",\n pricing_urls: [\"https://cohere.com/pricing\"],\n api_pattern: \"https://api\\\\.cohere\\\\.(?:ai|com)\",\n model_match: {\n starts_with: \"command-\"\n },\n provider_match: {\n contains: \"cohere\"\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: [\"usage\", \"billed_units\"],\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"tokens\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: [\"tokens\", \"input_tokens\"],\n dest: \"input_tokens\",\n required: false\n },\n {\n path: [\"tokens\", \"output_tokens\"],\n dest: \"output_tokens\",\n required: false\n },\n {\n path: \"cached_tokens\",\n dest: \"cache_read_tokens\",\n required: false\n }\n ]\n },\n {\n api_flavor: \"embeddings\",\n root: [\"meta\", \"billed_units\"],\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"c4ai-aya-expanse-32b\",\n name: \"Aya Expanse 32B\",\n description: \"Aya Expanse is a highly performant 32B multilingual model, designed to rival monolingual performance through innovations in instruction tuning with data arbitrage, preference training, and model merging. Serves 23 languages.\",\n match: {\n contains: \"aya-expanse-32b\"\n },\n context_window: 128000,\n price_comments: \"https://cohere.com/pricing FAQ: Aya Expanse models (8B and 32B) on the API are charged at $0.50/1M tokens for input and $1.50/1M tokens for output.\",\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.5\n }\n },\n {\n id: \"c4ai-aya-expanse-8b\",\n name: \"Aya Expanse 8B\",\n description: \"Aya Expanse is a highly performant 8B multilingual model, designed to rival monolingual performance through innovations in instruction tuning with data arbitrage, preference training, and model merging. Serves 23 languages.\",\n match: {\n contains: \"aya-expanse-8b\"\n },\n context_window: 8192,\n price_comments: \"Retired 2026-04-04 per https://docs.cohere.com/docs/models; last published price kept for historical usage. https://cohere.com/pricing FAQ: Aya Expanse models (8B and 32B) on the API are charged at $0.50/1M tokens for input and $1.50/1M tokens for output.\",\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.5\n },\n deprecated: true\n },\n {\n id: \"command\",\n name: \"Command\",\n description: \"Command is an instruction-following conversational model that performs language tasks with high quality, more reliably and with a longer context than our base generative models.\",\n match: {\n equals: \"command\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 2\n }\n },\n {\n id: \"command-a\",\n name: \"Command A\",\n description: `Command A is an open-weights 111B parameter model with a 256k context window focused on delivering great performance across agentic, multilingual, and coding use cases.\nCompared to other leading proprietary and open-weights models Command A delivers maximum performance with minimum hardware costs, excelling on business-critical agentic and multilingual tasks.`,\n match: {\n or: [\n {\n equals: \"command-a\"\n },\n {\n equals: \"command-a-03-2025\"\n }\n ]\n },\n prices: {\n input_mtok: 2.5,\n output_mtok: 10\n }\n },\n {\n id: \"command-light\",\n name: \"Command Light\",\n description: \"A smaller, faster version of Command. Almost as capable, but a lot faster.\",\n match: {\n starts_with: \"command-light\"\n },\n context_window: 4096,\n price_comments: \"Deprecated 2025-09-15 per https://docs.cohere.com/docs/models; still billable for existing customers. https://cohere.com/pricing FAQ: Command-light pricing is $0.30/1M tokens for input and $0.60/1M tokens for output.\",\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.6\n },\n deprecated: true\n },\n {\n id: \"command-r\",\n name: \"Command R\",\n description: \"Command-R is a 35B parameter model that performs conversational language tasks at a higher quality, more reliably, and with a longer context than previous models. It can be used for complex workflows like code generation, retrieval augmented generation (RAG), tool use, and agents.\",\n match: {\n or: [\n {\n equals: \"command-r\"\n },\n {\n equals: \"command-r-08-2024\"\n }\n ]\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n }\n },\n {\n id: \"command-r-plus\",\n name: \"Command R+\",\n description: \"Command R+ is a new, 104B-parameter LLM from Cohere. It's useful for roleplay, general consumer usecases, and Retrieval Augmented Generation (RAG).\",\n match: {\n or: [\n {\n equals: \"command-r-plus\"\n },\n {\n equals: \"command-r-plus-08-2024\"\n }\n ]\n },\n prices: {\n input_mtok: 2.5,\n output_mtok: 10\n }\n },\n {\n id: \"command-r7b\",\n name: \"Command R7B\",\n description: \"Command R7B (12-2024) is a small, fast update of the Command R+ model, delivered in December 2024. It excels at RAG, tool use, agents, and similar tasks requiring complex reasoning and multiple steps.\",\n match: {\n or: [\n {\n equals: \"command-r7b\"\n },\n {\n equals: \"command-r7b-12-2024\"\n }\n ]\n },\n prices: {\n input_mtok: 0.0375,\n output_mtok: 0.15\n }\n },\n {\n id: \"embed-v4.0\",\n name: \"Embed v4.0\",\n description: \"Embed v4.0 is a state-of-the-art embedding model designed for precise retrieval across noisy, multilingual, and multimodal data.\",\n match: {\n equals: \"embed-v4.0\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.12\n }\n }\n ]\n },\n {\n id: \"cursor\",\n name: \"Cursor\",\n pricing_urls: [\n \"https://cursor.com/docs/models-and-pricing\",\n \"https://cursor.com/docs/models/grok-4-6\",\n \"https://cursor.com/docs/models/grok-4-5\",\n \"https://cursor.com/docs/models/cursor-composer-2-5\"\n ],\n api_pattern: \"https://api\\\\.cursor\\\\.com\",\n description: \"Agentic coding models available through Cursor's editor, SDK, and Cloud Agents API.\",\n model_match: {\n starts_with: \"composer-\"\n },\n extractors: [\n {\n api_flavor: \"usage-event\",\n root: \"tokenUsage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"inputTokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"cacheWriteTokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"cacheReadTokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"cacheWriteTokens\",\n dest: \"cache_write_tokens\",\n required: true\n },\n {\n path: \"cacheReadTokens\",\n dest: \"cache_read_tokens\",\n required: true\n },\n {\n path: \"outputTokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"composer-2.5\",\n name: \"Composer 2.5\",\n description: \"Cursor's agentic coding model, optimized for long-running tasks and tool use.\",\n match: {\n or: [\n {\n equals: \"composer-2.5\"\n },\n {\n regex: \"^composer-2\\\\.5\\\\[fast=false\\\\]$\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"Standard on-demand usage. Fast is a separately priced variant.\",\n prices: {\n input_mtok: 0.5,\n cache_read_mtok: 0.2,\n output_mtok: 2.5\n }\n },\n {\n id: \"composer-2.5-fast\",\n name: \"Composer 2.5 Fast\",\n description: \"Faster Composer 2.5 speed tier with the same model intelligence.\",\n match: {\n or: [\n {\n equals: \"composer-2.5-fast\"\n },\n {\n regex: \"^composer-2\\\\.5\\\\[fast=true\\\\]$\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"Fast on-demand usage.\",\n prices: {\n input_mtok: 3,\n cache_read_mtok: 0.5,\n output_mtok: 15\n }\n },\n {\n id: \"grok-4.5\",\n name: \"Grok 4.5\",\n description: \"Cursor and SpaceXAI's agentic model for long-running coding and knowledge work.\",\n match: {\n or: [\n {\n equals: \"grok-4.5\"\n },\n {\n regex: \"^grok-4\\\\.5\\\\[fast=false\\\\]$\"\n }\n ]\n },\n context_window: 256000,\n price_comments: \"Standard on-demand usage. Fast is a separately priced variant.\",\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.5,\n output_mtok: 6\n }\n },\n {\n id: \"grok-4.5-fast\",\n name: \"Grok 4.5 Fast\",\n description: \"Faster Grok 4.5 speed tier for agentic coding and knowledge work.\",\n match: {\n or: [\n {\n equals: \"grok-4.5-fast\"\n },\n {\n regex: \"^grok-4\\\\.5\\\\[fast=true\\\\]$\"\n }\n ]\n },\n context_window: 256000,\n price_comments: \"Fast on-demand usage.\",\n prices: {\n input_mtok: 4,\n cache_read_mtok: 1,\n output_mtok: 18\n }\n },\n {\n id: \"grok-4.6\",\n name: \"Grok 4.6\",\n description: \"Cursor and SpaceXAI's frontier model for complex coding and knowledge work.\",\n match: {\n or: [\n {\n equals: \"grok-4.6\"\n },\n {\n regex: \"^grok-4\\\\.6\\\\[fast=false\\\\]$\"\n }\n ]\n },\n context_window: 256000,\n price_comments: \"Standard on-demand usage. Fast is a separately priced variant.\",\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.5,\n output_mtok: 6\n }\n },\n {\n id: \"grok-4.6-fast\",\n name: \"Grok 4.6 Fast\",\n description: \"Faster Grok 4.6 speed tier for complex coding and knowledge work.\",\n match: {\n or: [\n {\n equals: \"grok-4.6-fast\"\n },\n {\n regex: \"^grok-4\\\\.6\\\\[fast=true\\\\]$\"\n }\n ]\n },\n context_window: 256000,\n price_comments: \"Fast on-demand usage.\",\n prices: {\n input_mtok: 4,\n cache_read_mtok: 1,\n output_mtok: 12\n }\n },\n {\n id: \"grok-4.7\",\n name: \"Grok 4.7\",\n description: \"Cursor and SpaceXAI's frontier model for complex coding and knowledge work.\",\n match: {\n or: [\n {\n equals: \"grok-4.7\"\n },\n {\n regex: \"^grok-4\\\\.7\\\\[fast=false\\\\]$\"\n }\n ]\n },\n context_window: 500000,\n price_comments: \"Standard rates apply through 256k input tokens; longer requests use the 500k rates for all tokens. https://cursor.com/docs/models/grok-4-7\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 256000,\n price: 4\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 256000,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 6,\n tiers: [\n {\n start: 256000,\n price: 12\n }\n ]\n }\n }\n },\n {\n id: \"grok-4.7-fast\",\n name: \"Grok 4.7 Fast\",\n description: \"Faster Grok 4.7 speed tier for complex coding and knowledge work.\",\n match: {\n or: [\n {\n equals: \"grok-4.7-fast\"\n },\n {\n regex: \"^grok-4\\\\.7\\\\[fast=true\\\\]$\"\n }\n ]\n },\n context_window: 500000,\n price_comments: \"Fast rates apply through 256k input tokens; longer requests use the 500k Fast rates for all tokens. https://cursor.com/docs/models/grok-4-7\",\n prices: {\n input_mtok: {\n base: 4,\n tiers: [\n {\n start: 256000,\n price: 6\n }\n ]\n },\n cache_read_mtok: {\n base: 1,\n tiers: [\n {\n start: 256000,\n price: 1.5\n }\n ]\n },\n output_mtok: {\n base: 12,\n tiers: [\n {\n start: 256000,\n price: 18\n }\n ]\n }\n }\n }\n ]\n },\n {\n id: \"databricks\",\n name: \"Databricks\",\n pricing_urls: [\n \"https://www.databricks.com/product/pricing/foundation-model-serving\",\n \"https://docs.databricks.com/aws/en/machine-learning/foundation-model-apis/supported-models\"\n ],\n api_pattern: \"https://[^/]+\\\\.(?:cloud\\\\.databricks\\\\.com|azuredatabricks\\\\.net|gcp\\\\.databricks\\\\.com)/(?:serving-endpoints|ai-gateway)(?:/|$)\",\n description: \"Pay-per-token Foundation Model APIs for open-weight models hosted by Databricks on AWS, Azure and GCP workspaces.\",\n price_comments: \"Databricks bills pay-per-token usage in DBUs per 1M tokens. USD prices are the published Standard Pay Per Token DBU rates multiplied by $0.070 per DBU, the serverless real-time inference rate in AWS us-east-1, us-east-2 and us-west-2, Azure East US, East US 2, North Central US, West US 2 and West US 3, and every GCP US region, rounded to $0.001. Other regions charge $0.074-$0.170 per DBU and SAP Databricks $0.125-$0.199, so costs there scale with the DBU rate. Not represented: priority pay-per-token, the 10% data-residency uplift on Kimi K3, provisioned throughput, batch inference and committed-use discounts. Covers Databricks-hosted open-weight models only, not the proprietary Claude, GPT, Gemini and Grok models Databricks also serves.\",\n model_match: {\n or: [\n {\n starts_with: \"databricks-\"\n },\n {\n starts_with: \"system.ai.\"\n }\n ]\n },\n provider_match: {\n contains: \"databricks\"\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"cache_read_input_tokens\",\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: \"reasoning_tokens\",\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"cache_read_input_tokens\",\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: \"reasoning_tokens\",\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"responses\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"input_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"output_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"embeddings\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"databricks-bge-large-en\",\n name: \"BGE Large (En)\",\n match: {\n or: [\n {\n equals: \"databricks-bge-large-en\"\n },\n {\n equals: \"system.ai.bge-large-en\"\n }\n ]\n },\n context_window: 512,\n price_comments: \"1.429 DBU per 1M input tokens.\",\n prices: {\n input_mtok: 0.1\n }\n },\n {\n id: \"databricks-deepseek-v4-1-flash\",\n name: \"DeepSeek V4.1 Flash\",\n match: {\n or: [\n {\n equals: \"databricks-deepseek-v4-1-flash\"\n },\n {\n equals: \"system.ai.deepseek-v4-1-flash\"\n }\n ]\n },\n price_comments: \"4.286 input, 17.143 output and 0.429 cache-read DBU per 1M tokens.\",\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.03,\n output_mtok: 1.2\n }\n },\n {\n id: \"databricks-deepseek-v4-flash-0731\",\n name: \"DeepSeek V4 Flash (0731)\",\n match: {\n or: [\n {\n equals: \"databricks-deepseek-v4-flash-0731\"\n },\n {\n equals: \"system.ai.deepseek-v4-flash-0731\"\n }\n ]\n },\n price_comments: \"2 input, 4 output and 0.4 cache-read DBU per 1M tokens.\",\n prices: {\n input_mtok: 0.14,\n cache_read_mtok: 0.028,\n output_mtok: 0.28\n }\n },\n {\n id: \"databricks-deepseek-v4-pro-0813\",\n name: \"DeepSeek V4 Pro (0813)\",\n match: {\n or: [\n {\n equals: \"databricks-deepseek-v4-pro-0813\"\n },\n {\n equals: \"system.ai.deepseek-v4-pro-0813\"\n }\n ]\n },\n price_comments: \"18.857 input, 56.571 output and 1.886 cache-read DBU per 1M tokens. Retires on 2026-10-30.\",\n prices: {\n input_mtok: 1.32,\n cache_read_mtok: 0.132,\n output_mtok: 3.96\n },\n deprecated: true\n },\n {\n id: \"databricks-gemma-3-12b\",\n name: \"Gemma 3 12B\",\n match: {\n or: [\n {\n equals: \"databricks-gemma-3-12b\"\n },\n {\n equals: \"system.ai.gemma-3-12b\"\n }\n ]\n },\n context_window: 128000,\n price_comments: \"2.143 input and 7.143 output DBU per 1M tokens.\",\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.5\n }\n },\n {\n id: \"databricks-glm-5-2\",\n name: \"GLM 5.2\",\n match: {\n or: [\n {\n equals: \"databricks-glm-5-2\"\n },\n {\n equals: \"system.ai.glm-5-2\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"20 input, 62.857 output and 3.714 cache-read DBU per 1M tokens.\",\n prices: {\n input_mtok: 1.4,\n cache_read_mtok: 0.26,\n output_mtok: 4.4\n }\n },\n {\n id: \"databricks-glm-5-3\",\n name: \"GLM 5.3\",\n match: {\n or: [\n {\n equals: \"databricks-glm-5-3\"\n },\n {\n equals: \"system.ai.glm-5-3\"\n }\n ]\n },\n context_window: 1048576,\n price_comments: \"20 input, 62.857 output and 3.714 cache-read DBU per 1M tokens.\",\n prices: {\n input_mtok: 1.4,\n cache_read_mtok: 0.26,\n output_mtok: 4.4\n }\n },\n {\n id: \"databricks-glm-5-3-flash\",\n name: \"GLM 5.3 Flash\",\n match: {\n or: [\n {\n equals: \"databricks-glm-5-3-flash\"\n },\n {\n equals: \"system.ai.glm-5-3-flash\"\n }\n ]\n },\n context_window: 1048576,\n price_comments: \"2.143 input, 7.143 output and 0.429 cache-read DBU per 1M tokens.\",\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.03,\n output_mtok: 0.5\n }\n },\n {\n id: \"databricks-gpt-oss-120b\",\n name: \"GPT OSS 120B\",\n match: {\n or: [\n {\n equals: \"databricks-gpt-oss-120b\"\n },\n {\n equals: \"system.ai.gpt-oss-120b\"\n }\n ]\n },\n context_window: 128000,\n price_comments: \"2.143 input and 8.571 output DBU per 1M tokens.\",\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n }\n },\n {\n id: \"databricks-gpt-oss-20b\",\n name: \"GPT OSS 20B\",\n match: {\n or: [\n {\n equals: \"databricks-gpt-oss-20b\"\n },\n {\n equals: \"system.ai.gpt-oss-20b\"\n }\n ]\n },\n context_window: 128000,\n price_comments: \"1 input and 4.286 output DBU per 1M tokens.\",\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.3\n }\n },\n {\n id: \"databricks-gte-large-en\",\n name: \"GTE Large (En)\",\n match: {\n or: [\n {\n equals: \"databricks-gte-large-en\"\n },\n {\n equals: \"system.ai.gte-large-en\"\n }\n ]\n },\n context_window: 8192,\n price_comments: \"1.857 DBU per 1M input tokens.\",\n prices: {\n input_mtok: 0.13\n }\n },\n {\n id: \"databricks-inkling\",\n name: \"Inkling\",\n match: {\n or: [\n {\n equals: \"databricks-inkling\"\n },\n {\n equals: \"system.ai.inkling\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"14.286 input, 57.857 output and 2.429 cache-read DBU per 1M tokens. Retires on 2026-10-30.\",\n prices: {\n input_mtok: 1,\n cache_read_mtok: 0.17,\n output_mtok: 4.05\n },\n deprecated: true\n },\n {\n id: \"databricks-kimi-k2-7-code\",\n name: \"Kimi K2.7 Code\",\n match: {\n or: [\n {\n equals: \"databricks-kimi-k2-7-code\"\n },\n {\n equals: \"system.ai.kimi-k2-7-code\"\n }\n ]\n },\n price_comments: \"13.571 input, 57.143 output and 2.714 cache-read DBU per 1M tokens. Retires on 2026-10-30.\",\n prices: {\n input_mtok: 0.95,\n cache_read_mtok: 0.19,\n output_mtok: 4\n },\n deprecated: true\n },\n {\n id: \"databricks-kimi-k3\",\n name: \"Kimi K3\",\n match: {\n or: [\n {\n equals: \"databricks-kimi-k3\"\n },\n {\n equals: \"system.ai.kimi-k3\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"42.857 input, 214.286 output and 4.286 cache-read DBU per 1M tokens.\",\n prices: {\n input_mtok: 3,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"databricks-llama-4-maverick\",\n name: \"Llama 4 Maverick\",\n match: {\n or: [\n {\n equals: \"databricks-llama-4-maverick\"\n },\n {\n equals: \"system.ai.llama-4-maverick\"\n }\n ]\n },\n price_comments: \"7.143 input and 21.429 output DBU per 1M tokens.\",\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.5\n }\n },\n {\n id: \"databricks-meta-llama-3-1-8b-instruct\",\n name: \"Llama 3.1 8B Instruct\",\n match: {\n or: [\n {\n equals: \"databricks-meta-llama-3-1-8b-instruct\"\n },\n {\n equals: \"system.ai.meta-llama-3-1-8b-instruct\"\n }\n ]\n },\n context_window: 128000,\n price_comments: \"2.143 input and 6.429 output DBU per 1M tokens.\",\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.45\n }\n },\n {\n id: \"databricks-meta-llama-3-3-70b-instruct\",\n name: \"Llama 3.3 70B Instruct\",\n match: {\n or: [\n {\n equals: \"databricks-meta-llama-3-3-70b-instruct\"\n },\n {\n equals: \"system.ai.meta-llama-3-3-70b-instruct\"\n }\n ]\n },\n context_window: 128000,\n price_comments: \"7.143 input and 21.429 output DBU per 1M tokens.\",\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.5\n }\n },\n {\n id: \"databricks-qwen3-embedding-0-6b\",\n name: \"Qwen3 Embedding 0.6B\",\n match: {\n or: [\n {\n equals: \"databricks-qwen3-embedding-0-6b\"\n },\n {\n equals: \"system.ai.qwen3-embedding-0-6b\"\n }\n ]\n },\n price_comments: \"0.286 DBU per 1M input tokens.\",\n prices: {\n input_mtok: 0.02\n }\n },\n {\n id: \"databricks-qwen3-next-80b-a3b-instruct\",\n name: \"Qwen3 Next 80B A3B Instruct\",\n match: {\n or: [\n {\n equals: \"databricks-qwen3-next-80b-a3b-instruct\"\n },\n {\n equals: \"system.ai.qwen3-next-80b-a3b-instruct\"\n }\n ]\n },\n price_comments: \"2.143 input and 17.143 output DBU per 1M tokens.\",\n prices: {\n input_mtok: 0.15,\n output_mtok: 1.2\n }\n },\n {\n id: \"databricks-qwen35-122b-a10b\",\n name: \"Qwen3.5 122B A10B\",\n match: {\n or: [\n {\n equals: \"databricks-qwen35-122b-a10b\"\n },\n {\n equals: \"system.ai.qwen35-122b-a10b\"\n }\n ]\n },\n context_window: 256000,\n price_comments: \"3.143 input and 31.429 output DBU per 1M tokens.\",\n prices: {\n input_mtok: 0.22,\n output_mtok: 2.2\n }\n }\n ]\n },\n {\n id: \"deepseek\",\n name: \"Deepseek\",\n pricing_urls: [\"https://api-docs.deepseek.com/quick_start/pricing\"],\n api_pattern: \"https://api\\\\.deepseek\\\\.com\",\n price_comments: 'Deepseek off-peak pricing applies \"UTC 16:30-00:30\" so we switch it around and use the off-peak pricing as the default (first) price then the second price with a constraint is the \"standard\" pricing that applies \"UTC 00:30-16:30\".',\n model_match: {\n starts_with: \"deepseek\"\n },\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"deepseek-chat\",\n name: \"DeepSeek Chat\",\n description: \"DeepSeek-V3 is the latest model from the DeepSeek team, building upon the instruction following and coding abilities of the previous versions. Pre-trained on nearly 15 trillion tokens, the reported evaluations reveal that the model outperforms other open-source models and rivals leading closed-source models.\",\n match: {\n or: [\n {\n starts_with: \"deepseek-chat\"\n },\n {\n equals: \"deepseek-chat-v3-0324\"\n }\n ]\n },\n context_window: 64000,\n prices: [\n {\n prices: {\n input_mtok: 0.135,\n cache_read_mtok: 0.035,\n output_mtok: 0.55\n }\n },\n {\n constraint: {\n start_time: \"00:30:00Z\",\n end_time: \"16:30:00Z\",\n type: \"time_of_date\"\n },\n prices: {\n input_mtok: 0.27,\n cache_read_mtok: 0.07,\n output_mtok: 1.1\n }\n }\n ]\n },\n {\n id: \"deepseek-flash\",\n name: \"DeepSeek V4.1 Flash\",\n description: \"DeepSeek-V4.1-Flash, with native visual understanding. Supports both non-thinking and thinking (default) modes, JSON output, tool calls, chat prefix completion, and FIM completion (non-thinking only).\",\n match: {\n equals: \"deepseek-flash\"\n },\n context_window: 1e6,\n price_comments: 'Released 2026-09-10 (https://api-docs.deepseek.com/updates). Peak is \"UTC 01:00-04:00\" and \"UTC 06:00-10:00\" Monday to Friday, everything else is off-peak. Weekends and Chinese public holidays are off-peak all day, which needs a day-of-week condition, so on those days the peak prices here are an upper bound. See https://github.com/pydantic/genai-prices/issues/582.',\n prices: [\n {\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.003,\n output_mtok: 0.6\n }\n },\n {\n constraint: {\n start_time: \"01:00:00Z\",\n end_time: \"04:00:00Z\",\n type: \"time_of_date\"\n },\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.006,\n output_mtok: 1.2\n }\n },\n {\n constraint: {\n start_time: \"06:00:00Z\",\n end_time: \"10:00:00Z\",\n type: \"time_of_date\"\n },\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.006,\n output_mtok: 1.2\n }\n }\n ]\n },\n {\n id: \"deepseek-reasoner\",\n name: \"Deepseek R1\",\n description: \"DeepSeek R1 is here: Performance on par with OpenAI o1, but open-sourced and with fully open reasoning tokens. It's 671B parameters in size, with 37B active in an inference pass.\",\n match: {\n or: [\n {\n equals: \"deepseek-reasoner\"\n },\n {\n starts_with: \"deepseek-r1\"\n },\n {\n equals: \"deepseek-r1-0528\"\n }\n ]\n },\n context_window: 64000,\n prices: [\n {\n prices: {\n input_mtok: 0.135,\n cache_read_mtok: 0.035,\n output_mtok: 0.55\n }\n },\n {\n constraint: {\n start_time: \"00:30:00Z\",\n end_time: \"16:30:00Z\",\n type: \"time_of_date\"\n },\n prices: {\n input_mtok: 0.55,\n cache_read_mtok: 0.14,\n output_mtok: 2.19\n }\n }\n ]\n },\n {\n id: \"deepseek-v3.1-terminus\",\n name: \"DeepSeek V3.1 Terminus\",\n description: \"DeepSeek-V3.1 Terminus is an update to DeepSeek V3.1 that maintains the model's original capabilities while addressing issues reported by users, including language consistency and agent capabilities.\",\n match: {\n equals: \"deepseek-v3.1-terminus\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.27,\n cache_read_mtok: 0.13,\n output_mtok: 0.95\n }\n },\n {\n id: \"deepseek-v3.2\",\n name: \"DeepSeek V3.2\",\n description: \"DeepSeek-V3.2 is a large language model designed to harmonize high computational efficiency with strong reasoning and agentic tool-use performance.\",\n match: {\n equals: \"deepseek-v3.2\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.2288,\n output_mtok: 0.3432\n }\n },\n {\n id: \"deepseek-v3.2-exp\",\n name: \"DeepSeek V3.2 Exp\",\n description: \"DeepSeek-V3.2-Exp is an experimental large language model released by DeepSeek as an intermediate step between V3.1 and future architectures.\",\n match: {\n equals: \"deepseek-v3.2-exp\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.27,\n output_mtok: 0.41\n }\n },\n {\n id: \"deepseek-v4-flash\",\n name: \"DeepSeek V4 Flash\",\n description: \"DeepSeek-V4-Flash. Supports both non-thinking and thinking (default) modes, JSON output, tool calls, chat prefix completion, and FIM completion (non-thinking only).\",\n match: {\n or: [\n {\n starts_with: \"deepseek-v4-flash\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: 'Rates: flat before 2026-08-17; V4 off-peak from 2026-08-17; V4.1 Flash off-peak from 2026-09-10, when DeepSeek retired V4 Flash and routed `deepseek-v4-flash` to V4.1 Flash (https://api-docs.deepseek.com/updates). Peak is \"UTC 01:00-04:00\" and \"UTC 06:00-10:00\"; the two peak windows come last so they win during peak hours. `constraint` is a union, so one entry cannot carry both a date and a daily window: peak-hour requests resolve to the current V4.1 Flash peak rate whatever their date. Weekends are off-peak all day from 2026-08-23, which needs a day-of-week condition, so on Saturdays and Sundays the peak prices here are an upper bound. See https://github.com/pydantic/genai-prices/issues/582.',\n prices: [\n {\n prices: {\n input_mtok: 0.14,\n cache_read_mtok: 0.0028,\n output_mtok: 0.28\n }\n },\n {\n constraint: {\n start_date: \"2026-08-17\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 0.22,\n cache_read_mtok: 0.007,\n output_mtok: 0.66\n }\n },\n {\n constraint: {\n start_date: \"2026-09-10\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.003,\n output_mtok: 0.6\n }\n },\n {\n constraint: {\n start_time: \"01:00:00Z\",\n end_time: \"04:00:00Z\",\n type: \"time_of_date\"\n },\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.006,\n output_mtok: 1.2\n }\n },\n {\n constraint: {\n start_time: \"06:00:00Z\",\n end_time: \"10:00:00Z\",\n type: \"time_of_date\"\n },\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.006,\n output_mtok: 1.2\n }\n }\n ]\n },\n {\n id: \"deepseek-v4-pro\",\n name: \"DeepSeek V4 Pro\",\n description: \"DeepSeek-V4-Pro. Supports both non-thinking and thinking (default) modes, JSON output, tool calls, chat prefix completion, and FIM completion (non-thinking only).\",\n match: {\n or: [\n {\n starts_with: \"deepseek-v4-pro\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: 'From 2026-08-17 the V4 models use different peak windows from the V3 models: peak is \"UTC 01:00-04:00\" and \"UTC 06:00-10:00\", everything else is off-peak. The flat rate that applied before 2026-08-17 is kept as the unconstrained first price, the new off-peak rate is gated on start_date, and the two peak windows come last so they win during peak hours. Two things this layout still cannot say, because `constraint` is a union and one entry cannot carry both a date and a daily window. Requests from before 2026-08-17 that fall inside 01:00-04:00 or 06:00-10:00 UTC - 7 hours of every day - resolve to the new peak rate rather than the old flat rate. And from 2026-08-23 Deepseek bills off-peak all day at weekends (Beijing time), which needs a day-of-week condition, so on Saturdays and Sundays the peak prices here are an upper bound. See https://github.com/pydantic/genai-prices/issues/582.',\n prices: [\n {\n prices: {\n input_mtok: 0.435,\n cache_read_mtok: 0.003625,\n output_mtok: 0.87\n }\n },\n {\n constraint: {\n start_date: \"2026-08-17\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 0.66,\n cache_read_mtok: 0.022,\n output_mtok: 1.98\n }\n },\n {\n constraint: {\n start_time: \"01:00:00Z\",\n end_time: \"04:00:00Z\",\n type: \"time_of_date\"\n },\n prices: {\n input_mtok: 1.32,\n cache_read_mtok: 0.044,\n output_mtok: 3.96\n }\n },\n {\n constraint: {\n start_time: \"06:00:00Z\",\n end_time: \"10:00:00Z\",\n type: \"time_of_date\"\n },\n prices: {\n input_mtok: 1.32,\n cache_read_mtok: 0.044,\n output_mtok: 3.96\n }\n }\n ]\n }\n ]\n },\n {\n id: \"doubleword\",\n name: \"Doubleword\",\n pricing_urls: [\"https://docs.doubleword.ai/inference-api/models\"],\n api_pattern: \"https://api\\\\.doubleword\\\\.ai\",\n price_comments: \"Doubleword publishes Realtime, Async, and Batch prices. This provider currently encodes only Realtime pricing.\",\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"responses\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"input_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"embeddings\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"Qwen/Qwen3-14B-FP8\",\n name: \"Qwen3 14B\",\n match: {\n equals: \"Qwen/Qwen3-14B-FP8\"\n },\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.6\n }\n },\n {\n id: \"Qwen/Qwen3-Embedding-8B\",\n name: \"Qwen3 Embedding 8B\",\n match: {\n equals: \"Qwen/Qwen3-Embedding-8B\"\n },\n prices: {\n input_mtok: 0.04\n }\n },\n {\n id: \"Qwen/Qwen3-VL-235B-A22B-Instruct-FP8\",\n name: \"Qwen3 VL 235B A22B Instruct\",\n match: {\n equals: \"Qwen/Qwen3-VL-235B-A22B-Instruct-FP8\"\n },\n prices: {\n input_mtok: 0.6,\n output_mtok: 1.2\n }\n },\n {\n id: \"Qwen/Qwen3-VL-30B-A3B-Instruct-FP8\",\n name: \"Qwen3 VL 30B A3B Instruct\",\n match: {\n equals: \"Qwen/Qwen3-VL-30B-A3B-Instruct-FP8\"\n },\n prices: {\n input_mtok: 0.16,\n output_mtok: 0.8\n }\n },\n {\n id: \"Qwen/Qwen3.5-35B-A3B-FP8\",\n name: \"Qwen3.5 35B A3B\",\n match: {\n equals: \"Qwen/Qwen3.5-35B-A3B-FP8\"\n },\n prices: {\n input_mtok: 0.25,\n output_mtok: 2\n }\n },\n {\n id: \"Qwen/Qwen3.5-397B-A17B\",\n name: \"Qwen3.5 397B A17B\",\n match: {\n equals: \"Qwen/Qwen3.5-397B-A17B\"\n },\n prices: {\n input_mtok: 0.6,\n output_mtok: 3.6\n }\n },\n {\n id: \"Qwen/Qwen3.5-9B\",\n name: \"Qwen3.5 9B\",\n match: {\n equals: \"Qwen/Qwen3.5-9B\"\n },\n prices: {\n input_mtok: 0.08,\n output_mtok: 0.7\n }\n },\n {\n id: \"Qwen/Qwen3.6-35B-A3B-FP8\",\n name: \"Qwen3.6 35B A3B\",\n match: {\n equals: \"Qwen/Qwen3.6-35B-A3B-FP8\"\n },\n prices: {\n input_mtok: 0.25,\n output_mtok: 2\n }\n },\n {\n id: \"Qwen/Qwen3.8-27B-FP8\",\n name: \"Qwen3.8 27B\",\n match: {\n equals: \"Qwen/Qwen3.8-27B-FP8\"\n },\n context_window: 262144,\n price_comments: \"Realtime pricing. See https://docs.doubleword.ai/inference-api/models/qwen-qwen3-8-27b-fp8.\",\n prices: {\n input_mtok: 0.45,\n cache_read_mtok: 0.04,\n output_mtok: 3\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V4-Flash\",\n name: \"DeepSeek V4 Flash\",\n match: {\n equals: \"deepseek-ai/DeepSeek-V4-Flash\"\n },\n prices: {\n input_mtok: 0.14,\n output_mtok: 0.28\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V4-Flash-0731\",\n name: \"DeepSeek V4 Flash 0731\",\n match: {\n equals: \"deepseek-ai/DeepSeek-V4-Flash-0731\"\n },\n price_comments: \"Realtime pricing. See https://docs.doubleword.ai/inference-api/models/deepseek-ai-deepseek-v4-flash-0731.\",\n prices: {\n input_mtok: 0.09,\n cache_read_mtok: 0.02,\n output_mtok: 0.18\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V4-Pro\",\n name: \"DeepSeek V4 Pro\",\n match: {\n equals: \"deepseek-ai/DeepSeek-V4-Pro\"\n },\n prices: {\n input_mtok: 1.74,\n output_mtok: 3.48\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V4.1-Flash\",\n name: \"DeepSeek V4.1 Flash\",\n match: {\n equals: \"deepseek-ai/DeepSeek-V4.1-Flash\"\n },\n price_comments: \"Realtime pricing. See https://docs.doubleword.ai/inference-api/models/deepseek-ai-deepseek-v4-1-flash.\",\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.01,\n output_mtok: 0.6\n }\n },\n {\n id: \"google/gemma-4-31B-it\",\n name: \"Gemma 4 31B IT\",\n match: {\n equals: \"google/gemma-4-31B-it\"\n },\n prices: {\n input_mtok: 0.14,\n output_mtok: 0.4\n }\n },\n {\n id: \"mistralai/Devstral-2-123B-Instruct-2512\",\n name: \"Devstral 2 123B Instruct 2512\",\n match: {\n equals: \"mistralai/Devstral-2-123B-Instruct-2512\"\n },\n prices: {\n input_mtok: 0.4,\n output_mtok: 2\n }\n },\n {\n id: \"moonshotai/Kimi-K2.6\",\n name: \"Kimi K2.6\",\n match: {\n equals: \"moonshotai/Kimi-K2.6\"\n },\n prices: {\n input_mtok: 0.95,\n output_mtok: 4\n }\n },\n {\n id: \"moonshotai/kimi-k3\",\n name: \"Kimi K3\",\n match: {\n equals: \"moonshotai/kimi-k3\"\n },\n context_window: 1048576,\n price_comments: \"Realtime pricing. See https://docs.doubleword.ai/inference-api/models/moonshotai-kimi-k3.\",\n prices: {\n input_mtok: 3,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4\",\n name: \"Nemotron 3 Super 120B A12B\",\n match: {\n equals: \"nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4\"\n },\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.75\n }\n },\n {\n id: \"openai/gpt-oss-20b\",\n name: \"GPT OSS 20B\",\n match: {\n equals: \"openai/gpt-oss-20b\"\n },\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.3\n }\n },\n {\n id: \"zai-org/GLM-5.1-FP8\",\n name: \"GLM 5.1\",\n match: {\n equals: \"zai-org/GLM-5.1-FP8\"\n },\n prices: {\n input_mtok: 1.4,\n output_mtok: 4.4\n }\n },\n {\n id: \"zai-org/GLM-5.2-FP8\",\n name: \"GLM 5.2\",\n match: {\n equals: \"zai-org/GLM-5.2-FP8\"\n },\n context_window: 1048576,\n price_comments: \"Realtime pricing. See https://docs.doubleword.ai/inference-api/models/zai-org-glm-5-2-fp8.\",\n prices: {\n input_mtok: 0.93,\n cache_read_mtok: 0.19,\n output_mtok: 3\n }\n }\n ]\n },\n {\n id: \"fireworks\",\n name: \"Fireworks\",\n pricing_urls: [\"https://fireworks.ai/pricing\"],\n api_pattern: \"https://api\\\\.fireworks\\\\.ai\",\n model_match: {\n starts_with: \"accounts/fireworks/\"\n },\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"deepseek-r1-0528\",\n name: \"DeepSeek R1 0528\",\n description: \"The updated DeepSeek-R1-0528 model delivers major improvements in reasoning, inference, and accuracy through enhanced post-training optimization and greater computational resources. It now performs at a level approaching top-tier models like O3 and Gemini 2.5 Pro, with notable gains in complex tasks such as math and programming.\",\n match: {\n equals: \"accounts/fireworks/models/deepseek-r1-0528\"\n },\n context_window: 160000,\n prices: {\n input_mtok: 3,\n output_mtok: 8\n }\n },\n {\n id: \"deepseek-v3-0324\",\n name: \"Deepseek V3 03-24\",\n description: \"A strong Mixture-of-Experts (MoE) language model with 671B total parameters with 37B activated for each token from Deepseek. Updated checkpoint.\",\n match: {\n equals: \"accounts/fireworks/models/deepseek-v3-0324\"\n },\n context_window: 160000,\n price_comments: `docs give just one price - \"Pricing Per 1M Tokens\", we assume that's input and output`,\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"deepseek-v3p2\",\n name: \"Deepseek V3.2\",\n description: \"Model from Deepseek that harmonizes high computational efficiency with superior reasoning and agent performance. 675B parameter MoE model.\",\n match: {\n equals: \"accounts/fireworks/models/deepseek-v3p2\"\n },\n context_window: 163840,\n prices: {\n input_mtok: 0.56,\n cache_read_mtok: 0.28,\n output_mtok: 1.68\n }\n },\n {\n id: \"deepseek-v4-flash\",\n name: \"DeepSeek-V4-Flash\",\n description: \"Official release of DeepSeek-V4-Flash with enhanced agentic capabilities and speculative decoding.\",\n match: {\n or: [\n {\n equals: \"accounts/fireworks/models/deepseek-v4-flash\"\n },\n {\n equals: \"accounts/fireworks/models/deepseek-v4-flash-0731\"\n }\n ]\n },\n context_window: 1040000,\n price_comments: \"Standard serverless pricing. See https://docs.fireworks.ai/serverless/pricing.\",\n prices: {\n input_mtok: 0.14,\n cache_read_mtok: 0.028,\n output_mtok: 0.28\n }\n },\n {\n id: \"deepseek-v4-pro\",\n name: \"DeepSeek-V4-Pro\",\n match: {\n equals: \"accounts/fireworks/models/deepseek-v4-pro\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 1.74,\n cache_read_mtok: 0.145,\n output_mtok: 3.48\n }\n },\n {\n id: \"gemma-3-27b-it\",\n name: \"Gemma 3 27B Instruct\",\n match: {\n equals: \"accounts/fireworks/models/gemma-3-27b-it\"\n },\n context_window: 131000,\n price_comments: `docs give just one price - \"Pricing Per 1M Tokens\", we assume that's input and output`,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"glm-4p7\",\n name: \"GLM-4.7\",\n description: \"Next-generation general-purpose model from Z.ai optimized for coding, reasoning, and agentic workflows. 352B parameter MoE model with advanced thinking controls.\",\n match: {\n equals: \"accounts/fireworks/models/glm-4p7\"\n },\n context_window: 202752,\n prices: {\n input_mtok: 0.6,\n output_mtok: 2.2\n }\n },\n {\n id: \"glm-5p1\",\n name: \"GLM-5.1\",\n match: {\n equals: \"accounts/fireworks/models/glm-5p1\"\n },\n prices: {\n input_mtok: 1.4,\n cache_read_mtok: 0.26,\n output_mtok: 4.4\n }\n },\n {\n id: \"glm-5p1-fast\",\n name: \"GLM 5.1 Fast\",\n match: {\n equals: \"accounts/fireworks/routers/glm-5p1-fast\"\n },\n price_comments: \"Fast serverless pricing. See https://docs.fireworks.ai/serverless/pricing.\",\n prices: {\n input_mtok: 2.8,\n cache_read_mtok: 0.52,\n output_mtok: 8.8\n }\n },\n {\n id: \"glm-5p2\",\n name: \"GLM-5.2\",\n description: \"GLM-5.2 introduces a robust 1M-token context and advanced, multi-effort coding capabilities to significantly enhance performance on long-horizon tasks. Features a new IndexShare architecture and improved MTP layer for greater efficiency. 743B parameter MoE model from Z.ai.\",\n match: {\n equals: \"accounts/fireworks/models/glm-5p2\"\n },\n context_window: 1040000,\n prices: {\n input_mtok: 1.4,\n cache_read_mtok: 0.14,\n output_mtok: 4.4\n }\n },\n {\n id: \"glm-5p2-fast\",\n name: \"GLM 5.2 Fast\",\n match: {\n or: [\n {\n equals: \"accounts/fireworks/routers/glm-5p2-fast\"\n },\n {\n equals: \"accounts/fireworks/routers/glm-5p2-fast-us\"\n }\n ]\n },\n context_window: 1040000,\n price_comments: \"Fast and US-only serverless pricing. See https://docs.fireworks.ai/serverless/pricing.\",\n prices: {\n input_mtok: 2.1,\n cache_read_mtok: 0.21,\n output_mtok: 6.6\n }\n },\n {\n id: \"gpt-oss-120b\",\n name: \"OpenAI gpt-oss-120b\",\n description: \"OpenAI's open-weight 117B parameter MoE model designed for production, general purpose, high reasoning use-cases. Features powerful reasoning, agentic tasks, and versatile developer use cases.\",\n match: {\n equals: \"accounts/fireworks/models/gpt-oss-120b\"\n },\n context_window: 131072,\n price_comments: \"Standard serverless pricing. See https://fireworks.ai/models/fireworks/gpt-oss-120b.\",\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.014,\n output_mtok: 0.6\n }\n },\n {\n id: \"gpt-oss-20b\",\n name: \"OpenAI gpt-oss-20b\",\n description: \"OpenAI's open-weight 21.5B parameter model designed for powerful reasoning, agentic tasks, and versatile developer use cases. Optimized for lower latency and local or specialized tasks.\",\n match: {\n equals: \"accounts/fireworks/models/gpt-oss-20b\"\n },\n context_window: 131072,\n price_comments: \"Standard serverless pricing. See https://fireworks.ai/models/fireworks/gpt-oss-20b.\",\n prices: {\n input_mtok: 0.07,\n cache_read_mtok: 0.035,\n output_mtok: 0.3\n }\n },\n {\n id: \"inkling\",\n name: \"Inkling\",\n description: \"Thinking Machines Lab's open-weights multimodal Mixture-of-Experts model, trained across text, image, and audio.\",\n match: {\n equals: \"accounts/fireworks/models/inkling\"\n },\n context_window: 1040000,\n price_comments: \"Standard serverless pricing. See https://fireworks.ai/models/fireworks/inkling.\",\n prices: {\n input_mtok: 1,\n cache_read_mtok: 0.17,\n output_mtok: 4.05\n }\n },\n {\n id: \"kimi-k2p5\",\n name: \"Kimi K2.5\",\n description: \"Moonshot AI's flagship agentic model. Unifies vision and text, thinking and non-thinking modes, and single-agent and multi-agent execution into one model. 1T parameter MoE model.\",\n match: {\n equals: \"accounts/fireworks/models/kimi-k2p5\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.1,\n output_mtok: 3\n }\n },\n {\n id: \"kimi-k2p6\",\n name: \"Kimi K2.6\",\n match: {\n equals: \"accounts/fireworks/models/kimi-k2p6\"\n },\n prices: {\n input_mtok: 0.95,\n cache_read_mtok: 0.16,\n output_mtok: 4\n }\n },\n {\n id: \"kimi-k2p6-fast\",\n name: \"Kimi K2.6 Fast\",\n match: {\n equals: \"accounts/fireworks/routers/kimi-k2p6-fast\"\n },\n context_window: 262144,\n price_comments: \"Fast serverless pricing. See https://docs.fireworks.ai/serverless/pricing.\",\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.3,\n output_mtok: 8\n }\n },\n {\n id: \"kimi-k2p7-code\",\n name: \"Kimi K2.7 Code\",\n description: \"Kimi K2.7 Code is a coding-focused agentic model built upon Kimi K2.6, delivering substantial improvements on real-world long-horizon coding tasks while reducing thinking tokens by roughly 30% compared to its predecessor.\",\n match: {\n equals: \"accounts/fireworks/models/kimi-k2p7-code\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.95,\n cache_read_mtok: 0.19,\n output_mtok: 4\n }\n },\n {\n id: \"kimi-k2p7-code-fast\",\n name: \"Kimi K2.7 Code Fast\",\n match: {\n equals: \"accounts/fireworks/routers/kimi-k2p7-code-fast\"\n },\n context_window: 262144,\n price_comments: \"Fast serverless pricing. See https://docs.fireworks.ai/serverless/pricing.\",\n prices: {\n input_mtok: 1.9,\n cache_read_mtok: 0.38,\n output_mtok: 8\n }\n },\n {\n id: \"kimi-k3\",\n name: \"Kimi K3\",\n description: \"Moonshot AI's 2.81T-parameter flagship model with native visual understanding and a 1M-token context window.\",\n match: {\n equals: \"accounts/fireworks/models/kimi-k3\"\n },\n context_window: 1040000,\n price_comments: \"Standard serverless pricing. See https://fireworks.ai/models/fireworks/kimi-k3. Fast and US router variants have separate model IDs.\",\n prices: {\n input_mtok: 3,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"kimi-k3-fast\",\n name: \"Kimi K3 Fast\",\n match: {\n equals: \"accounts/fireworks/routers/kimi-k3-fast\"\n },\n context_window: 1040000,\n price_comments: \"Fast serverless pricing. See https://docs.fireworks.ai/serverless/pricing.\",\n prices: {\n input_mtok: 4.5,\n cache_read_mtok: 0.45,\n output_mtok: 22.5\n }\n },\n {\n id: \"kimi-k3-us\",\n name: \"Kimi K3 US\",\n match: {\n equals: \"accounts/fireworks/routers/kimi-k3-us\"\n },\n context_window: 1040000,\n price_comments: \"US-only serverless pricing. See https://docs.fireworks.ai/serverless/us-only-serverless.\",\n prices: {\n input_mtok: 3.3,\n cache_read_mtok: 0.33,\n output_mtok: 16.5\n }\n },\n {\n id: \"llama-v3p1-8b-instruct\",\n name: \"Llama 3.1 8B Instruct\",\n description: \"The Meta Llama 3.1 collection of multilingual large language models (LLMs) is a collection of pretrained and instruction tuned generative models in 8B, 70B and 405B sizes. The Llama 3.1 instruction tuned text only models (8B, 70B, 405B) are optimized for multilingual dialogue use cases and outperform many of the available open source and closed chat models on common industry benchmarks.\",\n match: {\n equals: \"accounts/fireworks/models/llama-v3p1-8b-instruct\"\n },\n context_window: 131000,\n price_comments: `docs give just one price - \"Pricing Per 1M Tokens\", we assume that's input and output`,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"llama4-maverick-instruct-basic\",\n name: \"Llama 4 Maverick Instruct (Basic)\",\n description: \"The Meta Llama 3.1 collection of multilingual large language models (LLMs) is a collection of pretrained and instruction tuned generative models in 8B, 70B and 405B sizes. The Llama 3.1 instruction tuned text only models (8B, 70B, 405B) are optimized for multilingual dialogue use cases and outperform many of the available open source and closed chat models on common industry benchmarks.\",\n match: {\n equals: \"accounts/fireworks/models/llama4-maverick-instruct-basic\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.22,\n output_mtok: 0.88\n }\n },\n {\n id: \"minimax-m2p1\",\n name: \"MiniMax-M2.1\",\n description: \"Built for strong real-world performance across complex, multi-language, and agent-driven workflows. 228B parameter model with robust support for systems, backend, web, mobile, and office-style tasks.\",\n match: {\n equals: \"accounts/fireworks/models/minimax-m2p1\"\n },\n context_window: 204800,\n prices: {\n input_mtok: 0.3,\n output_mtok: 1.2\n }\n },\n {\n id: \"minimax-m2p7\",\n name: \"MiniMax M2.7\",\n match: {\n equals: \"accounts/fireworks/models/minimax-m2p7\"\n },\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.06,\n output_mtok: 1.2\n }\n },\n {\n id: \"minimax-m3\",\n name: \"MiniMax M3\",\n description: \"Multimodal foundation model from MiniMax with text, image, and video inputs, a long context window, and long-horizon agentic work.\",\n match: {\n equals: \"accounts/fireworks/models/minimax-m3\"\n },\n context_window: 524288,\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.06,\n output_mtok: 1.2\n }\n },\n {\n id: \"nemotron-3-ultra-nvfp4\",\n name: \"NVIDIA Nemotron 3 Ultra NVFP4\",\n description: \"Frontier-scale LLM from NVIDIA using a hybrid Latent Mixture-of-Experts (LatentMoE) architecture with interleaved Mamba-2 and MoE layers plus select Attention layers. Features 55B active parameters out of 550B total and Multi-Token Prediction layers for faster generation, optimized for complex multi-step agents, long-context analysis, and high-accuracy reasoning over code, math, and science.\",\n match: {\n equals: \"accounts/fireworks/models/nemotron-3-ultra-nvfp4\"\n },\n context_window: 262000,\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.12,\n output_mtok: 2.4\n }\n },\n {\n id: \"qwen2p5-vl-72b-instruct\",\n name: \"Qwen2.5-VL 72B Instruct\",\n description: \"Latest Qwen's VLM model\",\n match: {\n equals: \"accounts/fireworks/models/qwen2p5-vl-72b-instruct\"\n },\n context_window: 128000,\n price_comments: `docs give just one price - \"Pricing Per 1M Tokens\", we assume that's input and output`,\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"qwen3-235b-a22b\",\n name: \"Qwen3 235B-A22B\",\n description: 'Qwen3 is the latest evolution in the Qwen LLM series, featuring both dense and MoE models with major advancements in reasoning, agent capabilities, multilingual support, and instruction following. It uniquely allows seamless switching between \"thinking\" (for complex logic, math, coding) and \"non-thinking\" modes (for fast, general dialogue), delivering strong performance across tasks.',\n match: {\n equals: \"accounts/fireworks/models/qwen3-235b-a22b\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.22,\n output_mtok: 0.88\n }\n },\n {\n id: \"qwen3p6-plus\",\n name: \"Qwen3.6 Plus\",\n match: {\n equals: \"accounts/fireworks/models/qwen3p6-plus\"\n },\n prices: {\n input_mtok: 0.5,\n cache_read_mtok: 0.1,\n output_mtok: 3\n }\n },\n {\n id: \"qwen3p7-plus\",\n name: \"Qwen3.7 Plus\",\n description: \"Qwen3.7 Plus is Alibaba's latest flagship closed model, available exclusively through Fireworks AI outside of Alibaba's own infrastructure.\",\n match: {\n equals: \"accounts/fireworks/models/qwen3p7-plus\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.4,\n cache_read_mtok: 0.08,\n output_mtok: 1.6\n }\n }\n ]\n },\n {\n id: \"github-copilot\",\n name: \"GitHub Copilot\",\n pricing_urls: [\n \"https://docs.github.com/en/copilot/reference/copilot-billing/models-and-pricing\",\n \"https://docs.github.com/en/copilot/concepts/billing/usage-based-billing-for-individuals\"\n ],\n api_pattern: \"https://api\\\\.githubcopilot\\\\.com\",\n description: \"OpenAI, Anthropic, Google, xAI, Moonshot AI and Microsoft models served through GitHub Copilot's OpenAI-compatible API at api.githubcopilot.com.\",\n price_comments: \"Copilot meters token usage in AI credits, where 1 credit = $0.01; the rates recorded here are the published per-model USD rates from the models-and-pricing page. Every response also carries a `copilot_usage.token_details[].cost_per_batch` figure in nano-credits, which agreed with the published rate for each of the nine priced models reachable on the plan used to check them on 2026-09-03. Long-context tiers reprice the whole request once the prompt crosses the threshold. Not represented: embedding models and Copilot-internal agent models, which have no published rate, and `gpt-4`, which Copilot routes to GPT-4.1 and bills at a rate the pricing page does not list. Catalog membership varies by subscription and by API integrator, so this is not the union of every plan's models.\",\n provider_match: {\n contains: \"copilot\"\n },\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"responses\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"input_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"input_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"output_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"claude-fable-5\",\n name: \"Claude Fable 5\",\n match: {\n equals: \"claude-fable-5\"\n },\n context_window: 264000,\n prices: {\n input_mtok: 10,\n cache_write_mtok: 12.5,\n cache_read_mtok: 1,\n output_mtok: 50\n }\n },\n {\n id: \"claude-fable-5.1\",\n name: \"Claude Fable 5.1\",\n match: {\n equals: \"claude-fable-5.1\"\n },\n context_window: 264000,\n prices: {\n input_mtok: 10,\n cache_write_mtok: 12.5,\n cache_read_mtok: 0.25,\n output_mtok: 50\n }\n },\n {\n id: \"claude-haiku-4.5\",\n name: \"Claude Haiku 4.5\",\n match: {\n equals: \"claude-haiku-4.5\"\n },\n context_window: 144000,\n prices: {\n input_mtok: 1,\n cache_write_mtok: 1.25,\n cache_read_mtok: 0.1,\n output_mtok: 5\n }\n },\n {\n id: \"claude-opus-4.7\",\n name: \"Claude Opus 4.7\",\n match: {\n equals: \"claude-opus-4.7\"\n },\n context_window: 264000,\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25\n }\n },\n {\n id: \"claude-opus-4.8\",\n name: \"Claude Opus 4.8\",\n match: {\n equals: \"claude-opus-4.8\"\n },\n context_window: 264000,\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25\n }\n },\n {\n id: \"claude-opus-4.8-fast\",\n name: \"Claude Opus 4.8 (fast mode)\",\n match: {\n equals: \"claude-opus-4.8-fast\"\n },\n context_window: 264000,\n price_comments: \"Fast mode is priced at 2x the standard Opus 4.8 rates.\",\n prices: {\n input_mtok: 10,\n cache_write_mtok: 12.5,\n cache_read_mtok: 1,\n output_mtok: 50\n }\n },\n {\n id: \"claude-opus-5\",\n name: \"Claude Opus 5\",\n match: {\n equals: \"claude-opus-5\"\n },\n context_window: 264000,\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25\n }\n },\n {\n id: \"claude-opus-5.5\",\n name: \"Claude Opus 5.5\",\n match: {\n equals: \"claude-opus-5.5\"\n },\n prices: {\n input_mtok: 4,\n cache_write_mtok: 5,\n cache_read_mtok: 0.2,\n output_mtok: 20\n }\n },\n {\n id: \"claude-sonnet-4\",\n name: \"Claude Sonnet 4\",\n match: {\n equals: \"claude-sonnet-4\"\n },\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"claude-sonnet-4.6\",\n name: \"Claude Sonnet 4.6\",\n match: {\n equals: \"claude-sonnet-4.6\"\n },\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"claude-sonnet-5\",\n name: \"Claude Sonnet 5\",\n match: {\n equals: \"claude-sonnet-5\"\n },\n context_window: 264000,\n prices: {\n input_mtok: 2,\n cache_write_mtok: 2.5,\n cache_read_mtok: 0.2,\n output_mtok: 10\n }\n },\n {\n id: \"claude-sonnet-5.5\",\n name: \"Claude Sonnet 5.5\",\n match: {\n equals: \"claude-sonnet-5.5\"\n },\n prices: {\n input_mtok: 2,\n cache_write_mtok: 2.5,\n cache_read_mtok: 0.1,\n output_mtok: 10\n }\n },\n {\n id: \"gemini-3.5-flash\",\n name: \"Gemini 3.5 Flash\",\n match: {\n equals: \"gemini-3.5-flash\"\n },\n context_window: 264000,\n price_comments: \"Copilot does not charge for cache writes on the Gemini models.\",\n prices: {\n input_mtok: 1.5,\n cache_read_mtok: 0.15,\n output_mtok: 9\n }\n },\n {\n id: \"gemini-3.6-flash\",\n name: \"Gemini 3.6 Flash\",\n match: {\n equals: \"gemini-3.6-flash\"\n },\n context_window: 264000,\n price_comments: \"Copilot does not charge for cache writes on the Gemini models.\",\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 3.75\n }\n },\n {\n id: \"gemini-3.7-flash\",\n name: \"Gemini 3.7 Flash\",\n match: {\n equals: \"gemini-3.7-flash\"\n },\n context_window: 264000,\n price_comments: \"Copilot does not charge for cache writes on the Gemini models.\",\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 3.75\n }\n },\n {\n id: \"gemini-3.8-flash\",\n name: \"Gemini 3.8 Flash\",\n match: {\n equals: \"gemini-3.8-flash\"\n },\n context_window: 265536,\n price_comments: \"Copilot does not charge for cache writes on the Gemini models.\",\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 3.75\n }\n },\n {\n id: \"gpt-3.5-turbo\",\n name: \"GPT 3.5 Turbo\",\n match: {\n or: [\n {\n equals: \"gpt-3.5-turbo\"\n },\n {\n equals: \"gpt-3.5-turbo-0613\"\n }\n ]\n },\n context_window: 16384,\n price_comments: \"Included in the subscription: the pricing page lists no rate, and a live request reported a zero token cost on 2026-09-03.\",\n prices: {}\n },\n {\n id: \"gpt-4.1\",\n name: \"GPT-4.1\",\n match: {\n or: [\n {\n equals: \"gpt-4.1\"\n },\n {\n equals: \"gpt-4.1-2025-04-14\"\n }\n ]\n },\n context_window: 128000,\n price_comments: \"Included in the subscription: the pricing page lists no rate, and a live request reported a zero token cost on 2026-09-03.\",\n prices: {}\n },\n {\n id: \"gpt-4o\",\n name: \"GPT-4o\",\n match: {\n or: [\n {\n equals: \"gpt-4o\"\n },\n {\n equals: \"gpt-4o-2024-05-13\"\n },\n {\n equals: \"gpt-4o-2024-08-06\"\n },\n {\n equals: \"gpt-4o-2024-11-20\"\n },\n {\n equals: \"gpt-4-o-preview\"\n }\n ]\n },\n context_window: 128000,\n price_comments: \"Included in the subscription: the pricing page lists no rate, and a live request reported a zero token cost on 2026-09-03.\",\n prices: {}\n },\n {\n id: \"gpt-4o-mini\",\n name: \"GPT-4o mini\",\n match: {\n or: [\n {\n equals: \"gpt-4o-mini\"\n },\n {\n equals: \"gpt-4o-mini-2024-07-18\"\n }\n ]\n },\n context_window: 128000,\n price_comments: \"Included in the subscription: the pricing page lists no rate, and a live request reported a zero token cost on 2026-09-03.\",\n prices: {}\n },\n {\n id: \"gpt-5-mini\",\n name: \"GPT-5 mini\",\n match: {\n or: [\n {\n equals: \"gpt-5-mini\"\n },\n {\n regex: \"^gpt-5-mini-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 264000,\n prices: {\n input_mtok: 0.25,\n cache_read_mtok: 0.025,\n output_mtok: 2\n }\n },\n {\n id: \"gpt-5.3-codex\",\n name: \"GPT-5.3-Codex\",\n match: {\n or: [\n {\n equals: \"gpt-5.3-codex\"\n },\n {\n regex: \"^gpt-5\\\\.3-codex-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 400000,\n prices: {\n input_mtok: 1.75,\n cache_read_mtok: 0.175,\n output_mtok: 14\n }\n },\n {\n id: \"gpt-5.4\",\n name: \"GPT-5.4\",\n match: {\n or: [\n {\n equals: \"gpt-5.4\"\n },\n {\n regex: \"^gpt-5\\\\.4-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Prompts over 272,000 tokens are billed at the long-context rates.\",\n prices: {\n input_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 272000,\n price: 0.5\n }\n ]\n },\n output_mtok: {\n base: 15,\n tiers: [\n {\n start: 272000,\n price: 22.5\n }\n ]\n }\n }\n },\n {\n id: \"gpt-5.4-mini\",\n name: \"GPT-5.4 mini\",\n match: {\n or: [\n {\n equals: \"gpt-5.4-mini\"\n },\n {\n regex: \"^gpt-5\\\\.4-mini-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 400000,\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 4.5\n }\n },\n {\n id: \"gpt-5.4-nano\",\n name: \"GPT-5.4 nano\",\n match: {\n or: [\n {\n equals: \"gpt-5.4-nano\"\n },\n {\n regex: \"^gpt-5\\\\.4-nano-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.02,\n output_mtok: 1.25\n }\n },\n {\n id: \"gpt-5.5\",\n name: \"GPT-5.5\",\n match: {\n or: [\n {\n equals: \"gpt-5.5\"\n },\n {\n regex: \"^gpt-5\\\\.5-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Prompts over 272,000 tokens are billed at the long-context rates.\",\n prices: {\n input_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 272000,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 30,\n tiers: [\n {\n start: 272000,\n price: 45\n }\n ]\n }\n }\n },\n {\n id: \"gpt-5.6-luna\",\n name: \"GPT-5.6 Luna\",\n match: {\n or: [\n {\n equals: \"gpt-5.6-luna\"\n },\n {\n regex: \"^gpt-5\\\\.6-luna-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 328000,\n price_comments: \"Prompts over 200,000 tokens are billed at the long-context rates.\",\n prices: {\n input_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 200000,\n price: 0.4\n }\n ]\n },\n cache_write_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 200000,\n price: 0.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.02,\n tiers: [\n {\n start: 200000,\n price: 0.04\n }\n ]\n },\n output_mtok: {\n base: 1.2,\n tiers: [\n {\n start: 200000,\n price: 1.8\n }\n ]\n }\n }\n },\n {\n id: \"gpt-5.6-sol\",\n name: \"GPT-5.6 Sol\",\n match: {\n or: [\n {\n equals: \"gpt-5.6-sol\"\n },\n {\n regex: \"^gpt-5\\\\.6-sol-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Prompts over 272,000 tokens are billed at the long-context rates.\",\n prices: {\n input_mtok: {\n base: 4,\n tiers: [\n {\n start: 272000,\n price: 8\n }\n ]\n },\n cache_write_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_read_mtok: {\n base: 0.4,\n tiers: [\n {\n start: 272000,\n price: 0.8\n }\n ]\n },\n output_mtok: {\n base: 20,\n tiers: [\n {\n start: 272000,\n price: 30\n }\n ]\n }\n }\n },\n {\n id: \"gpt-5.6-terra\",\n name: \"GPT-5.6 Terra\",\n match: {\n or: [\n {\n equals: \"gpt-5.6-terra\"\n },\n {\n regex: \"^gpt-5\\\\.6-terra-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Prompts over 272,000 tokens are billed at the long-context rates.\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 12,\n tiers: [\n {\n start: 272000,\n price: 18\n }\n ]\n }\n }\n },\n {\n id: \"gpt-6-astra\",\n name: \"GPT-6 Astra\",\n match: {\n or: [\n {\n equals: \"gpt-6-astra\"\n },\n {\n regex: \"^gpt-6-astra-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n price_comments: \"Prompts over 272,000 tokens are billed at the long-context rates.\",\n prices: {\n input_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 20\n }\n ]\n },\n cache_write_mtok: {\n base: 12.5,\n tiers: [\n {\n start: 272000,\n price: 25\n }\n ]\n },\n cache_read_mtok: {\n base: 1,\n tiers: [\n {\n start: 272000,\n price: 2\n }\n ]\n },\n output_mtok: {\n base: 50,\n tiers: [\n {\n start: 272000,\n price: 75\n }\n ]\n }\n }\n },\n {\n id: \"gpt-6-luna\",\n name: \"GPT-6 Luna\",\n match: {\n or: [\n {\n equals: \"gpt-6-luna\"\n },\n {\n regex: \"^gpt-6-luna-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n price_comments: \"Prompts over 272,000 tokens are billed at the long-context rates.\",\n prices: {\n input_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n cache_write_mtok: {\n base: 0.125,\n tiers: [\n {\n start: 272000,\n price: 0.25\n }\n ]\n },\n cache_read_mtok: {\n base: 0.01,\n tiers: [\n {\n start: 272000,\n price: 0.02\n }\n ]\n },\n output_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 272000,\n price: 0.75\n }\n ]\n }\n }\n },\n {\n id: \"gpt-6-sol\",\n name: \"GPT-6 Sol\",\n match: {\n or: [\n {\n equals: \"gpt-6-sol\"\n },\n {\n regex: \"^gpt-6-sol-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n price_comments: \"Prompts over 272,000 tokens are billed at the long-context rates.\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 15\n }\n ]\n }\n }\n },\n {\n id: \"gpt-6.1-sol\",\n name: \"GPT-6.1 Sol\",\n match: {\n or: [\n {\n equals: \"gpt-6.1-sol\"\n },\n {\n regex: \"^gpt-6\\\\.1-sol-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Prompts over 272,000 tokens are billed at the long-context rates.\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n output_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 15\n }\n ]\n }\n }\n },\n {\n id: \"grok-4.5\",\n name: \"Grok 4.5\",\n match: {\n equals: \"grok-4.5\"\n },\n context_window: 328000,\n price_comments: \"Prompts over 200,000 tokens are billed at the long-context rates.\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 200000,\n price: 4\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 200000,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 6,\n tiers: [\n {\n start: 200000,\n price: 12\n }\n ]\n }\n }\n },\n {\n id: \"grok-4.6\",\n name: \"Grok 4.6\",\n match: {\n equals: \"grok-4.6\"\n },\n context_window: 328000,\n price_comments: \"Prompts over 200,000 tokens are billed at the long-context rates.\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 200000,\n price: 4\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 200000,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 6,\n tiers: [\n {\n start: 200000,\n price: 12\n }\n ]\n }\n }\n },\n {\n id: \"grok-4.7\",\n name: \"Grok 4.7\",\n match: {\n equals: \"grok-4.7\"\n },\n price_comments: \"Prompts over 200,000 tokens are billed at the long-context rates.\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 200000,\n price: 4\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 200000,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 6,\n tiers: [\n {\n start: 200000,\n price: 12\n }\n ]\n }\n }\n },\n {\n id: \"kimi-k2.7-code\",\n name: \"Kimi K2.7 Code\",\n match: {\n equals: \"kimi-k2.7-code\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.95,\n cache_read_mtok: 0.19,\n output_mtok: 4\n }\n },\n {\n id: \"kimi-k3\",\n name: \"Kimi K3\",\n match: {\n equals: \"kimi-k3\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 3,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"mai-code-1-flash-picker\",\n name: \"MAI-Code-1-Flash\",\n match: {\n equals: \"mai-code-1-flash-picker\"\n },\n context_window: 256000,\n price_comments: \"The catalog id carries a `-picker` suffix; the pricing page names it MAI-Code-1-Flash.\",\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 4.5\n }\n },\n {\n id: \"mai-code-1.1-flash\",\n name: \"MAI-Code-1.1-Flash\",\n match: {\n equals: \"mai-code-1.1-flash\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.02,\n output_mtok: 1.2\n }\n }\n ]\n },\n {\n id: \"google\",\n name: \"Google\",\n pricing_urls: [\"https://ai.google.dev/gemini-api/docs/pricing\", \"https://cloud.google.com/vertex-ai/generative-ai/pricing\"],\n api_pattern: \"https://(.*\\\\.)?googleapis\\\\.com\",\n model_match: {\n contains: \"gemini\"\n },\n provider_match: {\n or: [\n {\n contains: \"google\"\n },\n {\n contains: \"vertex\"\n },\n {\n contains: \"gemini\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usageMetadata\",\n model_path: \"modelVersion\",\n mappings: [\n {\n path: \"promptTokenCount\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"cachedContentTokenCount\",\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\n \"cacheTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"TEXT\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"cache_text_read_tokens\",\n required: false\n },\n {\n path: [\n \"cacheTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"AUDIO\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"cache_audio_read_tokens\",\n required: false\n },\n {\n path: [\n \"cacheTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"IMAGE\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"cache_image_read_tokens\",\n required: false\n },\n {\n path: [\n \"cacheTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"DOCUMENT\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"cache_image_read_tokens\",\n required: false\n },\n {\n path: [\n \"cacheTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"VIDEO\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"cache_video_read_tokens\",\n required: false\n },\n {\n path: [\n \"promptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"TEXT\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_text_tokens\",\n required: false\n },\n {\n path: [\n \"promptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"AUDIO\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\n \"promptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"IMAGE\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_image_tokens\",\n required: false\n },\n {\n path: [\n \"promptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"DOCUMENT\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_image_tokens\",\n required: false\n },\n {\n path: [\n \"promptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"VIDEO\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_video_tokens\",\n required: false\n },\n {\n path: [\n \"candidatesTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"TEXT\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"output_text_tokens\",\n required: false\n },\n {\n path: [\n \"candidatesTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"AUDIO\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\n \"candidatesTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"IMAGE\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"output_image_tokens\",\n required: false\n },\n {\n path: [\n \"candidatesTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"DOCUMENT\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"output_image_tokens\",\n required: false\n },\n {\n path: [\n \"candidatesTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"VIDEO\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"output_video_tokens\",\n required: false\n },\n {\n path: \"candidatesTokenCount\",\n dest: \"output_tokens\",\n required: false\n },\n {\n path: \"thoughtsTokenCount\",\n dest: \"output_tokens\",\n required: false\n },\n {\n path: \"thoughtsTokenCount\",\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"toolUsePromptTokenCount\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"toolUsePromptTokenCount\",\n dest: \"input_tool_tokens\",\n required: false\n },\n {\n path: [\n \"toolUsePromptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"TEXT\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_text_tokens\",\n required: false\n },\n {\n path: [\n \"toolUsePromptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"TEXT\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_text_tool_tokens\",\n required: false\n },\n {\n path: [\n \"toolUsePromptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"AUDIO\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\n \"toolUsePromptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"AUDIO\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_audio_tool_tokens\",\n required: false\n },\n {\n path: [\n \"toolUsePromptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"IMAGE\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_image_tokens\",\n required: false\n },\n {\n path: [\n \"toolUsePromptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"IMAGE\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_image_tool_tokens\",\n required: false\n },\n {\n path: [\n \"toolUsePromptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"DOCUMENT\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_image_tokens\",\n required: false\n },\n {\n path: [\n \"toolUsePromptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"DOCUMENT\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_image_tool_tokens\",\n required: false\n },\n {\n path: [\n \"toolUsePromptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"VIDEO\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_video_tokens\",\n required: false\n },\n {\n path: [\n \"toolUsePromptTokensDetails\",\n {\n type: \"array-match\",\n field: \"modality\",\n match: {\n equals: \"VIDEO\"\n }\n },\n \"tokenCount\"\n ],\n dest: \"input_video_tool_tokens\",\n required: false\n }\n ]\n },\n {\n api_flavor: \"anthropic\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"cache_creation_input_tokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"cache_read_input_tokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"cache_creation_input_tokens\",\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"cache_creation\", \"ephemeral_5m_input_tokens\"],\n dest: \"cache_write_5m_tokens\",\n required: false\n },\n {\n path: [\"cache_creation\", \"ephemeral_1h_input_tokens\"],\n dest: \"cache_write_1h_tokens\",\n required: false\n },\n {\n path: \"cache_read_input_tokens\",\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n fallback_model_providers: [\"anthropic\"],\n models: [\n {\n id: \"claude-3-5-haiku\",\n match: {\n contains: \"claude-3-5-haiku\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 0.8,\n cache_write_mtok: 1,\n cache_read_mtok: 0.08,\n output_mtok: 4\n }\n },\n {\n id: \"claude-3-5-sonnet\",\n match: {\n contains: \"claude-3-5-sonnet\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"claude-3-7-sonnet\",\n match: {\n contains: \"claude-3-7-sonnet\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"claude-3-haiku\",\n match: {\n contains: \"claude-3-haiku\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 0.25,\n cache_write_mtok: 0.3,\n cache_read_mtok: 0.03,\n output_mtok: 1.25\n }\n },\n {\n id: \"claude-3-opus\",\n match: {\n contains: \"claude-3-opus\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 15,\n cache_write_mtok: 18.75,\n cache_read_mtok: 1.5,\n output_mtok: 75\n }\n },\n {\n id: \"claude-4-opus\",\n match: {\n or: [\n {\n contains: \"claude-4-opus\"\n },\n {\n contains: \"claude-opus-4@\"\n },\n {\n contains: \"claude-opus-4-0\"\n },\n {\n contains: \"claude-opus-4-1\"\n },\n {\n equals: \"claude-opus-4\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 15,\n cache_write_mtok: 18.75,\n cache_read_mtok: 1.5,\n output_mtok: 75\n }\n },\n {\n id: \"claude-4-sonnet\",\n match: {\n or: [\n {\n contains: \"claude-4-sonnet\"\n },\n {\n contains: \"claude-sonnet-4-0\"\n },\n {\n contains: \"claude-sonnet-4@\"\n },\n {\n contains: \"claude-sonnet-4-2025\"\n },\n {\n equals: \"claude-sonnet-4\"\n },\n {\n equals: \"anthropic/claude-sonnet-4\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"claude-fable-5\",\n match: {\n or: [\n {\n ends_with: \"claude-fable-5\"\n },\n {\n contains: \"claude-fable-5@\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window. Ref: https://cloud.google.com/vertex-ai/generative-ai/pricing#claude-models The one-hour cache write rate is the global endpoint's, from the same pricing page.\",\n prices: {\n input_mtok: 10,\n cache_write_mtok: 12.5,\n cache_read_mtok: 1,\n output_mtok: 50,\n cache_write_1h_mtok: 20\n }\n },\n {\n id: \"claude-fable-5-1\",\n match: {\n or: [\n {\n ends_with: \"claude-fable-5-1\"\n },\n {\n contains: \"claude-fable-5-1@\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window. Global endpoint; regional and multi-region endpoints add a 10% premium. Cache hits are 0.025x base input (not the usual 0.1x), unique to Fable 5.1 and Mythos 5.1. Ref: https://cloud.google.com/vertex-ai/generative-ai/pricing#claude-models Model ID ref: https://platform.claude.com/docs/en/build-with-claude/claude-on-vertex-ai The one-hour cache write rate is the global endpoint's, from the same pricing page.\",\n prices: {\n input_mtok: 10,\n cache_write_mtok: 12.5,\n cache_read_mtok: 0.25,\n output_mtok: 50,\n cache_write_1h_mtok: 20\n }\n },\n {\n id: \"claude-haiku-5-5\",\n match: {\n or: [\n {\n contains: \"claude-haiku-5-5\"\n },\n {\n contains: \"claude-haiku-5.5\"\n },\n {\n contains: \"claude-5-5-haiku\"\n },\n {\n contains: \"claude-5.5-haiku\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Global endpoint pricing; multi-region and regional endpoints carry a 10% premium. Prompts over 100K input tokens bill every token at the long-context rate. Rates match Anthropic's list price and OpenRouter's Google endpoint. Ref: https://cloud.google.com/vertex-ai/generative-ai/pricing#claude-models OpenRouter ref: https://openrouter.ai/api/v1/models/anthropic/claude-haiku-5.5/endpoints\",\n prices: {\n input_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 1e5,\n price: 0.5\n }\n ]\n },\n cache_write_mtok: {\n base: 0.125,\n tiers: [\n {\n start: 1e5,\n price: 0.625\n }\n ]\n },\n cache_read_mtok: {\n base: 0.01,\n tiers: [\n {\n start: 1e5,\n price: 0.05\n }\n ]\n },\n output_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 1e5,\n price: 2.5\n }\n ]\n },\n cache_write_1h_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 1e5,\n price: 1\n }\n ]\n }\n }\n },\n {\n id: \"claude-opus-4-5\",\n match: {\n or: [\n {\n contains: \"claude-4-5-opus\"\n },\n {\n contains: \"claude-opus-4-5\"\n },\n {\n contains: \"claude-4.5-opus\"\n },\n {\n contains: \"claude-opus-4.5\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"A 200K context window, so there is no long-context tier. Global endpoint rates; regional and multi-region endpoints carry a 10% premium. Ref: https://cloud.google.com/vertex-ai/generative-ai/pricing#claude-models Model ID ref: https://docs.cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10\n }\n },\n {\n id: \"claude-opus-4-6\",\n match: {\n or: [\n {\n contains: \"claude-4-6-opus\"\n },\n {\n contains: \"claude-opus-4-6\"\n },\n {\n contains: \"claude-4.6-opus\"\n },\n {\n contains: \"claude-opus-4.6\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across the full 1M context window since the long-context premium was removed on 2026-03-13, including on Vertex AI: https://claude.com/blog/1m-context-ga Ref: https://cloud.google.com/vertex-ai/generative-ai/pricing#claude-models (global endpoint)\",\n prices: [\n {\n prices: {\n input_mtok: {\n base: 5,\n tiers: [\n {\n start: 200000,\n price: 10\n }\n ]\n },\n cache_write_mtok: {\n base: 6.25,\n tiers: [\n {\n start: 200000,\n price: 12.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 200000,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 25,\n tiers: [\n {\n start: 200000,\n price: 37.5\n }\n ]\n }\n }\n },\n {\n constraint: {\n start_date: \"2026-03-13\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10\n }\n }\n ]\n },\n {\n id: \"claude-opus-4-7\",\n match: {\n or: [\n {\n contains: \"claude-4-7-opus\"\n },\n {\n contains: \"claude-opus-4-7\"\n },\n {\n contains: \"claude-4.7-opus\"\n },\n {\n contains: \"claude-opus-4.7\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window. Ref: https://cloud.google.com/vertex-ai/generative-ai/pricing#claude-models The one-hour cache write rate is the global endpoint's, from the same pricing page.\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10\n }\n },\n {\n id: \"claude-opus-4-8\",\n match: {\n or: [\n {\n contains: \"claude-4-8-opus\"\n },\n {\n contains: \"claude-opus-4-8\"\n },\n {\n contains: \"claude-4.8-opus\"\n },\n {\n contains: \"claude-opus-4.8\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window. Ref: https://cloud.google.com/vertex-ai/generative-ai/pricing#claude-models The one-hour cache write rate is the global endpoint's, from the same pricing page.\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10\n }\n },\n {\n id: \"claude-opus-5\",\n match: {\n or: [\n {\n contains: \"claude-5-opus\"\n },\n {\n ends_with: \"claude-opus-5\"\n },\n {\n contains: \"claude-opus-5@\"\n },\n {\n contains: \"claude-5.0-opus\"\n },\n {\n contains: \"claude-opus-5.0\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Global endpoint pricing, flat across the full 1M context window. Multi-region and regional endpoints carry a 10% premium. Ref: https://cloud.google.com/vertex-ai/generative-ai/pricing#claude-models The one-hour cache write rate is the global endpoint's, from the same pricing page.\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25,\n cache_write_1h_mtok: 10\n }\n },\n {\n id: \"claude-opus-5-5\",\n match: {\n or: [\n {\n contains: \"claude-5-5-opus\"\n },\n {\n ends_with: \"claude-opus-5-5\"\n },\n {\n contains: \"claude-opus-5-5@\"\n },\n {\n contains: \"claude-5.5-opus\"\n },\n {\n contains: \"claude-opus-5.5\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Global endpoint pricing, flat across the full 1M context window. Multi-region and regional endpoints carry a 10% premium. Google's pricing page renders client-side and could not be read when this entry was added; the rates follow Anthropic's list price, as every other Claude entry in this file does. Cache hits are 0.05x base input (not the usual 0.1x), as on Sonnet 5.5. Ref: https://cloud.google.com/vertex-ai/generative-ai/pricing#claude-models Anthropic ref: https://platform.claude.com/docs/en/about-claude/pricing#model-pricing Model ID ref: https://platform.claude.com/docs/en/models/opus-5-5/overview The one-hour cache write rate is the global endpoint's, from the same pricing page.\",\n prices: {\n input_mtok: 4,\n cache_write_mtok: 5,\n cache_read_mtok: 0.2,\n output_mtok: 20,\n cache_write_1h_mtok: 8\n }\n },\n {\n id: \"claude-sonnet-4-5\",\n match: {\n or: [\n {\n contains: \"claude-sonnet-4-5\"\n },\n {\n contains: \"claude-sonnet-4.5\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"Long-context rates apply above 200K input tokens. Ref: https://cloud.google.com/vertex-ai/generative-ai/pricing#partner-models The one-hour cache write rate is the global endpoint's, from the same pricing page.\",\n prices: {\n input_mtok: {\n base: 3,\n tiers: [\n {\n start: 200000,\n price: 6\n }\n ]\n },\n cache_write_mtok: {\n base: 3.75,\n tiers: [\n {\n start: 200000,\n price: 7.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.3,\n tiers: [\n {\n start: 200000,\n price: 0.6\n }\n ]\n },\n output_mtok: {\n base: 15,\n tiers: [\n {\n start: 200000,\n price: 22.5\n }\n ]\n },\n cache_write_1h_mtok: {\n base: 6,\n tiers: [\n {\n start: 200000,\n price: 12\n }\n ]\n }\n }\n },\n {\n id: \"claude-sonnet-4-6\",\n match: {\n or: [\n {\n contains: \"claude-sonnet-4-6\"\n },\n {\n contains: \"claude-sonnet-4.6\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across the full 1M context window. Ref: https://cloud.google.com/vertex-ai/generative-ai/pricing#partner-models The one-hour cache write rate is the global endpoint's, from the same pricing page.\",\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15,\n cache_write_1h_mtok: 6\n }\n },\n {\n id: \"claude-sonnet-5-5\",\n match: {\n or: [\n {\n contains: \"claude-sonnet-5-5\"\n },\n {\n contains: \"claude-sonnet-5.5\"\n },\n {\n contains: \"claude-5-5-sonnet\"\n },\n {\n contains: \"claude-5.5-sonnet\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Global endpoint pricing, flat across the full 1M context window. Multi-region and regional endpoints carry a 10% premium. Rates match Anthropic's list price and OpenRouter's Google endpoint. Cache hits are 0.05x base input (not the usual 0.1x), as on Opus 5.5. Ref: https://cloud.google.com/vertex-ai/generative-ai/pricing#claude-models Anthropic ref: https://platform.claude.com/docs/en/about-claude/pricing#model-pricing OpenRouter ref: https://openrouter.ai/api/v1/models/anthropic/claude-sonnet-5.5/endpoints\",\n prices: {\n input_mtok: 2,\n cache_write_mtok: 2.5,\n cache_read_mtok: 0.1,\n output_mtok: 10,\n cache_write_1h_mtok: 4\n }\n },\n {\n id: \"gemini-1.0-pro-vision-001\",\n name: \"gemini 1.0 pro vision\",\n description: \"Google's first-generation advanced multimodal model that can understand text, code, and images. It provides strong reasoning capabilities and follows instructions effectively.\",\n match: {\n equals: \"gemini-1.0-pro-vision-001\"\n },\n context_window: 32768,\n price_comments: \"I can't find anything about this model or it's pricing, so trusting the original source\",\n prices: {\n input_mtok: 0.125,\n output_mtok: 0.375\n }\n },\n {\n id: \"gemini-1.5-flash\",\n name: \"gemini 1.5 flash\",\n description: \"A faster, more cost-effective variant of Gemini 1.5 that maintains strong capabilities while optimizing for performance and cost efficiency. Suitable for production deployments requiring high throughput.\",\n match: {\n contains: \"gemini-1.5-flash\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: {\n base: 0.075,\n tiers: [\n {\n start: 128000,\n price: 0.15\n }\n ]\n },\n cache_read_mtok: {\n base: 0.01875,\n tiers: [\n {\n start: 128000,\n price: 0.0375\n }\n ]\n },\n output_mtok: {\n base: 0.3,\n tiers: [\n {\n start: 128000,\n price: 0.6\n }\n ]\n }\n }\n },\n {\n id: \"gemini-1.5-pro\",\n name: \"gemini 1.5 Pro\",\n description: \"Google's most capable multimodal model with an extremely long context window of up to 1 million tokens. It excels at complex reasoning, long-form content processing, and multimodal understanding.\",\n match: {\n contains: \"gemini-1.5-pro\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 128000,\n price: 2.5\n }\n ]\n },\n output_mtok: {\n base: 5,\n tiers: [\n {\n start: 128000,\n price: 10\n }\n ]\n }\n }\n },\n {\n id: \"gemini-2.0-flash\",\n name: \"gemini 2.0 flash\",\n description: \"The newest generation of Google's Gemini models, featuring improved reasoning, instruction following, and factual accuracy, with the Flash variant optimized for cost-efficiency and performance.\",\n match: {\n or: [\n {\n ends_with: \"gemini-2.0-flash\"\n },\n {\n contains: \"gemini-2.0-flash-0\"\n },\n {\n contains: \"gemini-2.0-flash-exp\"\n },\n {\n contains: \"gemini-2.0-flash-thinking\"\n },\n {\n contains: \"gemini-2.0-flash-latest\"\n }\n ]\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.025,\n output_mtok: 0.4,\n input_audio_mtok: 0.7,\n cache_audio_read_mtok: 0.175,\n web_searches_kcount: 35\n }\n },\n {\n id: \"gemini-2.0-flash-lite\",\n name: \"gemini 2.0 flash lite\",\n description: \"A lighter, more cost-effective version of Gemini 2.0 Flash, designed for applications requiring high efficiency while maintaining good performance. Ideal for high-volume, cost-sensitive deployments.\",\n match: {\n contains: \"gemini-2.0-flash-lite\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.075,\n output_mtok: 0.3\n }\n },\n {\n id: \"gemini-2.5-flash\",\n name: \"Gemini 2.5 Flash\",\n description: `Gemini 2.5 Flash is Google's state-of-the-art workhorse model, specifically designed for advanced reasoning, coding, mathematics, and scientific tasks. It includes built-in \"thinking\" capabilities, enabling it to provide responses with greater accuracy and nuanced context handling.`,\n match: {\n or: [\n {\n equals: \"gemini-2.5-flash\"\n },\n {\n equals: \"gemini-2.5-flash-latest\"\n },\n {\n equals: \"gemini-2.5-flash-preview-09-2025\"\n }\n ]\n },\n context_window: 1048576,\n price_comments: \"Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Batch halves input and output; context caching stays at the standard rate.\",\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.03,\n output_mtok: 2.5,\n input_audio_mtok: 1,\n cache_audio_read_mtok: 0.1,\n web_searches_kcount: 35\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 1.25,\n input_audio_mtok: 0.5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-2.5-flash-image\",\n name: \"Gemini 2.5 Flash Image\",\n description: \"Google's specialized image generation model optimized for fast, high-quality image generation. Outputs images at 1024x1024 resolution, with each image consuming 1290 output tokens.\",\n match: {\n or: [\n {\n equals: \"gemini-2.5-flash-image\"\n },\n {\n equals: \"gemini-2.5-flash-image-preview\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: 'Deprecated. Gemini API pricing page announces \"Gemini 2.5 Flash Image / Nano Banana (DEPRECATED \\u2014 shut down Oct 2, 2026); migrate to Gemini 3.1 Flash Image or 3.1 Flash Lite Image.\" (https://ai.google.dev/gemini-api/docs/pricing). Image output is priced at $30 per 1M tokens, with each 1024x1024 image = 1290 tokens = $0.039/image. Cache pricing is not available for this model. Text output uses the Gemini 2.5 Flash $2.50 rate and is the aggregate remainder when modality details omit text.',\n prices: {\n input_mtok: 0.3,\n output_mtok: 2.5,\n output_image_mtok: 30\n },\n deprecated: true\n },\n {\n id: \"gemini-2.5-flash-lite\",\n name: \"Gemini 2.5 Flash Lite\",\n description: 'Gemini 2.5 Flash-Lite is a lightweight reasoning model in the Gemini 2.5 family, optimized for ultra-low latency and cost efficiency. It offers improved throughput, faster token generation, and better performance across common benchmarks compared to earlier Flash models. By default, \"thinking\" (i.e. multi-pass reasoning) is disabled to prioritize speed, but developers can enable it via the Reasoning API parameter to selectively trade off cost for intelligence.',\n match: {\n or: [\n {\n equals: \"gemini-2.5-flash-lite\"\n },\n {\n and: [\n {\n starts_with: \"gemini-2.5-flash-lite-preview\"\n },\n {\n regex: \"^(?!.*-[tT][tT][sS](?:$|-))\"\n }\n ]\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Batch halves input and output; context caching stays at the standard rate.\",\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.01,\n output_mtok: 0.4,\n input_audio_mtok: 0.3,\n cache_audio_read_mtok: 0.03,\n web_searches_kcount: 35\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.2,\n input_audio_mtok: 0.15\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-2.5-flash-lite-preview-tts\",\n name: \"Gemini 2.5 Flash-Lite Preview TTS\",\n match: {\n equals: \"gemini-2.5-flash-lite-preview-tts\"\n },\n context_window: 8192,\n price_comments: \"See https://cloud.google.com/text-to-speech/pricing#gemini-tts.\",\n prices: {\n input_mtok: 0.5,\n output_mtok: 10\n }\n },\n {\n id: \"gemini-2.5-flash-preview\",\n name: \"Gemini 2.5 Flash Preview 05-20\",\n description: `Gemini 2.5 Flash May 20th Checkpoint is Google's state-of-the-art workhorse model, specifically designed for advanced reasoning, coding, mathematics, and scientific tasks. It includes built-in \"thinking\" capabilities, enabling it to provide responses with greater accuracy and nuanced context handling.`,\n match: {\n or: [\n {\n contains: \"gemini-2.5-flash-preview-05-20\"\n },\n {\n contains: \"gemini-2.5-flash-preview-04-17\"\n },\n {\n equals: \"gemini-2.5-flash-preview-05-20:thinking\"\n },\n {\n equals: \"gemini-2.5-flash-preview\"\n },\n {\n equals: \"gemini-2.5-flash-preview:thinking\"\n }\n ]\n },\n price_comments: \"from https://cloud.google.com/vertex-ai/generative-ai/pricing should be retired 2025-07-15\",\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6,\n web_searches_kcount: 35\n },\n deprecated: true\n },\n {\n id: \"gemini-2.5-flash-tts\",\n name: \"Gemini 2.5 Flash TTS\",\n match: {\n or: [\n {\n equals: \"gemini-2.5-flash-tts\"\n },\n {\n equals: \"gemini-2.5-flash-preview-tts\"\n }\n ]\n },\n context_window: 8192,\n price_comments: \"See https://cloud.google.com/text-to-speech/pricing#gemini-tts. Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Rates are from the Gemini API gemini-2.5-flash-preview-tts Batch tab.\",\n prices: {\n input_mtok: 0.5,\n output_mtok: 10\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.25,\n output_mtok: 5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-2.5-pro\",\n name: \"Gemini 2.5 Pro\",\n description: `Gemini 2.5 Pro is Google's state-of-the-art AI model designed for advanced reasoning, coding, mathematics, and scientific tasks. It employs \"thinking\" capabilities, enabling it to reason through responses with enhanced accuracy and nuanced context handling. Gemini 2.5 Pro achieves top-tier performance on multiple benchmarks, including first-place positioning on the LMArena leaderboard, reflecting superior human-preference alignment and complex problem-solving abilities.`,\n match: {\n and: [\n {\n starts_with: \"gemini-2.5-pro\"\n },\n {\n regex: \"^(?!.*-[tT][tT][sS](?:$|-))\"\n }\n ]\n },\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing#gemini-2.5-pro. Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Batch halves input and output in both prompt-size tiers; context caching stays at the standard rate.\",\n prices: {\n input_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 200000,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.125,\n tiers: [\n {\n start: 200000,\n price: 0.25\n }\n ]\n },\n output_mtok: {\n base: 10,\n tiers: [\n {\n start: 200000,\n price: 15\n }\n ]\n },\n web_searches_kcount: 35\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 0.625,\n tiers: [\n {\n start: 200000,\n price: 1.25\n }\n ]\n },\n output_mtok: {\n base: 5,\n tiers: [\n {\n start: 200000,\n price: 7.5\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-2.5-pro-tts\",\n name: \"Gemini 2.5 Pro TTS\",\n match: {\n or: [\n {\n equals: \"gemini-2.5-pro-tts\"\n },\n {\n equals: \"gemini-2.5-pro-preview-tts\"\n }\n ]\n },\n context_window: 8192,\n price_comments: \"See https://cloud.google.com/text-to-speech/pricing#gemini-tts. Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Rates are from the Gemini API gemini-2.5-pro-preview-tts Batch tab.\",\n prices: {\n input_mtok: 1,\n output_mtok: 20\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 10\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-3-flash-preview\",\n name: \"Gemini 3 Flash Preview\",\n description: \"Google's ultra-fast frontier model optimized for speed and efficiency. Delivers state-of-the-art performance while maintaining low latency and cost, with improved reasoning and coding capabilities.\",\n match: {\n or: [\n {\n equals: \"gemini-3-flash-preview\"\n },\n {\n starts_with: \"gemini-3-flash-preview-\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: 'See https://ai.google.dev/gemini-api/docs/pricing. Standard pricing shown; Batch API offers 50% discount on input/output. Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Batch halves input and output; context caching is \"Same as Standard\".',\n prices: [\n {\n prices: {\n input_mtok: 0.5,\n cache_read_mtok: 0.05,\n output_mtok: 3,\n input_audio_mtok: 1,\n cache_audio_read_mtok: 0.1\n }\n },\n {\n constraint: {\n start_date: \"2026-01-05\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 0.5,\n cache_read_mtok: 0.05,\n output_mtok: 3,\n input_audio_mtok: 1,\n cache_audio_read_mtok: 0.1,\n web_searches_kcount: 14\n }\n }\n ],\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.25,\n output_mtok: 1.5,\n input_audio_mtok: 0.5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-3-pro-image-preview\",\n name: \"Gemini 3 Pro Image Preview\",\n description: \"Google's image generation model optimized for high-quality image generation. Supports 1K/2K and 4K resolution outputs with flexible pricing based on image dimensions.\",\n match: {\n or: [\n {\n equals: \"gemini-3-pro-image\"\n },\n {\n starts_with: \"gemini-3-pro-image-preview\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing#gemini-3-pro-image. Image output is priced at $120 per 1M tokens, with each 1K/2K image = 1120 tokens = $0.134/image and each 4K image = 2000 tokens = $0.24/image. Text and thinking output is $12 per 1M tokens and is the aggregate remainder when modality details omit text. Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Batch input is $1 (text) and $0.0006 per 560-token image (~$1/1M); batch image output is listed per image only ($0.067 per 1K/2K image of 1120 tokens, $0.12 per 4K image of 2000 tokens), which is $60 per 1M tokens.\",\n prices: [\n {\n prices: {\n input_mtok: 2,\n output_mtok: 12,\n output_image_mtok: 120\n }\n },\n {\n constraint: {\n start_date: \"2026-01-05\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 12,\n output_image_mtok: 120,\n web_searches_kcount: 14\n }\n }\n ],\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 6,\n output_image_mtok: 60\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-3-pro-preview\",\n name: \"Gemini 3 Pro Preview\",\n description: \"The best model in the world for multimodal understanding, and our most powerful agentic and vibe-coding model yet.\",\n match: {\n or: [\n {\n starts_with: \"gemini-3-pro-preview\"\n },\n {\n equals: \"gemini-3-pro-text-preview\"\n }\n ]\n },\n prices: [\n {\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 200000,\n price: 4\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 200000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 12,\n tiers: [\n {\n start: 200000,\n price: 18\n }\n ]\n }\n }\n },\n {\n constraint: {\n start_date: \"2026-01-05\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 200000,\n price: 4\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 200000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 12,\n tiers: [\n {\n start: 200000,\n price: 18\n }\n ]\n },\n web_searches_kcount: 14\n }\n }\n ]\n },\n {\n id: \"gemini-3.1-flash-image-preview\",\n name: \"Gemini 3.1 Flash Image Preview\",\n description: \"Google's latest image generation model (Nano Banana 2) optimized for fast, high-quality image generation. Supports multiple output resolutions from 512px to 4K, with text and thinking output priced separately from image output tokens.\",\n match: {\n or: [\n {\n equals: \"gemini-3.1-flash-image\"\n },\n {\n starts_with: \"gemini-3.1-flash-image-preview\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing. Text and thinking output is priced at $3 per 1M tokens and is the aggregate remainder when modality details omit text. Image output is priced at $60 per 1M tokens. Preview model - pricing may change. Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Batch halves input, text output and image output.\",\n prices: {\n input_mtok: 0.5,\n output_mtok: 3,\n output_image_mtok: 60,\n web_searches_kcount: 14\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.25,\n output_mtok: 1.5,\n output_image_mtok: 30\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-3.1-flash-lite\",\n name: \"Gemini 3.1 Flash Lite\",\n description: \"Google's fastest and most cost-efficient Gemini 3 series model, built for intelligence at scale. Optimized for high-volume, low-latency applications while maintaining strong multimodal capabilities.\",\n match: {\n or: [\n {\n equals: \"gemini-3.1-flash-lite\"\n },\n {\n starts_with: \"gemini-3.1-flash-lite-preview\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing. Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Batch halves input, output and context caching.\",\n prices: {\n input_mtok: 0.25,\n cache_read_mtok: 0.025,\n output_mtok: 1.5,\n input_audio_mtok: 0.5,\n cache_audio_read_mtok: 0.05,\n web_searches_kcount: 14\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.125,\n cache_read_mtok: 0.0125,\n output_mtok: 0.75,\n input_audio_mtok: 0.25,\n cache_audio_read_mtok: 0.025\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-3.1-flash-lite-image\",\n name: \"Gemini 3.1 Flash Lite Image\",\n description: \"Google's low-latency, cost-efficient image generation and editing model.\",\n match: {\n starts_with: \"gemini-3.1-flash-lite-image\"\n },\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing#gemini-3.1-flash-lite-image. Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Batch halves input, text output and image output.\",\n prices: {\n input_mtok: 0.25,\n output_mtok: 1.5,\n output_image_mtok: 30\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.125,\n output_mtok: 0.75,\n output_image_mtok: 15\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-3.1-flash-live-preview\",\n name: \"Gemini 3.1 Flash Live Preview\",\n description: \"Google's low-latency audio-to-audio model with multimodal input support.\",\n match: {\n starts_with: \"gemini-3.1-flash-live-preview\"\n },\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing#gemini-3.1-flash-live-preview.\",\n prices: {\n input_mtok: 0.75,\n output_mtok: 4.5,\n input_audio_mtok: 3,\n output_audio_mtok: 12,\n input_image_mtok: 1,\n input_video_mtok: 1,\n web_searches_kcount: 14\n }\n },\n {\n id: \"gemini-3.1-pro-preview\",\n name: \"Gemini 3.1 Pro Preview\",\n description: \"The latest performance, intelligence, and usability improvements to the best model family in the world for multimodal understanding, agentic capabilities, and vibe-coding.\",\n match: {\n or: [\n {\n starts_with: \"gemini-3.1-pro-preview\"\n },\n {\n equals: \"gemini-pro-latest\"\n }\n ]\n },\n price_comments: 'Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Batch halves input and output in both prompt-size tiers; context caching is \"Same as Standard\".',\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 200000,\n price: 4\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 200000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 12,\n tiers: [\n {\n start: 200000,\n price: 18\n }\n ]\n },\n web_searches_kcount: 14\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 1,\n tiers: [\n {\n start: 200000,\n price: 2\n }\n ]\n },\n output_mtok: {\n base: 6,\n tiers: [\n {\n start: 200000,\n price: 9\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-3.5-flash\",\n name: \"Gemini 3.5 Flash\",\n description: \"Google's most intelligent model built for speed, combining frontier intelligence with improved reasoning, coding, and multimodal understanding.\",\n match: {\n or: [\n {\n equals: \"gemini-3.5-flash\"\n },\n {\n starts_with: \"gemini-3.5-flash-preview\"\n },\n {\n regex: \"^gemini-3\\\\.5-flash-\\\\d\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing. Standard tier pricing shown; Batch and Flex tiers offer 50% discount on input/output.\",\n prices: {\n input_mtok: 1.5,\n cache_read_mtok: 0.15,\n output_mtok: 9,\n web_searches_kcount: 14\n }\n },\n {\n id: \"gemini-3.5-flash-lite\",\n name: \"Gemini 3.5 Flash Lite\",\n description: \"Google's fastest and most cost-efficient Gemini 3.5 series model, optimized for high-volume, low-latency applications while maintaining strong multimodal capabilities.\",\n match: {\n or: [\n {\n starts_with: \"gemini-3.5-flash-lite\"\n },\n {\n equals: \"gemini-flash-lite-latest\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing. Standard tier pricing shown; Batch and Flex tiers offer 50% discount. Input rate is unified across text/image/video/audio (no separate audio rate). Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Batch halves input and output; batch context caching is $0.02, not half of the $0.03 standard rate.\",\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.03,\n output_mtok: 2.5,\n web_searches_kcount: 14\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.02,\n output_mtok: 1.25\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-3.6-flash\",\n name: \"Gemini 3.6 Flash\",\n description: \"Google's most intelligent model built for speed, combining frontier intelligence with improved reasoning, coding, and multimodal understanding.\",\n match: {\n starts_with: \"gemini-3.6-flash\"\n },\n context_window: 1e6,\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing. Standard tier pricing shown; Batch and Flex tiers offer 50% discount. No separate audio input rate documented. Introductory rates apply through 2026-12-31. The cache storage price ($0.50 per 1M tokens per hour) has no unit in prices/units.yml and is not represented here. Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Batch halves input, output and context caching, at both the introductory and the 2027-01-01 rates.\",\n prices: [\n {\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 3.75,\n web_searches_kcount: 14\n }\n },\n {\n constraint: {\n start_date: \"2027-01-01\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 1.5,\n cache_read_mtok: 0.15,\n output_mtok: 7.5,\n web_searches_kcount: 14\n }\n }\n ],\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 3.75\n },\n constraint: {\n start_date: \"2027-01-01\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.375,\n cache_read_mtok: 0.0375,\n output_mtok: 1.875\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-3.7-flash\",\n name: \"Gemini 3.7 Flash\",\n description: \"Google's most capable Flash model, built for complex coding, web development, agentic workflows and reliable multi-step execution.\",\n match: {\n starts_with: \"gemini-3.7-flash\"\n },\n context_window: 1e6,\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing. Standard tier pricing shown; Batch and Flex tiers offer 50% discount, Priority tier is 1.8x. Introductory rates apply through 2026-12-31. The cache storage price ($0.50 per 1M tokens per hour) has no unit in prices/units.yml and is not represented here.\",\n prices: [\n {\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 3.75,\n web_searches_kcount: 14\n }\n },\n {\n constraint: {\n start_date: \"2027-01-01\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 1.5,\n cache_read_mtok: 0.15,\n output_mtok: 7.5,\n web_searches_kcount: 14\n }\n }\n ]\n },\n {\n id: \"gemini-3.8-flash\",\n name: \"Gemini 3.8 Flash\",\n description: \"Google's most intelligent Flash model, engineered for long-horizon software engineering, autonomous agents, and complex enterprise workflows.\",\n match: {\n or: [\n {\n starts_with: \"gemini-3.8-flash\"\n },\n {\n equals: \"gemini-flash-latest\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing. Standard tier pricing shown; Batch and Flex tiers offer 50% discount, Priority tier is 1.8x. Introductory rates apply through 2026-12-31. The cache storage price ($0.50 per 1M tokens per hour) has no unit in prices/units.yml and is not represented here. Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Batch halves input, output and context caching, at both the introductory and the 2027-01-01 rates.\",\n prices: [\n {\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 3.75,\n web_searches_kcount: 14\n }\n },\n {\n constraint: {\n start_date: \"2027-01-01\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 1.5,\n cache_read_mtok: 0.15,\n output_mtok: 7.5,\n web_searches_kcount: 14\n }\n }\n ],\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 3.75\n },\n constraint: {\n start_date: \"2027-01-01\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.375,\n cache_read_mtok: 0.0375,\n output_mtok: 1.875\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-3.8-live\",\n name: \"Gemini 3.8 Live\",\n description: \"Google's low-latency audio-to-audio Live API models for real-time voice agents and live dialogue, including Gemini 3.8 Live Extended Thinking, which reasons in the background while it keeps talking.\",\n match: {\n starts_with: \"gemini-3.8-live\"\n },\n context_window: 131072,\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing - Gemini 3.8 Live and Gemini 3.8 Live Extended Thinking are priced the same as Gemini 3.1 Flash Live Preview, with thinking tokens included in the output price. Audio and image/video are also offered per minute, which has no unit here; the per-token prices are shown.\",\n prices: {\n input_mtok: 0.75,\n output_mtok: 4.5,\n input_audio_mtok: 3,\n output_audio_mtok: 12,\n input_image_mtok: 1,\n input_video_mtok: 1,\n web_searches_kcount: 14\n }\n },\n {\n id: \"gemini-4-argon\",\n name: \"Gemini 4 Argon\",\n description: \"Google's frontier model for complex, long-horizon workflows across real-world software engineering, enterprise knowledge work like legal and finance, and cybersecurity defense, with output of up to 1M tokens.\",\n match: {\n or: [\n {\n equals: \"gemini-4-argon\"\n },\n {\n starts_with: \"gemini-4-argon-preview\"\n },\n {\n regex: \"^gemini-4-argon-\\\\d\"\n }\n ]\n },\n context_window: 1048576,\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing. Standard tier pricing shown; Batch and Flex tiers offer 50% discount, Priority tier is 1.8x. Introductory rates apply through 2026-11-30. No prompt-size tiers or separate audio input rate are documented. Implicit caching only. Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Batch halves input, output and context caching, at both the introductory and the 2026-12-01 rates.\",\n prices: [\n {\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.1,\n output_mtok: 10,\n web_searches_kcount: 14\n }\n },\n {\n constraint: {\n start_date: \"2026-12-01\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 4,\n cache_read_mtok: 0.2,\n output_mtok: 20,\n web_searches_kcount: 14\n }\n }\n ],\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.1,\n output_mtok: 10\n },\n constraint: {\n start_date: \"2026-12-01\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 1,\n cache_read_mtok: 0.05,\n output_mtok: 5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-embedding-001\",\n match: {\n equals: \"gemini-embedding-001\"\n },\n prices: {\n input_mtok: 0.15\n }\n },\n {\n id: \"gemini-embedding-2\",\n name: \"Gemini Embedding 2\",\n description: \"Google's multimodal embedding model for text, images, audio, video, and documents.\",\n match: {\n equals: \"gemini-embedding-2\"\n },\n context_window: 8192,\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing#gemini-embedding-2. Batch rates verified on 2026-10-14 at https://ai.google.dev/gemini-api/docs/pricing (Batch tab); batch history before then is not modelled. Batch halves every input modality.\",\n prices: {\n input_mtok: 0.2,\n input_audio_mtok: 6.5,\n input_image_mtok: 0.45,\n input_video_mtok: 12\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.1,\n input_audio_mtok: 3.25,\n input_image_mtok: 0.225,\n input_video_mtok: 6\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gemini-flash-1.5\",\n name: \"Gemini 1.5 Flash\",\n description: \"Gemini 1.5 Flash is a foundation model that performs well at a variety of multimodal tasks such as visual understanding, classification, summarization, and creating content from image, audio and video. It's adept at processing visual and text inputs such as photographs, documents, infographics, and screenshots.\",\n match: {\n equals: \"gemini-flash-1.5\"\n },\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing#gemini-1.5-flash\",\n prices: {\n input_mtok: {\n base: 0.075,\n tiers: [\n {\n start: 128000,\n price: 0.15\n }\n ]\n },\n cache_read_mtok: {\n base: 0.01875,\n tiers: [\n {\n start: 128000,\n price: 0.0375\n }\n ]\n },\n output_mtok: {\n base: 0.3,\n tiers: [\n {\n start: 128000,\n price: 0.6\n }\n ]\n }\n }\n },\n {\n id: \"gemini-flash-1.5-8b\",\n name: \"gemini 1.5 flash\",\n description: \"A faster, more cost-effective variant of Gemini 1.5 that maintains strong capabilities while optimizing for performance and cost efficiency. Suitable for production deployments requiring high throughput.\",\n match: {\n equals: \"gemini-flash-1.5-8b\"\n },\n context_window: 1e6,\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing#gemini-1.5-flash-8b\",\n prices: {\n input_mtok: {\n base: 0.0375,\n tiers: [\n {\n start: 128000,\n price: 0.075\n }\n ]\n },\n cache_read_mtok: {\n base: 0.01,\n tiers: [\n {\n start: 128000,\n price: 0.02\n }\n ]\n },\n output_mtok: {\n base: 0.15,\n tiers: [\n {\n start: 128000,\n price: 0.3\n }\n ]\n }\n }\n },\n {\n id: \"gemini-live-2.5-flash\",\n name: \"Gemini Live 2.5 Flash\",\n description: \"Google's Live API model for low-latency bidirectional voice and video interactions, GA on Vertex AI (model id `gemini-live-2.5-flash`, served from the `global` location). The prefix match also covers the AI Studio preview ids (`gemini-live-2.5-flash-preview*`).\",\n match: {\n or: [\n {\n starts_with: \"gemini-live-2.5-flash\"\n },\n {\n starts_with: \"gemini-2.5-flash-native-audio-preview\"\n },\n {\n equals: \"gemini-2.5-flash-native-audio-latest\"\n }\n ]\n },\n price_comments: \"See https://cloud.google.com/vertex-ai/generative-ai/pricing (Live API) and https://ai.google.dev/gemini-api/docs/pricing - GA pricing matches the preview.\",\n prices: {\n input_mtok: 0.5,\n output_mtok: 2,\n input_audio_mtok: 3,\n output_audio_mtok: 12\n }\n },\n {\n id: \"gemini-omni-flash-preview\",\n name: \"Gemini Omni Flash Preview\",\n description: \"Google's video generation and editing model with multimodal input.\",\n match: {\n starts_with: \"gemini-omni-flash-preview\"\n },\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing#gemini-omni-flash-preview.\",\n prices: {\n input_mtok: 1.5,\n output_mtok: 9,\n output_video_mtok: 17.5\n }\n },\n {\n id: \"gemini-pro\",\n name: \"gemini 1.0 pro\",\n description: \"Google's first-generation advanced multimodal model that can understand text, code, and images. It provides strong reasoning capabilities and follows instructions effectively.\",\n match: {\n or: [\n {\n equals: \"gemini-pro\"\n },\n {\n equals: \"gemini-1.0-pro\"\n }\n ]\n },\n context_window: 32768,\n price_comments: \"I can't find anything so trusting these prices, not sure the model still exists\",\n prices: {\n input_mtok: 0.125,\n output_mtok: 0.375\n }\n },\n {\n id: \"gemini-pro-1.5\",\n name: \"Gemini 1.5 Pro\",\n description: \"Google's latest multimodal model, supports image and video[0] in text or chat prompts.\",\n match: {\n equals: \"gemini-pro-1.5\"\n },\n context_window: 2000000,\n price_comments: \"See https://ai.google.dev/gemini-api/docs/pricing#gemini-1.5-pro\",\n prices: {\n input_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 128000,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.3125,\n tiers: [\n {\n start: 128000,\n price: 0.625\n }\n ]\n },\n output_mtok: {\n base: 5,\n tiers: [\n {\n start: 128000,\n price: 10\n }\n ]\n }\n }\n },\n {\n id: \"gemma-2-27b-it\",\n name: \"Gemma 2 27B\",\n description: \"Gemma 2 27B by Google is an open model built from the same research and technology used to create the Gemini models. Gemma models are well-suited for a variety of text generation and instruction-following tasks.\",\n match: {\n equals: \"gemma-2-27b-it\"\n },\n price_comments: \"Imported from OpenRouter pricing; verify against Google pricing when native API pricing is published.\",\n prices: {\n input_mtok: 0.65,\n output_mtok: 0.65\n }\n },\n {\n id: \"gemma-3\",\n name: \"Gemma 3 (free)\",\n description: \"Lightweight, state-of the art, open model built from the same technology that powers our Gemini models.\",\n match: {\n or: [\n {\n starts_with: \"gemma-3-\"\n },\n {\n equals: \"gemma-3\"\n }\n ]\n },\n prices: {}\n },\n {\n id: \"gemma-3n\",\n name: \"Gemma 3n (free)\",\n description: \"Our open model built for efficient performance on everyday devices like mobile phones, laptops, and tablets.\",\n match: {\n or: [\n {\n starts_with: \"gemma-3n\"\n }\n ]\n },\n prices: {}\n },\n {\n id: \"gemma-4-26b-a4b-it\",\n name: \"Gemma 4 26B A4B\",\n description: \"Gemma 4 26B A4B IT is an instruction-tuned Mixture-of-Experts (MoE) model from Google DeepMind. Despite 25.2B total parameters, only 3.8B activate per token during inference.\",\n match: {\n equals: \"gemma-4-26b-a4b-it\"\n },\n context_window: 262144,\n price_comments: \"Gemini API model ID. Free of charge on the Gemini API with no paid tier, see https://ai.google.dev/gemini-api/docs/pricing. The Vertex AI Model-as-a-Service offering uses the gemma-4-26b-a4b-it-maas ID and is priced separately. The previous rates were imported from OpenRouter, not Google.\",\n prices: {}\n },\n {\n id: \"gemma-4-26b-a4b-it-maas\",\n name: \"Gemma 4 26B A4B (Vertex AI MaaS)\",\n description: \"Gemma 4 26B A4B IT served by Vertex AI as a managed Model-as-a-Service endpoint.\",\n match: {\n equals: \"gemma-4-26b-a4b-it-maas\"\n },\n context_window: 262144,\n price_comments: \"Vertex AI Model-as-a-Service rates, see https://cloud.google.com/vertex-ai/generative-ai/pricing. Model ID from https://cloud.google.com/vertex-ai/generative-ai/docs/maas/use-open-models.\",\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.015,\n output_mtok: 0.6\n }\n },\n {\n id: \"gemma-4-31b-it\",\n name: \"Gemma 4 31B\",\n description: \"Gemma 4 31B Instruct is Google DeepMind's 30.7B dense multimodal model supporting text and image input with text output. It features a 256K token context window, configurable thinking/reasoning mode, and native function calling.\",\n match: {\n equals: \"gemma-4-31b-it\"\n },\n context_window: 262144,\n price_comments: \"Free of charge on the Gemini API with no paid tier, see https://ai.google.dev/gemini-api/docs/pricing. Not offered as Model-as-a-Service on Vertex AI (only Gemma 4 26B is); there it is self-deployed from Model Garden and billed as endpoint compute, so no per-token price applies. The previous rates were imported from OpenRouter, not Google.\",\n prices: {}\n }\n ]\n },\n {\n id: \"groq\",\n name: \"Groq\",\n pricing_urls: [\"https://console.groq.com/docs/models\"],\n api_pattern: \"https://api\\\\.groq\\\\.com\",\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n }\n ]\n }\n ],\n models: [\n {\n id: \"canopylabs/orpheus-arabic-saudi\",\n name: \"Orpheus Arabic Saudi (Canopy Labs)\",\n match: {\n equals: \"canopylabs/orpheus-arabic-saudi\"\n },\n context_window: 4000,\n price_comments: \"https://console.groq.com/docs/models#canopylabs/orpheus-arabic-saudi\",\n prices: {\n input_mchars: 40\n }\n },\n {\n id: \"canopylabs/orpheus-v1-english\",\n name: \"Orpheus V1 English (Canopy Labs)\",\n match: {\n equals: \"canopylabs/orpheus-v1-english\"\n },\n context_window: 4000,\n price_comments: \"https://console.groq.com/docs/models#canopylabs/orpheus-v1-english\",\n prices: {\n input_mchars: 22\n }\n },\n {\n id: \"deepseek-r1-distill-llama-70b\",\n name: \"DeepSeek R1 Distill Llama 70B\",\n match: {\n equals: \"deepseek-r1-distill-llama-70b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.75,\n output_mtok: 0.99\n }\n },\n {\n id: \"gemma-7b-it\",\n match: {\n equals: \"gemma-7b-it\"\n },\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.07\n }\n },\n {\n id: \"gemma2-9b-it\",\n name: \"Gemma 2 9B 8k\",\n match: {\n or: [\n {\n equals: \"gemma2-9b-it\"\n },\n {\n equals: \"gemma2-9b\"\n }\n ]\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"llama-3.1-405b-reasoning\",\n match: {\n equals: \"llama-3.1-405b-reasoning\"\n },\n prices: {\n input_mtok: 0.59,\n output_mtok: 0.79\n }\n },\n {\n id: \"llama-3.1-70b-versatile\",\n match: {\n equals: \"llama-3.1-70b-versatile\"\n },\n prices: {\n input_mtok: 0.59,\n output_mtok: 0.79\n }\n },\n {\n id: \"llama-3.1-8b-instant\",\n name: \"Llama 3.1 8B Instant 128k\",\n match: {\n equals: \"llama-3.1-8b-instant\"\n },\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.08\n }\n },\n {\n id: \"llama-3.2-11b-text-preview\",\n match: {\n equals: \"llama-3.2-11b-text-preview\"\n },\n prices: {\n input_mtok: 0.18,\n output_mtok: 0.18\n }\n },\n {\n id: \"llama-3.2-11b-vision-preview\",\n match: {\n equals: \"llama-3.2-11b-vision-preview\"\n },\n prices: {\n input_mtok: 0.18,\n output_mtok: 0.18\n }\n },\n {\n id: \"llama-3.2-1b-preview\",\n match: {\n equals: \"llama-3.2-1b-preview\"\n },\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.04\n }\n },\n {\n id: \"llama-3.2-3b-preview\",\n match: {\n equals: \"llama-3.2-3b-preview\"\n },\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.06\n }\n },\n {\n id: \"llama-3.2-90b-text-preview\",\n match: {\n equals: \"llama-3.2-90b-text-preview\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"llama-3.2-90b-vision-preview\",\n match: {\n equals: \"llama-3.2-90b-vision-preview\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"llama-3.3-70b-specdec\",\n match: {\n equals: \"llama-3.3-70b-specdec\"\n },\n prices: {\n input_mtok: 0.59,\n output_mtok: 0.99\n }\n },\n {\n id: \"llama-3.3-70b-versatile\",\n name: \"Llama 3.3 70B Versatile 128k\",\n match: {\n equals: \"llama-3.3-70b-versatile\"\n },\n prices: {\n input_mtok: 0.59,\n output_mtok: 0.79\n }\n },\n {\n id: \"llama-guard-3-8b\",\n match: {\n equals: \"llama-guard-3-8b\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"llama2-70b-4096\",\n match: {\n equals: \"llama2-70b-4096\"\n },\n prices: {\n input_mtok: 0.7,\n output_mtok: 0.8\n }\n },\n {\n id: \"llama3-70b-8192\",\n match: {\n equals: \"llama3-70b-8192\"\n },\n prices: {\n input_mtok: 0.59,\n output_mtok: 0.79\n }\n },\n {\n id: \"llama3-8b-8192\",\n match: {\n equals: \"llama3-8b-8192\"\n },\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.08\n }\n },\n {\n id: \"llama3-groq-70b-8192-tool-use-preview\",\n match: {\n equals: \"llama3-groq-70b-8192-tool-use-preview\"\n },\n prices: {\n input_mtok: 0.89,\n output_mtok: 0.89\n }\n },\n {\n id: \"llama3-groq-8b-8192-tool-use-preview\",\n match: {\n equals: \"llama3-groq-8b-8192-tool-use-preview\"\n },\n prices: {\n input_mtok: 0.19,\n output_mtok: 0.19\n }\n },\n {\n id: \"meta-llama/llama-4-maverick-17b-128e-instruct\",\n name: \"Llama 4 Maverick 17B 128E\",\n match: {\n equals: \"meta-llama/llama-4-maverick-17b-128e-instruct\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.6\n }\n },\n {\n id: \"meta-llama/llama-4-scout-17b-16e-instruct\",\n name: \"Llama 4 Scout (17Bx16E) 128k\",\n match: {\n equals: \"meta-llama/llama-4-scout-17b-16e-instruct\"\n },\n prices: {\n input_mtok: 0.11,\n output_mtok: 0.34\n }\n },\n {\n id: \"meta-llama/llama-guard-4-12b\",\n name: \"Llama Guard 4 12B\",\n match: {\n equals: \"meta-llama/llama-guard-4-12b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"meta-llama/llama-prompt-guard-2-22m\",\n name: \"Llama Prompt Guard 2 22M\",\n match: {\n equals: \"meta-llama/llama-prompt-guard-2-22m\"\n },\n context_window: 512,\n price_comments: \"https://console.groq.com/docs/models#meta-llama/llama-prompt-guard-2-22m\",\n prices: {\n input_mtok: 0.03,\n output_mtok: 0.03\n }\n },\n {\n id: \"meta-llama/llama-prompt-guard-2-86m\",\n name: \"Llama Prompt Guard 2 86M\",\n match: {\n equals: \"meta-llama/llama-prompt-guard-2-86m\"\n },\n context_window: 512,\n price_comments: \"https://console.groq.com/docs/models#meta-llama/llama-prompt-guard-2-86m\",\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.04\n }\n },\n {\n id: \"mistral-saba-24b\",\n match: {\n equals: \"mistral-saba-24b\"\n },\n prices: {\n input_mtok: 0.79,\n output_mtok: 0.79\n }\n },\n {\n id: \"mixtral-8x7b-32768\",\n match: {\n equals: \"mixtral-8x7b-32768\"\n },\n prices: {\n input_mtok: 0.24,\n output_mtok: 0.24\n }\n },\n {\n id: \"moonshotai/kimi-k2-instruct\",\n name: \"Kimi K2 1T 128k\",\n match: {\n or: [\n {\n equals: \"moonshotai/kimi-k2-instruct\"\n },\n {\n equals: \"moonshotai/kimi-k2-instruct-0905\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 1,\n cache_read_mtok: 0.5,\n output_mtok: 3\n }\n },\n {\n id: \"openai/gpt-oss-120b\",\n description: `GPT-OSS 120B is OpenAI's flagship open source model, built on a Mixture-of-Experts (MoE) architecture with\n120 billion parameters and 128 experts.\n`,\n match: {\n equals: \"openai/gpt-oss-120b\"\n },\n context_window: 131072,\n price_comments: \"Batch API is 50% of the synchronous rates and does not stack with prompt caching: cached input is billed at the halved input rate. See https://console.groq.com/docs/batch.\",\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.075,\n output_mtok: 0.6\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.075,\n cache_read_mtok: 0.075,\n output_mtok: 0.3\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"openai/gpt-oss-20b\",\n description: `GPT-OSS 20B is OpenAI's flagship open source model, built on a Mixture-of-Experts (MoE) architecture with\n20 billion parameters and 32 experts.\n`,\n match: {\n equals: \"openai/gpt-oss-20b\"\n },\n context_window: 131072,\n price_comments: \"Batch API is 50% of the synchronous rates and does not stack with prompt caching: cached input is billed at the halved input rate. See https://console.groq.com/docs/batch.\",\n prices: {\n input_mtok: 0.075,\n cache_read_mtok: 0.0375,\n output_mtok: 0.3\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.0375,\n cache_read_mtok: 0.0375,\n output_mtok: 0.15\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"openai/gpt-oss-safeguard-20b\",\n name: \"GPT-OSS Safeguard 20B\",\n description: `GPT-OSS Safeguard 20B is OpenAI's open-weight safety model. It classifies content against a policy supplied\nin the prompt.\n`,\n match: {\n equals: \"openai/gpt-oss-safeguard-20b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.075,\n cache_read_mtok: 0.0375,\n output_mtok: 0.3\n }\n },\n {\n id: \"qwen/qwen3-32b\",\n name: \"Qwen3 32B 131k\",\n match: {\n equals: \"qwen/qwen3-32b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.29,\n output_mtok: 0.59\n }\n },\n {\n id: \"qwen/qwen3.8-27b\",\n name: \"Qwen 3.8 27B\",\n match: {\n equals: \"qwen/qwen3.8-27b\"\n },\n context_window: 131072,\n price_comments: \"Preview model. Groq lists no cached-input rate.\",\n prices: {\n input_mtok: 0.8,\n output_mtok: 4\n }\n },\n {\n id: \"whisper-large-v3\",\n name: \"Whisper Large V3\",\n match: {\n equals: \"whisper-large-v3\"\n },\n price_comments: \"See https://console.groq.com/docs/model/whisper-large-v3. Batch API is 50% of the synchronous rate, see https://console.groq.com/docs/batch.\",\n prices: {\n audio_hours: 0.111,\n input_audio_hours: 0.111\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n audio_hours: 0.0555,\n input_audio_hours: 0.0555\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"whisper-large-v3-turbo\",\n name: \"Whisper Large V3 Turbo\",\n match: {\n equals: \"whisper-large-v3-turbo\"\n },\n price_comments: \"See https://console.groq.com/docs/model/whisper-large-v3-turbo. Batch API is 50% of the synchronous rate, see https://console.groq.com/docs/batch.\",\n prices: {\n audio_hours: 0.04,\n input_audio_hours: 0.04\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n audio_hours: 0.02,\n input_audio_hours: 0.02\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n }\n ]\n },\n {\n id: \"huggingface_cerebras\",\n name: \"HuggingFace (cerebras)\",\n pricing_urls: [\"https://router.huggingface.co/v1/models\", \"https://huggingface.co/inference/models\"],\n api_pattern: \"https://router\\\\.huggingface\\\\.co/cerebras\",\n provider_match: {\n and: [\n {\n contains: \"huggingface\"\n },\n {\n contains: \"cerebras\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"meta-llama/Llama-3.1-8B-Instruct\",\n name: \"Llama-3.1-8B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.1-8b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.1-8b-instruct-fast\"\n }\n ]\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n }\n ]\n },\n {\n id: \"huggingface_fireworks-ai\",\n name: \"HuggingFace (fireworks-ai)\",\n pricing_urls: [\"https://router.huggingface.co/v1/models\", \"https://huggingface.co/inference/models\"],\n api_pattern: \"https://router\\\\.huggingface\\\\.co/fireworks-ai\",\n provider_match: {\n and: [\n {\n contains: \"huggingface\"\n },\n {\n contains: \"fireworks-ai\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"meta-llama/Llama-3.3-70B-Instruct\",\n name: \"Llama-3.3-70B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.3-70b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.3-70b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"openai/gpt-oss-120b\",\n name: \"gpt-oss-120b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-120b\"\n },\n {\n equals: \"openai/gpt-oss-120b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n }\n },\n {\n id: \"openai/gpt-oss-20b\",\n name: \"gpt-oss-20b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-20b\"\n },\n {\n equals: \"openai/gpt-oss-20b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.2\n }\n }\n ]\n },\n {\n id: \"huggingface_groq\",\n name: \"HuggingFace (groq)\",\n pricing_urls: [\"https://router.huggingface.co/v1/models\", \"https://huggingface.co/inference/models\"],\n api_pattern: \"https://router\\\\.huggingface\\\\.co/groq\",\n provider_match: {\n and: [\n {\n contains: \"huggingface\"\n },\n {\n contains: \"groq\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"Qwen/Qwen3-32B\",\n name: \"Qwen3-32B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-32b\"\n },\n {\n equals: \"qwen/qwen3-32b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.29,\n output_mtok: 0.59\n }\n },\n {\n id: \"meta-llama/Llama-3.3-70B-Instruct\",\n name: \"Llama-3.3-70B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.3-70b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.3-70b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.59,\n output_mtok: 0.79\n }\n },\n {\n id: \"meta-llama/Llama-4-Scout-17B-16E-Instruct\",\n name: \"Llama-4-Scout-17B-16E-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-4-scout-17b-16e-instruct\"\n },\n {\n equals: \"meta-llama/llama-4-scout-17b-16e-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.11,\n output_mtok: 0.34\n }\n },\n {\n id: \"openai/gpt-oss-120b\",\n name: \"gpt-oss-120b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-120b\"\n },\n {\n equals: \"openai/gpt-oss-120b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.75\n }\n },\n {\n id: \"openai/gpt-oss-20b\",\n name: \"gpt-oss-20b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-20b\"\n },\n {\n equals: \"openai/gpt-oss-20b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.5\n }\n }\n ]\n },\n {\n id: \"huggingface_hyperbolic\",\n name: \"HuggingFace (hyperbolic)\",\n pricing_urls: [\"https://router.huggingface.co/v1/models\", \"https://huggingface.co/inference/models\"],\n api_pattern: \"https://router\\\\.huggingface\\\\.co/hyperbolic\",\n provider_match: {\n and: [\n {\n contains: \"huggingface\"\n },\n {\n contains: \"hyperbolic\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"Qwen/Qwen2.5-VL-72B-Instruct\",\n name: \"Qwen2.5-VL-72B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen2.5-vl-72b-instruct\"\n },\n {\n equals: \"qwen/qwen2.5-vl-72b-instruct-fast\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.6,\n output_mtok: 0.6\n }\n },\n {\n id: \"Qwen/Qwen2.5-VL-7B-Instruct\",\n name: \"Qwen2.5-VL-7B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen2.5-vl-7b-instruct\"\n },\n {\n equals: \"qwen/qwen2.5-vl-7b-instruct-fast\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"Qwen/Qwen3-235B-A22B-Instruct-2507\",\n name: \"Qwen3-235B-A22B-Instruct-2507\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-235b-a22b-instruct-2507\"\n },\n {\n equals: \"qwen/qwen3-235b-a22b-instruct-2507-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 2,\n output_mtok: 2\n }\n },\n {\n id: \"Qwen/Qwen3-Coder-480B-A35B-Instruct\",\n name: \"Qwen3-Coder-480B-A35B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-coder-480b-a35b-instruct\"\n },\n {\n equals: \"qwen/qwen3-coder-480b-a35b-instruct-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 2,\n output_mtok: 2\n }\n },\n {\n id: \"Qwen/Qwen3-Next-80B-A3B-Instruct\",\n name: \"Qwen3-Next-80B-A3B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-next-80b-a3b-instruct\"\n },\n {\n equals: \"qwen/qwen3-next-80b-a3b-instruct-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.3\n }\n },\n {\n id: \"Qwen/Qwen3-Next-80B-A3B-Thinking\",\n name: \"Qwen3-Next-80B-A3B-Thinking\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-next-80b-a3b-thinking\"\n },\n {\n equals: \"qwen/qwen3-next-80b-a3b-thinking-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.3\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-R1\",\n name: \"DeepSeek-R1\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-r1\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-fast\"\n }\n ]\n },\n context_window: 163840,\n prices: {\n input_mtok: 2,\n output_mtok: 2\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-R1-0528\",\n name: \"DeepSeek-R1-0528\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-r1-0528\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-0528-fast\"\n }\n ]\n },\n context_window: 163840,\n prices: {\n input_mtok: 3,\n output_mtok: 3\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V3-0324\",\n name: \"DeepSeek-V3-0324\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-v3-0324\"\n },\n {\n equals: \"deepseek-ai/deepseek-v3-0324-fast\"\n }\n ]\n },\n context_window: 163840,\n prices: {\n input_mtok: 1.25,\n output_mtok: 1.25\n }\n },\n {\n id: \"meta-llama/Llama-3.3-70B-Instruct\",\n name: \"Llama-3.3-70B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.3-70b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.3-70b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.4,\n output_mtok: 0.4\n }\n },\n {\n id: \"openai/gpt-oss-120b\",\n name: \"gpt-oss-120b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-120b\"\n },\n {\n equals: \"openai/gpt-oss-120b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.3\n }\n },\n {\n id: \"openai/gpt-oss-20b\",\n name: \"gpt-oss-20b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-20b\"\n },\n {\n equals: \"openai/gpt-oss-20b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n }\n ]\n },\n {\n id: \"huggingface_nebius\",\n name: \"HuggingFace (nebius)\",\n pricing_urls: [\"https://router.huggingface.co/v1/models\", \"https://huggingface.co/inference/models\"],\n api_pattern: \"https://router\\\\.huggingface\\\\.co/nebius\",\n provider_match: {\n and: [\n {\n contains: \"huggingface\"\n },\n {\n contains: \"nebius\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"NousResearch/Hermes-4-405B\",\n name: \"Hermes-4-405B\",\n match: {\n or: [\n {\n equals: \"nousresearch/hermes-4-405b\"\n },\n {\n equals: \"nousresearch/hermes-4-405b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 1,\n output_mtok: 3\n }\n },\n {\n id: \"NousResearch/Hermes-4-70B\",\n name: \"Hermes-4-70B\",\n match: {\n or: [\n {\n equals: \"nousresearch/hermes-4-70b\"\n },\n {\n equals: \"nousresearch/hermes-4-70b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.13,\n output_mtok: 0.4\n }\n },\n {\n id: \"PrimeIntellect/INTELLECT-3-FP8\",\n name: \"INTELLECT-3-FP8\",\n match: {\n or: [\n {\n equals: \"primeintellect/intellect-3-fp8\"\n },\n {\n equals: \"primeintellect/intellect-3-fp8-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.2,\n output_mtok: 1.1\n }\n },\n {\n id: \"Qwen/Qwen2.5-Coder-7B\",\n name: \"Qwen2.5-Coder-7B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen2.5-coder-7b\"\n },\n {\n equals: \"qwen/qwen2.5-coder-7b-fast\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.03,\n output_mtok: 0.09\n }\n },\n {\n id: \"Qwen/Qwen2.5-VL-72B-Instruct\",\n name: \"Qwen2.5-VL-72B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen2.5-vl-72b-instruct\"\n },\n {\n equals: \"qwen/qwen2.5-vl-72b-instruct-fast\"\n }\n ]\n },\n context_window: 32000,\n prices: {\n input_mtok: 0.25,\n output_mtok: 0.75\n }\n },\n {\n id: \"Qwen/Qwen3-235B-A22B-Instruct-2507\",\n name: \"Qwen3-235B-A22B-Instruct-2507\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-235b-a22b-instruct-2507\"\n },\n {\n equals: \"qwen/qwen3-235b-a22b-instruct-2507-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.6\n }\n },\n {\n id: \"Qwen/Qwen3-235B-A22B-Thinking-2507\",\n name: \"Qwen3-235B-A22B-Thinking-2507\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-235b-a22b-thinking-2507\"\n },\n {\n equals: \"qwen/qwen3-235b-a22b-thinking-2507-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.8\n }\n },\n {\n id: \"Qwen/Qwen3-30B-A3B-Instruct-2507\",\n name: \"Qwen3-30B-A3B-Instruct-2507\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-30b-a3b-instruct-2507\"\n },\n {\n equals: \"qwen/qwen3-30b-a3b-instruct-2507-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.3\n }\n },\n {\n id: \"Qwen/Qwen3-30B-A3B-Thinking-2507\",\n name: \"Qwen3-30B-A3B-Thinking-2507\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-30b-a3b-thinking-2507\"\n },\n {\n equals: \"qwen/qwen3-30b-a3b-thinking-2507-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.3\n }\n },\n {\n id: \"Qwen/Qwen3-32B\",\n name: \"Qwen3-32B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-32b\"\n },\n {\n equals: \"qwen/qwen3-32b-fast\"\n }\n ]\n },\n context_window: 40960,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.3\n }\n },\n {\n id: \"Qwen/Qwen3-Coder-30B-A3B-Instruct\",\n name: \"Qwen3-Coder-30B-A3B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-coder-30b-a3b-instruct\"\n },\n {\n equals: \"qwen/qwen3-coder-30b-a3b-instruct-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.3\n }\n },\n {\n id: \"Qwen/Qwen3-Coder-480B-A35B-Instruct\",\n name: \"Qwen3-Coder-480B-A35B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-coder-480b-a35b-instruct\"\n },\n {\n equals: \"qwen/qwen3-coder-480b-a35b-instruct-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.4,\n output_mtok: 1.8\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-R1-0528\",\n name: \"DeepSeek-R1-0528\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-r1-0528\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-0528-fast\"\n }\n ]\n },\n context_window: 163840,\n prices: {\n input_mtok: 0.8,\n output_mtok: 2.4\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V3-0324\",\n name: \"DeepSeek-V3-0324\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-v3-0324\"\n },\n {\n equals: \"deepseek-ai/deepseek-v3-0324-fast\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.75,\n output_mtok: 2.25\n }\n },\n {\n id: \"google/gemma-2-2b-it\",\n name: \"gemma-2-2b-it\",\n match: {\n or: [\n {\n equals: \"google/gemma-2-2b-it\"\n },\n {\n equals: \"google/gemma-2-2b-it-fast\"\n }\n ]\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.02,\n output_mtok: 0.06\n }\n },\n {\n id: \"google/gemma-2-9b-it\",\n name: \"gemma-2-9b-it\",\n match: {\n or: [\n {\n equals: \"google/gemma-2-9b-it\"\n },\n {\n equals: \"google/gemma-2-9b-it-fast\"\n }\n ]\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.03,\n output_mtok: 0.09\n }\n },\n {\n id: \"google/gemma-3-27b-it\",\n name: \"gemma-3-27b-it\",\n match: {\n or: [\n {\n equals: \"google/gemma-3-27b-it\"\n },\n {\n equals: \"google/gemma-3-27b-it-fast\"\n }\n ]\n },\n context_window: 110000,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.6\n }\n },\n {\n id: \"meta-llama/Llama-3.1-8B-Instruct\",\n name: \"Llama-3.1-8B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.1-8b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.1-8b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.03,\n output_mtok: 0.09\n }\n },\n {\n id: \"meta-llama/Llama-3.3-70B-Instruct\",\n name: \"Llama-3.3-70B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.3-70b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.3-70b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.25,\n output_mtok: 0.75\n }\n },\n {\n id: \"moonshotai/Kimi-K2-Instruct\",\n name: \"Kimi-K2-Instruct\",\n match: {\n or: [\n {\n equals: \"moonshotai/kimi-k2-instruct\"\n },\n {\n equals: \"moonshotai/kimi-k2-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.5,\n output_mtok: 2.4\n }\n },\n {\n id: \"moonshotai/Kimi-K2-Thinking\",\n name: \"Kimi-K2-Thinking\",\n match: {\n or: [\n {\n equals: \"moonshotai/kimi-k2-thinking\"\n },\n {\n equals: \"moonshotai/kimi-k2-thinking-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.6,\n output_mtok: 2.5\n }\n },\n {\n id: \"nvidia/Llama-3_1-Nemotron-Ultra-253B-v1\",\n name: \"Llama-3_1-Nemotron-Ultra-253B-v1\",\n match: {\n or: [\n {\n equals: \"nvidia/llama-3_1-nemotron-ultra-253b-v1\"\n },\n {\n equals: \"nvidia/llama-3_1-nemotron-ultra-253b-v1-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.6,\n output_mtok: 1.8\n }\n },\n {\n id: \"nvidia/NVIDIA-Nemotron-Nano-12B-v2\",\n name: \"NVIDIA-Nemotron-Nano-12B-v2\",\n match: {\n or: [\n {\n equals: \"nvidia/nvidia-nemotron-nano-12b-v2\"\n },\n {\n equals: \"nvidia/nvidia-nemotron-nano-12b-v2-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.2\n }\n },\n {\n id: \"openai/gpt-oss-120b\",\n name: \"gpt-oss-120b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-120b\"\n },\n {\n equals: \"openai/gpt-oss-120b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n }\n },\n {\n id: \"zai-org/GLM-4.5\",\n name: \"GLM-4.5\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-4.5\"\n },\n {\n equals: \"zai-org/glm-4.5-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.6,\n output_mtok: 2.2\n }\n },\n {\n id: \"zai-org/GLM-4.5-Air\",\n name: \"GLM-4.5-Air\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-4.5-air\"\n },\n {\n equals: \"zai-org/glm-4.5-air-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.2,\n output_mtok: 1.2\n }\n }\n ]\n },\n {\n id: \"huggingface_novita\",\n name: \"HuggingFace (novita)\",\n pricing_urls: [\"https://router.huggingface.co/v1/models\", \"https://huggingface.co/inference/models\"],\n api_pattern: \"https://router\\\\.huggingface\\\\.co/novita\",\n provider_match: {\n and: [\n {\n contains: \"huggingface\"\n },\n {\n contains: \"novita\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"MiniMaxAI/MiniMax-M1-80k\",\n name: \"MiniMax-M1-80k\",\n match: {\n or: [\n {\n equals: \"minimaxai/minimax-m1-80k\"\n },\n {\n equals: \"minimaxai/minimax-m1-80k-fast\"\n }\n ]\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.55,\n output_mtok: 2.2\n }\n },\n {\n id: \"MiniMaxAI/MiniMax-M2\",\n name: \"MiniMax-M2\",\n match: {\n or: [\n {\n equals: \"minimaxai/minimax-m2\"\n },\n {\n equals: \"minimaxai/minimax-m2-fast\"\n },\n {\n equals: \"minimaxai/minimax-m2.1\"\n },\n {\n equals: \"minimaxai/minimax-m2.1-fast\"\n },\n {\n equals: \"minimaxai/minimax-m2.5\"\n },\n {\n equals: \"minimaxai/minimax-m2.5-fast\"\n }\n ]\n },\n context_window: 204800,\n prices: {\n input_mtok: 0.3,\n output_mtok: 1.2\n }\n },\n {\n id: \"NousResearch/Hermes-2-Pro-Llama-3-8B\",\n name: \"Hermes-2-Pro-Llama-3-8B\",\n match: {\n or: [\n {\n equals: \"nousresearch/hermes-2-pro-llama-3-8b\"\n },\n {\n equals: \"nousresearch/hermes-2-pro-llama-3-8b-fast\"\n }\n ]\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.14,\n output_mtok: 0.14\n }\n },\n {\n id: \"Qwen/Qwen2.5-72B-Instruct\",\n name: \"Qwen2.5-72B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen2.5-72b-instruct\"\n },\n {\n equals: \"qwen/qwen2.5-72b-instruct-fast\"\n }\n ]\n },\n context_window: 32000,\n prices: {\n input_mtok: 0.38,\n output_mtok: 0.4\n }\n },\n {\n id: \"Qwen/Qwen3-235B-A22B\",\n name: \"Qwen3-235B-A22B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-235b-a22b\"\n },\n {\n equals: \"qwen/qwen3-235b-a22b-fast\"\n }\n ]\n },\n context_window: 40960,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.8\n }\n },\n {\n id: \"Qwen/Qwen3-235B-A22B-Instruct-2507\",\n name: \"Qwen3-235B-A22B-Instruct-2507\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-235b-a22b-instruct-2507\"\n },\n {\n equals: \"qwen/qwen3-235b-a22b-instruct-2507-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.09,\n output_mtok: 0.58\n }\n },\n {\n id: \"Qwen/Qwen3-235B-A22B-Thinking-2507\",\n name: \"Qwen3-235B-A22B-Thinking-2507\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-235b-a22b-thinking-2507\"\n },\n {\n equals: \"qwen/qwen3-235b-a22b-thinking-2507-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.3,\n output_mtok: 3\n }\n },\n {\n id: \"Qwen/Qwen3-30B-A3B\",\n name: \"Qwen3-30B-A3B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-30b-a3b\"\n },\n {\n equals: \"qwen/qwen3-30b-a3b-fast\"\n }\n ]\n },\n context_window: 40960,\n prices: {\n input_mtok: 0.09,\n output_mtok: 0.45\n }\n },\n {\n id: \"Qwen/Qwen3-32B\",\n name: \"Qwen3-32B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-32b\"\n },\n {\n equals: \"qwen/qwen3-32b-fast\"\n }\n ]\n },\n context_window: 40960,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.45\n }\n },\n {\n id: \"Qwen/Qwen3-Coder-480B-A35B-Instruct\",\n name: \"Qwen3-Coder-480B-A35B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-coder-480b-a35b-instruct\"\n },\n {\n equals: \"qwen/qwen3-coder-480b-a35b-instruct-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.3,\n output_mtok: 1.3\n }\n },\n {\n id: \"Qwen/Qwen3-Coder-Next\",\n name: \"Qwen3-Coder-Next\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-coder-next\"\n },\n {\n equals: \"qwen/qwen3-coder-next-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.2,\n output_mtok: 1.5\n }\n },\n {\n id: \"Qwen/Qwen3-Next-80B-A3B-Instruct\",\n name: \"Qwen3-Next-80B-A3B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-next-80b-a3b-instruct\"\n },\n {\n equals: \"qwen/qwen3-next-80b-a3b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.15,\n output_mtok: 1.5\n }\n },\n {\n id: \"Qwen/Qwen3-Next-80B-A3B-Thinking\",\n name: \"Qwen3-Next-80B-A3B-Thinking\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-next-80b-a3b-thinking\"\n },\n {\n equals: \"qwen/qwen3-next-80b-a3b-thinking-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.15,\n output_mtok: 1.5\n }\n },\n {\n id: \"Qwen/Qwen3-VL-235B-A22B-Instruct\",\n name: \"Qwen3-VL-235B-A22B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-vl-235b-a22b-instruct\"\n },\n {\n equals: \"qwen/qwen3-vl-235b-a22b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.3,\n output_mtok: 1.5\n }\n },\n {\n id: \"Qwen/Qwen3-VL-235B-A22B-Thinking\",\n name: \"Qwen3-VL-235B-A22B-Thinking\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-vl-235b-a22b-thinking\"\n },\n {\n equals: \"qwen/qwen3-vl-235b-a22b-thinking-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.98,\n output_mtok: 3.95\n }\n },\n {\n id: \"Qwen/Qwen3-VL-30B-A3B-Instruct\",\n name: \"Qwen3-VL-30B-A3B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-vl-30b-a3b-instruct\"\n },\n {\n equals: \"qwen/qwen3-vl-30b-a3b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.7\n }\n },\n {\n id: \"Qwen/Qwen3-VL-30B-A3B-Thinking\",\n name: \"Qwen3-VL-30B-A3B-Thinking\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-vl-30b-a3b-thinking\"\n },\n {\n equals: \"qwen/qwen3-vl-30b-a3b-thinking-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.2,\n output_mtok: 1\n }\n },\n {\n id: \"Qwen/Qwen3-VL-8B-Instruct\",\n name: \"Qwen3-VL-8B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-vl-8b-instruct\"\n },\n {\n equals: \"qwen/qwen3-vl-8b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.08,\n output_mtok: 0.5\n }\n },\n {\n id: \"Qwen/Qwen3.5-122B-A10B\",\n name: \"Qwen3.5-122B-A10B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.5-122b-a10b\"\n },\n {\n equals: \"qwen/qwen3.5-122b-a10b-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.4,\n output_mtok: 3.2\n }\n },\n {\n id: \"Qwen/Qwen3.5-27B\",\n name: \"Qwen3.5-27B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.5-27b\"\n },\n {\n equals: \"qwen/qwen3.5-27b-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.3,\n output_mtok: 2.4\n }\n },\n {\n id: \"Qwen/Qwen3.5-35B-A3B\",\n name: \"Qwen3.5-35B-A3B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.5-35b-a3b\"\n },\n {\n equals: \"qwen/qwen3.5-35b-a3b-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.25,\n output_mtok: 2\n }\n },\n {\n id: \"Qwen/Qwen3.5-397B-A17B\",\n name: \"Qwen3.5-397B-A17B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.5-397b-a17b\"\n },\n {\n equals: \"qwen/qwen3.5-397b-a17b-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.6,\n output_mtok: 3.6\n }\n },\n {\n id: \"Sao10K/L3-70B-Euryale-v2.1\",\n name: \"L3-70B-Euryale-v2.1\",\n match: {\n or: [\n {\n equals: \"sao10k/l3-70b-euryale-v2.1\"\n },\n {\n equals: \"sao10k/l3-70b-euryale-v2.1-fast\"\n }\n ]\n },\n context_window: 8192,\n prices: {\n input_mtok: 1.48,\n output_mtok: 1.48\n }\n },\n {\n id: \"Sao10K/L3-8B-Lunaris-v1\",\n name: \"L3-8B-Lunaris-v1\",\n match: {\n or: [\n {\n equals: \"sao10k/l3-8b-lunaris-v1\"\n },\n {\n equals: \"sao10k/l3-8b-lunaris-v1-fast\"\n }\n ]\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.05\n }\n },\n {\n id: \"Sao10K/L3-8B-Stheno-v3.2\",\n name: \"L3-8B-Stheno-v3.2\",\n match: {\n or: [\n {\n equals: \"sao10k/l3-8b-stheno-v3.2\"\n },\n {\n equals: \"sao10k/l3-8b-stheno-v3.2-fast\"\n }\n ]\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.05\n }\n },\n {\n id: \"XiaomiMiMo/MiMo-V2-Flash\",\n name: \"MiMo-V2-Flash\",\n match: {\n or: [\n {\n equals: \"xiaomimimo/mimo-v2-flash\"\n },\n {\n equals: \"xiaomimimo/mimo-v2-flash-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.3\n }\n },\n {\n id: \"alpindale/WizardLM-2-8x22B\",\n name: \"WizardLM-2-8x22B\",\n match: {\n or: [\n {\n equals: \"alpindale/wizardlm-2-8x22b\"\n },\n {\n equals: \"alpindale/wizardlm-2-8x22b-fast\"\n }\n ]\n },\n context_window: 65535,\n prices: {\n input_mtok: 0.62,\n output_mtok: 0.62\n }\n },\n {\n id: \"baidu/ERNIE-4.5-21B-A3B-PT\",\n name: \"ERNIE-4.5-21B-A3B-PT\",\n match: {\n or: [\n {\n equals: \"baidu/ernie-4.5-21b-a3b-pt\"\n },\n {\n equals: \"baidu/ernie-4.5-21b-a3b-pt-fast\"\n }\n ]\n },\n context_window: 120000,\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.28\n }\n },\n {\n id: \"baidu/ERNIE-4.5-300B-A47B-Base-PT\",\n name: \"ERNIE-4.5-300B-A47B-Base-PT\",\n match: {\n or: [\n {\n equals: \"baidu/ernie-4.5-300b-a47b-base-pt\"\n },\n {\n equals: \"baidu/ernie-4.5-300b-a47b-base-pt-fast\"\n }\n ]\n },\n context_window: 123000,\n prices: {\n input_mtok: 0.28,\n output_mtok: 1.1\n }\n },\n {\n id: \"baidu/ERNIE-4.5-VL-28B-A3B-PT\",\n name: \"ERNIE-4.5-VL-28B-A3B-PT\",\n match: {\n or: [\n {\n equals: \"baidu/ernie-4.5-vl-28b-a3b-pt\"\n },\n {\n equals: \"baidu/ernie-4.5-vl-28b-a3b-pt-fast\"\n }\n ]\n },\n context_window: 30000,\n prices: {\n input_mtok: 0.14,\n output_mtok: 0.56\n }\n },\n {\n id: \"baidu/ERNIE-4.5-VL-424B-A47B-Base-PT\",\n name: \"ERNIE-4.5-VL-424B-A47B-Base-PT\",\n match: {\n or: [\n {\n equals: \"baidu/ernie-4.5-vl-424b-a47b-base-pt\"\n },\n {\n equals: \"baidu/ernie-4.5-vl-424b-a47b-base-pt-fast\"\n }\n ]\n },\n context_window: 123000,\n prices: {\n input_mtok: 0.42,\n output_mtok: 1.25\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-Prover-V2-671B\",\n name: \"DeepSeek-Prover-V2-671B\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-prover-v2-671b\"\n },\n {\n equals: \"deepseek-ai/deepseek-prover-v2-671b-fast\"\n }\n ]\n },\n context_window: 160000,\n prices: {\n input_mtok: 0.7,\n output_mtok: 2.5\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-R1\",\n name: \"DeepSeek-R1\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-r1\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-fast\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-0528\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-0528-fast\"\n }\n ]\n },\n context_window: 64000,\n prices: {\n input_mtok: 0.7,\n output_mtok: 2.5\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-R1-Distill-Llama-70B\",\n name: \"DeepSeek-R1-Distill-Llama-70B\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-r1-distill-llama-70b\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-distill-llama-70b-fast\"\n }\n ]\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.8,\n output_mtok: 0.8\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V3\",\n name: \"DeepSeek-V3\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-v3\"\n },\n {\n equals: \"deepseek-ai/deepseek-v3-fast\"\n }\n ]\n },\n context_window: 64000,\n prices: {\n input_mtok: 0.4,\n output_mtok: 1.3\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V3-0324\",\n name: \"DeepSeek-V3-0324\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-v3-0324\"\n },\n {\n equals: \"deepseek-ai/deepseek-v3-0324-fast\"\n }\n ]\n },\n context_window: 163840,\n prices: {\n input_mtok: 0.27,\n output_mtok: 1.12\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V3.1\",\n name: \"DeepSeek-V3.1\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-v3.1\"\n },\n {\n equals: \"deepseek-ai/deepseek-v3.1-fast\"\n },\n {\n equals: \"deepseek-ai/deepseek-v3.1-terminus\"\n },\n {\n equals: \"deepseek-ai/deepseek-v3.1-terminus-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.27,\n output_mtok: 1\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V3.2\",\n name: \"DeepSeek-V3.2\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-v3.2\"\n },\n {\n equals: \"deepseek-ai/deepseek-v3.2-fast\"\n }\n ]\n },\n context_window: 163840,\n prices: {\n input_mtok: 0.269,\n output_mtok: 0.4\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V3.2-Exp\",\n name: \"DeepSeek-V3.2-Exp\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-v3.2-exp\"\n },\n {\n equals: \"deepseek-ai/deepseek-v3.2-exp-fast\"\n }\n ]\n },\n context_window: 163840,\n prices: {\n input_mtok: 0.27,\n output_mtok: 0.41\n }\n },\n {\n id: \"meta-llama/Llama-3.1-8B-Instruct\",\n name: \"Llama-3.1-8B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.1-8b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.1-8b-instruct-fast\"\n }\n ]\n },\n context_window: 16384,\n prices: {\n input_mtok: 0.02,\n output_mtok: 0.05\n }\n },\n {\n id: \"meta-llama/Llama-3.3-70B-Instruct\",\n name: \"Llama-3.3-70B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.3-70b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.3-70b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.135,\n output_mtok: 0.4\n }\n },\n {\n id: \"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8\",\n name: \"Llama-4-Maverick-17B-128E-Instruct-FP8\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-4-maverick-17b-128e-instruct-fp8\"\n },\n {\n equals: \"meta-llama/llama-4-maverick-17b-128e-instruct-fp8-fast\"\n }\n ]\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.27,\n output_mtok: 0.85\n }\n },\n {\n id: \"meta-llama/Llama-4-Scout-17B-16E-Instruct\",\n name: \"Llama-4-Scout-17B-16E-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-4-scout-17b-16e-instruct\"\n },\n {\n equals: \"meta-llama/llama-4-scout-17b-16e-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.18,\n output_mtok: 0.59\n }\n },\n {\n id: \"meta-llama/Meta-Llama-3-70B-Instruct\",\n name: \"Meta-Llama-3-70B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/meta-llama-3-70b-instruct\"\n },\n {\n equals: \"meta-llama/meta-llama-3-70b-instruct-fast\"\n }\n ]\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.51,\n output_mtok: 0.74\n }\n },\n {\n id: \"meta-llama/Meta-Llama-3-8B-Instruct\",\n name: \"Meta-Llama-3-8B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/meta-llama-3-8b-instruct\"\n },\n {\n equals: \"meta-llama/meta-llama-3-8b-instruct-fast\"\n }\n ]\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.04\n }\n },\n {\n id: \"moonshotai/Kimi-K2-Instruct\",\n name: \"Kimi-K2-Instruct\",\n match: {\n or: [\n {\n equals: \"moonshotai/kimi-k2-instruct\"\n },\n {\n equals: \"moonshotai/kimi-k2-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.57,\n output_mtok: 2.3\n }\n },\n {\n id: \"moonshotai/Kimi-K2-Instruct-0905\",\n name: \"Kimi-K2-Instruct-0905\",\n match: {\n or: [\n {\n equals: \"moonshotai/kimi-k2-instruct-0905\"\n },\n {\n equals: \"moonshotai/kimi-k2-instruct-0905-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.6,\n output_mtok: 2.5\n }\n },\n {\n id: \"moonshotai/Kimi-K2-Thinking\",\n name: \"Kimi-K2-Thinking\",\n match: {\n or: [\n {\n equals: \"moonshotai/kimi-k2-thinking\"\n },\n {\n equals: \"moonshotai/kimi-k2-thinking-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.6,\n output_mtok: 2.5\n }\n },\n {\n id: \"moonshotai/Kimi-K2.5\",\n name: \"Kimi-K2.5\",\n match: {\n or: [\n {\n equals: \"moonshotai/kimi-k2.5\"\n },\n {\n equals: \"moonshotai/kimi-k2.5-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.6,\n output_mtok: 3\n }\n },\n {\n id: \"openai/gpt-oss-120b\",\n name: \"gpt-oss-120b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-120b\"\n },\n {\n equals: \"openai/gpt-oss-120b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.25\n }\n },\n {\n id: \"openai/gpt-oss-20b\",\n name: \"gpt-oss-20b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-20b\"\n },\n {\n equals: \"openai/gpt-oss-20b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.15\n }\n },\n {\n id: \"zai-org/AutoGLM-Phone-9B-Multilingual\",\n name: \"AutoGLM-Phone-9B-Multilingual\",\n match: {\n or: [\n {\n equals: \"zai-org/autoglm-phone-9b-multilingual\"\n },\n {\n equals: \"zai-org/autoglm-phone-9b-multilingual-fast\"\n }\n ]\n },\n context_window: 65536,\n prices: {\n input_mtok: 0.035,\n output_mtok: 0.138\n }\n },\n {\n id: \"zai-org/GLM-4-32B-0414\",\n name: \"GLM-4-32B-0414\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-4-32b-0414\"\n },\n {\n equals: \"zai-org/glm-4-32b-0414-fast\"\n }\n ]\n },\n context_window: 32000,\n prices: {\n input_mtok: 0.55,\n output_mtok: 1.66\n }\n },\n {\n id: \"zai-org/GLM-4.5\",\n name: \"GLM-4.5\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-4.5\"\n },\n {\n equals: \"zai-org/glm-4.5-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.6,\n output_mtok: 2.2\n }\n },\n {\n id: \"zai-org/GLM-4.5-Air\",\n name: \"GLM-4.5-Air\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-4.5-air\"\n },\n {\n equals: \"zai-org/glm-4.5-air-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.13,\n output_mtok: 0.85\n }\n },\n {\n id: \"zai-org/GLM-4.5V\",\n name: \"GLM-4.5V\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-4.5v\"\n },\n {\n equals: \"zai-org/glm-4.5v-fast\"\n }\n ]\n },\n context_window: 65536,\n prices: {\n input_mtok: 0.6,\n output_mtok: 1.8\n }\n },\n {\n id: \"zai-org/GLM-4.6\",\n name: \"GLM-4.6\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-4.6\"\n },\n {\n equals: \"zai-org/glm-4.6-fast\"\n }\n ]\n },\n context_window: 204800,\n prices: {\n input_mtok: 0.55,\n output_mtok: 2.2\n }\n },\n {\n id: \"zai-org/GLM-4.6V-Flash\",\n name: \"GLM-4.6V-Flash\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-4.6v-flash\"\n },\n {\n equals: \"zai-org/glm-4.6v-flash-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.9\n }\n },\n {\n id: \"zai-org/GLM-4.7\",\n name: \"GLM-4.7\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-4.7\"\n },\n {\n equals: \"zai-org/glm-4.7-fast\"\n }\n ]\n },\n context_window: 204800,\n prices: {\n input_mtok: 0.6,\n output_mtok: 2.2\n }\n },\n {\n id: \"zai-org/GLM-4.7-Flash\",\n name: \"GLM-4.7-Flash\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-4.7-flash\"\n },\n {\n equals: \"zai-org/glm-4.7-flash-fast\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.4\n }\n },\n {\n id: \"zai-org/GLM-5\",\n name: \"GLM-5\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-5\"\n },\n {\n equals: \"zai-org/glm-5-fast\"\n }\n ]\n },\n context_window: 202800,\n prices: {\n input_mtok: 1,\n output_mtok: 3.2\n }\n }\n ]\n },\n {\n id: \"huggingface_nscale\",\n name: \"HuggingFace (nscale)\",\n pricing_urls: [\"https://router.huggingface.co/v1/models\", \"https://huggingface.co/inference/models\"],\n api_pattern: \"https://router\\\\.huggingface\\\\.co/nscale\",\n provider_match: {\n and: [\n {\n contains: \"huggingface\"\n },\n {\n contains: \"nscale\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"Qwen/QwQ-32B\",\n name: \"QwQ-32B\",\n match: {\n or: [\n {\n equals: \"qwen/qwq-32b\"\n },\n {\n equals: \"qwen/qwq-32b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.18,\n output_mtok: 0.2\n }\n },\n {\n id: \"Qwen/Qwen2.5-Coder-32B-Instruct\",\n name: \"Qwen2.5-Coder-32B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen2.5-coder-32b-instruct\"\n },\n {\n equals: \"qwen/qwen2.5-coder-32b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.2\n }\n },\n {\n id: \"Qwen/Qwen2.5-Coder-3B-Instruct\",\n name: \"Qwen2.5-Coder-3B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen2.5-coder-3b-instruct\"\n },\n {\n equals: \"qwen/qwen2.5-coder-3b-instruct-fast\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.01,\n output_mtok: 0.03\n }\n },\n {\n id: \"Qwen/Qwen2.5-Coder-7B-Instruct\",\n name: \"Qwen2.5-Coder-7B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen2.5-coder-7b-instruct\"\n },\n {\n equals: \"qwen/qwen2.5-coder-7b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.01,\n output_mtok: 0.03\n }\n },\n {\n id: \"Qwen/Qwen3-14B\",\n name: \"Qwen3-14B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-14b\"\n },\n {\n equals: \"qwen/qwen3-14b-fast\"\n }\n ]\n },\n context_window: 40960,\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.2\n }\n },\n {\n id: \"Qwen/Qwen3-235B-A22B\",\n name: \"Qwen3-235B-A22B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-235b-a22b\"\n },\n {\n equals: \"qwen/qwen3-235b-a22b-fast\"\n },\n {\n equals: \"qwen/qwen3-235b-a22b-instruct-2507\"\n },\n {\n equals: \"qwen/qwen3-235b-a22b-instruct-2507-fast\"\n }\n ]\n },\n context_window: 32000,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.6\n }\n },\n {\n id: \"Qwen/Qwen3-32B\",\n name: \"Qwen3-32B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-32b\"\n },\n {\n equals: \"qwen/qwen3-32b-fast\"\n }\n ]\n },\n context_window: 40960,\n prices: {\n input_mtok: 0.08,\n output_mtok: 0.25\n }\n },\n {\n id: \"Qwen/Qwen3-4B-Instruct-2507\",\n name: \"Qwen3-4B-Instruct-2507\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-4b-instruct-2507\"\n },\n {\n equals: \"qwen/qwen3-4b-instruct-2507-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.01,\n output_mtok: 0.03\n }\n },\n {\n id: \"Qwen/Qwen3-4B-Thinking-2507\",\n name: \"Qwen3-4B-Thinking-2507\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-4b-thinking-2507\"\n },\n {\n equals: \"qwen/qwen3-4b-thinking-2507-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.01,\n output_mtok: 0.03\n }\n },\n {\n id: \"Qwen/Qwen3-8B\",\n name: \"Qwen3-8B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-8b\"\n },\n {\n equals: \"qwen/qwen3-8b-fast\"\n }\n ]\n },\n context_window: 40960,\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.18\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-R1-Distill-Llama-70B\",\n name: \"DeepSeek-R1-Distill-Llama-70B\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-r1-distill-llama-70b\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-distill-llama-70b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.75,\n output_mtok: 0.75\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-R1-Distill-Llama-8B\",\n name: \"DeepSeek-R1-Distill-Llama-8B\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-r1-distill-llama-8b\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-distill-llama-8b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.05\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B\",\n name: \"DeepSeek-R1-Distill-Qwen-1.5B\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-r1-distill-qwen-1.5b\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-distill-qwen-1.5b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-R1-Distill-Qwen-32B\",\n name: \"DeepSeek-R1-Distill-Qwen-32B\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-r1-distill-qwen-32b\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-distill-qwen-32b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.3\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B\",\n name: \"DeepSeek-R1-Distill-Qwen-7B\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-r1-distill-qwen-7b\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-distill-qwen-7b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.15\n }\n },\n {\n id: \"meta-llama/Llama-3.1-8B-Instruct\",\n name: \"Llama-3.1-8B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.1-8b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.1-8b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.06\n }\n },\n {\n id: \"meta-llama/Llama-3.3-70B-Instruct\",\n name: \"Llama-3.3-70B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.3-70b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.3-70b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.4,\n output_mtok: 0.4\n }\n },\n {\n id: \"meta-llama/Llama-4-Scout-17B-16E-Instruct\",\n name: \"Llama-4-Scout-17B-16E-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-4-scout-17b-16e-instruct\"\n },\n {\n equals: \"meta-llama/llama-4-scout-17b-16e-instruct-fast\"\n }\n ]\n },\n context_window: 890000,\n prices: {\n input_mtok: 0.09,\n output_mtok: 0.29\n }\n },\n {\n id: \"openai/gpt-oss-120b\",\n name: \"gpt-oss-120b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-120b\"\n },\n {\n equals: \"openai/gpt-oss-120b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.4\n }\n },\n {\n id: \"openai/gpt-oss-20b\",\n name: \"gpt-oss-20b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-20b\"\n },\n {\n equals: \"openai/gpt-oss-20b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.2\n }\n }\n ]\n },\n {\n id: \"huggingface_ovhcloud\",\n name: \"HuggingFace (ovhcloud)\",\n pricing_urls: [\"https://router.huggingface.co/v1/models\", \"https://huggingface.co/inference/models\"],\n api_pattern: \"https://router\\\\.huggingface\\\\.co/ovhcloud\",\n provider_match: {\n and: [\n {\n contains: \"huggingface\"\n },\n {\n contains: \"ovhcloud\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"Qwen/Qwen2.5-VL-72B-Instruct\",\n name: \"Qwen2.5-VL-72B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen2.5-vl-72b-instruct\"\n },\n {\n equals: \"qwen/qwen2.5-vl-72b-instruct-fast\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 1.01,\n output_mtok: 1.01\n }\n },\n {\n id: \"Qwen/Qwen3-32B\",\n name: \"Qwen3-32B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-32b\"\n },\n {\n equals: \"qwen/qwen3-32b-fast\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.09,\n output_mtok: 0.25\n }\n },\n {\n id: \"Qwen/Qwen3-Coder-30B-A3B-Instruct\",\n name: \"Qwen3-Coder-30B-A3B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-coder-30b-a3b-instruct\"\n },\n {\n equals: \"qwen/qwen3-coder-30b-a3b-instruct-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.26\n }\n },\n {\n id: \"meta-llama/Llama-3.1-8B-Instruct\",\n name: \"Llama-3.1-8B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.1-8b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.1-8b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.11,\n output_mtok: 0.11\n }\n },\n {\n id: \"meta-llama/Llama-3.3-70B-Instruct\",\n name: \"Llama-3.3-70B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.3-70b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.3-70b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.74,\n output_mtok: 0.74\n }\n },\n {\n id: \"openai/gpt-oss-120b\",\n name: \"gpt-oss-120b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-120b\"\n },\n {\n equals: \"openai/gpt-oss-120b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.09,\n output_mtok: 0.47\n }\n },\n {\n id: \"openai/gpt-oss-20b\",\n name: \"gpt-oss-20b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-20b\"\n },\n {\n equals: \"openai/gpt-oss-20b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.18\n }\n }\n ]\n },\n {\n id: \"huggingface_publicai\",\n name: \"HuggingFace (publicai)\",\n pricing_urls: [\"https://router.huggingface.co/v1/models\", \"https://huggingface.co/inference/models\"],\n api_pattern: \"https://router\\\\.huggingface\\\\.co/publicai\",\n provider_match: {\n and: [\n {\n contains: \"huggingface\"\n },\n {\n contains: \"publicai\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"aisingapore/Gemma-SEA-LION-v4-27B-IT\",\n name: \"Gemma-SEA-LION-v4-27B-IT\",\n match: {\n or: [\n {\n equals: \"aisingapore/gemma-sea-lion-v4-27b-it\"\n },\n {\n equals: \"aisingapore/gemma-sea-lion-v4-27b-it-fast\"\n }\n ]\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.4\n }\n },\n {\n id: \"aisingapore/Qwen-SEA-LION-v4-32B-IT\",\n name: \"Qwen-SEA-LION-v4-32B-IT\",\n match: {\n or: [\n {\n equals: \"aisingapore/qwen-sea-lion-v4-32b-it\"\n },\n {\n equals: \"aisingapore/qwen-sea-lion-v4-32b-it-fast\"\n }\n ]\n },\n prices: {\n input_mtok: 0.25,\n output_mtok: 0.5\n }\n },\n {\n id: \"allenai/Olmo-3-7B-Instruct\",\n name: \"Olmo-3-7B-Instruct\",\n match: {\n or: [\n {\n equals: \"allenai/olmo-3-7b-instruct\"\n },\n {\n equals: \"allenai/olmo-3-7b-instruct-fast\"\n }\n ]\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.2\n }\n },\n {\n id: \"allenai/Olmo-3.1-32B-Instruct\",\n name: \"Olmo-3.1-32B-Instruct\",\n match: {\n or: [\n {\n equals: \"allenai/olmo-3.1-32b-instruct\"\n },\n {\n equals: \"allenai/olmo-3.1-32b-instruct-fast\"\n }\n ]\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.6\n }\n },\n {\n id: \"dicta-il/DictaLM-3.0-24B-Thinking\",\n name: \"DictaLM-3.0-24B-Thinking\",\n match: {\n or: [\n {\n equals: \"dicta-il/dictalm-3.0-24b-thinking\"\n },\n {\n equals: \"dicta-il/dictalm-3.0-24b-thinking-fast\"\n }\n ]\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.4\n }\n },\n {\n id: \"swiss-ai/Apertus-70B-Instruct-2509\",\n name: \"Apertus-70B-Instruct-2509\",\n match: {\n or: [\n {\n equals: \"swiss-ai/apertus-70b-instruct-2509\"\n },\n {\n equals: \"swiss-ai/apertus-70b-instruct-2509-fast\"\n }\n ]\n },\n prices: {\n input_mtok: 0.82,\n output_mtok: 2.92\n }\n },\n {\n id: \"swiss-ai/Apertus-8B-Instruct-2509\",\n name: \"Apertus-8B-Instruct-2509\",\n match: {\n or: [\n {\n equals: \"swiss-ai/apertus-8b-instruct-2509\"\n },\n {\n equals: \"swiss-ai/apertus-8b-instruct-2509-fast\"\n }\n ]\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.2\n }\n },\n {\n id: \"utter-project/EuroLLM-22B-Instruct-2512\",\n name: \"EuroLLM-22B-Instruct-2512\",\n match: {\n or: [\n {\n equals: \"utter-project/eurollm-22b-instruct-2512\"\n },\n {\n equals: \"utter-project/eurollm-22b-instruct-2512-fast\"\n }\n ]\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.2\n }\n }\n ]\n },\n {\n id: \"huggingface_sambanova\",\n name: \"HuggingFace (sambanova)\",\n pricing_urls: [\"https://router.huggingface.co/v1/models\", \"https://huggingface.co/inference/models\"],\n api_pattern: \"https://router\\\\.huggingface\\\\.co/sambanova\",\n provider_match: {\n and: [\n {\n contains: \"huggingface\"\n },\n {\n contains: \"sambanova\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"Qwen/Qwen3-32B\",\n name: \"Qwen3-32B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-32b\"\n },\n {\n equals: \"qwen/qwen3-32b-fast\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.4,\n output_mtok: 0.8\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-R1-0528\",\n name: \"DeepSeek-R1-0528\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-r1-0528\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-0528-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 5,\n output_mtok: 7\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-R1-Distill-Llama-70B\",\n name: \"DeepSeek-R1-Distill-Llama-70B\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-r1-distill-llama-70b\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-distill-llama-70b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.7,\n output_mtok: 1.4\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V3-0324\",\n name: \"DeepSeek-V3-0324\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-v3-0324\"\n },\n {\n equals: \"deepseek-ai/deepseek-v3-0324-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 3,\n output_mtok: 4.5\n }\n },\n {\n id: \"meta-llama/Llama-3.1-8B-Instruct\",\n name: \"Llama-3.1-8B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.1-8b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.1-8b-instruct-fast\"\n }\n ]\n },\n context_window: 16384,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.2\n }\n },\n {\n id: \"meta-llama/Llama-3.3-70B-Instruct\",\n name: \"Llama-3.3-70B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.3-70b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.3-70b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.6,\n output_mtok: 1.2\n }\n },\n {\n id: \"openai/gpt-oss-120b\",\n name: \"gpt-oss-120b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-120b\"\n },\n {\n equals: \"openai/gpt-oss-120b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.22,\n output_mtok: 0.59\n }\n },\n {\n id: \"tokyotech-llm/Llama-3.3-Swallow-70B-Instruct-v0.4\",\n name: \"Llama-3.3-Swallow-70B-Instruct-v0.4\",\n match: {\n or: [\n {\n equals: \"tokyotech-llm/llama-3.3-swallow-70b-instruct-v0.4\"\n },\n {\n equals: \"tokyotech-llm/llama-3.3-swallow-70b-instruct-v0.4-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.6,\n output_mtok: 1.2\n }\n }\n ]\n },\n {\n id: \"huggingface_together\",\n name: \"HuggingFace (together)\",\n pricing_urls: [\"https://router.huggingface.co/v1/models\", \"https://huggingface.co/inference/models\"],\n api_pattern: \"https://router\\\\.huggingface\\\\.co/together\",\n provider_match: {\n and: [\n {\n contains: \"huggingface\"\n },\n {\n contains: \"together\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"EssentialAI/rnj-1-instruct\",\n name: \"rnj-1-instruct\",\n match: {\n or: [\n {\n equals: \"essentialai/rnj-1-instruct\"\n },\n {\n equals: \"essentialai/rnj-1-instruct-fast\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.15\n }\n },\n {\n id: \"Qwen/Qwen2.5-7B-Instruct\",\n name: \"Qwen2.5-7B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen2.5-7b-instruct\"\n },\n {\n equals: \"qwen/qwen2.5-7b-instruct-fast\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.3\n }\n },\n {\n id: \"Qwen/Qwen3-235B-A22B-Instruct-2507\",\n name: \"Qwen3-235B-A22B-Instruct-2507\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-235b-a22b-instruct-2507\"\n },\n {\n equals: \"qwen/qwen3-235b-a22b-instruct-2507-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.6\n }\n },\n {\n id: \"Qwen/Qwen3-Coder-480B-A35B-Instruct\",\n name: \"Qwen3-Coder-480B-A35B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-coder-480b-a35b-instruct\"\n },\n {\n equals: \"qwen/qwen3-coder-480b-a35b-instruct-fast\"\n },\n {\n equals: \"qwen/qwen3-coder-480b-a35b-instruct-fp8\"\n },\n {\n equals: \"qwen/qwen3-coder-480b-a35b-instruct-fp8-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 2,\n output_mtok: 2\n }\n },\n {\n id: \"Qwen/Qwen3-Coder-Next-FP8\",\n name: \"Qwen3-Coder-Next-FP8\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-coder-next-fp8\"\n },\n {\n equals: \"qwen/qwen3-coder-next-fp8-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.2\n }\n },\n {\n id: \"Qwen/Qwen3-Next-80B-A3B-Instruct\",\n name: \"Qwen3-Next-80B-A3B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-next-80b-a3b-instruct\"\n },\n {\n equals: \"qwen/qwen3-next-80b-a3b-instruct-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.15,\n output_mtok: 1.5\n }\n },\n {\n id: \"Qwen/Qwen3-VL-8B-Instruct\",\n name: \"Qwen3-VL-8B-Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-vl-8b-instruct\"\n },\n {\n equals: \"qwen/qwen3-vl-8b-instruct-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.18000000000000002,\n output_mtok: 0.68\n }\n },\n {\n id: \"Qwen/Qwen3.5-397B-A17B\",\n name: \"Qwen3.5-397B-A17B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.5-397b-a17b\"\n },\n {\n equals: \"qwen/qwen3.5-397b-a17b-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.6,\n output_mtok: 3.6\n }\n },\n {\n id: \"Qwen/Qwen3.5-9B\",\n name: \"Qwen3.5-9B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.5-9b\"\n },\n {\n equals: \"qwen/qwen3.5-9b-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.15\n }\n },\n {\n id: \"ServiceNow-AI/Apriel-1.6-15b-Thinker\",\n name: \"Apriel-1.6-15b-Thinker\",\n match: {\n or: [\n {\n equals: \"servicenow-ai/apriel-1.6-15b-thinker\"\n },\n {\n equals: \"servicenow-ai/apriel-1.6-15b-thinker-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {}\n },\n {\n id: \"deepcogito/cogito-671b-v2.1\",\n name: \"cogito-671b-v2.1\",\n match: {\n or: [\n {\n equals: \"deepcogito/cogito-671b-v2.1\"\n },\n {\n equals: \"deepcogito/cogito-671b-v2.1-fast\"\n },\n {\n equals: \"deepcogito/cogito-671b-v2.1-fp8\"\n },\n {\n equals: \"deepcogito/cogito-671b-v2.1-fp8-fast\"\n }\n ]\n },\n context_window: 163840,\n prices: {\n input_mtok: 1.25,\n output_mtok: 1.25\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-R1\",\n name: \"DeepSeek-R1\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-r1\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-fast\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-0528\"\n },\n {\n equals: \"deepseek-ai/deepseek-r1-0528-fast\"\n }\n ]\n },\n context_window: 163840,\n prices: {\n input_mtok: 3,\n output_mtok: 7\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V3\",\n name: \"DeepSeek-V3\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-v3\"\n },\n {\n equals: \"deepseek-ai/deepseek-v3-fast\"\n },\n {\n equals: \"deepseek-ai/deepseek-v3-0324\"\n },\n {\n equals: \"deepseek-ai/deepseek-v3-0324-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 1.25,\n output_mtok: 1.25\n }\n },\n {\n id: \"deepseek-ai/DeepSeek-V3.1\",\n name: \"DeepSeek-V3.1\",\n match: {\n or: [\n {\n equals: \"deepseek-ai/deepseek-v3.1\"\n },\n {\n equals: \"deepseek-ai/deepseek-v3.1-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.6,\n output_mtok: 1.7\n }\n },\n {\n id: \"google/gemma-3n-E4B-it\",\n name: \"gemma-3n-E4B-it\",\n match: {\n or: [\n {\n equals: \"google/gemma-3n-e4b-it\"\n },\n {\n equals: \"google/gemma-3n-e4b-it-fast\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.02,\n output_mtok: 0.04\n }\n },\n {\n id: \"meta-llama/Llama-3.3-70B-Instruct\",\n name: \"Llama-3.3-70B-Instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.3-70b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.3-70b-instruct-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.88,\n output_mtok: 0.88\n }\n },\n {\n id: \"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8\",\n name: \"Llama-4-Maverick-17B-128E-Instruct-FP8\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-4-maverick-17b-128e-instruct-fp8\"\n },\n {\n equals: \"meta-llama/llama-4-maverick-17b-128e-instruct-fp8-fast\"\n }\n ]\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.27,\n output_mtok: 0.85\n }\n },\n {\n id: \"moonshotai/Kimi-K2.5\",\n name: \"Kimi-K2.5\",\n match: {\n or: [\n {\n equals: \"moonshotai/kimi-k2.5\"\n },\n {\n equals: \"moonshotai/kimi-k2.5-fast\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.5,\n output_mtok: 2.8\n }\n },\n {\n id: \"openai/gpt-oss-120b\",\n name: \"gpt-oss-120b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-120b\"\n },\n {\n equals: \"openai/gpt-oss-120b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n }\n },\n {\n id: \"openai/gpt-oss-20b\",\n name: \"gpt-oss-20b\",\n match: {\n or: [\n {\n equals: \"openai/gpt-oss-20b\"\n },\n {\n equals: \"openai/gpt-oss-20b-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.2\n }\n },\n {\n id: \"zai-org/GLM-4.5-Air-FP8\",\n name: \"GLM-4.5-Air-FP8\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-4.5-air-fp8\"\n },\n {\n equals: \"zai-org/glm-4.5-air-fp8-fast\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.2,\n output_mtok: 1.1\n }\n },\n {\n id: \"zai-org/GLM-4.6\",\n name: \"GLM-4.6\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-4.6\"\n },\n {\n equals: \"zai-org/glm-4.6-fast\"\n }\n ]\n },\n context_window: 202752,\n prices: {\n input_mtok: 0.6,\n output_mtok: 2.2\n }\n },\n {\n id: \"zai-org/GLM-4.7-FP8\",\n name: \"GLM-4.7-FP8\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-4.7-fp8\"\n },\n {\n equals: \"zai-org/glm-4.7-fp8-fast\"\n }\n ]\n },\n context_window: 202752,\n prices: {\n input_mtok: 0.45,\n output_mtok: 2\n }\n },\n {\n id: \"zai-org/GLM-5\",\n name: \"GLM-5\",\n match: {\n or: [\n {\n equals: \"zai-org/glm-5\"\n },\n {\n equals: \"zai-org/glm-5-fast\"\n }\n ]\n },\n context_window: 202752,\n prices: {\n input_mtok: 1,\n output_mtok: 3.2\n }\n }\n ]\n },\n {\n id: \"meta\",\n name: \"Meta\",\n pricing_urls: [\n \"https://dev.meta.ai/docs/pricing-rate-limits\",\n \"https://dev.meta.ai/docs/protocols/chat-completions\",\n \"https://dev.meta.ai/docs/protocols/responses\",\n \"https://dev.meta.ai/docs/prompt-caching\"\n ],\n api_pattern: \"https://api\\\\.meta\\\\.ai(?:/|$)\",\n model_match: {\n or: [\n {\n equals: \"muse-wonder-1.0\"\n },\n {\n starts_with: \"muse-spark-\"\n }\n ]\n },\n provider_match: {\n equals: \"meta\"\n },\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"responses\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"input_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"output_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"muse-spark-1.1\",\n name: \"Muse Spark 1.1\",\n match: {\n equals: \"muse-spark-1.1\"\n },\n context_window: 1048576,\n price_comments: \"Standard mode pricing: $1.25 input, $0.15 cached input, $4.25 output per million tokens, plus $2.50 per 1,000 web search queries. Muse Spark 1.1 has no Contributor mode variant. https://dev.meta.ai/docs/pricing-rate-limits\",\n prices: {\n input_mtok: 1.25,\n cache_read_mtok: 0.15,\n output_mtok: 4.25,\n web_searches_kcount: 2.5\n }\n },\n {\n id: \"muse-spark-1.2\",\n name: \"Muse Spark 1.2\",\n match: {\n equals: \"muse-spark-1.2\"\n },\n context_window: 1048576,\n price_comments: \"Standard mode pricing: $1.25 input, $0.15 cached input, $4.25 output per million tokens, plus $2.50 per 1,000 web search queries. No long-context premium. https://dev.meta.ai/docs/pricing-rate-limits\",\n prices: {\n input_mtok: 1.25,\n cache_read_mtok: 0.15,\n output_mtok: 4.25,\n web_searches_kcount: 2.5\n }\n },\n {\n id: \"muse-spark-1.2-contributor\",\n name: \"Muse Spark 1.2 (Contributor mode)\",\n match: {\n equals: \"muse-spark-1.2-contributor\"\n },\n context_window: 1048576,\n price_comments: \"Contributor mode pricing: $0.10 input, $0.002 cached input, $0.20 output per million tokens. Web search is billed at the same $2.50 per 1,000 queries as Standard mode (the docs apply it to every Muse Spark model, and OpenRouter's contributor endpoints list web_search at $0.0025 per query). https://dev.meta.ai/docs/pricing-rate-limits\",\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.002,\n output_mtok: 0.2,\n web_searches_kcount: 2.5\n }\n },\n {\n id: \"muse-spark-1.3\",\n name: \"Muse Spark 1.3\",\n match: {\n equals: \"muse-spark-1.3\"\n },\n context_window: 1048576,\n price_comments: \"Standard mode pricing: $1.25 input, $0.15 cached input, $4.25 output per million tokens, plus $2.50 per 1,000 web search queries. No long-context premium. https://dev.meta.ai/docs/pricing-rate-limits\",\n prices: {\n input_mtok: 1.25,\n cache_read_mtok: 0.15,\n output_mtok: 4.25,\n web_searches_kcount: 2.5\n }\n },\n {\n id: \"muse-spark-1.3-contributor\",\n name: \"Muse Spark 1.3 (Contributor mode)\",\n match: {\n equals: \"muse-spark-1.3-contributor\"\n },\n context_window: 1048576,\n price_comments: \"Contributor mode pricing: $0.10 input, $0.002 cached input, $0.20 output per million tokens. Web search is billed at the same $2.50 per 1,000 queries as Standard mode (the docs apply it to every Muse Spark model, and OpenRouter's contributor endpoints list web_search at $0.0025 per query). https://dev.meta.ai/docs/pricing-rate-limits\",\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.002,\n output_mtok: 0.2,\n web_searches_kcount: 2.5\n }\n },\n {\n id: \"muse-wonder-1.0\",\n name: \"Muse Wonder 1.0\",\n match: {\n equals: \"muse-wonder-1.0\"\n },\n context_window: 1048576,\n price_comments: \"Standard mode pricing: $4 input, $0.20 cached input, $20 output per million tokens, plus $2.50 per 1,000 web search queries. The usage response has no documented billable-search count, so callers provide web_searches. No Messages extractor is defined because the docs do not consistently confirm Wonder support. https://dev.meta.ai/docs/pricing-rate-limits\",\n prices: {\n input_mtok: 4,\n cache_read_mtok: 0.2,\n output_mtok: 20,\n web_searches_kcount: 2.5\n }\n }\n ]\n },\n {\n id: \"minimax\",\n name: \"MiniMax\",\n pricing_urls: [\"https://platform.minimax.io/docs/guides/pricing-paygo\"],\n api_pattern: \"https://api\\\\.minimax(i)?\\\\.(?:com|io)\",\n price_comments: \"Prices sourced from MiniMax international platform USD pricing (platform.minimax.io, May 2026). M2.1, M2.1-highspeed, and M2 are legacy models; prices inferred from CNY pricing at the equivalent 7.00 CNY/USD rate used by the international platform for current models.\",\n model_match: {\n or: [\n {\n starts_with: \"MiniMax-M\"\n },\n {\n starts_with: \"minimax-m\"\n },\n {\n equals: \"minimax-01\"\n },\n {\n equals: \"M2-her\"\n },\n {\n equals: \"m2-her\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"cache_creation_input_tokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"cache_read_input_tokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: \"cache_creation_input_tokens\",\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: \"cache_read_input_tokens\",\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"responses\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"input_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"M2-her\",\n name: \"MiniMax M2-her\",\n description: \"MiniMax M2-her, a text dialogue model optimized for role-playing and multi-turn conversations. 64,000 token context window. No cache support.\",\n match: {\n or: [\n {\n equals: \"M2-her\"\n },\n {\n equals: \"m2-her\"\n }\n ]\n },\n context_window: 64000,\n prices: {\n input_mtok: 0.3,\n output_mtok: 1.2\n }\n },\n {\n id: \"MiniMax-M2\",\n name: \"MiniMax M2\",\n description: \"MiniMax M2 (legacy), a multimodal language model with 204,800 token context window. Supports agentic capabilities and advanced reasoning.\",\n match: {\n or: [\n {\n equals: \"MiniMax-M2\"\n },\n {\n equals: \"minimax-m2\"\n },\n {\n equals: \"MiniMax-M2.1\"\n },\n {\n equals: \"minimax-m2.1\"\n },\n {\n equals: \"MiniMax-M2.5\"\n },\n {\n equals: \"minimax-m2.5\"\n }\n ]\n },\n context_window: 204800,\n prices: {\n input_mtok: 0.3,\n cache_write_mtok: 0.375,\n cache_read_mtok: 0.03,\n output_mtok: 1.2\n }\n },\n {\n id: \"MiniMax-M2.1-highspeed\",\n name: \"MiniMax M2.1 Highspeed\",\n description: \"MiniMax M2.1 highspeed variant (legacy) with higher throughput.\",\n match: {\n or: [\n {\n contains: \"M2.1-highspeed\"\n },\n {\n contains: \"m2.1-highspeed\"\n }\n ]\n },\n context_window: 204800,\n prices: {\n input_mtok: 0.6,\n cache_write_mtok: 0.375,\n cache_read_mtok: 0.03,\n output_mtok: 2.4\n }\n },\n {\n id: \"MiniMax-M2.5-highspeed\",\n name: \"MiniMax M2.5 Highspeed\",\n description: \"MiniMax M2.5 highspeed variant with higher throughput.\",\n match: {\n or: [\n {\n contains: \"M2.5-highspeed\"\n },\n {\n contains: \"m2.5-highspeed\"\n }\n ]\n },\n context_window: 204800,\n prices: {\n input_mtok: 0.6,\n cache_write_mtok: 0.375,\n cache_read_mtok: 0.03,\n output_mtok: 2.4\n }\n },\n {\n id: \"MiniMax-M2.7\",\n name: \"MiniMax M2.7\",\n description: \"MiniMax M2.7, a multimodal language model with 204,800 token context window. Achieves top performance in real-world engineering, office productivity, and character-rich interaction.\",\n match: {\n or: [\n {\n equals: \"MiniMax-M2.7\"\n },\n {\n equals: \"minimax-m2.7\"\n }\n ]\n },\n context_window: 204800,\n prices: {\n input_mtok: 0.3,\n cache_write_mtok: 0.375,\n cache_read_mtok: 0.06,\n output_mtok: 1.2\n }\n },\n {\n id: \"MiniMax-M2.7-highspeed\",\n name: \"MiniMax M2.7 Highspeed\",\n description: \"MiniMax M2.7 highspeed variant with higher throughput.\",\n match: {\n or: [\n {\n contains: \"M2.7-highspeed\"\n },\n {\n contains: \"m2.7-highspeed\"\n }\n ]\n },\n context_window: 204800,\n prices: {\n input_mtok: 0.6,\n cache_write_mtok: 0.375,\n cache_read_mtok: 0.06,\n output_mtok: 2.4\n }\n },\n {\n id: \"minimax-01\",\n name: \"MiniMax-01\",\n description: \"MiniMax-01 combines MiniMax-Text-01 for text generation and MiniMax-VL-01 for image understanding.\",\n match: {\n equals: \"minimax-01\"\n },\n context_window: 1000192,\n price_comments: \"Imported from OpenRouter pricing; verify against MiniMax pricing when native API pricing is published.\",\n prices: {\n input_mtok: 0.2,\n output_mtok: 1.1\n }\n },\n {\n id: \"minimax-m1\",\n name: \"MiniMax M1\",\n description: 'MiniMax-M1 is a large-scale, open-weight reasoning model designed for extended context and high-efficiency inference. It leverages a hybrid Mixture-of-Experts (MoE) architecture paired with a custom \"lightning attention\" mechanism, allowing efficient long-context processing.',\n match: {\n equals: \"minimax-m1\"\n },\n price_comments: \"Imported from OpenRouter pricing; verify against MiniMax pricing when native API pricing is published.\",\n prices: {\n input_mtok: 0.4,\n output_mtok: 2.2\n }\n },\n {\n id: \"minimax-m3\",\n name: \"MiniMax M3\",\n description: \"MiniMax-M3 is a multimodal foundation model from MiniMax. It supports text, image, and video inputs with text output, a 1M-token context window, and long-horizon agentic work.\",\n match: {\n equals: \"minimax-m3\"\n },\n context_window: 1e6,\n price_comments: `Prices from MiniMax pay-as-you-go page (https://platform.minimax.io/docs/guides/pricing-paygo, 2026-07-01), standard service tier \"Permanent 50% off\" effective rate. Inputs over 512K tokens bill at 2x per MiniMax's length-based tiering.`,\n prices: {\n input_mtok: {\n base: 0.3,\n tiers: [\n {\n start: 512000,\n price: 0.6\n }\n ]\n },\n cache_read_mtok: {\n base: 0.06,\n tiers: [\n {\n start: 512000,\n price: 0.12\n }\n ]\n },\n output_mtok: {\n base: 1.2,\n tiers: [\n {\n start: 512000,\n price: 2.4\n }\n ]\n }\n }\n }\n ]\n },\n {\n id: \"mistral\",\n name: \"Mistral\",\n pricing_urls: [\"https://mistral.ai/pricing/api\"],\n api_pattern: \"https://api\\\\.mistral\\\\.ai\",\n price_comments: 'Batch API usage is charged at 50% of the standard rates for every model (\"We offer a 50% discount on batch API\"). Batch rates for chat models, OCR 4.1 pages and Voxtral Mini Transcribe 2 are read from the Batch pricing mode at https://docs.mistral.ai/inference/pricing; OCR annotated pages, OCR 3, Voxtral Small and Mistral Embed apply the documented 50% discount to their current standard rates. Only models Mistral still serves carry a batch variant. https://docs.mistral.ai/capabilities/batch/',\n model_match: {\n regex: \"^(?![^/]+/)(?:labs-)?(?:(?:mi|code|dev|magi|mini)stral|mixtral|pixtral|voxtral|open-(?:mistral|mixtral))\"\n },\n provider_match: {\n starts_with: \"mistral\"\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"ocr\",\n root: \"usage_info\",\n model_path: \"model\",\n mappings: [\n {\n path: \"pages_processed\",\n dest: \"input_document_pages\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"ocr_annotated\",\n root: \"usage_info\",\n model_path: \"model\",\n mappings: [\n {\n path: \"pages_processed\",\n dest: \"input_document_pages\",\n required: true\n },\n {\n path: \"pages_processed\",\n dest: \"input_annotated_document_pages\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"codestral\",\n name: \"Codestral\",\n description: \"Mistral's cutting-edge language model for coding. Codestral specializes in low-latency, high-frequency tasks such as fill-in-the-middle (FIM), code correction and test generation.\",\n match: {\n or: [\n {\n equals: \"codestral-latest\"\n },\n {\n equals: \"codestral-2501\"\n }\n ]\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.9\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.45\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"codestral-2508\",\n name: \"Codestral 2508\",\n description: \"Mistral's cutting-edge language model for coding released end of July 2025. Codestral specializes in low-latency, high-frequency tasks such as fill-in-the-middle (FIM), code correction and test generation.\",\n match: {\n equals: \"codestral-2508\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.03,\n output_mtok: 0.9\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.015,\n output_mtok: 0.45\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"devstral-2512\",\n name: \"Devstral 2 2512\",\n description: \"Devstral 2 is a state-of-the-art open-source model by Mistral AI specializing in agentic coding. It is a 123B-parameter dense transformer model supporting a 256K context window.\",\n match: {\n equals: \"devstral-2512\"\n },\n context_window: 262144,\n price_comments: \"Deprecated 2026-05-22. Mistral recommends Mistral Medium 3.5 for new integrations. https://docs.mistral.ai/models/devstral-2-25-12\",\n prices: {\n input_mtok: 0.4,\n cache_read_mtok: 0.04,\n output_mtok: 2\n },\n deprecated: true\n },\n {\n id: \"devstral-small\",\n name: \"Devstral Small\",\n description: \"Devstral-Small-2505 is a 24B parameter agentic LLM fine-tuned from Mistral-Small-3.1, jointly developed by Mistral AI and All Hands AI for advanced software engineering tasks. It is optimized for codebase exploration, multi-file editing, and integration into coding agents, achieving state-of-the-art results on SWE-Bench Verified (46.8%).\",\n match: {\n or: [\n {\n equals: \"devstral-small\"\n },\n {\n equals: \"devstral-small-2505\"\n },\n {\n equals: \"devstral-small-2507\"\n },\n {\n equals: \"devstral-small-latest\"\n },\n {\n equals: \"labs-devstral-small-2512\"\n }\n ]\n },\n price_comments: \"The previous $0.06/$0.12 rates were not Mistral's; every Devstral Small generation is documented at $0.10/$0.30. Ref: https://github.com/mistralai/platform-docs-public/tree/main/src/schema/models/models (devstral-small-*.ts)\",\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.3\n },\n deprecated: true\n },\n {\n id: \"devstral-small:free\",\n name: \"Devstral Small (free)\",\n description: \"Devstral-Small-2505 is a 24B parameter agentic LLM fine-tuned from Mistral-Small-3.1, jointly developed by Mistral AI and All Hands AI for advanced software engineering tasks. It is optimized for codebase exploration, multi-file editing, and integration into coding agents, achieving state-of-the-art results on SWE-Bench Verified (46.8%).\",\n match: {\n equals: \"devstral-small:free\"\n },\n prices: {}\n },\n {\n id: \"magistral-medium\",\n name: \"Magistral Medium\",\n description: \"Magistral is Mistral's first reasoning model. It is ideal for general purpose use requiring longer thought processing and better accuracy than with non-reasoning LLMs. From legal research and financial forecasting to software development and creative storytelling \\u2014 this model solves multi-step challenges where transparency and precision are critical.\",\n match: {\n or: [\n {\n starts_with: \"magistral-medium\"\n }\n ]\n },\n price_comments: \"Magistral Medium 1.2 was deprecated 2026-05-22. Mistral recommends Mistral Medium 3.5. https://docs.mistral.ai/models/magistral-medium-1-2-25-09 No context_window: this record matches versions with different context limits.\",\n prices: {\n input_mtok: 2,\n output_mtok: 5\n },\n deprecated: true\n },\n {\n id: \"magistral-small\",\n name: \"Magistral Small\",\n description: \"Magistral Small is a 24B parameter instruction-tuned model based on Mistral-Small-3.1 (2503), enhanced through supervised fine-tuning on traces from Magistral Medium and further refined via reinforcement learning. It is optimized for reasoning and supports a wide multilingual range, including over 20 languages.\",\n match: {\n starts_with: \"magistral-small-\"\n },\n price_comments: \"Magistral Small 1.2 was deprecated 2026-04-30. Mistral recommends Mistral Small 4. https://docs.mistral.ai/models/magistral-small-1-2-25-09 No context_window: this record matches versions with different context limits.\",\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.5\n },\n deprecated: true\n },\n {\n id: \"ministral-14b-2512\",\n name: \"Ministral 3 14B 2512\",\n description: \"The largest model in the Ministral 3 family, Ministral 3 14B offers frontier capabilities and performance comparable to its larger Mistral Small 3.2 24B counterpart.\",\n match: {\n equals: \"ministral-14b-2512\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.02,\n output_mtok: 0.2\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.01,\n output_mtok: 0.1\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"ministral-3b\",\n name: \"Ministral 3B\",\n description: \"Ministral 3B is a 3B parameter model optimized for on-device and edge computing. It excels in knowledge, commonsense reasoning, and function-calling, outperforming larger models like Mistral 7B on most benchmarks. Supporting up to 128k context length, it's ideal for orchestrating agentic workflows and specialist tasks with efficient inference.\",\n match: {\n equals: \"ministral-3b\"\n },\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.04\n }\n },\n {\n id: \"ministral-3b-2512\",\n name: \"Ministral 3 3B 2512\",\n description: \"The smallest model in the Ministral 3 family, Ministral 3 3B is a powerful, efficient tiny language model with vision capabilities.\",\n match: {\n equals: \"ministral-3b-2512\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.01,\n output_mtok: 0.1\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.05,\n cache_read_mtok: 0.005,\n output_mtok: 0.05\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"ministral-8b\",\n name: \"Ministral 8B 24.10\",\n description: \"Ministral 8B is an 8B parameter model featuring a unique interleaved sliding-window attention pattern for faster, memory-efficient inference. Designed for edge use cases, it supports up to 128k context length and excels in knowledge and reasoning tasks. It outperforms peers in the sub-10B category, making it perfect for low-latency, privacy-first applications.\",\n match: {\n or: [\n {\n equals: \"ministral-8b\"\n },\n {\n equals: \"ministral-8b-2410\"\n }\n ]\n },\n price_comments: \"The previous $1 output price was incorrect; Mistral documented $0.10 from launch. Ref: https://github.com/mistralai/platform-docs-public/blob/4422b455194a/src/schema/models/models/ministral-8b-24-1.ts\",\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"ministral-8b-2512\",\n name: \"Ministral 3 8B 2512\",\n description: \"Ministral 3 8B is an efficient text and vision model for edge deployment.\",\n match: {\n equals: \"ministral-8b-2512\"\n },\n context_window: 262144,\n price_comments: \"Ref: https://mistral.ai/pricing/api\",\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.015,\n output_mtok: 0.15\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.075,\n cache_read_mtok: 0.0075,\n output_mtok: 0.075\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"ministral-8b-latest\",\n name: \"Ministral 8B Latest\",\n match: {\n equals: \"ministral-8b-latest\"\n },\n price_comments: \"The latest alias moved from Ministral 8B 24.10 to Ministral 3 8B on 2025-12-02. Ref: https://github.com/mistralai/platform-docs-public/commit/4975b09514f95978cfeeea814562000348548107\",\n prices: [\n {\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n constraint: {\n start_date: \"2025-12-02\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.015,\n output_mtok: 0.15\n }\n }\n ],\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.075,\n cache_read_mtok: 0.0075,\n output_mtok: 0.075\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"mistral-7b\",\n name: \"Mistral 7B\",\n match: {\n or: [\n {\n equals: \"mistral-7b\"\n },\n {\n equals: \"open-mistral-7b\"\n }\n ]\n },\n prices: {\n input_mtok: 0.25,\n output_mtok: 0.25\n }\n },\n {\n id: \"mistral-embed\",\n match: {\n equals: \"mistral-embed\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.05\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"mistral-large\",\n name: \"Mistral Large\",\n description: \"This is Mistral AI's flagship model, Mistral Large 2 (version `mistral-large-2407`). It's a proprietary weights-available model and excels at reasoning, code, JSON, chat, and more. Read the launch announcement here.\",\n match: {\n or: [\n {\n equals: \"mistral-large\"\n },\n {\n equals: \"mistral-large-latest\"\n },\n {\n equals: \"mistral-large-2407\"\n },\n {\n equals: \"mistral-large-2411\"\n }\n ]\n },\n prices: {\n input_mtok: 2,\n output_mtok: 6\n }\n },\n {\n id: \"mistral-large-2512\",\n name: \"Mistral Large 3 2512\",\n description: \"Mistral Large 3 2512 is Mistral's most capable model to date, featuring a sparse mixture-of-experts architecture with 41B active parameters (675B total), and released under the Apache 2.0 license.\",\n match: {\n equals: \"mistral-large-2512\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.5,\n cache_read_mtok: 0.05,\n output_mtok: 1.5\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.25,\n cache_read_mtok: 0.025,\n output_mtok: 0.75\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"mistral-large-4\",\n name: \"Mistral Large 4\",\n match: {\n or: [\n {\n equals: \"mistral-large-4\"\n },\n {\n equals: \"mistral-large-4-0\"\n }\n ]\n },\n context_window: 1048576,\n price_comments: \"Current launch promotional rates are $0.68 input, $0.07 cached input, and $2.09 output per million tokens. The published list rates are $1.36/$0.14/$4.18. The October 6 changelog says the promotion lasts two weeks but does not state an exact effective end date, so no future price change is scheduled here. The batch variant is half the promotional rate; when the list rates return, prepend a dated batch variant at $0.68/$0.07/$2.09. https://docs.mistral.ai/models/mistral-large-4-0 https://docs.mistral.ai/inference/pricing https://docs.mistral.ai/resources/changelogs\",\n prices: {\n input_mtok: 0.68,\n cache_read_mtok: 0.07,\n output_mtok: 2.09\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.34,\n cache_read_mtok: 0.035,\n output_mtok: 1.045\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"mistral-medium-2312\",\n name: \"Mistral Medium 1\",\n description: \"Mistral's first enterprise-grade model, released in December 2023.\",\n match: {\n equals: \"mistral-medium-2312\"\n },\n price_comments: \"Retired on 2025-06-16; retained for historical usage records at its original $2.70/$8.10 per MTok rates. Ref: https://github.com/mistralai/platform-docs-public/blob/222f4ba9114f84ce9f2d718b9a12fbac1e527f4b/src/schema/models/models/mistral-medium-1-0-23-12.ts\",\n prices: {\n input_mtok: 2.7,\n output_mtok: 8.1\n }\n },\n {\n id: \"mistral-medium-3-1\",\n name: \"Mistral Medium 3 and 3.1\",\n description: \"Mistral Medium 3 is a high-performance enterprise-grade language model designed to deliver frontier-level capabilities at significantly reduced operational cost. It balances state-of-the-art reasoning and multimodal performance with 8\\xD7 lower cost compared to traditional large models, making it suitable for scalable deployments across professional and industrial use cases.\",\n match: {\n or: [\n {\n equals: \"mistral-medium\"\n },\n {\n equals: \"mistral-medium-2505\"\n },\n {\n equals: \"mistral-medium-2508\"\n }\n ]\n },\n price_comments: \"Mistral Medium 3 and 3.1 retain their original $0.40/$2 per MTok rates. Ref: https://github.com/mistralai/platform-docs-public/blob/222f4ba9114f84ce9f2d718b9a12fbac1e527f4b/src/schema/models/models/mistral-medium-3-1-25-08.ts\",\n prices: {\n input_mtok: 0.4,\n cache_read_mtok: 0.04,\n output_mtok: 2\n }\n },\n {\n id: \"mistral-medium-3-5\",\n name: \"Mistral Medium 3.5\",\n description: \"Mistral Medium 3.5 is a frontier-class multimodal model optimized for agentic and coding use cases.\",\n match: {\n or: [\n {\n equals: \"mistral-medium-3.5\"\n },\n {\n equals: \"mistral-medium-3-5\"\n },\n {\n equals: \"mistral-medium-3\"\n },\n {\n equals: \"mistral-medium-2604\"\n }\n ]\n },\n context_window: 262144,\n price_comments: \"Ref: https://mistral.ai/pricing/api\",\n prices: {\n input_mtok: 1.5,\n cache_read_mtok: 0.15,\n output_mtok: 7.5\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 3.75\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"mistral-medium-latest\",\n name: \"Mistral Medium Latest\",\n match: {\n equals: \"mistral-medium-latest\"\n },\n price_comments: \"The latest alias moved from Mistral Medium 3.1 to Mistral Medium 3.5 on 2026-06-16. Ref: https://github.com/mistralai/platform-docs-public/commit/cc58c1186b1ca8ad65658f5dfd3ebd29de778c7f\",\n prices: [\n {\n prices: {\n input_mtok: 0.4,\n cache_read_mtok: 0.04,\n output_mtok: 2\n }\n },\n {\n constraint: {\n start_date: \"2026-06-16\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 1.5,\n cache_read_mtok: 0.15,\n output_mtok: 7.5\n }\n }\n ],\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 3.75\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"mistral-nemo\",\n name: \"Mistral NeMo\",\n description: \"A 12B parameter model with a 128k token context length built by Mistral in collaboration with NVIDIA.\",\n match: {\n or: [\n {\n equals: \"mistral-nemo\"\n },\n {\n equals: \"open-mistral-nemo\"\n }\n ]\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.15\n }\n },\n {\n id: \"mistral-nemo:free\",\n name: \"Mistral Nemo (free)\",\n description: \"A 12B parameter model with a 128k token context length built by Mistral in collaboration with NVIDIA.\",\n match: {\n equals: \"mistral-nemo:free\"\n },\n prices: {}\n },\n {\n id: \"mistral-ocr-2503\",\n name: \"Mistral OCR\",\n match: {\n or: [\n {\n equals: \"mistral-ocr-2503\"\n },\n {\n equals: \"mistral-ocr-2503-completion\"\n }\n ]\n },\n price_comments: \"Mistral launched OCR at 1,000 pages per US dollar: https://mistral.ai/news/mistral-ocr/\",\n prices: {\n input_document_kpages: 1\n },\n deprecated: true\n },\n {\n id: \"mistral-ocr-2505\",\n name: \"Mistral OCR 2\",\n match: {\n or: [\n {\n equals: \"mistral-ocr-2505\"\n },\n {\n equals: \"mistral-ocr-2505-completion\"\n }\n ]\n },\n price_comments: \"OCR 2 retained the original OCR page price and introduced structured annotations on May 22, 2025: https://docs.mistral.ai/resources/changelogs\",\n prices: {\n input_document_kpages: 1,\n input_annotated_document_kpages: 3\n },\n deprecated: true\n },\n {\n id: \"mistral-ocr-2512\",\n name: \"Mistral OCR 3\",\n match: {\n or: [\n {\n equals: \"mistral-ocr-2512\"\n },\n {\n equals: \"mistral-ocr-2512-completion\"\n }\n ]\n },\n context_window: 16384,\n price_comments: \"https://docs.mistral.ai/models/ocr-3-25-12\",\n prices: {\n input_document_kpages: 2,\n input_annotated_document_kpages: 3\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_document_kpages: 1,\n input_annotated_document_kpages: 1.5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"mistral-ocr-4-0\",\n name: \"Mistral OCR 4.0\",\n match: {\n or: [\n {\n equals: \"mistral-ocr-4-0\"\n },\n {\n equals: \"mistral-ocr-4-0-completion\"\n }\n ]\n },\n context_window: 16384,\n price_comments: \"Deprecated 2026-09-29. Mistral recommends OCR 4.1 for new integrations. https://docs.mistral.ai/models/ocr-4-0\",\n prices: {\n input_document_kpages: 4,\n input_annotated_document_kpages: 5\n },\n deprecated: true\n },\n {\n id: \"mistral-ocr-4-1\",\n name: \"Mistral OCR 4.1\",\n match: {\n or: [\n {\n equals: \"mistral-ocr-4\"\n },\n {\n equals: \"mistral-ocr-4-completion\"\n },\n {\n equals: \"mistral-ocr-4-1\"\n },\n {\n equals: \"mistral-ocr-4-1-completion\"\n }\n ]\n },\n context_window: 16384,\n price_comments: \"https://docs.mistral.ai/models/ocr-4-1\",\n prices: {\n input_document_kpages: 4,\n input_annotated_document_kpages: 5\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_document_kpages: 2,\n input_annotated_document_kpages: 2.5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"mistral-ocr-latest\",\n name: \"Mistral OCR Latest\",\n match: {\n or: [\n {\n equals: \"mistral-ocr-latest\"\n },\n {\n equals: \"mistral-ocr-latest-completion\"\n }\n ]\n },\n price_comments: \"The latest alias moved to OCR 3 on December 18, 2025, OCR 4.0 on June 23, 2026, and OCR 4.1 on July 16, 2026. OCR 4.1 kept the OCR 4.0 rates: https://docs.mistral.ai/resources/changelogs\",\n prices: [\n {\n prices: {\n input_document_kpages: 1\n }\n },\n {\n constraint: {\n start_date: \"2025-05-22\",\n type: \"start_date\"\n },\n prices: {\n input_document_kpages: 1,\n input_annotated_document_kpages: 3\n }\n },\n {\n constraint: {\n start_date: \"2025-12-18\",\n type: \"start_date\"\n },\n prices: {\n input_document_kpages: 2,\n input_annotated_document_kpages: 3\n }\n },\n {\n constraint: {\n start_date: \"2026-06-23\",\n type: \"start_date\"\n },\n prices: {\n input_document_kpages: 4,\n input_annotated_document_kpages: 5\n }\n }\n ],\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_document_kpages: 2,\n input_annotated_document_kpages: 2.5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"mistral-saba\",\n name: \"Mistral Saba\",\n description: \"Mistral Saba is a 24B-parameter language model specifically designed for the Middle East and South Asia, delivering accurate and contextually relevant responses while maintaining efficient performance. Trained on curated regional datasets, it supports multiple Indian-origin languages\\u2014including Tamil and Malayalam\\u2014alongside Arabic. This makes it a versatile option for a range of regional and multilingual applications. Read more at the blog post here\",\n match: {\n or: [\n {\n equals: \"mistral-saba\"\n },\n {\n equals: \"mistral-saba-latest\"\n }\n ]\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.6\n }\n },\n {\n id: \"mistral-small-24b-instruct-2501\",\n name: \"Mistral Small 3\",\n description: \"Mistral Small 3 is a 24B-parameter language model optimized for low-latency performance across common AI tasks. Released under the Apache 2.0 license, it features both pre-trained and instruction-tuned versions designed for efficient local deployment.\",\n match: {\n equals: \"mistral-small-24b-instruct-2501\"\n },\n price_comments: \"Can't find pricing on this model, so just trusting open router\",\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.08\n }\n },\n {\n id: \"mistral-small-24b-instruct-2501:free\",\n name: \"Mistral Small 3 (free)\",\n description: \"Mistral Small 3 is a 24B-parameter language model optimized for low-latency performance across common AI tasks. Released under the Apache 2.0 license, it features both pre-trained and instruction-tuned versions designed for efficient local deployment.\",\n match: {\n equals: \"mistral-small-24b-instruct-2501:free\"\n },\n prices: {}\n },\n {\n id: \"mistral-small-2603\",\n name: \"Mistral Small 4\",\n description: \"Mistral Small 4 is the next major release in the Mistral Small family, unifying the capabilities of several flagship Mistral models into a single system.\",\n match: {\n equals: \"mistral-small-2603\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.015,\n output_mtok: 0.6\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.075,\n cache_read_mtok: 0.0075,\n output_mtok: 0.3\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"mistral-small-3.1-24b-instruct\",\n name: \"Mistral Small 3.1 24B\",\n description: \"Mistral Small 3.1 24B Instruct is an upgraded variant of Mistral Small 3 (2501), featuring 24 billion parameters with advanced multimodal capabilities.\",\n match: {\n equals: \"mistral-small-3.1-24b-instruct\"\n },\n price_comments: \"Imported from OpenRouter pricing; verify against Mistral pricing when native API pricing is published.\",\n prices: {\n input_mtok: 0.351,\n output_mtok: 0.555\n }\n },\n {\n id: \"mistral-small-3.2-24b-instruct\",\n name: \"Mistral Small 3.2 24B\",\n description: \"Mistral-Small-3.2-24B-Instruct-2506 is an updated 24B parameter model from Mistral optimized for instruction following, repetition reduction, and improved function calling.\",\n match: {\n equals: \"mistral-small-3.2-24b-instruct\"\n },\n price_comments: \"Imported from OpenRouter pricing; verify against Mistral pricing when native API pricing is published.\",\n prices: {\n input_mtok: 0.075,\n output_mtok: 0.2\n }\n },\n {\n id: \"mistral-small-latest\",\n name: \"Mistral Small 3.2\",\n description: \"SOTA. Multimodal. Multilingual. Apache 2.0.\",\n match: {\n equals: \"mistral-small-latest\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.3\n }\n },\n {\n id: \"mistral-tiny\",\n name: \"Mistral Tiny\",\n description: \"Note: This model is being deprecated. Recommended replacement is the newer Ministral 8B\",\n match: {\n equals: \"mistral-tiny\"\n },\n prices: {\n input_mtok: 0.25,\n output_mtok: 0.25\n },\n deprecated: true\n },\n {\n id: \"mixtral-8x22b-instruct\",\n name: \"Mixtral 8x22B Instruct\",\n description: `Mistral's official instruct fine-tuned version of Mixtral 8x22B. It uses 39B active parameters out of 141B, offering unparalleled cost efficiency for its size. Its strengths include:\n- strong math, coding, and reasoning\n- large context length (64k)\n- fluency in English, French, Italian, German, and Spanish`,\n match: {\n equals: \"mixtral-8x22b-instruct\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"mixtral-8x7b\",\n name: \"Mixtral 8x7B\",\n match: {\n or: [\n {\n starts_with: \"mixtral-8x7b\"\n },\n {\n equals: \"open-mixtral-8x7b\"\n }\n ]\n },\n prices: {\n input_mtok: 0.7,\n output_mtok: 0.7\n }\n },\n {\n id: \"pixtral-12b\",\n name: \"Pixtral 12B\",\n description: \"The first multi-modal, text+image-to-text model from Mistral AI. Its weights were launched via torrent: https://x.com/mistralai/status/1833758285167722836.\",\n match: {\n or: [\n {\n equals: \"pixtral-12b\"\n },\n {\n equals: \"pixtral-12b-latest\"\n }\n ]\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.15\n }\n },\n {\n id: \"pixtral-large\",\n name: \"Pixtral Large 2411\",\n description: \"Pixtral Large is a 124B parameter, open-weight, multimodal model built on top of Mistral Large 2. The model is able to understand documents, charts and natural images.\",\n match: {\n or: [\n {\n equals: \"pixtral-large-latest\"\n },\n {\n equals: \"pixtral-large-2411\"\n }\n ]\n },\n prices: {\n input_mtok: 2,\n output_mtok: 6\n }\n },\n {\n id: \"voxtral-mini-2602\",\n name: \"Voxtral Mini Transcribe 2\",\n match: {\n or: [\n {\n equals: \"voxtral-mini-latest\"\n },\n {\n equals: \"voxtral-mini-2602\"\n }\n ]\n },\n context_window: 16384,\n price_comments: \"See https://docs.mistral.ai/models/voxtral-mini-transcribe-26-02.\",\n prices: {\n audio_hours: 0.18,\n input_audio_hours: 0.18\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n audio_hours: 0.09,\n input_audio_hours: 0.09\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"voxtral-small-24b-2507\",\n name: \"Voxtral Small 24B 2507\",\n description: \"Voxtral Small is an enhancement of Mistral Small 3, incorporating state-of-the-art audio input capabilities while retaining best-in-class text performance. It excels at speech transcription, translation and audio understanding.\",\n match: {\n or: [\n {\n equals: \"voxtral-small-24b-2507\"\n },\n {\n equals: \"voxtral-small-2507\"\n },\n {\n equals: \"voxtral-small-latest\"\n }\n ]\n },\n context_window: 32768,\n price_comments: \"Mistral raised output pricing from $0.30 to $0.40 per MTok on 2026-08-11. Ref: https://github.com/mistralai/platform-docs-public/commit/1996c3f1eca754d02436a37fcc899440794a18a5\",\n prices: [\n {\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.01,\n output_mtok: 0.3\n }\n },\n {\n constraint: {\n start_date: \"2026-08-11\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.01,\n output_mtok: 0.4\n }\n }\n ],\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.05,\n cache_read_mtok: 0.005,\n output_mtok: 0.2\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n }\n ]\n },\n {\n id: \"modal\",\n name: \"Modal\",\n pricing_urls: [\"https://modal.com/library\"],\n api_pattern: \"https://[^/]+\\\\.modal\\\\.(?:run|direct)(?:/|$)\",\n provider_match: {\n contains: \"modal\"\n },\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: \"reasoning_tokens\",\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"responses\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"input_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"input_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"output_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"moonshotai/Kimi-K3\",\n name: \"Kimi K3\",\n description: \"Moonshot AI's 2.8-trillion-parameter multimodal reasoning model, served by Modal as a Shared Endpoint.\",\n match: {\n equals: \"moonshotai/Kimi-K3\"\n },\n context_window: 1048576,\n price_comments: \"Modal Shared Endpoint pricing. Reasoning tokens use the same $15/MTok rate as completion tokens. See https://modal.com/library/moonshot/kimi-k3.\",\n prices: {\n input_mtok: 3,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"thinkingmachines/Inkling-NVFP4\",\n name: \"Inkling NVFP4\",\n description: \"Thinking Machines Lab's reasoning model, served by Modal as a Shared Endpoint.\",\n match: {\n equals: \"thinkingmachines/Inkling-NVFP4\"\n },\n price_comments: \"Modal Shared Endpoint dashboard pricing. Reasoning tokens use the same $5/MTok rate as completion tokens.\",\n prices: {\n input_mtok: 1.2,\n cache_read_mtok: 0.27,\n output_mtok: 5\n }\n }\n ]\n },\n {\n id: \"moonshotai\",\n name: \"MoonshotAi\",\n pricing_urls: [\n \"https://platform.kimi.ai/docs/models.md\",\n \"https://platform.kimi.ai/docs/pricing/chat-k3.md\",\n \"https://platform.kimi.ai/docs/pricing/chat-k27-code.md\",\n \"https://platform.kimi.ai/docs/pricing/chat-k26.md\",\n \"https://platform.kimi.ai/docs/pricing/chat-k25.md\",\n \"https://platform.kimi.ai/docs/pricing/chat-v1.md\"\n ],\n api_pattern: \"https://api\\\\.moonshot\\\\.\",\n model_match: {\n or: [\n {\n starts_with: \"kimi\"\n },\n {\n starts_with: \"moonshot\"\n }\n ]\n },\n provider_match: {\n contains: \"moonshot\"\n },\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"kimi-k2\",\n name: \"Kimi K2 0711\",\n description: \"Kimi K2 Instruct is a large-scale Mixture-of-Experts (MoE) language model developed by Moonshot AI, featuring 1 trillion total parameters with 32 billion active per forward pass.\",\n match: {\n equals: \"kimi-k2\"\n },\n prices: {\n input_mtok: 0.57,\n output_mtok: 2.3\n },\n deprecated: true\n },\n {\n id: \"kimi-k2-0711-preview\",\n name: \"Kimi K2 0711 Preview\",\n description: \"MoE foundation model with exceptional coding and agent capabilities, featuring 1 trillion total parameters and 32 billion activated parameters.\",\n match: {\n equals: \"kimi-k2-0711-preview\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.15,\n output_mtok: 2.5\n },\n deprecated: true\n },\n {\n id: \"kimi-k2-0905-preview\",\n name: \"Kimi K2 0905 Preview\",\n description: \"Based on kimi-k2-0711-preview, with enhanced agentic coding abilities, improved frontend code quality and practicality, and better context understanding. MoE foundation model with 1 trillion total parameters and 32 billion activated parameters.\",\n match: {\n equals: \"kimi-k2-0905-preview\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.15,\n output_mtok: 2.5\n },\n deprecated: true\n },\n {\n id: \"kimi-k2-thinking\",\n name: \"Kimi K2 Thinking\",\n description: \"A thinking model with general agentic and reasoning capabilities, specializing in deep reasoning tasks.\",\n match: {\n equals: \"kimi-k2-thinking\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.15,\n output_mtok: 2.5\n },\n deprecated: true\n },\n {\n id: \"kimi-k2-thinking-turbo\",\n name: \"Kimi K2 Thinking Turbo\",\n description: \"High-speed version of kimi-k2-thinking, suitable for scenarios requiring both deep reasoning and extremely fast responses.\",\n match: {\n equals: \"kimi-k2-thinking-turbo\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 1.15,\n cache_read_mtok: 0.15,\n output_mtok: 8\n },\n deprecated: true\n },\n {\n id: \"kimi-k2-turbo-preview\",\n name: \"Kimi K2 Turbo Preview\",\n description: \"High-speed version of kimi-k2, always aligned with the latest kimi-k2. Same model parameters as kimi-k2, output speed up to 60 tokens/sec (max 100 tokens/sec).\",\n match: {\n starts_with: \"kimi-k2-turbo\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 1.15,\n cache_read_mtok: 0.15,\n output_mtok: 8\n },\n deprecated: true\n },\n {\n id: \"kimi-k2.5\",\n name: \"Kimi K2.5\",\n description: \"Kimi's most versatile model featuring a native multimodal architecture that supports both visual and text input, thinking and non-thinking modes, and dialogue and agent tasks. Supports automatic context caching, ToolCalls, JSON Mode, Partial Mode, and internet search.\",\n match: {\n starts_with: \"kimi-k2.5\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.1,\n output_mtok: 3\n },\n deprecated: true\n },\n {\n id: \"kimi-k2.6\",\n name: \"Kimi K2.6\",\n description: \"Kimi's most capable model with enhanced long-context coding stability, improved instruction compliance and self-correction capabilities. Native multimodal architecture supporting text, image, and video input, thinking and non-thinking modes, and agent tasks. Supports automatic context caching, ToolCalls, JSON Mode, Partial Mode, and internet search.\",\n match: {\n starts_with: \"kimi-k2.6\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.95,\n cache_read_mtok: 0.16,\n output_mtok: 4\n }\n },\n {\n id: \"kimi-k2.7-code\",\n name: \"Kimi K2.7 Code\",\n description: \"Kimi's most intelligent coding model, capable of completing programming tasks with higher success rates in long context. It features a native multimodal architecture that supports text, image, video input, thinking modes, dialogue, and agent tasks.\",\n match: {\n equals: \"kimi-k2.7-code\"\n },\n context_window: 262144,\n price_comments: \"Ref: https://platform.kimi.ai/docs/pricing/chat-k27-code.md\",\n prices: {\n input_mtok: 0.95,\n cache_read_mtok: 0.19,\n output_mtok: 4\n }\n },\n {\n id: \"kimi-k2.7-code-highspeed\",\n name: \"Kimi K2.7 Code HighSpeed\",\n description: \"High-speed version of Kimi K2.7 Code, with output speed of approximately 180 tokens/sec and up to 260 tokens/sec in short context scenarios.\",\n match: {\n equals: \"kimi-k2.7-code-highspeed\"\n },\n context_window: 262144,\n price_comments: \"Ref: https://platform.kimi.ai/docs/pricing/chat-k27-code.md\",\n prices: {\n input_mtok: 1.9,\n cache_read_mtok: 0.38,\n output_mtok: 8\n }\n },\n {\n id: \"kimi-k3\",\n name: \"Kimi K3\",\n description: \"Kimi's flagship reasoning model with always-on thinking, native multimodal (image and video) input, tool use, and structured output. 2.8 trillion total parameters MoE.\",\n match: {\n equals: \"kimi-k3\"\n },\n context_window: 1048576,\n price_comments: \"Ref: https://platform.kimi.ai/docs/pricing/chat-k3.md\",\n prices: {\n input_mtok: 3,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"moonshot-v1-128k\",\n name: \"Moonshot V1 128K\",\n match: {\n or: [\n {\n equals: \"moonshot-v1-128k\"\n },\n {\n equals: \"moonshot-v1-128k-vision-preview\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 2,\n output_mtok: 5\n },\n deprecated: true\n },\n {\n id: \"moonshot-v1-32k\",\n name: \"Moonshot V1 32K\",\n match: {\n or: [\n {\n equals: \"moonshot-v1-32k\"\n },\n {\n equals: \"moonshot-v1-32k-vision-preview\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 1,\n output_mtok: 3\n },\n deprecated: true\n },\n {\n id: \"moonshot-v1-8k\",\n name: \"Moonshot V1 8K\",\n match: {\n or: [\n {\n equals: \"moonshot-v1-8k\"\n },\n {\n equals: \"moonshot-v1-8k-vision-preview\"\n }\n ]\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.2,\n output_mtok: 2\n },\n deprecated: true\n }\n ]\n },\n {\n id: \"novita\",\n name: \"Novita\",\n pricing_urls: [\"https://novita.ai/pricing\"],\n api_pattern: \"https://api\\\\.novita\\\\.ai\",\n models: [\n {\n id: \"Sao10K/L3-8B-Stheno-v3.2\",\n match: {\n equals: \"Sao10K/L3-8B-Stheno-v3.2\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.05\n }\n },\n {\n id: \"cognitivecomputations/dolphin-mixtral-8x22b\",\n match: {\n equals: \"cognitivecomputations/dolphin-mixtral-8x22b\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"deepseek/deepseek-r1\",\n match: {\n equals: \"deepseek/deepseek-r1\"\n },\n context_window: 64000,\n prices: {\n input_mtok: 4,\n output_mtok: 4\n }\n },\n {\n id: \"deepseek/deepseek-r1-distill-llama-70b\",\n match: {\n equals: \"deepseek/deepseek-r1-distill-llama-70b\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.8,\n output_mtok: 0.8\n }\n },\n {\n id: \"deepseek/deepseek-r1-distill-llama-8b\",\n match: {\n equals: \"deepseek/deepseek-r1-distill-llama-8b\"\n },\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.04\n }\n },\n {\n id: \"deepseek/deepseek-r1-distill-qwen-14b\",\n match: {\n equals: \"deepseek/deepseek-r1-distill-qwen-14b\"\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.15\n }\n },\n {\n id: \"deepseek/deepseek-r1-distill-qwen-32b\",\n match: {\n equals: \"deepseek/deepseek-r1-distill-qwen-32b\"\n },\n context_window: 64000,\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.3\n }\n },\n {\n id: \"deepseek/deepseek_v3\",\n match: {\n equals: \"deepseek/deepseek_v3\"\n },\n context_window: 64000,\n prices: {\n input_mtok: 0.89,\n output_mtok: 0.89\n }\n },\n {\n id: \"google/gemma-2-9b-it\",\n match: {\n equals: \"google/gemma-2-9b-it\"\n },\n prices: {\n input_mtok: 0.08,\n output_mtok: 0.08\n }\n },\n {\n id: \"gryphe/mythomax-l2-13b\",\n match: {\n equals: \"gryphe/mythomax-l2-13b\"\n },\n context_window: 4096,\n prices: {\n input_mtok: 0.09,\n output_mtok: 0.09\n }\n },\n {\n id: \"jondurbin/airoboros-l2-70b\",\n match: {\n equals: \"jondurbin/airoboros-l2-70b\"\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 0.5\n }\n },\n {\n id: \"meta-llama/llama-3-70b-instruct\",\n match: {\n equals: \"meta-llama/llama-3-70b-instruct\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.51,\n output_mtok: 0.74\n }\n },\n {\n id: \"meta-llama/llama-3-8b-instruct\",\n match: {\n equals: \"meta-llama/llama-3-8b-instruct\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.04\n }\n },\n {\n id: \"meta-llama/llama-3.1-70b-instruct\",\n match: {\n equals: \"meta-llama/llama-3.1-70b-instruct\"\n },\n prices: {\n input_mtok: 0.34,\n output_mtok: 0.39\n }\n },\n {\n id: \"meta-llama/llama-3.1-8b-instruct\",\n match: {\n or: [\n {\n equals: \"meta-llama/llama-3.1-8b-instruct\"\n },\n {\n equals: \"meta-llama/llama-3.1-8b-instruct-max\"\n }\n ]\n },\n context_window: 16384,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.05\n }\n },\n {\n id: \"meta-llama/llama-3.1-8b-instruct-bf16\",\n match: {\n equals: \"meta-llama/llama-3.1-8b-instruct-bf16\"\n },\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.06\n }\n },\n {\n id: \"meta-llama/llama-3.2-11b-vision-instruct\",\n match: {\n equals: \"meta-llama/llama-3.2-11b-vision-instruct\"\n },\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.06\n }\n },\n {\n id: \"meta-llama/llama-3.2-1b-instruct\",\n match: {\n equals: \"meta-llama/llama-3.2-1b-instruct\"\n },\n context_window: 131000,\n prices: {\n input_mtok: 0.02,\n output_mtok: 0.02\n }\n },\n {\n id: \"meta-llama/llama-3.2-3b-instruct\",\n match: {\n equals: \"meta-llama/llama-3.2-3b-instruct\"\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.03,\n output_mtok: 0.05\n }\n },\n {\n id: \"meta-llama/llama-3.3-70b-instruct\",\n match: {\n equals: \"meta-llama/llama-3.3-70b-instruct\"\n },\n context_window: 12288,\n prices: {\n input_mtok: 0.39,\n output_mtok: 0.39\n }\n },\n {\n id: \"microsoft/wizardlm-2-8x22b\",\n match: {\n equals: \"microsoft/wizardlm-2-8x22b\"\n },\n context_window: 65535,\n prices: {\n input_mtok: 0.62,\n output_mtok: 0.62\n }\n },\n {\n id: \"mistralai/mistral-7b-instruct\",\n match: {\n equals: \"mistralai/mistral-7b-instruct\"\n },\n prices: {\n input_mtok: 0.059,\n output_mtok: 0.059\n }\n },\n {\n id: \"mistralai/mistral-nemo\",\n match: {\n equals: \"mistralai/mistral-nemo\"\n },\n context_window: 60288,\n prices: {\n input_mtok: 0.17,\n output_mtok: 0.17\n }\n },\n {\n id: \"nousresearch/hermes-2-pro-llama-3-8b\",\n match: {\n equals: \"nousresearch/hermes-2-pro-llama-3-8b\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.14,\n output_mtok: 0.14\n }\n },\n {\n id: \"nousresearch/nous-hermes-llama2-13b\",\n match: {\n equals: \"nousresearch/nous-hermes-llama2-13b\"\n },\n context_window: 4096,\n prices: {\n input_mtok: 0.17,\n output_mtok: 0.17\n }\n },\n {\n id: \"openchat/openchat-7b\",\n match: {\n equals: \"openchat/openchat-7b\"\n },\n context_window: 4096,\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.06\n }\n },\n {\n id: \"qwen/qwen-2-7b-instruct\",\n match: {\n equals: \"qwen/qwen-2-7b-instruct\"\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.054,\n output_mtok: 0.054\n }\n },\n {\n id: \"qwen/qwen-2-vl-72b-instruct\",\n match: {\n equals: \"qwen/qwen-2-vl-72b-instruct\"\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.45,\n output_mtok: 0.45\n }\n },\n {\n id: \"qwen/qwen-2.5-72b-instruct\",\n match: {\n equals: \"qwen/qwen-2.5-72b-instruct\"\n },\n context_window: 32000,\n prices: {\n input_mtok: 0.38,\n output_mtok: 0.4\n }\n },\n {\n id: \"sao10k/l3-70b-euryale-v2.1\",\n match: {\n equals: \"sao10k/l3-70b-euryale-v2.1\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 1.48,\n output_mtok: 1.48\n }\n },\n {\n id: \"sao10k/l3-8b-lunaris\",\n match: {\n equals: \"sao10k/l3-8b-lunaris\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.05\n }\n },\n {\n id: \"sao10k/l31-70b-euryale-v2.2\",\n match: {\n equals: \"sao10k/l31-70b-euryale-v2.2\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 1.48,\n output_mtok: 1.48\n }\n },\n {\n id: \"sophosympatheia/midnight-rose-70b\",\n match: {\n equals: \"sophosympatheia/midnight-rose-70b\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 0.8\n }\n },\n {\n id: \"teknium/openhermes-2.5-mistral-7b\",\n match: {\n equals: \"teknium/openhermes-2.5-mistral-7b\"\n },\n context_window: 4096,\n prices: {\n input_mtok: 0.17,\n output_mtok: 0.17\n }\n }\n ]\n },\n {\n id: \"openai\",\n name: \"OpenAI\",\n pricing_urls: [\n \"https://developers.openai.com/api/docs/pricing\",\n \"https://developers.openai.com/api/docs/guides/prompt-caching\",\n \"https://platform.openai.com/docs/pricing\",\n \"https://openai.com/api/pricing/\",\n \"https://platform.openai.com/docs/models\",\n \"https://help.openai.com/en/articles/7127956-how-much-does-gpt-4-cost\"\n ],\n api_pattern: \"https://api\\\\.openai\\\\.com\",\n model_match: {\n or: [\n {\n starts_with: \"gpt-\"\n },\n {\n equals: \"whisper-1\"\n },\n {\n regex: \"^o[134]\"\n }\n ]\n },\n provider_match: {\n contains: \"openai\"\n },\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"responses\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"input_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"input_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"output_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"realtime\",\n root: [\"response\", \"usage\"],\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"text_tokens\"],\n dest: \"input_text_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"image_tokens\"],\n dest: \"input_image_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"cached_tokens_details\", \"text_tokens\"],\n dest: \"cache_text_read_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"cached_tokens_details\", \"audio_tokens\"],\n dest: \"cache_audio_read_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"cached_tokens_details\", \"image_tokens\"],\n dest: \"cache_image_read_tokens\",\n required: false\n },\n {\n path: [\"output_token_details\", \"text_tokens\"],\n dest: \"output_text_tokens\",\n required: false\n },\n {\n path: [\"output_token_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: false\n }\n ]\n },\n {\n api_flavor: \"images\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"input_tokens_details\", \"text_tokens\"],\n dest: \"input_text_tokens\",\n required: true\n },\n {\n path: [\"input_tokens_details\", \"image_tokens\"],\n dest: \"input_image_tokens\",\n required: true\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n },\n {\n path: [\"output_tokens_details\", \"text_tokens\"],\n dest: \"output_text_tokens\",\n required: false\n },\n {\n path: [\"output_tokens_details\", \"image_tokens\"],\n dest: \"output_image_tokens\",\n required: false\n }\n ]\n },\n {\n api_flavor: \"embeddings\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"live\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"seconds\",\n dest: \"audio_seconds\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"ada\",\n match: {\n or: [\n {\n equals: \"ada\"\n },\n {\n equals: \"text-ada-001\"\n }\n ]\n },\n prices: {\n input_mtok: 0.4,\n output_mtok: 0.4\n }\n },\n {\n id: \"babbage\",\n match: {\n equals: \"babbage\"\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 0.5\n }\n },\n {\n id: \"chatgpt-4o-latest\",\n name: \"ChatGPT-4o\",\n description: \"OpenAI ChatGPT 4o is continually updated by OpenAI to point to the current version of GPT-4o used by ChatGPT. It therefore differs slightly from the API version of GPT-4o in that it has additional RLHF. It is intended for research and evaluation.\",\n match: {\n equals: \"chatgpt-4o-latest\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 5,\n output_mtok: 15\n }\n },\n {\n id: \"codex-mini\",\n name: \"Codex Mini\",\n description: \"codex-mini-latest is a fine-tuned version of o4-mini specifically for use in Codex CLI. For direct use in the API, we recommend starting with gpt-4.1.\",\n match: {\n or: [\n {\n equals: \"codex-mini\"\n },\n {\n equals: \"codex-mini-latest\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 1.5,\n cache_read_mtok: 0.375,\n output_mtok: 6\n }\n },\n {\n id: \"computer-use\",\n name: \"Computer use\",\n match: {\n starts_with: \"computer-use\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 3,\n output_mtok: 12\n }\n },\n {\n id: \"curie\",\n match: {\n or: [\n {\n equals: \"curie\"\n },\n {\n equals: \"text-curie-001\"\n }\n ]\n },\n prices: {\n input_mtok: 2,\n output_mtok: 2\n }\n },\n {\n id: \"davinci\",\n match: {\n or: [\n {\n equals: \"davinci\"\n },\n {\n equals: \"text-davinci-001\"\n }\n ]\n },\n prices: {\n input_mtok: 20,\n output_mtok: 20\n }\n },\n {\n id: \"ft:gpt-3.5-turbo-\",\n description: \"GPT-3.5 Turbo fine tuned.\",\n match: {\n starts_with: \"ft:gpt-3.5-turbo\"\n },\n price_comments: \"Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 3,\n output_mtok: 6\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 1.5,\n output_mtok: 3\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"ft:gpt-4o\",\n description: \"GPT-4o fine tuned.\",\n match: {\n starts_with: \"ft:gpt-4o-2024-\"\n },\n price_comments: \"No batch variant: on 2026-10-14 the pricing page listed batch rates of $2.225/$12.50, which are not the documented 50% discount and could not be confirmed. https://developers.openai.com/api/docs/pricing\",\n prices: {\n input_mtok: 3.75,\n output_mtok: 15\n }\n },\n {\n id: \"ft:gpt-4o-mini\",\n description: \"GPT-4o Mini fine tuned.\",\n match: {\n starts_with: \"ft:gpt-4o-mini-2024-\"\n },\n price_comments: \"Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 0.3,\n output_mtok: 1.2\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-3.5-0301\",\n match: {\n or: [\n {\n equals: \"gpt-3.5-turbo-0301\"\n },\n {\n equals: \"gpt-3.5-0301\"\n }\n ]\n },\n prices: {\n input_mtok: 1.5,\n output_mtok: 2\n }\n },\n {\n id: \"gpt-3.5-turbo\",\n name: \"gpt 3.5 turbo\",\n description: \"GPT-3.5 Turbo offers a balance between cost and performance.\",\n match: {\n or: [\n {\n equals: \"gpt-3.5-turbo\"\n },\n {\n equals: \"gpt-35-turbo\"\n },\n {\n equals: \"gpt-3.5-turbo-0125\"\n }\n ]\n },\n context_window: 16385,\n price_comments: \"Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.5\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.25,\n output_mtok: 0.75\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-3.5-turbo-0613\",\n match: {\n equals: \"gpt-3.5-turbo-0613\"\n },\n context_window: 16385,\n prices: {\n input_mtok: 1.5,\n output_mtok: 2\n }\n },\n {\n id: \"gpt-3.5-turbo-1106\",\n match: {\n equals: \"gpt-3.5-turbo-1106\"\n },\n context_window: 16385,\n price_comments: \"Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. The Batch table lists the same rates as standard.\",\n prices: {\n input_mtok: 1,\n output_mtok: 2\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {},\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-3.5-turbo-16k\",\n name: \"GPT-3.5 Turbo 16k\",\n description: \"This model offers four times the context length of gpt-3.5-turbo, allowing it to support approximately 20 pages of text in a single request at a higher cost. Training data: up to Sep 2021.\",\n match: {\n or: [\n {\n equals: \"gpt-3.5-turbo-16k\"\n },\n {\n equals: \"gpt-3.5-turbo-16k-0613\"\n },\n {\n equals: \"gpt-35-turbo-16k-0613\"\n },\n {\n equals: \"gpt-35-turbo-16k\"\n }\n ]\n },\n context_window: 16385,\n prices: {\n input_mtok: 3,\n output_mtok: 4\n }\n },\n {\n id: \"gpt-3.5-turbo-instruct\",\n name: \"gpt 3.5 turbo instruct\",\n description: \"GPT-3.5 Turbo offers a balance between cost and performance.\",\n match: {\n or: [\n {\n starts_with: \"gpt-3.5-turbo-instruct\"\n },\n {\n equals: \"gpt-3.5-turbo-instruct-0914\"\n }\n ]\n },\n context_window: 16385,\n prices: {\n input_mtok: 1.5,\n output_mtok: 2\n }\n },\n {\n id: \"gpt-4\",\n name: \"gpt 4\",\n description: \"GPT-4 is the latest and most advanced model in the GPT series, demonstrating sophisticated capabilities in complex reasoning, theory of mind, and narrative understanding.\",\n match: {\n or: [\n {\n equals: \"gpt-4\"\n },\n {\n equals: \"gpt-4-0314\"\n },\n {\n equals: \"gpt-4-0613\"\n },\n {\n starts_with: \"ft:gpt-4-0\"\n }\n ]\n },\n context_window: 8192,\n price_comments: \"Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 30,\n output_mtok: 60\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 15,\n output_mtok: 30\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-4-32k\",\n name: \"gpt 4\",\n description: \"GPT-4 is the latest and most advanced model in the GPT series, demonstrating sophisticated capabilities in complex reasoning, theory of mind, and narrative understanding.\",\n match: {\n or: [\n {\n equals: \"gpt-4-32k\"\n },\n {\n equals: \"gpt-4-32k-0314\"\n },\n {\n equals: \"gpt-4-32k-0613\"\n }\n ]\n },\n context_window: 32000,\n price_comments: \"see https://help.openai.com/en/articles/7127956-how-much-does-gpt-4-cost\",\n prices: {\n input_mtok: 60,\n output_mtok: 120\n }\n },\n {\n id: \"gpt-4-turbo\",\n name: \"gpt 4 turbo\",\n description: \"GPT-4 Turbo offers a balance between cost and performance.\",\n match: {\n or: [\n {\n equals: \"gpt-4-turbo\"\n },\n {\n equals: \"gpt-4-turbo-2024-04-09\"\n },\n {\n equals: \"gpt-4-turbo-0125-preview\"\n },\n {\n equals: \"gpt-4-0125-preview\"\n },\n {\n equals: \"gpt-4-1106-preview\"\n },\n {\n equals: \"gpt-4-turbo-preview\"\n }\n ]\n },\n context_window: 128000,\n price_comments: \"Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 10,\n output_mtok: 30\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 5,\n output_mtok: 15\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-4-vision-preview\",\n name: \"gpt 4 vision\",\n description: \"GPT-4 Vision is a model that offers a balance between cost and performance.\",\n match: {\n or: [\n {\n equals: \"gpt-4-vision-preview\"\n },\n {\n equals: \"gpt-4-1106-vision-preview\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 10,\n output_mtok: 30\n }\n },\n {\n id: \"gpt-4.1\",\n name: \"gpt 4.1\",\n description: \"GPT-4.1 is OpenAI's latest flagship model, offering major improvements in coding, instruction following, and long context understanding with up to 1 million tokens of context.\",\n match: {\n or: [\n {\n equals: \"gpt-4.1\"\n },\n {\n equals: \"gpt-4.1-2025-04-14\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. The Batch table publishes no cached-input rate, so cached input keeps the standard rate. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision.\",\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.5,\n output_mtok: 8,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 3.5,\n cache_read_mtok: 0.875,\n output_mtok: 14\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 3.5,\n cache_read_mtok: 0.875,\n output_mtok: 14\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 4\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-4.1-mini\",\n name: \"gpt 4.1 mini\",\n description: \"GPT-4.1 Mini is a significant leap in small model performance, matching or exceeding GPT-4o in many benchmarks while reducing latency by nearly half and cost by 83%.\",\n match: {\n or: [\n {\n equals: \"gpt-4.1-mini\"\n },\n {\n equals: \"gpt-4.1-mini-2025-04-14\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. The Batch table publishes no cached-input rate, so cached input keeps the standard rate. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision.\",\n prices: {\n input_mtok: 0.4,\n cache_read_mtok: 0.1,\n output_mtok: 1.6,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 0.7,\n cache_read_mtok: 0.175,\n output_mtok: 2.8\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 0.7,\n cache_read_mtok: 0.175,\n output_mtok: 2.8\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.8\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-4.1-nano\",\n name: \"gpt 4.1 nano\",\n description: \"GPT-4.1 Nano is OpenAI's fastest and cheapest model, delivering exceptional performance for its size with a 1 million token context window, ideal for classification and autocompletion tasks.\",\n match: {\n or: [\n {\n equals: \"gpt-4.1-nano\"\n },\n {\n equals: \"gpt-4.1-nano-2025-04-14\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. The Batch table publishes no cached-input rate, so cached input keeps the standard rate. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision.\",\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.025,\n output_mtok: 0.4\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.05,\n output_mtok: 0.8\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.05,\n output_mtok: 0.8\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.2\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-4.5-preview\",\n name: \"GPT-4.5 (Preview)\",\n description: \"GPT-4.5 (Preview) is a research preview of OpenAI's latest language model, designed to advance capabilities in reasoning, creativity, and multi-turn conversation. It builds on previous iterations with improvements in world knowledge, contextual coherence, and the ability to follow user intent more effectively.\",\n match: {\n starts_with: \"gpt-4.5-preview\"\n },\n prices: {\n input_mtok: 75,\n cache_read_mtok: 37.5,\n output_mtok: 150\n }\n },\n {\n id: \"gpt-4o\",\n name: \"gpt 4o\",\n description: \"GPT-4 Optimized (GPT-4o) is designed for high performance in reasoning, creativity, and technical tasks while maintaining consistent output quality.\",\n match: {\n or: [\n {\n equals: \"gpt-4o\"\n },\n {\n equals: \"gpt-4o-2024-08-06\"\n },\n {\n equals: \"gpt-4o-2024-11-20\"\n }\n ]\n },\n context_window: 128000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. The Batch table publishes no cached-input rate, so cached input keeps the standard rate. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision.\",\n prices: {\n input_mtok: 2.5,\n cache_read_mtok: 1.25,\n output_mtok: 10,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 4.25,\n cache_read_mtok: 2.125,\n output_mtok: 17\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 4.25,\n cache_read_mtok: 2.125,\n output_mtok: 17\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 1.25,\n output_mtok: 5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-4o-2024-05-13\",\n name: \"gpt 4o (2024-05-13)\",\n description: \"Original GPT-4o snapshot, retained at its launch rates, which are higher than later gpt-4o snapshots and have no cached-input discount.\",\n match: {\n equals: \"gpt-4o-2024-05-13\"\n },\n context_window: 128000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision. Split out of the shared gpt-4o entry: OpenAI lists this snapshot separately at $5/$15 with no cached-input price. Ref: https://developers.openai.com/api/docs/pricing.md\",\n prices: {\n input_mtok: 5,\n output_mtok: 15,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 8.75,\n output_mtok: 26.25\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 8.75,\n output_mtok: 26.25\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 2.5,\n output_mtok: 7.5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-4o-audio-preview\",\n name: \"gpt 4o audio preview\",\n description: \"Audio model for gpt-4o\",\n match: {\n starts_with: \"gpt-4o-audio-preview\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 2.5,\n output_mtok: 10\n }\n },\n {\n id: \"gpt-4o-mini\",\n name: \"gpt 4o mini\",\n description: \"GPT-4o Mini is a cost-optimized variant of GPT-4o, designed for high-efficiency processing while maintaining strong performance. It excels in rapid inference and resource-efficient operations, making it ideal for production deployments requiring a balance of cost and capability.\",\n match: {\n or: [\n {\n equals: \"gpt-4o-mini\"\n },\n {\n equals: \"gpt-4o-mini-2024-07-18\"\n },\n {\n equals: \"gpt-4o-mini-search-preview\"\n },\n {\n equals: \"gpt-4o-mini-search-preview-2025-03-11\"\n }\n ]\n },\n context_window: 128000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. The Batch table publishes no cached-input rate, so cached input keeps the standard rate. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision.\",\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.075,\n output_mtok: 0.6,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 0.25,\n cache_read_mtok: 0.125,\n output_mtok: 1\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 0.25,\n cache_read_mtok: 0.125,\n output_mtok: 1\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.075,\n output_mtok: 0.3\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-4o-mini-2024-07-18.ft-\",\n description: \"GPT-4o Mini fine tuned.\",\n match: {\n starts_with: \"gpt-4o-mini-2024-07-18.ft-\"\n },\n price_comments: \"Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 0.3,\n output_mtok: 1.2\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-4o-mini-audio-preview\",\n name: \"gpt 4o mini audio preview\",\n description: \"Audio model for gpt-4o mini\",\n match: {\n starts_with: \"gpt-4o-mini-audio\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n }\n },\n {\n id: \"gpt-4o-mini-realtime-preview\",\n match: {\n starts_with: \"gpt-4o-mini-realtime\"\n },\n context_window: 16000,\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.3,\n output_mtok: 2.4,\n input_audio_mtok: 10,\n cache_audio_read_mtok: 0.3,\n output_audio_mtok: 20\n }\n },\n {\n id: \"gpt-4o-mini-transcribe\",\n match: {\n equals: \"gpt-4o-mini-transcribe\"\n },\n context_window: 16000,\n prices: {\n input_mtok: 1.25,\n output_mtok: 5,\n input_audio_mtok: 3\n }\n },\n {\n id: \"gpt-4o-mini-tts\",\n match: {\n equals: \"gpt-4o-mini-tts\"\n },\n prices: {\n input_mtok: 0.6,\n output_mtok: 12\n }\n },\n {\n id: \"gpt-4o-realtime-preview\",\n match: {\n starts_with: \"gpt-4o-realtime\"\n },\n context_window: 32000,\n prices: {\n input_mtok: 5,\n cache_read_mtok: 2.5,\n output_mtok: 20,\n input_audio_mtok: 40,\n cache_audio_read_mtok: 2.5,\n output_audio_mtok: 80\n }\n },\n {\n id: \"gpt-4o-search-preview\",\n name: \"GPT-4o Search Preview\",\n description: \"GPT-4o Search Previewis a specialized model for web search in Chat Completions. It is trained to understand and execute web search queries.\",\n match: {\n or: [\n {\n equals: \"gpt-4o-search-preview\"\n },\n {\n equals: \"gpt-4o-search-preview-2025-03-11\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 2.5,\n output_mtok: 10\n }\n },\n {\n id: \"gpt-4o-transcribe\",\n match: {\n or: [\n {\n equals: \"gpt-4o-transcribe\"\n },\n {\n equals: \"gpt-4o-transcribe-diarize\"\n }\n ]\n },\n context_window: 16000,\n price_comments: \"See https://developers.openai.com/api/docs/models/gpt-4o-transcribe-diarize.\",\n prices: {\n input_mtok: 2.5,\n output_mtok: 10,\n input_audio_mtok: 6\n }\n },\n {\n id: \"gpt-4o:extended\",\n name: \"GPT-4o (extended)\",\n description: `GPT-4o (\"o\" for \"omni\") is OpenAI's latest AI model, supporting both text and image inputs with text outputs. It maintains the intelligence level of GPT-4 Turbo while being twice as fast and 50% more cost-effective. GPT-4o also offers improved performance in processing non-English languages and enhanced visual capabilities.`,\n match: {\n equals: \"gpt-4o:extended\"\n },\n prices: {\n input_mtok: 6,\n output_mtok: 18\n }\n },\n {\n id: \"gpt-5\",\n name: \"GPT-5\",\n description: \"GPT-5 is OpenAI's flagship model for coding, reasoning, and agentic tasks across domains.\",\n match: {\n or: [\n {\n equals: \"gpt-5\"\n },\n {\n equals: \"gpt-5-2025-08-07\"\n },\n {\n equals: \"gpt-5-chat\"\n },\n {\n equals: \"gpt-5-chat-latest\"\n },\n {\n equals: \"gpt-5-codex\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision.\",\n prices: {\n input_mtok: 1.25,\n cache_read_mtok: 0.125,\n output_mtok: 10,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: 0.625,\n cache_read_mtok: 0.0625,\n output_mtok: 5\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 2.5,\n cache_read_mtok: 0.25,\n output_mtok: 20\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 2.5,\n cache_read_mtok: 0.25,\n output_mtok: 20\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.625,\n cache_read_mtok: 0.0625,\n output_mtok: 5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5-image\",\n match: {\n equals: \"gpt-5-image\"\n },\n price_comments: \"Seen on OpenRouter before OpenAI\",\n prices: {\n input_mtok: 10,\n cache_read_mtok: 1.25,\n output_mtok: 10\n }\n },\n {\n id: \"gpt-5-image-mini\",\n match: {\n equals: \"gpt-5-image-mini\"\n },\n price_comments: \"Seen on OpenRouter before OpenAI\",\n prices: {\n input_mtok: 2.5,\n cache_read_mtok: 0.25,\n output_mtok: 2\n }\n },\n {\n id: \"gpt-5-mini\",\n name: \"GPT-5 mini\",\n description: \"GPT-5 mini is a faster, more cost-efficient version of GPT-5. It's great for well-defined tasks and precise prompts.\",\n match: {\n or: [\n {\n equals: \"gpt-5-mini\"\n },\n {\n equals: \"gpt-5-mini-2025-08-07\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision.\",\n prices: {\n input_mtok: 0.25,\n cache_read_mtok: 0.025,\n output_mtok: 2,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: 0.125,\n cache_read_mtok: 0.0125,\n output_mtok: 1\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 0.45,\n cache_read_mtok: 0.045,\n output_mtok: 3.6\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 0.45,\n cache_read_mtok: 0.045,\n output_mtok: 3.6\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.125,\n cache_read_mtok: 0.0125,\n output_mtok: 1\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5-nano\",\n name: \"GPT-5 nano\",\n description: \"GPT-5 Nano is OpenAI's fastest, cheapest version of GPT-5. It's great for summarization and classification tasks.\",\n match: {\n or: [\n {\n equals: \"gpt-5-nano\"\n },\n {\n starts_with: \"gpt-5-nano-\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision.\",\n prices: {\n input_mtok: 0.05,\n cache_read_mtok: 0.005,\n output_mtok: 0.4,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: 0.025,\n cache_read_mtok: 0.0025,\n output_mtok: 0.2\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.025,\n cache_read_mtok: 0.0025,\n output_mtok: 0.2\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5-pro\",\n match: {\n or: [\n {\n equals: \"gpt-5-pro\"\n },\n {\n equals: \"gpt-5-pro-2025-10-06\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 15,\n output_mtok: 120,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 7.5,\n output_mtok: 60\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5.1\",\n name: \"GPT-5.1\",\n description: \"The best model for coding and agentic tasks across industries\",\n match: {\n or: [\n {\n equals: \"gpt-5.1\"\n },\n {\n equals: \"gpt-5.1-2025-11-13\"\n },\n {\n equals: \"gpt-5.1-codex\"\n },\n {\n equals: \"gpt-5.1-codex-max\"\n },\n {\n equals: \"gpt-5.1-chat\"\n },\n {\n equals: \"gpt-5.1-chat-latest\"\n },\n {\n equals: \"gpt-5-1\"\n },\n {\n equals: \"gpt-5-1-2025-11-13\"\n },\n {\n equals: \"gpt-5-1-codex\"\n },\n {\n equals: \"gpt-5-1-codex-max\"\n },\n {\n equals: \"gpt-5-1-chat\"\n },\n {\n equals: \"gpt-5-1-chat-latest\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision.\",\n prices: {\n input_mtok: 1.25,\n cache_read_mtok: 0.125,\n output_mtok: 10,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: 0.625,\n cache_read_mtok: 0.0625,\n output_mtok: 5\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 2.5,\n cache_read_mtok: 0.25,\n output_mtok: 20\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 2.5,\n cache_read_mtok: 0.25,\n output_mtok: 20\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.625,\n cache_read_mtok: 0.0625,\n output_mtok: 5\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5.1-codex-mini\",\n name: \"GPT-5.1 Codex Mini\",\n match: {\n or: [\n {\n equals: \"gpt-5.1-codex-mini\"\n },\n {\n equals: \"gpt-5.1-mini\"\n },\n {\n equals: \"gpt-5-1-codex-mini\"\n },\n {\n equals: \"gpt-5-1-mini\"\n }\n ]\n },\n context_window: 400000,\n prices: {\n input_mtok: 0.25,\n cache_read_mtok: 0.025,\n output_mtok: 2\n }\n },\n {\n id: \"gpt-5.2\",\n name: \"GPT-5.2\",\n description: \"The best model for coding and agentic tasks across industries\",\n match: {\n or: [\n {\n equals: \"gpt-5.2\"\n },\n {\n equals: \"gpt-5.2-2025-12-11\"\n },\n {\n equals: \"gpt-5-2\"\n },\n {\n equals: \"gpt-5-2-2025-12-11\"\n },\n {\n equals: \"gpt-5.2-chat\"\n },\n {\n equals: \"gpt-5.2-chat-latest\"\n },\n {\n equals: \"gpt-5-2-chat\"\n },\n {\n equals: \"gpt-5-2-chat-latest\"\n },\n {\n equals: \"gpt-5.2-codex\"\n },\n {\n equals: \"gpt-5-2-codex\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision.\",\n prices: {\n input_mtok: 1.75,\n cache_read_mtok: 0.175,\n output_mtok: 14,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: 0.875,\n cache_read_mtok: 0.0875,\n output_mtok: 7\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 3.5,\n cache_read_mtok: 0.35,\n output_mtok: 28\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 3.5,\n cache_read_mtok: 0.35,\n output_mtok: 28\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.875,\n cache_read_mtok: 0.0875,\n output_mtok: 7\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5.2-pro\",\n description: \"Version of GPT-5.2 that produces smarter and more precise responses.\",\n match: {\n or: [\n {\n equals: \"gpt-5.2-pro\"\n },\n {\n equals: \"gpt-5.2-pro-2025-12-11\"\n },\n {\n equals: \"gpt-5-2-pro-2025-12-11\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 21,\n output_mtok: 168,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 10.5,\n output_mtok: 84\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5.3\",\n name: \"GPT-5.3 Chat\",\n description: \"GPT-5.3 Instant model used in ChatGPT\",\n match: {\n or: [\n {\n equals: \"gpt-5.3\"\n },\n {\n equals: \"gpt-5-3\"\n },\n {\n equals: \"gpt-5.3-chat\"\n },\n {\n equals: \"gpt-5.3-chat-latest\"\n },\n {\n equals: \"gpt-5-3-chat\"\n },\n {\n equals: \"gpt-5-3-chat-latest\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 1.75,\n cache_read_mtok: 0.175,\n output_mtok: 14\n }\n },\n {\n id: \"gpt-5.3-codex\",\n name: \"GPT-5.3-Codex\",\n description: \"The most capable agentic coding model\",\n match: {\n or: [\n {\n equals: \"gpt-5.3-codex\"\n },\n {\n equals: \"gpt-5-3-codex\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision.\",\n prices: {\n input_mtok: 1.75,\n cache_read_mtok: 0.175,\n output_mtok: 14\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 3.5,\n cache_read_mtok: 0.35,\n output_mtok: 28\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 3.5,\n cache_read_mtok: 0.35,\n output_mtok: 28\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5.4\",\n name: \"GPT-5.4\",\n description: \"OpenAI's most capable model with a 1.05M token context window.\",\n match: {\n or: [\n {\n equals: \"gpt-5.4\"\n },\n {\n equals: \"gpt-5.4-2026-03-05\"\n },\n {\n equals: \"gpt-5-4\"\n },\n {\n equals: \"gpt-5-4-2026-03-05\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Batch cached rates use the published rounded values. Tier history before this verification is not modeled. Flex cached input uses the published $0.13/MTok rate. The long-context rates apply to prompts with more than 272K input tokens. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. Ref: https://developers.openai.com/api/docs/models/gpt-5.4\",\n prices: {\n input_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 272000,\n price: 0.5\n }\n ]\n },\n output_mtok: {\n base: 15,\n tiers: [\n {\n start: 272000,\n price: 22.5\n }\n ]\n },\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 272000,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.13,\n tiers: [\n {\n start: 272000,\n price: 0.25\n }\n ]\n },\n output_mtok: {\n base: 7.5,\n tiers: [\n {\n start: 272000,\n price: 11.25\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 5,\n cache_read_mtok: 0.5,\n output_mtok: 30\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 5,\n cache_read_mtok: 0.5,\n output_mtok: 30\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 272000,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.13,\n tiers: [\n {\n start: 272000,\n price: 0.25\n }\n ]\n },\n output_mtok: {\n base: 7.5,\n tiers: [\n {\n start: 272000,\n price: 11.25\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5.4-image-2\",\n name: \"GPT-5.4 Image 2\",\n description: \"GPT-5.4 Image 2 combines OpenAI's GPT-5.4 model with state-of-the-art image generation capabilities from GPT Image 2. It enables rich multimodal workflows across reasoning, coding, and image generation.\",\n match: {\n equals: \"gpt-5.4-image-2\"\n },\n price_comments: \"Imported from OpenRouter pricing; verify against OpenAI pricing when native API pricing is published.\",\n prices: {\n input_mtok: 8,\n cache_read_mtok: 2,\n output_mtok: 15\n }\n },\n {\n id: \"gpt-5.4-mini\",\n name: \"GPT-5.4 mini\",\n description: \"Our strongest mini model yet for coding, computer use, and subagents.\",\n match: {\n or: [\n {\n equals: \"gpt-5.4-mini\"\n },\n {\n equals: \"gpt-5.4-mini-2026-03-17\"\n },\n {\n equals: \"gpt-5-4-mini\"\n },\n {\n equals: \"gpt-5-4-mini-2026-03-17\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision.\",\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 4.5,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: 0.375,\n cache_read_mtok: 0.0375,\n output_mtok: 2.25\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 1.5,\n cache_read_mtok: 0.15,\n output_mtok: 9\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 1.5,\n cache_read_mtok: 0.15,\n output_mtok: 9\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.375,\n cache_read_mtok: 0.0375,\n output_mtok: 2.25\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5.4-nano\",\n name: \"GPT-5.4 nano\",\n description: \"Our cheapest GPT-5.4-class model for simple high-volume tasks.\",\n match: {\n or: [\n {\n equals: \"gpt-5.4-nano\"\n },\n {\n equals: \"gpt-5.4-nano-2026-03-17\"\n },\n {\n equals: \"gpt-5-4-nano\"\n },\n {\n equals: \"gpt-5-4-nano-2026-03-17\"\n }\n ]\n },\n context_window: 400000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision.\",\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.02,\n output_mtok: 1.25,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.01,\n output_mtok: 0.625\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.01,\n output_mtok: 0.625\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5.4-pro\",\n name: \"GPT-5.4 Pro\",\n description: \"Version of GPT-5.4 that produces smarter and more precise responses.\",\n match: {\n or: [\n {\n equals: \"gpt-5.4-pro\"\n },\n {\n equals: \"gpt-5.4-pro-2026-03-05\"\n },\n {\n equals: \"gpt-5-4-pro\"\n },\n {\n equals: \"gpt-5-4-pro-2026-03-05\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision. The long-context rates apply to prompts with more than 272K input tokens. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. Ref: https://developers.openai.com/api/docs/models/gpt-5.4-pro\",\n prices: {\n input_mtok: {\n base: 30,\n tiers: [\n {\n start: 272000,\n price: 60\n }\n ]\n },\n output_mtok: {\n base: 180,\n tiers: [\n {\n start: 272000,\n price: 270\n }\n ]\n },\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: {\n base: 15,\n tiers: [\n {\n start: 272000,\n price: 30\n }\n ]\n },\n output_mtok: {\n base: 90,\n tiers: [\n {\n start: 272000,\n price: 135\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 15,\n tiers: [\n {\n start: 272000,\n price: 30\n }\n ]\n },\n output_mtok: {\n base: 90,\n tiers: [\n {\n start: 272000,\n price: 135\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5.5\",\n name: \"GPT-5.5\",\n description: \"The best model for coding and agentic tasks across industries\",\n match: {\n or: [\n {\n equals: \"gpt-5.5\"\n },\n {\n equals: \"gpt-5.5-2026-04-23\"\n },\n {\n equals: \"gpt-5.5-2026-04-24\"\n },\n {\n equals: \"gpt-5-5\"\n },\n {\n equals: \"gpt-5-5-2026-04-23\"\n },\n {\n equals: \"gpt-5-5-2026-04-24\"\n },\n {\n equals: \"gpt-5.5-chat\"\n },\n {\n equals: \"gpt-5.5-chat-latest\"\n },\n {\n equals: \"gpt-5-5-chat\"\n },\n {\n equals: \"gpt-5-5-chat-latest\"\n },\n {\n equals: \"gpt-5.5-codex\"\n },\n {\n equals: \"gpt-5-5-codex\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision. Prompts with more than 272K input tokens bill the full request at 2x input and cached input and 1.5x output. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. Ref: https://developers.openai.com/api/docs/models/gpt-5.5\",\n prices: {\n input_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 272000,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 30,\n tiers: [\n {\n start: 272000,\n price: 45\n }\n ]\n },\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 272000,\n price: 0.5\n }\n ]\n },\n output_mtok: {\n base: 15,\n tiers: [\n {\n start: 272000,\n price: 22.5\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 12.5,\n cache_read_mtok: 1.25,\n output_mtok: 75\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 12.5,\n cache_read_mtok: 1.25,\n output_mtok: 75\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 272000,\n price: 0.5\n }\n ]\n },\n output_mtok: {\n base: 15,\n tiers: [\n {\n start: 272000,\n price: 22.5\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5.5-pro\",\n name: \"GPT-5.5 Pro\",\n description: \"Version of GPT-5.5 that produces smarter and more precise responses.\",\n match: {\n or: [\n {\n equals: \"gpt-5.5-pro\"\n },\n {\n equals: \"gpt-5.5-pro-2026-04-23\"\n },\n {\n equals: \"gpt-5-5-pro\"\n },\n {\n equals: \"gpt-5-5-pro-2026-04-23\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. The Batch table publishes only base rates; no long-context Batch surcharge is modeled. Tier history before this verification is not modeled. The table publishes only base Flex rates; no long-context Flex surcharge is modeled. Standard prompts with more than 272K input tokens bill the full request at 2x input and 1.5x output. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. Ref: https://developers.openai.com/api/docs/pricing\",\n prices: {\n input_mtok: {\n base: 30,\n tiers: [\n {\n start: 272000,\n price: 60\n }\n ]\n },\n output_mtok: {\n base: 180,\n tiers: [\n {\n start: 272000,\n price: 270\n }\n ]\n },\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: 15,\n output_mtok: 90\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 15,\n output_mtok: 90\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5.6-luna\",\n name: \"GPT-5.6 Luna\",\n description: \"GPT-5.6 model optimized for cost-sensitive workloads.\",\n match: {\n or: [\n {\n equals: \"gpt-5.6-luna\"\n },\n {\n equals: \"gpt-5-6-luna\"\n },\n {\n regex: \"^gpt-5\\\\.6-luna-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n },\n {\n regex: \"^gpt-5-6-luna-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision. Cache writes are billed at 1.25x the uncached input rate. The 2x input and 1.5x output long-context rates apply to prompts with more than 272K input tokens. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. OpenAI reduced Luna prices by 80% on 2026-07-30. Refs: https://developers.openai.com/api/docs/models/gpt-5.6-luna, https://developers.openai.com/api/docs/changelog\",\n prices: [\n {\n prices: {\n input_mtok: {\n base: 1,\n tiers: [\n {\n start: 272000,\n price: 2\n }\n ]\n },\n cache_write_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 272000,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n output_mtok: {\n base: 6,\n tiers: [\n {\n start: 272000,\n price: 9\n }\n ]\n },\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n }\n },\n {\n constraint: {\n start_date: \"2026-07-30\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n cache_write_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 272000,\n price: 0.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.02,\n tiers: [\n {\n start: 272000,\n price: 0.04\n }\n ]\n },\n output_mtok: {\n base: 1.2,\n tiers: [\n {\n start: 272000,\n price: 1.8\n }\n ]\n },\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n }\n }\n ],\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n cache_write_mtok: {\n base: 0.125,\n tiers: [\n {\n start: 272000,\n price: 0.25\n }\n ]\n },\n cache_read_mtok: {\n base: 0.01,\n tiers: [\n {\n start: 272000,\n price: 0.02\n }\n ]\n },\n output_mtok: {\n base: 0.6,\n tiers: [\n {\n start: 272000,\n price: 0.9\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: {\n base: 0.4,\n tiers: [\n {\n start: 272000,\n price: 0.8\n }\n ]\n },\n cache_write_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 272000,\n price: 1\n }\n ]\n },\n cache_read_mtok: {\n base: 0.04,\n tiers: [\n {\n start: 272000,\n price: 0.08\n }\n ]\n },\n output_mtok: {\n base: 2.4,\n tiers: [\n {\n start: 272000,\n price: 3.6\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: {\n base: 0.4,\n tiers: [\n {\n start: 272000,\n price: 0.8\n }\n ]\n },\n cache_write_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 272000,\n price: 1\n }\n ]\n },\n cache_read_mtok: {\n base: 0.04,\n tiers: [\n {\n start: 272000,\n price: 0.08\n }\n ]\n },\n output_mtok: {\n base: 2.4,\n tiers: [\n {\n start: 272000,\n price: 3.6\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n cache_write_mtok: {\n base: 0.125,\n tiers: [\n {\n start: 272000,\n price: 0.25\n }\n ]\n },\n cache_read_mtok: {\n base: 0.01,\n tiers: [\n {\n start: 272000,\n price: 0.02\n }\n ]\n },\n output_mtok: {\n base: 0.6,\n tiers: [\n {\n start: 272000,\n price: 0.9\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5.6-sol\",\n name: \"GPT-5.6 Sol\",\n description: \"Frontier model for complex professional work.\",\n match: {\n or: [\n {\n equals: \"gpt-5.6-sol\"\n },\n {\n equals: \"gpt-5-6-sol\"\n },\n {\n equals: \"gpt-5.6\"\n },\n {\n equals: \"gpt-5-6\"\n },\n {\n regex: \"^gpt-5\\\\.6-sol-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n },\n {\n regex: \"^gpt-5-6-sol-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision. Cache writes are billed at 1.25x the uncached input rate. The 2x input and 1.5x output long-context rates apply to prompts with more than 272K input tokens. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. OpenAI reduced Sol input prices by 20% and output prices by 33% on 2026-08-21. Refs: https://developers.openai.com/api/docs/models/gpt-5.6-sol, https://developers.openai.com/api/docs/changelog\",\n prices: [\n {\n prices: {\n input_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_write_mtok: {\n base: 6.25,\n tiers: [\n {\n start: 272000,\n price: 12.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 272000,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 30,\n tiers: [\n {\n start: 272000,\n price: 45\n }\n ]\n },\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n }\n },\n {\n constraint: {\n start_date: \"2026-08-21\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: {\n base: 4,\n tiers: [\n {\n start: 272000,\n price: 8\n }\n ]\n },\n cache_write_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_read_mtok: {\n base: 0.4,\n tiers: [\n {\n start: 272000,\n price: 0.8\n }\n ]\n },\n output_mtok: {\n base: 20,\n tiers: [\n {\n start: 272000,\n price: 30\n }\n ]\n },\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n }\n }\n ],\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 15\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: {\n base: 8,\n tiers: [\n {\n start: 272000,\n price: 16\n }\n ]\n },\n cache_write_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 20\n }\n ]\n },\n cache_read_mtok: {\n base: 0.8,\n tiers: [\n {\n start: 272000,\n price: 1.6\n }\n ]\n },\n output_mtok: {\n base: 40,\n tiers: [\n {\n start: 272000,\n price: 60\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: {\n base: 8,\n tiers: [\n {\n start: 272000,\n price: 16\n }\n ]\n },\n cache_write_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 20\n }\n ]\n },\n cache_read_mtok: {\n base: 0.8,\n tiers: [\n {\n start: 272000,\n price: 1.6\n }\n ]\n },\n output_mtok: {\n base: 40,\n tiers: [\n {\n start: 272000,\n price: 60\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 15\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-5.6-terra\",\n name: \"GPT-5.6 Terra\",\n description: \"GPT-5.6 model that balances intelligence and cost.\",\n match: {\n or: [\n {\n equals: \"gpt-5.6-terra\"\n },\n {\n equals: \"gpt-5-6-terra\"\n },\n {\n regex: \"^gpt-5\\\\.6-terra-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n },\n {\n regex: \"^gpt-5-6-terra-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision. Cache writes are billed at 1.25x the uncached input rate. The 2x input and 1.5x output long-context rates apply to prompts with more than 272K input tokens. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. OpenAI reduced Terra prices by 20% on 2026-07-30. Refs: https://developers.openai.com/api/docs/models/gpt-5.6-terra, https://developers.openai.com/api/docs/changelog\",\n prices: [\n {\n prices: {\n input_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_write_mtok: {\n base: 3.125,\n tiers: [\n {\n start: 272000,\n price: 6.25\n }\n ]\n },\n cache_read_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 272000,\n price: 0.5\n }\n ]\n },\n output_mtok: {\n base: 15,\n tiers: [\n {\n start: 272000,\n price: 22.5\n }\n ]\n },\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n }\n },\n {\n constraint: {\n start_date: \"2026-07-30\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 12,\n tiers: [\n {\n start: 272000,\n price: 18\n }\n ]\n },\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n }\n }\n ],\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: {\n base: 1,\n tiers: [\n {\n start: 272000,\n price: 2\n }\n ]\n },\n cache_write_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 272000,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n output_mtok: {\n base: 6,\n tiers: [\n {\n start: 272000,\n price: 9\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: {\n base: 4,\n tiers: [\n {\n start: 272000,\n price: 8\n }\n ]\n },\n cache_write_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_read_mtok: {\n base: 0.4,\n tiers: [\n {\n start: 272000,\n price: 0.8\n }\n ]\n },\n output_mtok: {\n base: 24,\n tiers: [\n {\n start: 272000,\n price: 36\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: {\n base: 4,\n tiers: [\n {\n start: 272000,\n price: 8\n }\n ]\n },\n cache_write_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_read_mtok: {\n base: 0.4,\n tiers: [\n {\n start: 272000,\n price: 0.8\n }\n ]\n },\n output_mtok: {\n base: 24,\n tiers: [\n {\n start: 272000,\n price: 36\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 1,\n tiers: [\n {\n start: 272000,\n price: 2\n }\n ]\n },\n cache_write_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 272000,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n output_mtok: {\n base: 6,\n tiers: [\n {\n start: 272000,\n price: 9\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-6-astra\",\n name: \"GPT-6 Astra\",\n description: \"Our most capable model, built for the hardest end-to-end work.\",\n match: {\n or: [\n {\n equals: \"gpt-6-astra\"\n },\n {\n regex: \"^gpt-6-astra-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision. Cache writes are billed at 1.25x the uncached input rate. The 2x input and 1.5x output long-context rates apply to prompts with more than 272K input tokens. Tier starts use 272000 because the pricing engines select a tier when the token count is greater than start. Refs: https://developers.openai.com/api/docs/models/gpt-6-astra, https://developers.openai.com/api/docs/pricing\",\n prices: {\n input_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 20\n }\n ]\n },\n cache_write_mtok: {\n base: 12.5,\n tiers: [\n {\n start: 272000,\n price: 25\n }\n ]\n },\n cache_read_mtok: {\n base: 1,\n tiers: [\n {\n start: 272000,\n price: 2\n }\n ]\n },\n output_mtok: {\n base: 50,\n tiers: [\n {\n start: 272000,\n price: 75\n }\n ]\n },\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_write_mtok: {\n base: 6.25,\n tiers: [\n {\n start: 272000,\n price: 12.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 272000,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 25,\n tiers: [\n {\n start: 272000,\n price: 37.5\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: {\n base: 20,\n tiers: [\n {\n start: 272000,\n price: 40\n }\n ]\n },\n cache_write_mtok: {\n base: 25,\n tiers: [\n {\n start: 272000,\n price: 50\n }\n ]\n },\n cache_read_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n output_mtok: {\n base: 100,\n tiers: [\n {\n start: 272000,\n price: 150\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: {\n base: 20,\n tiers: [\n {\n start: 272000,\n price: 40\n }\n ]\n },\n cache_write_mtok: {\n base: 25,\n tiers: [\n {\n start: 272000,\n price: 50\n }\n ]\n },\n cache_read_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n output_mtok: {\n base: 100,\n tiers: [\n {\n start: 272000,\n price: 150\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_write_mtok: {\n base: 6.25,\n tiers: [\n {\n start: 272000,\n price: 12.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 272000,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 25,\n tiers: [\n {\n start: 272000,\n price: 37.5\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-6-luna\",\n name: \"GPT-6 Luna\",\n description: \"Efficient model for focused, high-volume tasks.\",\n match: {\n or: [\n {\n equals: \"gpt-6-luna\"\n },\n {\n regex: \"^gpt-6-luna-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision. Cache reads cost 10% of input; cache writes cost 1.25x. Prompts with more than 272K input tokens cost 2x for input and cache tokens and 1.5x for output. Tier starts use 272000 because the pricing engines select a tier when token count exceeds start. Ref: https://developers.openai.com/api/docs/models/gpt-6-luna\",\n prices: {\n input_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n cache_write_mtok: {\n base: 0.125,\n tiers: [\n {\n start: 272000,\n price: 0.25\n }\n ]\n },\n cache_read_mtok: {\n base: 0.01,\n tiers: [\n {\n start: 272000,\n price: 0.02\n }\n ]\n },\n output_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 272000,\n price: 0.75\n }\n ]\n },\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: {\n base: 0.05,\n tiers: [\n {\n start: 272000,\n price: 0.1\n }\n ]\n },\n cache_write_mtok: {\n base: 0.0625,\n tiers: [\n {\n start: 272000,\n price: 0.125\n }\n ]\n },\n cache_read_mtok: {\n base: 0.005,\n tiers: [\n {\n start: 272000,\n price: 0.01\n }\n ]\n },\n output_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 272000,\n price: 0.375\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n cache_write_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 272000,\n price: 0.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.02,\n tiers: [\n {\n start: 272000,\n price: 0.04\n }\n ]\n },\n output_mtok: {\n base: 1,\n tiers: [\n {\n start: 272000,\n price: 1.5\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n cache_write_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 272000,\n price: 0.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.02,\n tiers: [\n {\n start: 272000,\n price: 0.04\n }\n ]\n },\n output_mtok: {\n base: 1,\n tiers: [\n {\n start: 272000,\n price: 1.5\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 0.05,\n tiers: [\n {\n start: 272000,\n price: 0.1\n }\n ]\n },\n cache_write_mtok: {\n base: 0.0625,\n tiers: [\n {\n start: 272000,\n price: 0.125\n }\n ]\n },\n cache_read_mtok: {\n base: 0.005,\n tiers: [\n {\n start: 272000,\n price: 0.01\n }\n ]\n },\n output_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 272000,\n price: 0.375\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-6-sol\",\n name: \"GPT-6 Sol\",\n description: \"Model for complex coding and agentic workflows.\",\n match: {\n or: [\n {\n equals: \"gpt-6-sol\"\n },\n {\n regex: \"^gpt-6-sol-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision. Cache reads cost 10% of input; cache writes cost 1.25x. Prompts with more than 272K input tokens cost 2x for input and cache tokens and 1.5x for output. Tier starts use 272000 because the pricing engines select a tier when token count exceeds start. Ref: https://developers.openai.com/api/docs/models/gpt-6-sol\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 15\n }\n ]\n },\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: {\n base: 1,\n tiers: [\n {\n start: 272000,\n price: 2\n }\n ]\n },\n cache_write_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 272000,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n output_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 7.5\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: {\n base: 4,\n tiers: [\n {\n start: 272000,\n price: 8\n }\n ]\n },\n cache_write_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_read_mtok: {\n base: 0.4,\n tiers: [\n {\n start: 272000,\n price: 0.8\n }\n ]\n },\n output_mtok: {\n base: 20,\n tiers: [\n {\n start: 272000,\n price: 30\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: {\n base: 4,\n tiers: [\n {\n start: 272000,\n price: 8\n }\n ]\n },\n cache_write_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_read_mtok: {\n base: 0.4,\n tiers: [\n {\n start: 272000,\n price: 0.8\n }\n ]\n },\n output_mtok: {\n base: 20,\n tiers: [\n {\n start: 272000,\n price: 30\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 1,\n tiers: [\n {\n start: 272000,\n price: 2\n }\n ]\n },\n cache_write_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 272000,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n output_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 7.5\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-6.1-sol\",\n name: \"GPT-6.1 Sol\",\n description: \"Model for complex coding and professional work at a lower cost than GPT-6 Astra.\",\n match: {\n or: [\n {\n equals: \"gpt-6.1-sol\"\n },\n {\n regex: \"^gpt-6\\\\.1-sol-\\\\d{4}-\\\\d{2}-\\\\d{2}$\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision. Released 2026-09-29. Cache reads cost 5% of input; cache writes cost 1.25x. Prompts with more than 272K input tokens cost 2x for input and cache tokens and 1.5x for output. Tier starts use 272000 because the pricing engines select a tier when token count exceeds start. Refs: https://developers.openai.com/api/docs/models/gpt-6.1-sol, https://developers.openai.com/api/docs/changelog\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n output_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 15\n }\n ]\n },\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: {\n base: 1,\n tiers: [\n {\n start: 272000,\n price: 2\n }\n ]\n },\n cache_write_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 272000,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.05,\n tiers: [\n {\n start: 272000,\n price: 0.1\n }\n ]\n },\n output_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 7.5\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: {\n base: 4,\n tiers: [\n {\n start: 272000,\n price: 8\n }\n ]\n },\n cache_write_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 20,\n tiers: [\n {\n start: 272000,\n price: 30\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: {\n base: 4,\n tiers: [\n {\n start: 272000,\n price: 8\n }\n ]\n },\n cache_write_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 20,\n tiers: [\n {\n start: 272000,\n price: 30\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 1,\n tiers: [\n {\n start: 272000,\n price: 2\n }\n ]\n },\n cache_write_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 272000,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.05,\n tiers: [\n {\n start: 272000,\n price: 0.1\n }\n ]\n },\n output_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 7.5\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-audio\",\n name: \"GPT Audio\",\n description: \"The gpt-audio model is OpenAI's first generally available audio model. The new snapshot features an upgraded decoder for more natural-sounding voices and maintains better voice consistency.\",\n match: {\n or: [\n {\n equals: \"gpt-audio\"\n },\n {\n equals: \"gpt-audio-2025-08-28\"\n },\n {\n equals: \"gpt-audio-1.5\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 2.5,\n output_mtok: 10,\n input_audio_mtok: 32,\n output_audio_mtok: 64\n }\n },\n {\n id: \"gpt-audio-mini\",\n name: \"GPT Audio Mini\",\n description: \"A cost-efficient version of GPT Audio. The new snapshot features an upgraded decoder for more natural sounding voices and maintains better voice consistency.\",\n match: {\n or: [\n {\n equals: \"gpt-audio-mini\"\n },\n {\n equals: \"gpt-audio-mini-2025-10-06\"\n },\n {\n equals: \"gpt-audio-mini-2025-12-15\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.6,\n output_mtok: 2.4,\n input_audio_mtok: 10,\n output_audio_mtok: 20\n }\n },\n {\n id: \"gpt-chat-latest\",\n name: \"GPT Chat Latest\",\n description: \"GPT Chat Latest points to OpenAI's stable API alias `chat-latest` that always resolves to the latest Instant chat model used in ChatGPT.\",\n match: {\n equals: \"gpt-chat-latest\"\n },\n price_comments: \"Imported from OpenRouter pricing; verify against OpenAI pricing when native API pricing is published.\",\n prices: {\n input_mtok: 5,\n cache_read_mtok: 0.5,\n output_mtok: 30\n }\n },\n {\n id: \"gpt-image-1\",\n name: \"GPT Image 1\",\n description: \"OpenAI's previous image generation model.\",\n match: {\n equals: \"gpt-image-1\"\n },\n price_comments: \"See https://developers.openai.com/api/docs/models/gpt-image-1. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Batch cached rates use the published rounded values.\",\n prices: {\n input_mtok: 5,\n cache_read_mtok: 1.25,\n output_mtok: 40,\n input_image_mtok: 10,\n cache_image_read_mtok: 2.5\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 2.5,\n cache_read_mtok: 0.63,\n output_mtok: 20,\n input_image_mtok: 5,\n cache_image_read_mtok: 1.25\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-image-1-mini\",\n name: \"GPT Image 1 Mini\",\n description: \"A cost-efficient image generation model from OpenAI with text input pricing.\",\n match: {\n or: [\n {\n equals: \"gpt-image-1-mini\"\n }\n ]\n },\n price_comments: \"See https://developers.openai.com/api/docs/models/gpt-image-1-mini. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Batch cached rates use the published rounded values.\",\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.2,\n output_mtok: 8,\n input_image_mtok: 2.5,\n cache_image_read_mtok: 0.25\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 1,\n cache_read_mtok: 0.1,\n output_mtok: 4,\n input_image_mtok: 1.25,\n cache_image_read_mtok: 0.13\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-image-1.5\",\n name: \"GPT Image 1.5\",\n description: \"An improved image generation model from OpenAI supporting text input and output pricing.\",\n match: {\n or: [\n {\n equals: \"gpt-image-1.5\"\n },\n {\n equals: \"gpt-image-1.5-2025-12-16\"\n }\n ]\n },\n price_comments: \"See https://developers.openai.com/api/docs/models/gpt-image-1.5. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. Batch cached rates use the published rounded values.\",\n prices: {\n input_mtok: 5,\n cache_read_mtok: 1.25,\n output_mtok: 10,\n input_image_mtok: 8,\n cache_image_read_mtok: 2,\n output_image_mtok: 32\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 2.5,\n cache_read_mtok: 0.63,\n output_mtok: 5,\n input_image_mtok: 4,\n cache_image_read_mtok: 1,\n output_image_mtok: 16\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-image-2\",\n name: \"GPT Image 2\",\n description: \"OpenAI's latest image generation model with text input pricing.\",\n match: {\n or: [\n {\n equals: \"gpt-image-2\"\n },\n {\n equals: \"gpt-image-2-2026-04-21\"\n }\n ]\n },\n price_comments: \"See https://developers.openai.com/api/docs/models/gpt-image-2. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 5,\n cache_read_mtok: 1.25,\n output_mtok: 30,\n input_image_mtok: 8,\n cache_image_read_mtok: 2\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 2.5,\n cache_read_mtok: 0.625,\n output_mtok: 15,\n input_image_mtok: 4,\n cache_image_read_mtok: 1\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-image-2.5-flare\",\n name: \"GPT Image 2.5 Flare\",\n description: \"OpenAI's GPT Image 2.5 model for most applications, faster than GPT Image 2.\",\n match: {\n or: [\n {\n equals: \"gpt-image-2.5-flare\"\n },\n {\n equals: \"gpt-image-2.5-flare-2026-09-08\"\n }\n ]\n },\n price_comments: \"See https://developers.openai.com/api/docs/models/gpt-image-2.5-flare. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 5,\n cache_read_mtok: 1.25,\n output_mtok: 30,\n input_image_mtok: 8,\n cache_image_read_mtok: 2\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 2.5,\n cache_read_mtok: 0.625,\n output_mtok: 15,\n input_image_mtok: 4,\n cache_image_read_mtok: 1\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-image-2.5-sunburst\",\n name: \"GPT Image 2.5 Sunburst\",\n description: \"OpenAI's GPT Image 2.5 model for premium visual work, with tighter control across edits.\",\n match: {\n or: [\n {\n equals: \"gpt-image-2.5-sunburst\"\n },\n {\n equals: \"gpt-image-2.5-sunburst-2026-09-08\"\n }\n ]\n },\n price_comments: \"See https://developers.openai.com/api/docs/models/gpt-image-2.5-sunburst. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 5,\n cache_read_mtok: 1.25,\n output_mtok: 30,\n input_image_mtok: 8,\n cache_image_read_mtok: 2\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 2.5,\n cache_read_mtok: 0.625,\n output_mtok: 15,\n input_image_mtok: 4,\n cache_image_read_mtok: 1\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"gpt-live-1\",\n match: {\n equals: \"gpt-live-1\"\n },\n price_comments: \"Voice sessions are billed per second of session duration, covering both input and output audio. The Responses backend a session delegates to is billed separately, at that model's own prices. See https://developers.openai.com/api/docs/models/gpt-live-1.\",\n prices: {\n audio_hours: 3\n }\n },\n {\n id: \"gpt-oss-120b\",\n name: \"gpt-oss-120b\",\n description: \"gpt-oss-120b is an open-weight, 117B-parameter Mixture-of-Experts (MoE) language model from OpenAI designed for high-reasoning, agentic, and general-purpose production use cases.\",\n match: {\n equals: \"gpt-oss-120b\"\n },\n context_window: 131072,\n price_comments: \"Imported from OpenRouter pricing; verify against OpenAI pricing when native API pricing is published.\",\n prices: {\n input_mtok: 0.039,\n output_mtok: 0.18\n }\n },\n {\n id: \"gpt-oss-20b\",\n name: \"gpt-oss-20b\",\n description: \"gpt-oss-20b is an open-weight 21B parameter model released by OpenAI under the Apache 2.0 license. It uses a Mixture-of-Experts (MoE) architecture with 3.6B active parameters per forward pass.\",\n match: {\n equals: \"gpt-oss-20b\"\n },\n context_window: 131072,\n price_comments: \"Imported from OpenRouter pricing; verify against OpenAI pricing when native API pricing is published.\",\n prices: {\n input_mtok: 0.029,\n output_mtok: 0.14\n }\n },\n {\n id: \"gpt-oss-safeguard-20b\",\n name: \"gpt-oss-safeguard-20b\",\n description: \"gpt-oss-safeguard-20b is a safety reasoning model from OpenAI built upon gpt-oss-20b. This open-weight, 21B-parameter Mixture-of-Experts (MoE) model offers lower latency for safety tasks.\",\n match: {\n equals: \"gpt-oss-safeguard-20b\"\n },\n context_window: 131072,\n price_comments: \"Imported from OpenRouter pricing; verify against OpenAI pricing when native API pricing is published.\",\n prices: {\n input_mtok: 0.075,\n cache_read_mtok: 0.037,\n output_mtok: 0.3\n }\n },\n {\n id: \"gpt-realtime\",\n match: {\n or: [\n {\n equals: \"gpt-realtime\"\n },\n {\n equals: \"gpt-realtime-2025-08-28\"\n },\n {\n equals: \"gpt-realtime-1.5\"\n }\n ]\n },\n context_window: 32000,\n price_comments: \"See https://developers.openai.com/api/docs/models/gpt-realtime.\",\n prices: {\n input_mtok: 4,\n cache_read_mtok: 0.4,\n output_mtok: 16,\n input_audio_mtok: 32,\n cache_audio_read_mtok: 0.4,\n output_audio_mtok: 64,\n input_image_mtok: 5,\n cache_image_read_mtok: 0.5\n }\n },\n {\n id: \"gpt-realtime-2\",\n match: {\n or: [\n {\n equals: \"gpt-realtime-2\"\n },\n {\n equals: \"gpt-realtime-2.1\"\n }\n ]\n },\n context_window: 128000,\n price_comments: \"See https://developers.openai.com/api/docs/models/gpt-realtime-2.1.\",\n prices: {\n input_mtok: 4,\n cache_read_mtok: 0.4,\n output_mtok: 24,\n input_audio_mtok: 32,\n cache_audio_read_mtok: 0.4,\n output_audio_mtok: 64,\n input_image_mtok: 5,\n cache_image_read_mtok: 0.5\n }\n },\n {\n id: \"gpt-realtime-mini\",\n match: {\n or: [\n {\n equals: \"gpt-realtime-mini\"\n },\n {\n equals: \"gpt-realtime-2.1-mini\"\n },\n {\n equals: \"gpt-realtime-mini-2025-12-15\"\n },\n {\n equals: \"gpt-realtime-mini-2025-10-06\"\n }\n ]\n },\n price_comments: \"See https://developers.openai.com/api/docs/models/gpt-realtime-2.1-mini.\",\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.06,\n output_mtok: 2.4,\n input_audio_mtok: 10,\n cache_audio_read_mtok: 0.3,\n output_audio_mtok: 20,\n input_image_mtok: 0.8,\n cache_image_read_mtok: 0.08\n }\n },\n {\n id: \"gpt-transcribe\",\n match: {\n equals: \"gpt-transcribe\"\n },\n price_comments: \"See https://developers.openai.com/api/docs/models/gpt-transcribe.\",\n prices: {\n audio_hours: 0.27,\n input_audio_hours: 0.27\n }\n },\n {\n id: \"moderation\",\n description: \"All OpenAI moderation models and endpoints are free of charge\",\n match: {\n contains: \"moderation\"\n },\n prices: {}\n },\n {\n id: \"o1\",\n name: \"o1\",\n description: \"O1 is a model that offers a balance between cost and performance.\",\n match: {\n or: [\n {\n equals: \"o1\"\n },\n {\n equals: \"o1-2024-12-17\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. The Batch table publishes no cached-input rate, so cached input keeps the standard rate.\",\n prices: {\n input_mtok: 15,\n cache_read_mtok: 7.5,\n output_mtok: 60\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 7.5,\n output_mtok: 30\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"o1-mini\",\n name: \"o1 mini\",\n description: \"O1 Mini is a model that offers a balance between cost and performance.\",\n match: {\n or: [\n {\n equals: \"o1-mini\"\n },\n {\n equals: \"o1-mini-2024-09-12\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 1.1,\n cache_read_mtok: 0.55,\n output_mtok: 4.4\n }\n },\n {\n id: \"o1-preview\",\n name: \"o1 preview\",\n description: \"Preview release of OpenAI's first reasoning model.\",\n match: {\n or: [\n {\n equals: \"o1-preview\"\n },\n {\n equals: \"o1-preview-2024-09-12\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 15,\n cache_read_mtok: 7.5,\n output_mtok: 60\n }\n },\n {\n id: \"o1-pro\",\n name: \"o1-pro\",\n description: \"The o1 series of models are trained with reinforcement learning to think before they answer and perform complex reasoning. The o1-pro model uses more compute to think harder and provide consistently better answers.\",\n match: {\n or: [\n {\n equals: \"o1-pro\"\n },\n {\n equals: \"o1-pro-2025-03-19\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 150,\n output_mtok: 600\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 75,\n output_mtok: 300\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"o3\",\n name: \"o3\",\n description: \"o3 is a well-rounded and powerful model across domains. It sets a new standard for math, science, coding, and visual reasoning tasks. It also excels at technical writing and instruction-following. Use it to think through multi-step problems that involve analysis across text, code, and images. Note that BYOK is required for this model. Set up here: https://openrouter.ai/settings/integrations\",\n match: {\n or: [\n {\n equals: \"o3\"\n },\n {\n equals: \"o3-2025-04-16\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. The Batch table publishes no cached-input rate, so cached input keeps the standard rate. Tier history before this verification is not modeled. Flex cached rates retain half-rate precision.\",\n prices: [\n {\n prices: {\n input_mtok: 10,\n cache_read_mtok: 0.5,\n output_mtok: 40,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n }\n },\n {\n constraint: {\n start_date: \"2025-06-10\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.5,\n output_mtok: 8,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n }\n }\n ],\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: 1,\n cache_read_mtok: 0.25,\n output_mtok: 4\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 3.5,\n cache_read_mtok: 0.875,\n output_mtok: 14\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 3.5,\n cache_read_mtok: 0.875,\n output_mtok: 14\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 4\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"o3-deep-research\",\n match: {\n or: [\n {\n equals: \"o3-deep-research\"\n },\n {\n equals: \"o3-deep-research-2025-06-26\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"Deep research supports web search and Responses file search. Reasoning-model web search (including preview) costs $10/1k calls; Responses file search costs $2.50/1k calls. See https://developers.openai.com/api/docs/guides/deep-research and https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 10,\n cache_read_mtok: 2.5,\n output_mtok: 40,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n }\n },\n {\n id: \"o3-mini\",\n name: \"o3 Mini\",\n description: \"OpenAI o3-mini is a cost-efficient language model optimized for STEM reasoning tasks, particularly excelling in science, mathematics, and coding.\",\n match: {\n or: [\n {\n equals: \"o3-mini\"\n },\n {\n equals: \"o3-mini-2025-01-31\"\n },\n {\n equals: \"o3-mini-high\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. The Batch table publishes no cached-input rate, so cached input keeps the standard rate.\",\n prices: {\n input_mtok: 1.1,\n cache_read_mtok: 0.55,\n output_mtok: 4.4\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.55,\n output_mtok: 2.2\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"o3-pro\",\n name: \"o3 Pro\",\n description: \"The o-series of models are trained with reinforcement learning to think before they answer and perform complex reasoning. The o3-pro model uses more compute to think harder and provide consistently better answers.\",\n match: {\n or: [\n {\n equals: \"o3-pro\"\n },\n {\n equals: \"o3-pro-2025-06-10\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 20,\n output_mtok: 80\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 10,\n output_mtok: 40\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"o4-mini\",\n name: \"o4 Mini High\",\n description: \"OpenAI o4-mini-high is the same model as o4-mini with reasoning_effort set to high.\",\n match: {\n or: [\n {\n equals: \"o4-mini-2025-04-16\"\n },\n {\n equals: \"o4-mini-high\"\n },\n {\n equals: \"o4-mini\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"Flex/priority/fast rates verified on 2026-10-12 at https://developers.openai.com/api/docs/pricing. Batch rates verified on 2026-10-14 at https://developers.openai.com/api/docs/pricing. The Batch table publishes no cached-input rate, so cached input keeps the standard rate. Tier history before this verification is not modeled. Flex cached input uses the published $0.138/MTok rate.\",\n prices: {\n input_mtok: 1.1,\n cache_read_mtok: 0.275,\n output_mtok: 4.4,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n },\n price_variants: [\n {\n when: {\n service_tier: \"default\"\n },\n prices: {}\n },\n {\n when: {\n service_tier: \"flex\"\n },\n prices: {\n input_mtok: 0.55,\n cache_read_mtok: 0.138,\n output_mtok: 2.2\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"priority\"\n },\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.5,\n output_mtok: 8\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n service_tier: \"fast\"\n },\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.5,\n output_mtok: 8\n },\n constraint: {\n start_date: \"2026-10-12\",\n type: \"start_date\"\n }\n },\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: 0.55,\n output_mtok: 2.2\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"o4-mini-deep-research\",\n match: {\n or: [\n {\n equals: \"o4-mini-deep-research\"\n },\n {\n equals: \"o4-mini-deep-research-2025-06-26\"\n }\n ]\n },\n context_window: 200000,\n price_comments: \"Deep research supports web search and Responses file search. Reasoning-model web search (including preview) costs $10/1k calls; Responses file search costs $2.50/1k calls. See https://developers.openai.com/api/docs/guides/deep-research and https://developers.openai.com/api/docs/pricing.\",\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.5,\n output_mtok: 8,\n web_searches_kcount: 10,\n storage_searches_kcount: 2.5\n }\n },\n {\n id: \"text-davinci-002\",\n match: {\n equals: \"text-davinci-002\"\n },\n prices: {\n input_mtok: 20,\n output_mtok: 20\n }\n },\n {\n id: \"text-davinci-003\",\n match: {\n equals: \"text-davinci-003\"\n },\n prices: {\n input_mtok: 20,\n output_mtok: 20\n }\n },\n {\n id: \"text-embedding-3-large\",\n name: \"text embedding 3\",\n description: \"Text Embedding 3 is a model that offers a balance between cost and performance.\",\n match: {\n equals: \"text-embedding-3-large\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.13\n }\n },\n {\n id: \"text-embedding-3-small\",\n name: \"text embedding 3\",\n description: \"Text Embedding 3 is a model that offers a balance between cost and performance.\",\n match: {\n equals: \"text-embedding-3-small\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.02\n }\n },\n {\n id: \"text-embedding-ada-002\",\n name: \"text embedding ada\",\n description: \"Text Embedding Ada is a model that offers a balance between cost and performance.\",\n match: {\n or: [\n {\n equals: \"text-embedding-ada\"\n },\n {\n equals: \"text-embedding-ada-002\"\n },\n {\n equals: \"text-embedding-ada-002-v2\"\n }\n ]\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.1\n }\n },\n {\n id: \"whisper-1\",\n match: {\n equals: \"whisper-1\"\n },\n price_comments: \"See https://developers.openai.com/api/docs/models/whisper-1.\",\n prices: {\n audio_hours: 0.36,\n input_audio_hours: 0.36\n }\n }\n ]\n },\n {\n id: \"openai-decisions\",\n name: \"OpenAI Decisions\",\n pricing_urls: [\n \"https://developers.openai.com/api/docs/guides/decisions\",\n \"https://developers.openai.com/api/reference/resources/decisions\",\n \"https://developers.openai.com/api/docs/models/gpt-6-luna\"\n ],\n api_pattern: \"https://api\\\\.openai\\\\.com\",\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"input_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"input_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"output_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"responses\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"input_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"input_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"output_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"gpt-6-luna\",\n name: \"GPT-6 Luna Decisions\",\n match: {\n equals: \"gpt-6-luna\"\n },\n context_window: 1050000,\n price_comments: \"Decisions input is billed at the standard GPT-6 Luna input rate, with the 2x rate applying above 272K input tokens. Cache subsets are included in input pricing and have no separate charge. Regional-processing +10% premiums are not represented here, consistent with the standard OpenAI provider. Refs: https://developers.openai.com/api/docs/guides/decisions, https://developers.openai.com/api/reference/resources/decisions, https://developers.openai.com/api/docs/models/gpt-6-luna\",\n prices: {\n input_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n }\n }\n }\n ]\n },\n {\n id: \"openrouter\",\n name: \"OpenRouter\",\n pricing_urls: [\"https://openrouter.ai/models\"],\n api_pattern: \"https://(api\\\\.)?openrouter\\\\.ai\",\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"decisions\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"01-ai/yi-large\",\n match: {\n equals: \"01-ai/yi-large\"\n },\n prices: {\n input_mtok: 3,\n output_mtok: 3\n }\n },\n {\n id: \"aetherwiing/mn-starcannon-12b\",\n match: {\n equals: \"aetherwiing/mn-starcannon-12b\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"agentica-org/deepcoder-14b-preview:free\",\n match: {\n equals: \"agentica-org/deepcoder-14b-preview:free\"\n },\n prices: {}\n },\n {\n id: \"ai21/jamba-1-5-large\",\n match: {\n equals: \"ai21/jamba-1-5-large\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 8\n }\n },\n {\n id: \"ai21/jamba-1-5-mini\",\n match: {\n equals: \"ai21/jamba-1-5-mini\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.4\n }\n },\n {\n id: \"ai21/jamba-1.6-large\",\n match: {\n equals: \"ai21/jamba-1.6-large\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 8\n }\n },\n {\n id: \"ai21/jamba-1.6-mini\",\n match: {\n equals: \"ai21/jamba-1.6-mini\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.4\n }\n },\n {\n id: \"ai21/jamba-instruct\",\n match: {\n equals: \"ai21/jamba-instruct\"\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 0.7\n }\n },\n {\n id: \"ai21/jamba-large-1.7\",\n name: \"Jamba Large 1.7\",\n match: {\n equals: \"ai21/jamba-large-1.7\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 8\n }\n },\n {\n id: \"aion-labs/aion-1.0\",\n name: \"Aion-1.0\",\n match: {\n equals: \"aion-labs/aion-1.0\"\n },\n prices: {\n input_mtok: 4,\n output_mtok: 8\n }\n },\n {\n id: \"aion-labs/aion-1.0-mini\",\n name: \"Aion-1.0-Mini\",\n match: {\n equals: \"aion-labs/aion-1.0-mini\"\n },\n prices: {\n input_mtok: 0.7,\n output_mtok: 1.4\n }\n },\n {\n id: \"aion-labs/aion-2.0\",\n name: \"Aion-2.0\",\n match: {\n equals: \"aion-labs/aion-2.0\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.8,\n cache_read_mtok: 0.2,\n output_mtok: 1.6\n }\n },\n {\n id: \"aion-labs/aion-rp-llama-3.1-8b\",\n match: {\n equals: \"aion-labs/aion-rp-llama-3.1-8b\"\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"alfredpros/codellama-7b-instruct-solidity\",\n match: {\n equals: \"alfredpros/codellama-7b-instruct-solidity\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"all-hands/openhands-lm-32b-v0.1\",\n match: {\n equals: \"all-hands/openhands-lm-32b-v0.1\"\n },\n prices: {\n input_mtok: 2.6,\n output_mtok: 3.4\n }\n },\n {\n id: \"allenai/molmo-7b-d:free\",\n match: {\n equals: \"allenai/molmo-7b-d:free\"\n },\n prices: {}\n },\n {\n id: \"allenai/olmo-3-32b-think\",\n name: \"Olmo 3 32B Think\",\n match: {\n equals: \"allenai/olmo-3-32b-think\"\n },\n context_window: 65536,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.5\n }\n },\n {\n id: \"alpindale/goliath-120b\",\n match: {\n equals: \"alpindale/goliath-120b\"\n },\n prices: {\n input_mtok: 6.5625,\n output_mtok: 9.375\n }\n },\n {\n id: \"alpindale/magnum-72b\",\n match: {\n equals: \"alpindale/magnum-72b\"\n },\n prices: {\n input_mtok: 1.5,\n output_mtok: 2.25\n }\n },\n {\n id: \"amazon/nova-2-lite-v1\",\n name: \"Nova 2 Lite\",\n match: {\n equals: \"amazon/nova-2-lite-v1\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.3,\n output_mtok: 2.5\n }\n },\n {\n id: \"amazon/nova-lite-v1\",\n match: {\n equals: \"amazon/nova-lite-v1\"\n },\n context_window: 300000,\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.24\n }\n },\n {\n id: \"amazon/nova-micro-v1\",\n match: {\n equals: \"amazon/nova-micro-v1\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.035,\n output_mtok: 0.14\n }\n },\n {\n id: \"amazon/nova-premier-v1\",\n name: \"Nova Premier 1.0\",\n match: {\n equals: \"amazon/nova-premier-v1\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 2.5,\n cache_read_mtok: 0.625,\n output_mtok: 12.5\n }\n },\n {\n id: \"amazon/nova-pro-v1\",\n match: {\n equals: \"amazon/nova-pro-v1\"\n },\n context_window: 300000,\n prices: {\n input_mtok: 0.8,\n output_mtok: 3.2\n }\n },\n {\n id: \"anthracite-org/magnum-v2-72b\",\n match: {\n equals: \"anthracite-org/magnum-v2-72b\"\n },\n prices: {\n input_mtok: 3,\n output_mtok: 3\n }\n },\n {\n id: \"anthracite-org/magnum-v4-72b\",\n match: {\n equals: \"anthracite-org/magnum-v4-72b\"\n },\n context_window: 32768,\n prices: {\n input_mtok: 1.5,\n output_mtok: 2.25\n }\n },\n {\n id: \"anthropic/claude-2\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-2\"\n },\n {\n equals: \"anthropic/claude-2.0\"\n },\n {\n equals: \"anthropic/claude-2.0:beta\"\n },\n {\n equals: \"anthropic/claude-2.1\"\n },\n {\n equals: \"anthropic/claude-2.1:beta\"\n },\n {\n equals: \"anthropic/claude-2:beta\"\n }\n ]\n },\n prices: {\n input_mtok: 8,\n output_mtok: 24\n }\n },\n {\n id: \"anthropic/claude-3-haiku\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-3-haiku\"\n },\n {\n equals: \"anthropic/claude-3-haiku:beta\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 0.25,\n output_mtok: 1.25\n }\n },\n {\n id: \"anthropic/claude-3-opus\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-3-opus\"\n },\n {\n equals: \"anthropic/claude-3-opus:beta\"\n }\n ]\n },\n prices: {\n input_mtok: 15,\n output_mtok: 75\n }\n },\n {\n id: \"anthropic/claude-3-sonnet\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-3-sonnet\"\n },\n {\n equals: \"anthropic/claude-3-sonnet:beta\"\n }\n ]\n },\n prices: {\n input_mtok: 3,\n output_mtok: 15\n }\n },\n {\n id: \"anthropic/claude-3.5-haiku\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-3.5-haiku\"\n },\n {\n equals: \"anthropic/claude-3.5-haiku-20241022\"\n },\n {\n equals: \"anthropic/claude-3.5-haiku-20241022:beta\"\n },\n {\n equals: \"anthropic/claude-3.5-haiku:beta\"\n }\n ]\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 4\n }\n },\n {\n id: \"anthropic/claude-3.5-sonnet\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-3.5-sonnet\"\n },\n {\n equals: \"anthropic/claude-3.5-sonnet-20240620\"\n },\n {\n equals: \"anthropic/claude-3.5-sonnet-20240620:beta\"\n },\n {\n equals: \"anthropic/claude-3.5-sonnet:beta\"\n }\n ]\n },\n prices: {\n input_mtok: 3,\n output_mtok: 15\n }\n },\n {\n id: \"anthropic/claude-3.7-sonnet\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-3.7-sonnet\"\n },\n {\n equals: \"anthropic/claude-3.7-sonnet:beta\"\n },\n {\n equals: \"anthropic/claude-3.7-sonnet:thinking\"\n }\n ]\n },\n prices: {\n input_mtok: 3,\n output_mtok: 15\n }\n },\n {\n id: \"anthropic/claude-fable-5\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-fable-5\"\n },\n {\n equals: \"anthropic/claude-fable-5:beta\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window (no tiered pricing). Ref: https://platform.claude.com/docs/en/about-claude/pricing#long-context-pricing\",\n prices: {\n input_mtok: 10,\n cache_write_mtok: 12.5,\n cache_read_mtok: 1,\n output_mtok: 50\n }\n },\n {\n id: \"anthropic/claude-fable-5.1\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-fable-5.1\"\n },\n {\n equals: \"anthropic/claude-fable-5.1:beta\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window (no tiered pricing). Cache hits are 0.025x base input (not the usual 0.1x), unique to Fable 5.1 and Mythos 5.1. Ref: https://platform.claude.com/docs/en/about-claude/pricing#model-pricing Cache-read rate confirmed via https://openrouter.ai/api/v1/models\",\n prices: {\n input_mtok: 10,\n cache_write_mtok: 12.5,\n cache_read_mtok: 0.25,\n output_mtok: 50\n }\n },\n {\n id: \"anthropic/claude-haiku-4.5\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-haiku-4.5\"\n },\n {\n equals: \"anthropic/claude-4.5-haiku-20251001\"\n },\n {\n equals: \"anthropic/claude-4.5-haiku-20251001:beta\"\n },\n {\n equals: \"anthropic/claude-haiku-4.5-20251001\"\n },\n {\n equals: \"anthropic/claude-haiku-4.5-20251001:beta\"\n },\n {\n equals: \"anthropic/claude-haiku-4.5:beta\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 1,\n cache_write_mtok: 1.25,\n cache_read_mtok: 0.1,\n output_mtok: 5\n }\n },\n {\n id: \"anthropic/claude-haiku-5.5\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-haiku-5.5\"\n },\n {\n regex: \"^anthropic/claude-haiku-5\\\\.5-\\\\d{8}$\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Prompts over 100,000 tokens bill every token at the long-context rate (OpenRouter min_prompt_tokens override). The response model field resolves to the dated snapshot anthropic/claude-haiku-5.5-20261007. Ref: https://platform.claude.com/docs/en/about-claude/pricing#model-pricing Cache-read and 5m/1h cache-write rates confirmed via https://openrouter.ai/api/v1/models/anthropic/claude-haiku-5.5/endpoints\",\n prices: {\n input_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 1e5,\n price: 0.5\n }\n ]\n },\n cache_write_mtok: {\n base: 0.125,\n tiers: [\n {\n start: 1e5,\n price: 0.625\n }\n ]\n },\n cache_read_mtok: {\n base: 0.01,\n tiers: [\n {\n start: 1e5,\n price: 0.05\n }\n ]\n },\n output_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 1e5,\n price: 2.5\n }\n ]\n },\n cache_write_1h_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 1e5,\n price: 1\n }\n ]\n },\n web_searches_kcount: 10\n }\n },\n {\n id: \"anthropic/claude-haiku-5.5:batch\",\n name: \"Claude Haiku 5.5 Batch\",\n match: {\n equals: \"anthropic/claude-haiku-5.5:batch\"\n },\n context_window: 1e6,\n price_comments: \"OpenRouter's batch route bills input, cache, and output tokens at half the standard rates; web searches retain their $0.01 per-call rate. Ref: https://openrouter.ai/api/v1/models.\",\n prices: {\n input_mtok: {\n base: 0.05,\n tiers: [\n {\n start: 1e5,\n price: 0.25\n }\n ]\n },\n cache_write_mtok: {\n base: 0.0625,\n tiers: [\n {\n start: 1e5,\n price: 0.3125\n }\n ]\n },\n cache_read_mtok: {\n base: 0.005,\n tiers: [\n {\n start: 1e5,\n price: 0.025\n }\n ]\n },\n output_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 1e5,\n price: 1.25\n }\n ]\n },\n cache_write_1h_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 1e5,\n price: 0.5\n }\n ]\n },\n web_searches_kcount: 10\n }\n },\n {\n id: \"anthropic/claude-opus-4\",\n name: \"Claude Opus 4\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-opus-4\"\n },\n {\n equals: \"anthropic/claude-opus-4.1\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 15,\n cache_write_mtok: 18.75,\n cache_read_mtok: 1.5,\n output_mtok: 75\n }\n },\n {\n id: \"anthropic/claude-opus-4.5\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-opus-4.5\"\n },\n {\n equals: \"anthropic/claude-4.5-opus-20251124\"\n },\n {\n equals: \"anthropic/claude-4.5-opus-20251124:beta\"\n },\n {\n equals: \"anthropic/claude-opus-4.5-20251124\"\n },\n {\n equals: \"anthropic/claude-opus-4.5-20251124:beta\"\n },\n {\n equals: \"anthropic/claude-opus-4.5:beta\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25\n }\n },\n {\n id: \"anthropic/claude-opus-4.6\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-opus-4.6\"\n },\n {\n equals: \"anthropic/claude-4.6-opus-20260205\"\n },\n {\n equals: \"anthropic/claude-4.6-opus-20260205:beta\"\n },\n {\n equals: \"anthropic/claude-opus-4.6-20260205\"\n },\n {\n equals: \"anthropic/claude-opus-4.6-20260205:beta\"\n },\n {\n equals: \"anthropic/claude-opus-4.6:beta\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window (no tiered pricing). Ref: https://platform.claude.com/docs/en/about-claude/pricing#long-context-pricing\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25\n }\n },\n {\n id: \"anthropic/claude-opus-4.6-fast\",\n name: \"Claude Opus 4.6 (Fast)\",\n match: {\n equals: \"anthropic/claude-opus-4.6-fast\"\n },\n prices: {\n input_mtok: 30,\n cache_write_mtok: 37.5,\n cache_read_mtok: 3,\n output_mtok: 150\n }\n },\n {\n id: \"anthropic/claude-opus-4.7\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-opus-4.7\"\n },\n {\n equals: \"anthropic/claude-opus-4.7:beta\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window (no tiered pricing). Ref: https://platform.claude.com/docs/en/about-claude/pricing#long-context-pricing\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25\n }\n },\n {\n id: \"anthropic/claude-opus-4.7-fast\",\n name: \"Claude Opus 4.7 (Fast)\",\n match: {\n equals: \"anthropic/claude-opus-4.7-fast\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 30,\n cache_write_mtok: 37.5,\n cache_read_mtok: 3,\n output_mtok: 150\n }\n },\n {\n id: \"anthropic/claude-opus-4.8\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-opus-4.8\"\n },\n {\n equals: \"anthropic/claude-opus-4.8:beta\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window (no tiered pricing). Ref: https://platform.claude.com/docs/en/about-claude/pricing#long-context-pricing\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25\n }\n },\n {\n id: \"anthropic/claude-opus-4.8-fast\",\n name: \"Claude Opus 4.8 (Fast)\",\n match: {\n equals: \"anthropic/claude-opus-4.8-fast\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 10,\n cache_write_mtok: 12.5,\n cache_read_mtok: 1,\n output_mtok: 50\n }\n },\n {\n id: \"anthropic/claude-opus-5\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-opus-5\"\n },\n {\n equals: \"anthropic/claude-opus-5:beta\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window (no tiered pricing). Ref: https://platform.claude.com/docs/en/about-claude/pricing#long-context-pricing\",\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25\n }\n },\n {\n id: \"anthropic/claude-opus-5-fast\",\n name: \"Claude Opus 5 (Fast)\",\n match: {\n equals: \"anthropic/claude-opus-5-fast\"\n },\n context_window: 1e6,\n price_comments: \"Fast mode premium pricing, applies across the full context window. Ref: https://platform.claude.com/docs/en/about-claude/pricing#fast-mode-pricing\",\n prices: {\n input_mtok: 10,\n cache_write_mtok: 12.5,\n cache_read_mtok: 1,\n output_mtok: 50\n }\n },\n {\n id: \"anthropic/claude-opus-5.5\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-opus-5.5\"\n },\n {\n equals: \"anthropic/claude-opus-5.5:beta\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window (no tiered pricing). Cache hits are 0.05x base input (not the usual 0.1x), as on Sonnet 5.5. Ref: https://platform.claude.com/docs/en/about-claude/pricing#model-pricing Cache-read rate confirmed via https://openrouter.ai/api/v1/models\",\n prices: {\n input_mtok: 4,\n cache_write_mtok: 5,\n cache_read_mtok: 0.2,\n output_mtok: 20\n }\n },\n {\n id: \"anthropic/claude-sonnet-4\",\n name: \"Claude Sonnet 4\",\n match: {\n equals: \"anthropic/claude-sonnet-4\"\n },\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"anthropic/claude-sonnet-4.5\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-sonnet-4.5\"\n },\n {\n equals: \"anthropic/claude-4.5-sonnet-20250929\"\n },\n {\n equals: \"anthropic/claude-4.5-sonnet-20250929:beta\"\n },\n {\n equals: \"anthropic/claude-sonnet-4.5-20250929\"\n },\n {\n equals: \"anthropic/claude-sonnet-4.5-20250929:beta\"\n },\n {\n equals: \"anthropic/claude-sonnet-4.5:beta\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Tiered pricing: Unlike 4.6 models, Sonnet 4.5 has long-context surcharge. Ref: https://platform.claude.com/docs/en/about-claude/pricing#long-context-pricing\",\n prices: {\n input_mtok: {\n base: 3,\n tiers: [\n {\n start: 200000,\n price: 6\n }\n ]\n },\n cache_write_mtok: {\n base: 3.75,\n tiers: [\n {\n start: 200000,\n price: 7.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.3,\n tiers: [\n {\n start: 200000,\n price: 0.6\n }\n ]\n },\n output_mtok: {\n base: 15,\n tiers: [\n {\n start: 200000,\n price: 22.5\n }\n ]\n }\n }\n },\n {\n id: \"anthropic/claude-sonnet-4.6\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-sonnet-4.6\"\n },\n {\n equals: \"anthropic/claude-4.6-sonnet-20260217\"\n },\n {\n equals: \"anthropic/claude-4.6-sonnet-20260217:beta\"\n },\n {\n equals: \"anthropic/claude-sonnet-4.6-20260217\"\n },\n {\n equals: \"anthropic/claude-sonnet-4.6-20260217:beta\"\n },\n {\n equals: \"anthropic/claude-sonnet-4.6:beta\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across full 1M context window (no tiered pricing). Ref: https://platform.claude.com/docs/en/about-claude/pricing#long-context-pricing\",\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"anthropic/claude-sonnet-5\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-sonnet-5\"\n },\n {\n equals: \"anthropic/claude-sonnet-5:beta\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across the full 1M context window (no tiered pricing). Anthropic made the introductory $2/$10 per MTok rates permanent and cancelled the previously scheduled increase. Refs: https://openrouter.ai/anthropic/claude-sonnet-5, https://platform.claude.com/docs/en/about-claude/pricing\",\n prices: {\n input_mtok: 2,\n cache_write_mtok: 2.5,\n cache_read_mtok: 0.2,\n output_mtok: 10\n }\n },\n {\n id: \"anthropic/claude-sonnet-5.5\",\n match: {\n or: [\n {\n equals: \"anthropic/claude-sonnet-5.5\"\n },\n {\n equals: \"anthropic/claude-sonnet-5.5:beta\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Flat pricing across the full 1M context window (no tiered pricing). Cache hits are 0.05x base input (not the usual 0.1x), as on Opus 5.5. Ref: https://platform.claude.com/docs/en/about-claude/pricing#model-pricing Cache-read rate confirmed via https://openrouter.ai/api/v1/models\",\n prices: {\n input_mtok: 2,\n cache_write_mtok: 2.5,\n cache_read_mtok: 0.1,\n output_mtok: 10\n }\n },\n {\n id: \"anubis-pro-105b-v1\",\n name: \"Anubis Pro 105B V1\",\n match: {\n equals: \"anubis-pro-105b-v1\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1\n }\n },\n {\n id: \"arcee-ai/coder-large\",\n name: \"Coder Large\",\n match: {\n equals: \"arcee-ai/coder-large\"\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 0.8\n }\n },\n {\n id: \"arcee-ai/trinity-large-thinking\",\n name: \"Trinity Large Thinking\",\n match: {\n equals: \"arcee-ai/trinity-large-thinking\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.22,\n cache_read_mtok: 0.06,\n output_mtok: 0.85\n }\n },\n {\n id: \"arcee-ai/trinity-mini\",\n name: \"Trinity Mini\",\n match: {\n equals: \"arcee-ai/trinity-mini\"\n },\n prices: {\n input_mtok: 0.045,\n output_mtok: 0.15\n }\n },\n {\n id: \"arcee-ai/virtuoso-large\",\n name: \"Virtuoso Large\",\n match: {\n equals: \"arcee-ai/virtuoso-large\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.75,\n output_mtok: 1.2\n }\n },\n {\n id: \"arcee-blitz\",\n name: \"Arcee Blitz\",\n match: {\n equals: \"arcee-blitz\"\n },\n prices: {\n input_mtok: 0.45,\n output_mtok: 0.75\n }\n },\n {\n id: \"arliai/qwq-32b-arliai-rpr-v1:free\",\n match: {\n equals: \"arliai/qwq-32b-arliai-rpr-v1:free\"\n },\n prices: {}\n },\n {\n id: \"baidu/ernie-4.5-vl-424b-a47b\",\n name: \"ERNIE 4.5 VL 424B A47B\",\n match: {\n equals: \"baidu/ernie-4.5-vl-424b-a47b\"\n },\n context_window: 123000,\n prices: {\n input_mtok: 0.42,\n output_mtok: 1.25\n }\n },\n {\n id: \"bytedance-research/ui-tars-72b:free\",\n match: {\n equals: \"bytedance-research/ui-tars-72b:free\"\n },\n prices: {}\n },\n {\n id: \"bytedance-seed/seed-1.6\",\n name: \"Seed 1.6\",\n match: {\n equals: \"bytedance-seed/seed-1.6\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.25,\n output_mtok: 2\n }\n },\n {\n id: \"bytedance-seed/seed-1.6-flash\",\n name: \"Seed 1.6 Flash\",\n match: {\n equals: \"bytedance-seed/seed-1.6-flash\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.075,\n output_mtok: 0.3\n }\n },\n {\n id: \"bytedance-seed/seed-2.0-lite\",\n name: \"Seed-2.0-Lite\",\n match: {\n equals: \"bytedance-seed/seed-2.0-lite\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.25,\n output_mtok: 2\n }\n },\n {\n id: \"bytedance-seed/seed-2.0-mini\",\n name: \"Seed-2.0-Mini\",\n match: {\n equals: \"bytedance-seed/seed-2.0-mini\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.4\n }\n },\n {\n id: \"bytedance/ui-tars-1.5-7b\",\n name: \"UI-TARS 7B\",\n match: {\n equals: \"bytedance/ui-tars-1.5-7b\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.2\n }\n },\n {\n id: \"caller-large\",\n name: \"Caller Large\",\n match: {\n equals: \"caller-large\"\n },\n prices: {\n input_mtok: 0.55,\n output_mtok: 0.85\n }\n },\n {\n id: \"chatgpt-4o-latest\",\n name: \"ChatGPT-4o\",\n match: {\n equals: \"chatgpt-4o-latest\"\n },\n prices: {\n input_mtok: 5,\n output_mtok: 15\n }\n },\n {\n id: \"claude-2\",\n name: \"Claude v2\",\n match: {\n or: [\n {\n equals: \"claude-2\"\n },\n {\n equals: \"claude-2.0\"\n },\n {\n equals: \"claude-2.0:beta\"\n },\n {\n equals: \"claude-2.1\"\n },\n {\n equals: \"claude-2.1:beta\"\n },\n {\n equals: \"claude-2:beta\"\n }\n ]\n },\n prices: {\n input_mtok: 8,\n output_mtok: 24\n }\n },\n {\n id: \"claude-3-opus\",\n name: \"Claude 3 Opus\",\n match: {\n or: [\n {\n equals: \"claude-3-opus\"\n },\n {\n equals: \"claude-3-opus:beta\"\n }\n ]\n },\n prices: {\n input_mtok: 15,\n cache_write_mtok: 18.75,\n cache_read_mtok: 1.5,\n output_mtok: 75\n }\n },\n {\n id: \"claude-3-sonnet\",\n name: \"Claude 3 Sonnet\",\n match: {\n or: [\n {\n equals: \"claude-3-sonnet\"\n },\n {\n equals: \"claude-3-sonnet:beta\"\n }\n ]\n },\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"claude-3.5-sonnet\",\n name: \"Claude 3.5 Sonnet\",\n match: {\n or: [\n {\n equals: \"claude-3.5-sonnet\"\n },\n {\n equals: \"claude-3.5-sonnet-20240620\"\n },\n {\n equals: \"claude-3.5-sonnet-20240620:beta\"\n },\n {\n equals: \"claude-3.5-sonnet:beta\"\n }\n ]\n },\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"claude-3.7-sonnet\",\n name: \"Claude 3.7 Sonnet\",\n match: {\n or: [\n {\n equals: \"claude-3.7-sonnet\"\n },\n {\n equals: \"claude-3.7-sonnet:beta\"\n },\n {\n equals: \"claude-3.7-sonnet:thinking\"\n }\n ]\n },\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"cloudflare/clef\",\n name: \"Clef\",\n match: {\n equals: \"cloudflare/clef\"\n },\n context_window: 65536,\n price_comments: \"Input only. https://openrouter.ai/api/v1/models/cloudflare/clef/endpoints\",\n prices: {\n input_mtok: 0.24\n }\n },\n {\n id: \"cloudflare/clef-flash\",\n name: \"Clef Flash\",\n match: {\n equals: \"cloudflare/clef-flash\"\n },\n context_window: 65536,\n price_comments: \"Input only. https://openrouter.ai/api/v1/models/cloudflare/clef-flash/endpoints\",\n prices: {\n input_mtok: 0.09\n }\n },\n {\n id: \"codellama-7b-instruct-solidity\",\n name: \"CodeLLaMa 7B Instruct Solidity\",\n match: {\n equals: \"codellama-7b-instruct-solidity\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"codestral-2501\",\n name: \"Codestral 2501\",\n match: {\n equals: \"codestral-2501\"\n },\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.9\n }\n },\n {\n id: \"codex-mini\",\n name: \"Codex Mini\",\n match: {\n equals: \"codex-mini\"\n },\n prices: {\n input_mtok: 1.5,\n cache_read_mtok: 0.375,\n output_mtok: 6\n }\n },\n {\n id: \"cognitivecomputations/dolphin-mistral-24b-venice-edition:free\",\n name: \"Uncensored (free)\",\n match: {\n equals: \"cognitivecomputations/dolphin-mistral-24b-venice-edition:free\"\n },\n prices: {}\n },\n {\n id: \"cognitivecomputations/dolphin-mixtral-8x22b\",\n match: {\n equals: \"cognitivecomputations/dolphin-mixtral-8x22b\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"cognitivecomputations/dolphin-mixtral-8x7b\",\n match: {\n equals: \"cognitivecomputations/dolphin-mixtral-8x7b\"\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 0.5\n }\n },\n {\n id: \"cognitivecomputations/dolphin3.0-mistral-24b:free\",\n match: {\n equals: \"cognitivecomputations/dolphin3.0-mistral-24b:free\"\n },\n prices: {}\n },\n {\n id: \"cognitivecomputations/dolphin3.0-r1-mistral-24b:free\",\n match: {\n equals: \"cognitivecomputations/dolphin3.0-r1-mistral-24b:free\"\n },\n prices: {}\n },\n {\n id: \"cohere/command\",\n match: {\n equals: \"cohere/command\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 2\n }\n },\n {\n id: \"cohere/command-a\",\n match: {\n equals: \"cohere/command-a\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 2.5,\n output_mtok: 10\n }\n },\n {\n id: \"cohere/command-r\",\n match: {\n or: [\n {\n equals: \"cohere/command-r\"\n },\n {\n equals: \"cohere/command-r-03-2024\"\n }\n ]\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.5\n }\n },\n {\n id: \"cohere/command-r-08-2024\",\n match: {\n equals: \"cohere/command-r-08-2024\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n }\n },\n {\n id: \"cohere/command-r-plus\",\n match: {\n or: [\n {\n equals: \"cohere/command-r-plus\"\n },\n {\n equals: \"cohere/command-r-plus-04-2024\"\n }\n ]\n },\n prices: {\n input_mtok: 3,\n output_mtok: 15\n }\n },\n {\n id: \"cohere/command-r-plus-08-2024\",\n match: {\n equals: \"cohere/command-r-plus-08-2024\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 2.5,\n output_mtok: 10\n }\n },\n {\n id: \"cohere/command-r7b-12-2024\",\n match: {\n equals: \"cohere/command-r7b-12-2024\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.0375,\n output_mtok: 0.15\n }\n },\n {\n id: \"command\",\n name: \"Command\",\n match: {\n equals: \"command\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 2\n }\n },\n {\n id: \"command-r\",\n name: \"Command R\",\n match: {\n or: [\n {\n equals: \"command-r\"\n },\n {\n equals: \"command-r-03-2024\"\n }\n ]\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.5\n }\n },\n {\n id: \"command-r-plus\",\n name: \"Command R+\",\n match: {\n or: [\n {\n equals: \"command-r-plus\"\n },\n {\n equals: \"command-r-plus-04-2024\"\n }\n ]\n },\n prices: {\n input_mtok: 3,\n output_mtok: 15\n }\n },\n {\n id: \"deepcoder-14b-preview:free\",\n name: \"Deepcoder 14B Preview (free)\",\n match: {\n equals: \"deepcoder-14b-preview:free\"\n },\n prices: {}\n },\n {\n id: \"deepcogito/cogito-v2.1-671b\",\n name: \"Cogito v2.1 671B\",\n match: {\n equals: \"deepcogito/cogito-v2.1-671b\"\n },\n prices: {\n input_mtok: 1.25,\n output_mtok: 1.25\n }\n },\n {\n id: \"deephermes-3-llama-3-8b-preview:free\",\n name: \"DeepHermes 3 Llama 3 8B Preview (free)\",\n match: {\n equals: \"deephermes-3-llama-3-8b-preview:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek-chat-v3-0324:free\",\n name: \"DeepSeek V3 0324 (free)\",\n match: {\n equals: \"deepseek-chat-v3-0324:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek-chat:free\",\n name: \"DeepSeek V3 (free)\",\n match: {\n equals: \"deepseek-chat:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek-prover-v2\",\n name: \"DeepSeek Prover V2\",\n match: {\n equals: \"deepseek-prover-v2\"\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 2.18\n }\n },\n {\n id: \"deepseek-r1-0528-qwen3-8b\",\n name: \"Deepseek R1 0528 Qwen3 8B\",\n match: {\n equals: \"deepseek-r1-0528-qwen3-8b\"\n },\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.1\n }\n },\n {\n id: \"deepseek-r1-0528-qwen3-8b:free\",\n name: \"Deepseek R1 0528 Qwen3 8B (free)\",\n match: {\n equals: \"deepseek-r1-0528-qwen3-8b:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek-r1-0528:free\",\n name: \"R1 0528 (free)\",\n match: {\n equals: \"deepseek-r1-0528:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek-r1-distill-llama-70b:free\",\n name: \"R1 Distill Llama 70B (free)\",\n match: {\n equals: \"deepseek-r1-distill-llama-70b:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek-r1-distill-llama-8b\",\n name: \"R1 Distill Llama 8B\",\n match: {\n equals: \"deepseek-r1-distill-llama-8b\"\n },\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.04\n }\n },\n {\n id: \"deepseek-r1-distill-qwen-1.5b\",\n name: \"R1 Distill Qwen 1.5B\",\n match: {\n equals: \"deepseek-r1-distill-qwen-1.5b\"\n },\n prices: {\n input_mtok: 0.18,\n output_mtok: 0.18\n }\n },\n {\n id: \"deepseek-r1-distill-qwen-14b\",\n name: \"R1 Distill Qwen 14B\",\n match: {\n equals: \"deepseek-r1-distill-qwen-14b\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.15\n }\n },\n {\n id: \"deepseek-r1-distill-qwen-14b:free\",\n name: \"R1 Distill Qwen 14B (free)\",\n match: {\n equals: \"deepseek-r1-distill-qwen-14b:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek-r1-distill-qwen-32b:free\",\n name: \"R1 Distill Qwen 32B (free)\",\n match: {\n equals: \"deepseek-r1-distill-qwen-32b:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek-r1-distill-qwen-7b\",\n name: \"R1 Distill Qwen 7B\",\n match: {\n equals: \"deepseek-r1-distill-qwen-7b\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.2\n }\n },\n {\n id: \"deepseek-r1:free\",\n name: \"R1 (free)\",\n match: {\n equals: \"deepseek-r1:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek-r1t-chimera:free\",\n name: \"DeepSeek R1T Chimera (free)\",\n match: {\n equals: \"deepseek-r1t-chimera:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek-v3-base:free\",\n name: \"DeepSeek V3 Base (free)\",\n match: {\n equals: \"deepseek-v3-base:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek/deepseek-chat\",\n name: \"DeepSeek V3\",\n match: {\n equals: \"deepseek/deepseek-chat\"\n },\n prices: {\n input_mtok: 0.38,\n output_mtok: 0.89\n }\n },\n {\n id: \"deepseek/deepseek-chat-v3-0324\",\n name: \"DeepSeek V3 0324\",\n match: {\n equals: \"deepseek/deepseek-chat-v3-0324\"\n },\n context_window: 163840,\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.88\n }\n },\n {\n id: \"deepseek/deepseek-chat-v3-0324:free\",\n match: {\n equals: \"deepseek/deepseek-chat-v3-0324:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek/deepseek-chat-v3.1\",\n name: \"DeepSeek V3.1\",\n match: {\n equals: \"deepseek/deepseek-chat-v3.1\"\n },\n prices: {\n input_mtok: 0.21,\n cache_read_mtok: 0.13,\n output_mtok: 0.79\n }\n },\n {\n id: \"deepseek/deepseek-chat:free\",\n match: {\n equals: \"deepseek/deepseek-chat:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek/deepseek-r1\",\n name: \"R1\",\n match: {\n equals: \"deepseek/deepseek-r1\"\n },\n context_window: 64000,\n prices: {\n input_mtok: 0.45,\n output_mtok: 2.15\n }\n },\n {\n id: \"deepseek/deepseek-r1-0528\",\n name: \"R1 0528\",\n match: {\n equals: \"deepseek/deepseek-r1-0528\"\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 2.15\n }\n },\n {\n id: \"deepseek/deepseek-r1-distill-llama-70b\",\n name: \"R1 Distill Llama 70B\",\n match: {\n equals: \"deepseek/deepseek-r1-distill-llama-70b\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.4\n }\n },\n {\n id: \"deepseek/deepseek-r1-distill-llama-70b:free\",\n match: {\n equals: \"deepseek/deepseek-r1-distill-llama-70b:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek/deepseek-r1-distill-llama-8b\",\n match: {\n equals: \"deepseek/deepseek-r1-distill-llama-8b\"\n },\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.04\n }\n },\n {\n id: \"deepseek/deepseek-r1-distill-qwen-1.5b\",\n match: {\n equals: \"deepseek/deepseek-r1-distill-qwen-1.5b\"\n },\n prices: {\n input_mtok: 0.18,\n output_mtok: 0.18\n }\n },\n {\n id: \"deepseek/deepseek-r1-distill-qwen-14b\",\n match: {\n equals: \"deepseek/deepseek-r1-distill-qwen-14b\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.15\n }\n },\n {\n id: \"deepseek/deepseek-r1-distill-qwen-14b:free\",\n match: {\n equals: \"deepseek/deepseek-r1-distill-qwen-14b:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek/deepseek-r1-distill-qwen-32b\",\n name: \"R1 Distill Qwen 32B\",\n match: {\n equals: \"deepseek/deepseek-r1-distill-qwen-32b\"\n },\n prices: {\n input_mtok: 0.12,\n output_mtok: 0.18\n }\n },\n {\n id: \"deepseek/deepseek-r1-distill-qwen-32b:free\",\n match: {\n equals: \"deepseek/deepseek-r1-distill-qwen-32b:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek/deepseek-r1-zero:free\",\n match: {\n equals: \"deepseek/deepseek-r1-zero:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek/deepseek-r1:free\",\n match: {\n equals: \"deepseek/deepseek-r1:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek/deepseek-v3-base:free\",\n match: {\n equals: \"deepseek/deepseek-v3-base:free\"\n },\n prices: {}\n },\n {\n id: \"deepseek/deepseek-v3.1-terminus\",\n name: \"DeepSeek V3.1 Terminus\",\n match: {\n equals: \"deepseek/deepseek-v3.1-terminus\"\n },\n context_window: 163840,\n prices: {\n input_mtok: 0.23,\n output_mtok: 0.9\n }\n },\n {\n id: \"deepseek/deepseek-v3.2\",\n name: \"DeepSeek V3.2\",\n match: {\n equals: \"deepseek/deepseek-v3.2\"\n },\n prices: {\n input_mtok: 0.2288,\n output_mtok: 0.3432\n }\n },\n {\n id: \"deepseek/deepseek-v3.2-exp\",\n name: \"DeepSeek V3.2 Exp\",\n match: {\n equals: \"deepseek/deepseek-v3.2-exp\"\n },\n context_window: 163840,\n prices: {\n input_mtok: 0.27,\n output_mtok: 0.41\n }\n },\n {\n id: \"deepseek/deepseek-v4-flash\",\n name: \"DeepSeek V4 Flash\",\n match: {\n equals: \"deepseek/deepseek-v4-flash\"\n },\n prices: {\n input_mtok: 0.0983,\n cache_read_mtok: 0.0197,\n output_mtok: 0.1966\n }\n },\n {\n id: \"deepseek/deepseek-v4-pro\",\n name: \"DeepSeek V4 Pro\",\n match: {\n equals: \"deepseek/deepseek-v4-pro\"\n },\n prices: {\n input_mtok: 0.435,\n cache_read_mtok: 0.003625,\n output_mtok: 0.87\n }\n },\n {\n id: \"devstral-small\",\n name: \"Devstral Small\",\n match: {\n equals: \"devstral-small\"\n },\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.12\n }\n },\n {\n id: \"devstral-small:free\",\n name: \"Devstral Small (free)\",\n match: {\n equals: \"devstral-small:free\"\n },\n prices: {}\n },\n {\n id: \"dobby-mini-unhinged-plus-llama-3.1-8b\",\n name: \"Dobby Mini Plus Llama 3.1 8B\",\n match: {\n equals: \"dobby-mini-unhinged-plus-llama-3.1-8b\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"dolphin-mixtral-8x22b\",\n name: \"Dolphin 2.9.2 Mixtral 8x22B \\uD83D\\uDC2C\",\n match: {\n equals: \"dolphin-mixtral-8x22b\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"dolphin3.0-mistral-24b:free\",\n name: \"Dolphin3.0 Mistral 24B (free)\",\n match: {\n equals: \"dolphin3.0-mistral-24b:free\"\n },\n prices: {}\n },\n {\n id: \"dolphin3.0-r1-mistral-24b:free\",\n name: \"Dolphin3.0 R1 Mistral 24B (free)\",\n match: {\n equals: \"dolphin3.0-r1-mistral-24b:free\"\n },\n prices: {}\n },\n {\n id: \"eleutherai/llemma_7b\",\n match: {\n equals: \"eleutherai/llemma_7b\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"essentialai/rnj-1-instruct\",\n name: \"Rnj 1 Instruct\",\n match: {\n equals: \"essentialai/rnj-1-instruct\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.15\n }\n },\n {\n id: \"eva-llama-3.33-70b\",\n name: \"EVA Llama 3.33 70B\",\n match: {\n equals: \"eva-llama-3.33-70b\"\n },\n prices: {\n input_mtok: 4,\n output_mtok: 6\n }\n },\n {\n id: \"eva-qwen-2.5-32b\",\n name: \"EVA Qwen2.5 32B\",\n match: {\n equals: \"eva-qwen-2.5-32b\"\n },\n prices: {\n input_mtok: 2.6,\n output_mtok: 3.4\n }\n },\n {\n id: \"eva-qwen-2.5-72b\",\n name: \"EVA Qwen2.5 72B\",\n match: {\n equals: \"eva-qwen-2.5-72b\"\n },\n prices: {\n input_mtok: 4,\n output_mtok: 6\n }\n },\n {\n id: \"eva-unit-01/eva-llama-3.33-70b\",\n match: {\n equals: \"eva-unit-01/eva-llama-3.33-70b\"\n },\n prices: {\n input_mtok: 4,\n output_mtok: 6\n }\n },\n {\n id: \"eva-unit-01/eva-qwen-2.5-32b\",\n match: {\n equals: \"eva-unit-01/eva-qwen-2.5-32b\"\n },\n prices: {\n input_mtok: 2.6,\n output_mtok: 3.4\n }\n },\n {\n id: \"eva-unit-01/eva-qwen-2.5-72b\",\n match: {\n equals: \"eva-unit-01/eva-qwen-2.5-72b\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 1.2\n }\n },\n {\n id: \"featherless/qwerky-72b:free\",\n match: {\n equals: \"featherless/qwerky-72b:free\"\n },\n prices: {}\n },\n {\n id: \"fimbulvetr-11b-v2\",\n name: \"Fimbulvetr 11B v2\",\n match: {\n equals: \"fimbulvetr-11b-v2\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"gemini-2.0-flash-001\",\n name: \"Gemini 2.0 Flash\",\n match: {\n equals: \"gemini-2.0-flash-001\"\n },\n prices: {\n input_mtok: 0.1,\n cache_write_mtok: 0.1833,\n cache_read_mtok: 0.025,\n output_mtok: 0.4\n }\n },\n {\n id: \"gemini-2.0-flash-exp:free\",\n name: \"Gemini 2.0 Flash Experimental (free)\",\n match: {\n equals: \"gemini-2.0-flash-exp:free\"\n },\n prices: {}\n },\n {\n id: \"gemini-2.0-flash-lite-001\",\n name: \"Gemini 2.0 Flash Lite\",\n match: {\n equals: \"gemini-2.0-flash-lite-001\"\n },\n prices: {\n input_mtok: 0.075,\n output_mtok: 0.3\n }\n },\n {\n id: \"gemini-2.5-flash-lite-preview-06-17\",\n name: \"Gemini 2.5 Flash Lite Preview 06-17\",\n match: {\n equals: \"gemini-2.5-flash-lite-preview-06-17\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.4\n }\n },\n {\n id: \"gemini-2.5-flash-preview\",\n name: \"Gemini 2.5 Flash Preview 04-17\",\n match: {\n or: [\n {\n equals: \"gemini-2.5-flash-preview\"\n },\n {\n equals: \"gemini-2.5-flash-preview-05-20\"\n }\n ]\n },\n prices: {\n input_mtok: 0.15,\n cache_write_mtok: 0.2333,\n cache_read_mtok: 0.0375,\n output_mtok: 0.6\n }\n },\n {\n id: \"gemini-2.5-flash-preview-05-20:thinking\",\n name: \"Gemini 2.5 Flash Preview 05-20 (thinking)\",\n match: {\n equals: \"gemini-2.5-flash-preview-05-20:thinking\"\n },\n prices: {\n input_mtok: 0.15,\n cache_write_mtok: 0.2333,\n cache_read_mtok: 0.0375,\n output_mtok: 3.5\n }\n },\n {\n id: \"gemini-2.5-flash-preview:thinking\",\n name: \"Gemini 2.5 Flash Preview 04-17 (thinking)\",\n match: {\n equals: \"gemini-2.5-flash-preview:thinking\"\n },\n prices: {\n input_mtok: 0.15,\n cache_write_mtok: 0.2333,\n cache_read_mtok: 0.0375,\n output_mtok: 3.5\n }\n },\n {\n id: \"gemini-2.5-pro-exp-03-25\",\n name: \"Gemini 2.5 Pro Experimental\",\n match: {\n equals: \"gemini-2.5-pro-exp-03-25\"\n },\n prices: {}\n },\n {\n id: \"gemini-flash-1.5\",\n name: \"Gemini 1.5 Flash\",\n match: {\n equals: \"gemini-flash-1.5\"\n },\n prices: {\n input_mtok: 0.075,\n cache_write_mtok: 0.1583,\n cache_read_mtok: 0.01875,\n output_mtok: 0.3\n }\n },\n {\n id: \"gemini-flash-1.5-8b\",\n name: \"Gemini 1.5 Flash 8B\",\n match: {\n equals: \"gemini-flash-1.5-8b\"\n },\n prices: {\n input_mtok: 0.0375,\n cache_write_mtok: 0.0583,\n cache_read_mtok: 0.01,\n output_mtok: 0.15\n }\n },\n {\n id: \"gemini-pro-1.5\",\n name: \"Gemini 1.5 Pro\",\n match: {\n equals: \"gemini-pro-1.5\"\n },\n prices: {\n input_mtok: 1.25,\n output_mtok: 5\n }\n },\n {\n id: \"gemma-2-9b-it\",\n name: \"Gemma 2 9B\",\n match: {\n equals: \"gemma-2-9b-it\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"gemma-2-9b-it:free\",\n name: \"Gemma 2 9B (free)\",\n match: {\n equals: \"gemma-2-9b-it:free\"\n },\n prices: {}\n },\n {\n id: \"gemma-3-12b-it:free\",\n name: \"Gemma 3 12B (free)\",\n match: {\n equals: \"gemma-3-12b-it:free\"\n },\n prices: {}\n },\n {\n id: \"gemma-3-27b-it:free\",\n name: \"Gemma 3 27B (free)\",\n match: {\n equals: \"gemma-3-27b-it:free\"\n },\n prices: {}\n },\n {\n id: \"gemma-3-4b-it:free\",\n name: \"Gemma 3 4B (free)\",\n match: {\n equals: \"gemma-3-4b-it:free\"\n },\n prices: {}\n },\n {\n id: \"gemma-3n-e4b-it:free\",\n name: \"Gemma 3n 4B (free)\",\n match: {\n equals: \"gemma-3n-e4b-it:free\"\n },\n prices: {}\n },\n {\n id: \"glm-4-32b\",\n name: \"GLM 4 32B\",\n match: {\n equals: \"glm-4-32b\"\n },\n prices: {\n input_mtok: 0.24,\n output_mtok: 0.24\n }\n },\n {\n id: \"glm-4-32b:free\",\n name: \"GLM 4 32B (free)\",\n match: {\n equals: \"glm-4-32b:free\"\n },\n prices: {}\n },\n {\n id: \"glm-4.5-air:free\",\n name: \"GLM 4.5 Air (free)\",\n match: {\n equals: \"glm-4.5-air:free\"\n },\n prices: {}\n },\n {\n id: \"glm-5v-turbo\",\n name: \"GLM 5V Turbo\",\n match: {\n equals: \"glm-5v-turbo\"\n },\n prices: {\n input_mtok: 1.2,\n cache_read_mtok: 0.24,\n output_mtok: 4\n }\n },\n {\n id: \"glm-z1-32b\",\n name: \"GLM Z1 32B\",\n match: {\n equals: \"glm-z1-32b\"\n },\n prices: {\n input_mtok: 0.24,\n output_mtok: 0.24\n }\n },\n {\n id: \"glm-z1-32b:free\",\n name: \"GLM Z1 32B (free)\",\n match: {\n equals: \"glm-z1-32b:free\"\n },\n prices: {}\n },\n {\n id: \"glm-z1-rumination-32b\",\n name: \"GLM Z1 Rumination 32B\",\n match: {\n equals: \"glm-z1-rumination-32b\"\n },\n prices: {\n input_mtok: 0.24,\n output_mtok: 0.24\n }\n },\n {\n id: \"goliath-120b\",\n name: \"Goliath 120B\",\n match: {\n equals: \"goliath-120b\"\n },\n prices: {\n input_mtok: 10,\n output_mtok: 12.5\n }\n },\n {\n id: \"google/gemini-2.0-flash-001\",\n match: {\n equals: \"google/gemini-2.0-flash-001\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.4\n }\n },\n {\n id: \"google/gemini-2.0-flash-exp:free\",\n match: {\n equals: \"google/gemini-2.0-flash-exp:free\"\n },\n prices: {}\n },\n {\n id: \"google/gemini-2.0-flash-lite-001\",\n match: {\n equals: \"google/gemini-2.0-flash-lite-001\"\n },\n prices: {\n input_mtok: 0.075,\n output_mtok: 0.3\n }\n },\n {\n id: \"google/gemini-2.0-flash-thinking-exp-1219:free\",\n match: {\n equals: \"google/gemini-2.0-flash-thinking-exp-1219:free\"\n },\n prices: {}\n },\n {\n id: \"google/gemini-2.0-flash-thinking-exp:free\",\n match: {\n equals: \"google/gemini-2.0-flash-thinking-exp:free\"\n },\n prices: {}\n },\n {\n id: \"google/gemini-2.5-flash\",\n name: \"Gemini 2.5 Flash\",\n match: {\n equals: \"google/gemini-2.5-flash\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.3,\n cache_write_mtok: 0.3833,\n cache_read_mtok: 0.075,\n output_mtok: 2.5\n }\n },\n {\n id: \"google/gemini-2.5-flash-image\",\n name: \"Nano Banana (Gemini 2.5 Flash Image)\",\n match: {\n equals: \"google/gemini-2.5-flash-image\"\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.3,\n cache_write_mtok: 0.08333333333333334,\n cache_read_mtok: 0.03,\n output_mtok: 2.5\n }\n },\n {\n id: \"google/gemini-2.5-flash-lite\",\n name: \"Gemini 2.5 Flash Lite\",\n match: {\n equals: \"google/gemini-2.5-flash-lite\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.1,\n cache_write_mtok: 0.08333333333333334,\n cache_read_mtok: 0.01,\n output_mtok: 0.4\n }\n },\n {\n id: \"google/gemini-2.5-flash-lite-preview-09-2025\",\n name: \"Gemini 2.5 Flash Lite Preview 09-2025\",\n match: {\n equals: \"google/gemini-2.5-flash-lite-preview-09-2025\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.4\n }\n },\n {\n id: \"google/gemini-2.5-flash-preview\",\n match: {\n equals: \"google/gemini-2.5-flash-preview\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n }\n },\n {\n id: \"google/gemini-2.5-flash-preview-09-2025\",\n name: \"Gemini 2.5 Flash Preview 09-2025\",\n match: {\n equals: \"google/gemini-2.5-flash-preview-09-2025\"\n },\n prices: {\n input_mtok: 0.3,\n cache_write_mtok: 0.383,\n cache_read_mtok: 0.075,\n output_mtok: 2.5\n }\n },\n {\n id: \"google/gemini-2.5-flash-preview:thinking\",\n match: {\n equals: \"google/gemini-2.5-flash-preview:thinking\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 3.5\n }\n },\n {\n id: \"google/gemini-2.5-pro\",\n name: \"Gemini 2.5 Pro\",\n match: {\n or: [\n {\n equals: \"google/gemini-2.5-pro\"\n },\n {\n equals: \"google/gemini-2.5-pro-preview\"\n },\n {\n equals: \"google/gemini-2.5-pro-preview-05-06\"\n }\n ]\n },\n context_window: 1048576,\n prices: {\n input_mtok: 1.25,\n cache_write_mtok: 1.625,\n cache_read_mtok: 0.31,\n output_mtok: 10\n }\n },\n {\n id: \"google/gemini-2.5-pro-exp-03-25:free\",\n match: {\n equals: \"google/gemini-2.5-pro-exp-03-25:free\"\n },\n prices: {}\n },\n {\n id: \"google/gemini-2.5-pro-preview-03-25\",\n match: {\n equals: \"google/gemini-2.5-pro-preview-03-25\"\n },\n prices: {\n input_mtok: 1.25,\n output_mtok: 10\n }\n },\n {\n id: \"google/gemini-3-flash-preview\",\n name: \"Gemini 3 Flash Preview\",\n match: {\n equals: \"google/gemini-3-flash-preview\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.5,\n cache_write_mtok: 0.08333333333333334,\n cache_read_mtok: 0.05,\n output_mtok: 3\n }\n },\n {\n id: \"google/gemini-3-pro-image-preview\",\n name: \"Nano Banana Pro (Gemini 3 Pro Image Preview)\",\n match: {\n equals: \"google/gemini-3-pro-image-preview\"\n },\n context_window: 65536,\n prices: {\n input_mtok: 2,\n cache_write_mtok: 0.375,\n cache_read_mtok: 0.2,\n output_mtok: 12\n }\n },\n {\n id: \"google/gemini-3.1-flash-image-preview\",\n name: \"Nano Banana 2 (Gemini 3.1 Flash Image Preview)\",\n match: {\n equals: \"google/gemini-3.1-flash-image-preview\"\n },\n context_window: 65536,\n prices: {\n input_mtok: 0.5,\n output_mtok: 3\n }\n },\n {\n id: \"google/gemini-3.1-flash-lite\",\n name: \"Gemini 3.1 Flash Lite\",\n match: {\n or: [\n {\n equals: \"google/gemini-3.1-flash-lite\"\n },\n {\n equals: \"google/gemini-3.1-flash-lite-preview\"\n }\n ]\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.25,\n cache_write_mtok: 0.08333333333333334,\n cache_read_mtok: 0.025,\n output_mtok: 1.5\n }\n },\n {\n id: \"google/gemini-3.1-pro-preview\",\n name: \"Gemini 3.1 Pro Preview\",\n match: {\n or: [\n {\n equals: \"google/gemini-3.1-pro-preview\"\n },\n {\n equals: \"google/gemini-3.1-pro-preview-customtools\"\n }\n ]\n },\n context_window: 1048576,\n prices: {\n input_mtok: 2,\n cache_write_mtok: 0.375,\n cache_read_mtok: 0.2,\n output_mtok: 12\n }\n },\n {\n id: \"google/gemini-3.5-flash\",\n name: \"Gemini 3.5 Flash\",\n match: {\n or: [\n {\n equals: \"google/gemini-3.5-flash\"\n },\n {\n regex: \"^google/gemini-3\\\\.5-flash-\\\\d{8}$\"\n }\n ]\n },\n context_window: 1048576,\n prices: {\n input_mtok: 1.5,\n cache_write_mtok: 0.08333333333333334,\n cache_read_mtok: 0.15,\n output_mtok: 9\n }\n },\n {\n id: \"google/gemini-3.8-flash\",\n name: \"Gemini 3.8 Flash\",\n match: {\n or: [\n {\n equals: \"google/gemini-3.8-flash\"\n },\n {\n regex: \"^google/gemini-3\\\\.8-flash-\\\\d{8}$\"\n }\n ]\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.75,\n cache_write_mtok: 0.041666666666666664,\n cache_read_mtok: 0.075,\n output_mtok: 3.75\n }\n },\n {\n id: \"google/gemini-flash-1.5\",\n match: {\n equals: \"google/gemini-flash-1.5\"\n },\n prices: {\n input_mtok: 0.075,\n output_mtok: 0.3\n }\n },\n {\n id: \"google/gemini-flash-1.5-8b\",\n match: {\n equals: \"google/gemini-flash-1.5-8b\"\n },\n prices: {\n input_mtok: 0.0375,\n output_mtok: 0.15\n }\n },\n {\n id: \"google/gemini-flash-1.5-8b-exp\",\n match: {\n equals: \"google/gemini-flash-1.5-8b-exp\"\n },\n prices: {}\n },\n {\n id: \"google/gemini-pro\",\n match: {\n or: [\n {\n equals: \"google/gemini-pro\"\n },\n {\n equals: \"google/gemini-pro-vision\"\n }\n ]\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.5\n }\n },\n {\n id: \"google/gemini-pro-1.5\",\n match: {\n equals: \"google/gemini-pro-1.5\"\n },\n prices: {\n input_mtok: 1.25,\n output_mtok: 5\n }\n },\n {\n id: \"google/gemma-2-27b-it\",\n name: \"Gemma 2 27B\",\n match: {\n equals: \"google/gemma-2-27b-it\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.8,\n output_mtok: 0.8\n }\n },\n {\n id: \"google/gemma-2-9b-it\",\n match: {\n equals: \"google/gemma-2-9b-it\"\n },\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.07\n }\n },\n {\n id: \"google/gemma-2-9b-it:free\",\n match: {\n equals: \"google/gemma-2-9b-it:free\"\n },\n prices: {}\n },\n {\n id: \"google/gemma-3-12b-it\",\n name: \"Gemma 3 12B\",\n match: {\n equals: \"google/gemma-3-12b-it\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.1\n }\n },\n {\n id: \"google/gemma-3-12b-it:free\",\n match: {\n equals: \"google/gemma-3-12b-it:free\"\n },\n prices: {}\n },\n {\n id: \"google/gemma-3-1b-it:free\",\n match: {\n equals: \"google/gemma-3-1b-it:free\"\n },\n prices: {}\n },\n {\n id: \"google/gemma-3-27b-it\",\n name: \"Gemma 3 27B\",\n match: {\n equals: \"google/gemma-3-27b-it\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.2\n }\n },\n {\n id: \"google/gemma-3-27b-it:free\",\n match: {\n equals: \"google/gemma-3-27b-it:free\"\n },\n prices: {}\n },\n {\n id: \"google/gemma-3-4b-it\",\n name: \"Gemma 3 4B\",\n match: {\n equals: \"google/gemma-3-4b-it\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.02,\n output_mtok: 0.04\n }\n },\n {\n id: \"google/gemma-3-4b-it:free\",\n match: {\n equals: \"google/gemma-3-4b-it:free\"\n },\n prices: {}\n },\n {\n id: \"google/gemma-3n-e4b-it\",\n name: \"Gemma 3n 4B\",\n match: {\n equals: \"google/gemma-3n-e4b-it\"\n },\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.12\n }\n },\n {\n id: \"google/gemma-4-26b-a4b-it\",\n name: \"Gemma 4 26B A4B\",\n match: {\n equals: \"google/gemma-4-26b-a4b-it\"\n },\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.33\n }\n },\n {\n id: \"google/gemma-4-26b-a4b-it:free\",\n name: \"Gemma 4 26B A4B (free)\",\n match: {\n equals: \"google/gemma-4-26b-a4b-it:free\"\n },\n context_window: 262144,\n prices: {}\n },\n {\n id: \"google/gemma-4-31b-it\",\n name: \"Gemma 4 31B\",\n match: {\n equals: \"google/gemma-4-31b-it\"\n },\n context_window: 262144,\n price_comments: \"OpenRouter reports the cheapest active endpoint for this model. It was Venice ($0.12/$0.36) when checked on 2026-06-09 and DeepInfra ($0.09/$0.34) on 2026-08-27; OpenRouter publishes no history, so the dated entry starts on the day the lower rate was verified. Ref: https://openrouter.ai/api/v1/models/google/gemma-4-31b-it/endpoints\",\n prices: [\n {\n prices: {\n input_mtok: 0.12,\n cache_read_mtok: 0.09,\n output_mtok: 0.36\n }\n },\n {\n constraint: {\n start_date: \"2026-08-27\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 0.09,\n cache_read_mtok: 0.05,\n output_mtok: 0.34\n }\n }\n ]\n },\n {\n id: \"google/gemma-4-31b-it:free\",\n name: \"Gemma 4 31B (free)\",\n match: {\n equals: \"google/gemma-4-31b-it:free\"\n },\n context_window: 262144,\n prices: {}\n },\n {\n id: \"google/learnlm-1.5-pro-experimental:free\",\n match: {\n equals: \"google/learnlm-1.5-pro-experimental:free\"\n },\n prices: {}\n },\n {\n id: \"google/lyria-3-clip-preview\",\n name: \"Lyria 3 Clip Preview\",\n match: {\n equals: \"google/lyria-3-clip-preview\"\n },\n context_window: 1048576,\n prices: {}\n },\n {\n id: \"google/lyria-3-pro-preview\",\n name: \"Lyria 3 Pro Preview\",\n match: {\n equals: \"google/lyria-3-pro-preview\"\n },\n context_window: 1048576,\n prices: {}\n },\n {\n id: \"google/palm-2-chat-bison\",\n match: {\n or: [\n {\n equals: \"google/palm-2-chat-bison\"\n },\n {\n equals: \"google/palm-2-chat-bison-32k\"\n }\n ]\n },\n prices: {\n input_mtok: 1,\n output_mtok: 2\n }\n },\n {\n id: \"google/palm-2-codechat-bison\",\n match: {\n or: [\n {\n equals: \"google/palm-2-codechat-bison\"\n },\n {\n equals: \"google/palm-2-codechat-bison-32k\"\n }\n ]\n },\n prices: {\n input_mtok: 1,\n output_mtok: 2\n }\n },\n {\n id: \"gpt-3.5-turbo-1106\",\n name: \"GPT-3.5 Turbo 16k (older v1106)\",\n match: {\n equals: \"gpt-3.5-turbo-1106\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 2\n }\n },\n {\n id: \"gpt-4-1106-preview\",\n name: \"GPT-4 Turbo (older v1106)\",\n match: {\n equals: \"gpt-4-1106-preview\"\n },\n prices: {\n input_mtok: 10,\n output_mtok: 30\n }\n },\n {\n id: \"gpt-4.5-preview\",\n name: \"GPT-4.5 (Preview)\",\n match: {\n equals: \"gpt-4.5-preview\"\n },\n prices: {\n input_mtok: 75,\n cache_read_mtok: 37.5,\n output_mtok: 150\n }\n },\n {\n id: \"gpt-4o:extended\",\n name: \"GPT-4o (extended)\",\n match: {\n equals: \"gpt-4o:extended\"\n },\n prices: {\n input_mtok: 6,\n output_mtok: 18\n }\n },\n {\n id: \"grok-2-1212\",\n name: \"Grok 2 1212\",\n match: {\n equals: \"grok-2-1212\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 10\n }\n },\n {\n id: \"grok-2-vision-1212\",\n name: \"Grok 2 Vision 1212\",\n match: {\n equals: \"grok-2-vision-1212\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 10\n }\n },\n {\n id: \"grok-3\",\n name: \"Grok 3\",\n match: {\n or: [\n {\n equals: \"grok-3\"\n },\n {\n equals: \"grok-3-beta\"\n }\n ]\n },\n prices: {\n input_mtok: 3,\n cache_read_mtok: 0.75,\n output_mtok: 15\n }\n },\n {\n id: \"grok-3-mini\",\n name: \"Grok 3 Mini\",\n match: {\n or: [\n {\n equals: \"grok-3-mini\"\n },\n {\n equals: \"grok-3-mini-beta\"\n }\n ]\n },\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.075,\n output_mtok: 0.5\n }\n },\n {\n id: \"grok-beta\",\n name: \"Grok Beta\",\n match: {\n equals: \"grok-beta\"\n },\n prices: {\n input_mtok: 5,\n output_mtok: 15\n }\n },\n {\n id: \"grok-vision-beta\",\n name: \"Grok Vision Beta\",\n match: {\n equals: \"grok-vision-beta\"\n },\n prices: {\n input_mtok: 5,\n output_mtok: 15\n }\n },\n {\n id: \"gryphe/mythomax-l2-13b\",\n match: {\n equals: \"gryphe/mythomax-l2-13b\"\n },\n prices: {\n input_mtok: 0.065,\n output_mtok: 0.065\n }\n },\n {\n id: \"hermes-2-pro-llama-3-8b\",\n name: \"Hermes 2 Pro - Llama-3 8B\",\n match: {\n equals: \"hermes-2-pro-llama-3-8b\"\n },\n prices: {\n input_mtok: 0.025,\n output_mtok: 0.04\n }\n },\n {\n id: \"huggingfaceh4/zephyr-7b-beta:free\",\n match: {\n equals: \"huggingfaceh4/zephyr-7b-beta:free\"\n },\n prices: {}\n },\n {\n id: \"ibm-granite/granite-4.0-h-micro\",\n name: \"Granite 4.0 Micro\",\n match: {\n equals: \"ibm-granite/granite-4.0-h-micro\"\n },\n context_window: 131000,\n prices: {\n input_mtok: 0.017,\n output_mtok: 0.112\n }\n },\n {\n id: \"ibm-granite/granite-4.1-8b\",\n name: \"Granite 4.1 8B\",\n match: {\n equals: \"ibm-granite/granite-4.1-8b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.1\n }\n },\n {\n id: \"inception/mercury-2\",\n name: \"Mercury 2\",\n match: {\n equals: \"inception/mercury-2\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.25,\n cache_read_mtok: 0.025,\n output_mtok: 0.75\n }\n },\n {\n id: \"inclusionai/ling-2.6-1t\",\n name: \"Ling-2.6-1T\",\n match: {\n equals: \"inclusionai/ling-2.6-1t\"\n },\n prices: {\n input_mtok: 0.075,\n cache_read_mtok: 0.015,\n output_mtok: 0.625\n }\n },\n {\n id: \"inclusionai/ling-2.6-flash\",\n name: \"Ling-2.6-flash\",\n match: {\n equals: \"inclusionai/ling-2.6-flash\"\n },\n prices: {\n input_mtok: 0.01,\n cache_read_mtok: 0.002,\n output_mtok: 0.03\n }\n },\n {\n id: \"inclusionai/ring-2.6-1t\",\n name: \"Ring-2.6-1T\",\n match: {\n equals: \"inclusionai/ring-2.6-1t\"\n },\n prices: {\n input_mtok: 0.075,\n cache_read_mtok: 0.015,\n output_mtok: 0.625\n }\n },\n {\n id: \"infermatic/mn-inferor-12b\",\n match: {\n equals: \"infermatic/mn-inferor-12b\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"inflection/inflection-3-pi\",\n name: \"Inflection 3 Pi\",\n match: {\n equals: \"inflection/inflection-3-pi\"\n },\n prices: {\n input_mtok: 2.5,\n output_mtok: 10\n }\n },\n {\n id: \"inflection/inflection-3-productivity\",\n name: \"Inflection 3 Productivity\",\n match: {\n equals: \"inflection/inflection-3-productivity\"\n },\n prices: {\n input_mtok: 2.5,\n output_mtok: 10\n }\n },\n {\n id: \"internvl3-14b:free\",\n name: \"InternVL3 14B (free)\",\n match: {\n equals: \"internvl3-14b:free\"\n },\n prices: {}\n },\n {\n id: \"internvl3-2b:free\",\n name: \"InternVL3 2B (free)\",\n match: {\n equals: \"internvl3-2b:free\"\n },\n prices: {}\n },\n {\n id: \"jamba-1.6-large\",\n name: \"Jamba 1.6 Large\",\n match: {\n equals: \"jamba-1.6-large\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 8\n }\n },\n {\n id: \"jamba-1.6-mini\",\n name: \"Jamba Mini 1.6\",\n match: {\n equals: \"jamba-1.6-mini\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.4\n }\n },\n {\n id: \"jondurbin/airoboros-l2-70b\",\n match: {\n equals: \"jondurbin/airoboros-l2-70b\"\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 0.5\n }\n },\n {\n id: \"kimi-dev-72b:free\",\n name: \"Kimi Dev 72b (free)\",\n match: {\n equals: \"kimi-dev-72b:free\"\n },\n prices: {}\n },\n {\n id: \"kimi-vl-a3b-thinking:free\",\n name: \"Kimi VL A3B Thinking (free)\",\n match: {\n equals: \"kimi-vl-a3b-thinking:free\"\n },\n prices: {}\n },\n {\n id: \"kwaipilot/kat-coder-pro-v2\",\n name: \"KAT-Coder-Pro V2\",\n match: {\n equals: \"kwaipilot/kat-coder-pro-v2\"\n },\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.06,\n output_mtok: 1.2\n }\n },\n {\n id: \"l3-euryale-70b\",\n name: \"Llama 3 Euryale 70B v2.1\",\n match: {\n equals: \"l3-euryale-70b\"\n },\n prices: {\n input_mtok: 1.48,\n output_mtok: 1.48\n }\n },\n {\n id: \"latitudegames/wayfarer-large-70b-llama-3.3\",\n match: {\n equals: \"latitudegames/wayfarer-large-70b-llama-3.3\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 0.9\n }\n },\n {\n id: \"lfm-3b\",\n name: \"LFM 3B\",\n match: {\n equals: \"lfm-3b\"\n },\n prices: {\n input_mtok: 0.02,\n output_mtok: 0.02\n }\n },\n {\n id: \"lfm-40b\",\n name: \"LFM 40B MoE\",\n match: {\n equals: \"lfm-40b\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.15\n }\n },\n {\n id: \"lfm-7b\",\n name: \"LFM 7B\",\n match: {\n equals: \"lfm-7b\"\n },\n prices: {\n input_mtok: 0.01,\n output_mtok: 0.01\n }\n },\n {\n id: \"liquid/lfm-2-24b-a2b\",\n name: \"LFM2-24B-A2B\",\n match: {\n equals: \"liquid/lfm-2-24b-a2b\"\n },\n prices: {\n input_mtok: 0.03,\n output_mtok: 0.12\n }\n },\n {\n id: \"liquid/lfm-2.5-1.2b-instruct:free\",\n name: \"LFM2.5-1.2B-Instruct (free)\",\n match: {\n equals: \"liquid/lfm-2.5-1.2b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"liquid/lfm-2.5-1.2b-thinking:free\",\n name: \"LFM2.5-1.2B-Thinking (free)\",\n match: {\n equals: \"liquid/lfm-2.5-1.2b-thinking:free\"\n },\n prices: {}\n },\n {\n id: \"liquid/lfm-3b\",\n match: {\n equals: \"liquid/lfm-3b\"\n },\n prices: {\n input_mtok: 0.02,\n output_mtok: 0.02\n }\n },\n {\n id: \"liquid/lfm-40b\",\n match: {\n equals: \"liquid/lfm-40b\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.15\n }\n },\n {\n id: \"liquid/lfm-7b\",\n match: {\n equals: \"liquid/lfm-7b\"\n },\n prices: {\n input_mtok: 0.01,\n output_mtok: 0.01\n }\n },\n {\n id: \"llama-3-lumimaid-70b\",\n name: \"Llama 3 Lumimaid 70B\",\n match: {\n equals: \"llama-3-lumimaid-70b\"\n },\n prices: {\n input_mtok: 4,\n output_mtok: 6\n }\n },\n {\n id: \"llama-3-lumimaid-8b\",\n name: \"Llama 3 Lumimaid 8B\",\n match: {\n equals: \"llama-3-lumimaid-8b\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 1.25\n }\n },\n {\n id: \"llama-3.1-405b\",\n name: \"Llama 3.1 405B (base)\",\n match: {\n equals: \"llama-3.1-405b\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 2\n }\n },\n {\n id: \"llama-3.1-405b-instruct\",\n name: \"Llama 3.1 405B Instruct\",\n match: {\n equals: \"llama-3.1-405b-instruct\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 0.8\n }\n },\n {\n id: \"llama-3.1-8b-instruct:free\",\n name: \"Llama 3.1 8B Instruct (free)\",\n match: {\n equals: \"llama-3.1-8b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"llama-3.1-lumimaid-70b\",\n name: \"Lumimaid v0.2 70B\",\n match: {\n equals: \"llama-3.1-lumimaid-70b\"\n },\n prices: {\n input_mtok: 2.5,\n output_mtok: 3\n }\n },\n {\n id: \"llama-3.1-lumimaid-8b\",\n name: \"Lumimaid v0.2 8B\",\n match: {\n equals: \"llama-3.1-lumimaid-8b\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 1.25\n }\n },\n {\n id: \"llama-3.1-nemotron-70b-instruct\",\n name: \"Llama 3.1 Nemotron 70B Instruct\",\n match: {\n equals: \"llama-3.1-nemotron-70b-instruct\"\n },\n prices: {\n input_mtok: 0.12,\n output_mtok: 0.3\n }\n },\n {\n id: \"llama-3.1-nemotron-ultra-253b-v1\",\n name: \"Llama 3.1 Nemotron Ultra 253B v1\",\n match: {\n equals: \"llama-3.1-nemotron-ultra-253b-v1\"\n },\n prices: {\n input_mtok: 0.6,\n output_mtok: 1.8\n }\n },\n {\n id: \"llama-3.1-nemotron-ultra-253b-v1:free\",\n name: \"Llama 3.1 Nemotron Ultra 253B v1 (free)\",\n match: {\n equals: \"llama-3.1-nemotron-ultra-253b-v1:free\"\n },\n prices: {}\n },\n {\n id: \"llama-3.1-sonar-large-128k-online\",\n name: \"Llama 3.1 Sonar 70B Online\",\n match: {\n equals: \"llama-3.1-sonar-large-128k-online\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 1\n }\n },\n {\n id: \"llama-3.1-sonar-small-128k-online\",\n name: \"Llama 3.1 Sonar 8B Online\",\n match: {\n equals: \"llama-3.1-sonar-small-128k-online\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"llama-3.2-11b-vision-instruct:free\",\n name: \"Llama 3.2 11B Vision Instruct (free)\",\n match: {\n equals: \"llama-3.2-11b-vision-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"llama-3.2-1b-instruct:free\",\n name: \"Llama 3.2 1B Instruct (free)\",\n match: {\n equals: \"llama-3.2-1b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"llama-3.2-90b-vision-instruct\",\n name: \"Llama 3.2 90B Vision Instruct\",\n match: {\n equals: \"llama-3.2-90b-vision-instruct\"\n },\n prices: {\n input_mtok: 1.2,\n output_mtok: 1.2\n }\n },\n {\n id: \"llama-3.3-8b-instruct:free\",\n name: \"Llama 3.3 8B Instruct (free)\",\n match: {\n equals: \"llama-3.3-8b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"llama-3.3-nemotron-super-49b-v1\",\n name: \"Llama 3.3 Nemotron Super 49B v1\",\n match: {\n equals: \"llama-3.3-nemotron-super-49b-v1\"\n },\n prices: {\n input_mtok: 0.13,\n output_mtok: 0.4\n }\n },\n {\n id: \"llama-3.3-nemotron-super-49b-v1:free\",\n name: \"Llama 3.3 Nemotron Super 49B v1 (free)\",\n match: {\n equals: \"llama-3.3-nemotron-super-49b-v1:free\"\n },\n prices: {}\n },\n {\n id: \"llama-4-maverick:free\",\n name: \"Llama 4 Maverick (free)\",\n match: {\n equals: \"llama-4-maverick:free\"\n },\n prices: {}\n },\n {\n id: \"llama-4-scout:free\",\n name: \"Llama 4 Scout (free)\",\n match: {\n equals: \"llama-4-scout:free\"\n },\n prices: {}\n },\n {\n id: \"llama-guard-2-8b\",\n name: \"LlamaGuard 2 8B\",\n match: {\n equals: \"llama-guard-2-8b\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"llama3.1-typhoon2-70b-instruct\",\n name: \"Typhoon2 70B Instruct\",\n match: {\n equals: \"llama3.1-typhoon2-70b-instruct\"\n },\n prices: {\n input_mtok: 0.88,\n output_mtok: 0.88\n }\n },\n {\n id: \"llemma_7b\",\n name: \"Llemma 7b\",\n match: {\n equals: \"llemma_7b\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"maestro-reasoning\",\n name: \"Maestro Reasoning\",\n match: {\n equals: \"maestro-reasoning\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 3.3\n }\n },\n {\n id: \"magistral-medium-2506\",\n name: \"Magistral Medium 2506\",\n match: {\n or: [\n {\n equals: \"magistral-medium-2506\"\n },\n {\n equals: \"magistral-medium-2506:thinking\"\n }\n ]\n },\n prices: {\n input_mtok: 2,\n output_mtok: 5\n }\n },\n {\n id: \"magistral-small-2506\",\n name: \"Magistral Small 2506\",\n match: {\n equals: \"magistral-small-2506\"\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.5\n }\n },\n {\n id: \"magnum-72b\",\n name: \"Magnum 72B\",\n match: {\n equals: \"magnum-72b\"\n },\n prices: {\n input_mtok: 4,\n output_mtok: 6\n }\n },\n {\n id: \"magnum-v2-72b\",\n name: \"Magnum v2 72B\",\n match: {\n equals: \"magnum-v2-72b\"\n },\n prices: {\n input_mtok: 3,\n output_mtok: 3\n }\n },\n {\n id: \"mai-ds-r1:free\",\n name: \"MAI DS R1 (free)\",\n match: {\n equals: \"mai-ds-r1:free\"\n },\n prices: {}\n },\n {\n id: \"mancer/weaver\",\n match: {\n equals: \"mancer/weaver\"\n },\n context_window: 8000,\n prices: {\n input_mtok: 1.125,\n output_mtok: 1.125\n }\n },\n {\n id: \"mercury-coder-small-beta\",\n name: \"Mercury Coder Small Beta\",\n match: {\n equals: \"mercury-coder-small-beta\"\n },\n prices: {\n input_mtok: 0.25,\n output_mtok: 1\n }\n },\n {\n id: \"meta-llama/llama-2-13b-chat\",\n match: {\n equals: \"meta-llama/llama-2-13b-chat\"\n },\n prices: {\n input_mtok: 0.22,\n output_mtok: 0.22\n }\n },\n {\n id: \"meta-llama/llama-2-70b-chat\",\n match: {\n equals: \"meta-llama/llama-2-70b-chat\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"meta-llama/llama-3-70b-instruct\",\n name: \"Llama 3 70B Instruct\",\n match: {\n equals: \"meta-llama/llama-3-70b-instruct\"\n },\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.4\n }\n },\n {\n id: \"meta-llama/llama-3-8b-instruct\",\n name: \"Llama 3 8B Instruct\",\n match: {\n equals: \"meta-llama/llama-3-8b-instruct\"\n },\n prices: {\n input_mtok: 0.03,\n output_mtok: 0.06\n }\n },\n {\n id: \"meta-llama/llama-3.1-405b\",\n match: {\n equals: \"meta-llama/llama-3.1-405b\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 2\n }\n },\n {\n id: \"meta-llama/llama-3.1-405b-instruct\",\n match: {\n equals: \"meta-llama/llama-3.1-405b-instruct\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 0.8\n }\n },\n {\n id: \"meta-llama/llama-3.1-405b:free\",\n match: {\n equals: \"meta-llama/llama-3.1-405b:free\"\n },\n prices: {}\n },\n {\n id: \"meta-llama/llama-3.1-70b-instruct\",\n name: \"Llama 3.1 70B Instruct\",\n match: {\n equals: \"meta-llama/llama-3.1-70b-instruct\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.28\n }\n },\n {\n id: \"meta-llama/llama-3.1-8b-instruct\",\n match: {\n equals: \"meta-llama/llama-3.1-8b-instruct\"\n },\n prices: {\n input_mtok: 0.02,\n output_mtok: 0.03\n }\n },\n {\n id: \"meta-llama/llama-3.1-8b-instruct:free\",\n match: {\n equals: \"meta-llama/llama-3.1-8b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"meta-llama/llama-3.2-11b-vision-instruct\",\n name: \"Llama 3.2 11B Vision Instruct\",\n match: {\n equals: \"meta-llama/llama-3.2-11b-vision-instruct\"\n },\n prices: {\n input_mtok: 0.049,\n output_mtok: 0.049\n }\n },\n {\n id: \"meta-llama/llama-3.2-11b-vision-instruct:free\",\n match: {\n equals: \"meta-llama/llama-3.2-11b-vision-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"meta-llama/llama-3.2-1b-instruct\",\n name: \"Llama 3.2 1B Instruct\",\n match: {\n equals: \"meta-llama/llama-3.2-1b-instruct\"\n },\n context_window: 60000,\n prices: {\n input_mtok: 0.005,\n output_mtok: 0.01\n }\n },\n {\n id: \"meta-llama/llama-3.2-1b-instruct:free\",\n match: {\n equals: \"meta-llama/llama-3.2-1b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"meta-llama/llama-3.2-3b-instruct\",\n name: \"Llama 3.2 3B Instruct\",\n match: {\n equals: \"meta-llama/llama-3.2-3b-instruct\"\n },\n prices: {\n input_mtok: 0.01,\n output_mtok: 0.02\n }\n },\n {\n id: \"meta-llama/llama-3.2-3b-instruct:free\",\n name: \"Llama 3.2 3B Instruct (free)\",\n match: {\n equals: \"meta-llama/llama-3.2-3b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"meta-llama/llama-3.2-90b-vision-instruct\",\n match: {\n equals: \"meta-llama/llama-3.2-90b-vision-instruct\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"meta-llama/llama-3.3-70b-instruct\",\n name: \"Llama 3.3 70B Instruct\",\n match: {\n equals: \"meta-llama/llama-3.3-70b-instruct\"\n },\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.24\n }\n },\n {\n id: \"meta-llama/llama-3.3-70b-instruct:free\",\n name: \"Llama 3.3 70B Instruct (free)\",\n match: {\n equals: \"meta-llama/llama-3.3-70b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"meta-llama/llama-4-maverick\",\n name: \"Llama 4 Maverick\",\n match: {\n equals: \"meta-llama/llama-4-maverick\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n }\n },\n {\n id: \"meta-llama/llama-4-maverick:free\",\n match: {\n equals: \"meta-llama/llama-4-maverick:free\"\n },\n prices: {}\n },\n {\n id: \"meta-llama/llama-4-scout\",\n name: \"Llama 4 Scout\",\n match: {\n equals: \"meta-llama/llama-4-scout\"\n },\n prices: {\n input_mtok: 0.08,\n output_mtok: 0.3\n }\n },\n {\n id: \"meta-llama/llama-4-scout:free\",\n match: {\n equals: \"meta-llama/llama-4-scout:free\"\n },\n prices: {}\n },\n {\n id: \"meta-llama/llama-guard-2-8b\",\n match: {\n equals: \"meta-llama/llama-guard-2-8b\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"meta-llama/llama-guard-3-8b\",\n name: \"Llama Guard 3 8B\",\n match: {\n equals: \"meta-llama/llama-guard-3-8b\"\n },\n prices: {\n input_mtok: 0.02,\n output_mtok: 0.06\n }\n },\n {\n id: \"meta-llama/llama-guard-4-12b\",\n name: \"Llama Guard 4 12B\",\n match: {\n equals: \"meta-llama/llama-guard-4-12b\"\n },\n context_window: 163840,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.05\n }\n },\n {\n id: \"meta/muse-spark-1.1\",\n match: {\n or: [\n {\n equals: \"meta/muse-spark-1.1\"\n },\n {\n equals: \"meta/muse-spark-1.1-20260709\"\n }\n ]\n },\n context_window: 1048576,\n price_comments: \"https://openrouter.ai/api/v1/models/meta/muse-spark-1.1-20260709/endpoints\",\n prices: {\n input_mtok: 1.25,\n cache_read_mtok: 0.15,\n output_mtok: 4.25,\n web_searches_kcount: 2.5\n }\n },\n {\n id: \"meta/muse-spark-1.2\",\n match: {\n or: [\n {\n equals: \"meta/muse-spark-1.2\"\n },\n {\n equals: \"meta/muse-spark-1.2-20260805\"\n }\n ]\n },\n context_window: 1048576,\n price_comments: \"https://openrouter.ai/api/v1/models/meta/muse-spark-1.2-20260805/endpoints\",\n prices: {\n input_mtok: 1.25,\n cache_read_mtok: 0.15,\n output_mtok: 4.25,\n web_searches_kcount: 2.5\n }\n },\n {\n id: \"meta/muse-spark-1.2-contributor\",\n match: {\n or: [\n {\n equals: \"meta/muse-spark-1.2-contributor\"\n },\n {\n equals: \"meta/muse-spark-1.2-contributor-20260805\"\n }\n ]\n },\n context_window: 1048576,\n price_comments: \"https://openrouter.ai/api/v1/models/meta/muse-spark-1.2-contributor-20260805/endpoints\",\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.002,\n output_mtok: 0.2,\n web_searches_kcount: 2.5\n }\n },\n {\n id: \"meta/muse-spark-1.3\",\n match: {\n or: [\n {\n equals: \"meta/muse-spark-1.3\"\n },\n {\n equals: \"meta/muse-spark-1.3-20260902\"\n }\n ]\n },\n context_window: 1048576,\n price_comments: \"https://openrouter.ai/api/v1/models/meta/muse-spark-1.3-20260902/endpoints\",\n prices: {\n input_mtok: 1.25,\n cache_read_mtok: 0.15,\n output_mtok: 4.25,\n web_searches_kcount: 2.5\n }\n },\n {\n id: \"meta/muse-spark-1.3-contributor\",\n match: {\n or: [\n {\n equals: \"meta/muse-spark-1.3-contributor\"\n },\n {\n equals: \"meta/muse-spark-1.3-contributor-20260902\"\n }\n ]\n },\n context_window: 1048576,\n price_comments: \"https://openrouter.ai/api/v1/models/meta/muse-spark-1.3-contributor-20260902/endpoints\",\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.002,\n output_mtok: 0.2,\n web_searches_kcount: 2.5\n }\n },\n {\n id: \"meta/muse-wonder\",\n match: {\n or: [\n {\n equals: \"meta/muse-wonder\"\n },\n {\n equals: \"meta/muse-wonder-20261012\"\n }\n ]\n },\n context_window: 1048576,\n price_comments: \"https://openrouter.ai/api/v1/models/meta/muse-wonder-20261012/endpoints\",\n prices: {\n input_mtok: 4,\n cache_read_mtok: 0.2,\n output_mtok: 20,\n web_searches_kcount: 2.5\n }\n },\n {\n id: \"microsoft/phi-3-medium-128k-instruct\",\n match: {\n equals: \"microsoft/phi-3-medium-128k-instruct\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 1\n }\n },\n {\n id: \"microsoft/phi-3-mini-128k-instruct\",\n match: {\n equals: \"microsoft/phi-3-mini-128k-instruct\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"microsoft/phi-3.5-mini-128k-instruct\",\n match: {\n equals: \"microsoft/phi-3.5-mini-128k-instruct\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"microsoft/phi-4\",\n match: {\n equals: \"microsoft/phi-4\"\n },\n context_window: 16384,\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.14\n }\n },\n {\n id: \"microsoft/phi-4-mini-instruct\",\n name: \"Phi 4 Mini Instruct\",\n match: {\n equals: \"microsoft/phi-4-mini-instruct\"\n },\n prices: {\n input_mtok: 0.08,\n output_mtok: 0.35\n }\n },\n {\n id: \"microsoft/phi-4-multimodal-instruct\",\n match: {\n equals: \"microsoft/phi-4-multimodal-instruct\"\n },\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.1\n }\n },\n {\n id: \"microsoft/wizardlm-2-7b\",\n match: {\n equals: \"microsoft/wizardlm-2-7b\"\n },\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.07\n }\n },\n {\n id: \"microsoft/wizardlm-2-8x22b\",\n match: {\n equals: \"microsoft/wizardlm-2-8x22b\"\n },\n context_window: 65535,\n prices: {\n input_mtok: 0.5,\n output_mtok: 0.5\n }\n },\n {\n id: \"midnight-rose-70b\",\n name: \"Midnight Rose 70B\",\n match: {\n equals: \"midnight-rose-70b\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 0.8\n }\n },\n {\n id: \"minimax-m1:extended\",\n name: \"MiniMax M1 (extended)\",\n match: {\n equals: \"minimax-m1:extended\"\n },\n prices: {\n input_mtok: 0.55,\n output_mtok: 2.2\n }\n },\n {\n id: \"minimax/minimax-01\",\n name: \"MiniMax-01\",\n match: {\n equals: \"minimax/minimax-01\"\n },\n context_window: 1000192,\n prices: {\n input_mtok: 0.2,\n output_mtok: 1.1\n }\n },\n {\n id: \"minimax/minimax-m1\",\n name: \"MiniMax M1\",\n match: {\n equals: \"minimax/minimax-m1\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.3,\n output_mtok: 1.65\n }\n },\n {\n id: \"minimax/minimax-m2\",\n name: \"MiniMax M2\",\n match: {\n equals: \"minimax/minimax-m2\"\n },\n prices: {\n input_mtok: 0.255,\n cache_read_mtok: 0.03,\n output_mtok: 1\n }\n },\n {\n id: \"minimax/minimax-m2-her\",\n name: \"MiniMax M2-her\",\n match: {\n or: [\n {\n equals: \"minimax/minimax-m2-her\"\n },\n {\n equals: \"minimax/minimax-m2-her-20260123\"\n }\n ]\n },\n context_window: 65536,\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.03,\n output_mtok: 1.2\n }\n },\n {\n id: \"minimax/minimax-m2.1\",\n name: \"MiniMax M2.1\",\n match: {\n equals: \"minimax/minimax-m2.1\"\n },\n context_window: 204800,\n prices: {\n input_mtok: 0.29,\n cache_read_mtok: 0.03,\n output_mtok: 0.95\n }\n },\n {\n id: \"minimax/minimax-m2.5\",\n name: \"MiniMax M2.5\",\n match: {\n or: [\n {\n equals: \"minimax/minimax-m2.5\"\n },\n {\n equals: \"minimax/minimax-m2.5-20260211\"\n }\n ]\n },\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.05,\n output_mtok: 0.9\n }\n },\n {\n id: \"minimax/minimax-m2.7\",\n name: \"MiniMax M2.7\",\n match: {\n or: [\n {\n equals: \"minimax/minimax-m2.7\"\n },\n {\n equals: \"minimax/minimax-m2.7-20260318\"\n }\n ]\n },\n prices: {\n input_mtok: 0.27,\n cache_read_mtok: 0.054,\n output_mtok: 1.08\n }\n },\n {\n id: \"minimax/minimax-m3\",\n name: \"MiniMax M3\",\n match: {\n or: [\n {\n equals: \"minimax/minimax-m3\"\n },\n {\n equals: \"minimax/minimax-m3-20260531\"\n }\n ]\n },\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.06,\n output_mtok: 1.2\n }\n },\n {\n id: \"ministral-3b\",\n name: \"Ministral 3B\",\n match: {\n equals: \"ministral-3b\"\n },\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.04\n }\n },\n {\n id: \"ministral-8b\",\n name: \"Ministral 8B\",\n match: {\n equals: \"ministral-8b\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"mistral-7b-instruct\",\n name: \"Mistral 7B Instruct\",\n match: {\n or: [\n {\n equals: \"mistral-7b-instruct\"\n },\n {\n equals: \"mistral-7b-instruct-v0.3\"\n }\n ]\n },\n prices: {\n input_mtok: 0.028,\n output_mtok: 0.054\n }\n },\n {\n id: \"mistral-7b-instruct-v0.1\",\n name: \"Mistral 7B Instruct v0.1\",\n match: {\n equals: \"mistral-7b-instruct-v0.1\"\n },\n prices: {\n input_mtok: 0.11,\n output_mtok: 0.19\n }\n },\n {\n id: \"mistral-7b-instruct-v0.2\",\n name: \"Mistral 7B Instruct v0.2\",\n match: {\n equals: \"mistral-7b-instruct-v0.2\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"mistral-7b-instruct:free\",\n name: \"Mistral 7B Instruct (free)\",\n match: {\n equals: \"mistral-7b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"mistral-medium\",\n name: \"Mistral Medium\",\n match: {\n equals: \"mistral-medium\"\n },\n prices: {\n input_mtok: 2.75,\n output_mtok: 8.1\n }\n },\n {\n id: \"mistral-nemo:free\",\n name: \"Mistral Nemo (free)\",\n match: {\n equals: \"mistral-nemo:free\"\n },\n prices: {}\n },\n {\n id: \"mistral-small\",\n name: \"Mistral Small\",\n match: {\n equals: \"mistral-small\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.6\n }\n },\n {\n id: \"mistral-small-24b-instruct-2501:free\",\n name: \"Mistral Small 3 (free)\",\n match: {\n equals: \"mistral-small-24b-instruct-2501:free\"\n },\n prices: {}\n },\n {\n id: \"mistral-small-3.1-24b-instruct:free\",\n name: \"Mistral Small 3.1 24B (free)\",\n match: {\n equals: \"mistral-small-3.1-24b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"mistral-small-3.2-24b-instruct:free\",\n name: \"Mistral Small 3.2 24B (free)\",\n match: {\n equals: \"mistral-small-3.2-24b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"mistral-tiny\",\n name: \"Mistral Tiny\",\n match: {\n equals: \"mistral-tiny\"\n },\n prices: {\n input_mtok: 0.25,\n output_mtok: 0.25\n }\n },\n {\n id: \"mistral/ministral-8b\",\n match: {\n equals: \"mistral/ministral-8b\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"mistralai/codestral-2501\",\n match: {\n equals: \"mistralai/codestral-2501\"\n },\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.9\n }\n },\n {\n id: \"mistralai/codestral-2508\",\n name: \"Codestral 2508\",\n match: {\n equals: \"mistralai/codestral-2508\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.03,\n output_mtok: 0.9\n }\n },\n {\n id: \"mistralai/codestral-mamba\",\n match: {\n equals: \"mistralai/codestral-mamba\"\n },\n prices: {\n input_mtok: 0.25,\n output_mtok: 0.25\n }\n },\n {\n id: \"mistralai/devstral-2512\",\n name: \"Devstral 2 2512\",\n match: {\n equals: \"mistralai/devstral-2512\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.4,\n cache_read_mtok: 0.04,\n output_mtok: 2\n }\n },\n {\n id: \"mistralai/ministral-14b-2512\",\n name: \"Ministral 3 14B 2512\",\n match: {\n equals: \"mistralai/ministral-14b-2512\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.02,\n output_mtok: 0.2\n }\n },\n {\n id: \"mistralai/ministral-3b\",\n match: {\n equals: \"mistralai/ministral-3b\"\n },\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.04\n }\n },\n {\n id: \"mistralai/ministral-3b-2512\",\n name: \"Ministral 3 3B 2512\",\n match: {\n equals: \"mistralai/ministral-3b-2512\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.01,\n output_mtok: 0.1\n }\n },\n {\n id: \"mistralai/ministral-8b\",\n match: {\n equals: \"mistralai/ministral-8b\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"mistralai/ministral-8b-2512\",\n name: \"Ministral 3 8B 2512\",\n match: {\n equals: \"mistralai/ministral-8b-2512\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.015,\n output_mtok: 0.15\n }\n },\n {\n id: \"mistralai/mistral-7b-instruct\",\n match: {\n or: [\n {\n equals: \"mistralai/mistral-7b-instruct\"\n },\n {\n equals: \"mistralai/mistral-7b-instruct-v0.3\"\n }\n ]\n },\n prices: {\n input_mtok: 0.029,\n output_mtok: 0.059\n }\n },\n {\n id: \"mistralai/mistral-7b-instruct-v0.1\",\n match: {\n equals: \"mistralai/mistral-7b-instruct-v0.1\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"mistralai/mistral-7b-instruct-v0.2\",\n match: {\n equals: \"mistralai/mistral-7b-instruct-v0.2\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"mistralai/mistral-7b-instruct:free\",\n match: {\n equals: \"mistralai/mistral-7b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"mistralai/mistral-large\",\n name: \"Mistral Large\",\n match: {\n or: [\n {\n equals: \"mistralai/mistral-large\"\n },\n {\n equals: \"mistralai/mistral-large-2407\"\n },\n {\n equals: \"mistral-large-2411\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 2,\n output_mtok: 6\n }\n },\n {\n id: \"mistralai/mistral-large-2512\",\n name: \"Mistral Large 3 2512\",\n match: {\n equals: \"mistralai/mistral-large-2512\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.5,\n cache_read_mtok: 0.05,\n output_mtok: 1.5\n }\n },\n {\n id: \"mistralai/mistral-large-4-0\",\n match: {\n or: [\n {\n equals: \"mistralai/mistral-large-4-0\"\n },\n {\n equals: \"mistralai/mistral-large-4-0-20261006\"\n }\n ]\n },\n price_comments: \"Current launch promotional rates are $0.68 input, $0.07 cached input, and $2.09 output per million tokens. The October 6 changelog says the promotion lasts two weeks but does not state an exact effective end date, so no future price change is scheduled here. This model is available on the global route with a 1,048,576-token context and the EU route with a 524,288-token context, so no single context_window is listed. The EU endpoint is priced at 10% above its global counterpart; callers can select it with request context route=mistral/eu. https://openrouter.ai/api/v1/models/mistralai/mistral-large-4-0/endpoints https://docs.mistral.ai/resources/changelogs\",\n prices: {\n input_mtok: 0.68,\n cache_read_mtok: 0.07,\n output_mtok: 2.09\n },\n price_variants: [\n {\n when: {\n route: \"mistral/eu\"\n },\n prices: {\n input_mtok: 0.748,\n cache_read_mtok: 0.077,\n output_mtok: 2.299\n }\n }\n ]\n },\n {\n id: \"mistralai/mistral-medium\",\n match: {\n equals: \"mistralai/mistral-medium\"\n },\n prices: {\n input_mtok: 2.75,\n output_mtok: 8.1\n }\n },\n {\n id: \"mistralai/mistral-medium-3\",\n name: \"Mistral Medium 3\",\n match: {\n equals: \"mistralai/mistral-medium-3\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.4,\n output_mtok: 2\n }\n },\n {\n id: \"mistralai/mistral-medium-3-5\",\n name: \"Mistral Medium 3.5\",\n match: {\n equals: \"mistralai/mistral-medium-3-5\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 1.5,\n output_mtok: 7.5\n }\n },\n {\n id: \"mistralai/mistral-medium-3.1\",\n name: \"Mistral Medium 3.1\",\n match: {\n equals: \"mistralai/mistral-medium-3.1\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.4,\n cache_read_mtok: 0.04,\n output_mtok: 2\n }\n },\n {\n id: \"mistralai/mistral-nemo\",\n name: \"Mistral Nemo\",\n match: {\n equals: \"mistralai/mistral-nemo\"\n },\n prices: {\n input_mtok: 0.01,\n output_mtok: 0.019\n }\n },\n {\n id: \"mistralai/mistral-nemo:free\",\n match: {\n equals: \"mistralai/mistral-nemo:free\"\n },\n prices: {}\n },\n {\n id: \"mistralai/mistral-saba\",\n name: \"Saba\",\n match: {\n equals: \"mistralai/mistral-saba\"\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.6\n }\n },\n {\n id: \"mistralai/mistral-small\",\n match: {\n equals: \"mistralai/mistral-small\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.6\n }\n },\n {\n id: \"mistralai/mistral-small-24b-instruct-2501\",\n name: \"Mistral Small 3\",\n match: {\n equals: \"mistralai/mistral-small-24b-instruct-2501\"\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.09\n }\n },\n {\n id: \"mistralai/mistral-small-24b-instruct-2501:free\",\n match: {\n equals: \"mistralai/mistral-small-24b-instruct-2501:free\"\n },\n prices: {}\n },\n {\n id: \"mistralai/mistral-small-2603\",\n name: \"Mistral Small 4\",\n match: {\n equals: \"mistralai/mistral-small-2603\"\n },\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.015,\n output_mtok: 0.6\n }\n },\n {\n id: \"mistralai/mistral-small-3.1-24b-instruct\",\n name: \"Mistral Small 3.1 24B\",\n match: {\n equals: \"mistralai/mistral-small-3.1-24b-instruct\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.15\n }\n },\n {\n id: \"mistralai/mistral-small-3.1-24b-instruct:free\",\n match: {\n equals: \"mistralai/mistral-small-3.1-24b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"mistralai/mistral-small-3.2-24b-instruct\",\n name: \"Mistral Small 3.2 24B\",\n match: {\n equals: \"mistralai/mistral-small-3.2-24b-instruct\"\n },\n prices: {\n input_mtok: 0.075,\n output_mtok: 0.2\n }\n },\n {\n id: \"mistralai/mistral-tiny\",\n match: {\n equals: \"mistralai/mistral-tiny\"\n },\n prices: {\n input_mtok: 0.25,\n output_mtok: 0.25\n }\n },\n {\n id: \"mistralai/mixtral-8x22b-instruct\",\n match: {\n equals: \"mistralai/mixtral-8x22b-instruct\"\n },\n context_window: 65536,\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"mistralai/mixtral-8x7b-instruct\",\n match: {\n equals: \"mistralai/mixtral-8x7b-instruct\"\n },\n prices: {\n input_mtok: 0.24,\n output_mtok: 0.24\n }\n },\n {\n id: \"mistralai/pixtral-12b\",\n match: {\n equals: \"mistralai/pixtral-12b\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"mistralai/pixtral-large-2411\",\n match: {\n equals: \"mistralai/pixtral-large-2411\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 6\n }\n },\n {\n id: \"mistralai/voxtral-small-24b-2507\",\n name: \"Voxtral Small 24B 2507\",\n match: {\n equals: \"mistralai/voxtral-small-24b-2507\"\n },\n context_window: 32000,\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.01,\n output_mtok: 0.3\n }\n },\n {\n id: \"mixtral-8x7b-instruct\",\n name: \"Mixtral 8x7B Instruct\",\n match: {\n equals: \"mixtral-8x7b-instruct\"\n },\n prices: {\n input_mtok: 0.08,\n output_mtok: 0.24\n }\n },\n {\n id: \"mn-celeste-12b\",\n name: \"Mistral Nemo 12B Celeste\",\n match: {\n equals: \"mn-celeste-12b\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"mn-inferor-12b\",\n name: \"Mistral Nemo Inferor 12B\",\n match: {\n equals: \"mn-inferor-12b\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"mn-starcannon-12b\",\n name: \"Starcannon 12B\",\n match: {\n equals: \"mn-starcannon-12b\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"moonshotai/kimi-k2\",\n name: \"Kimi K2 0711\",\n match: {\n equals: \"moonshotai/kimi-k2\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.57,\n output_mtok: 2.3\n }\n },\n {\n id: \"moonshotai/kimi-k2-0905\",\n name: \"Kimi K2 0905\",\n match: {\n equals: \"moonshotai/kimi-k2-0905\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.6,\n output_mtok: 2.5\n }\n },\n {\n id: \"moonshotai/kimi-k2-thinking\",\n name: \"Kimi K2 Thinking\",\n match: {\n equals: \"moonshotai/kimi-k2-thinking\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.6,\n output_mtok: 2.5\n }\n },\n {\n id: \"moonshotai/kimi-k2.5\",\n name: \"Kimi K2.5\",\n match: {\n equals: \"moonshotai/kimi-k2.5\"\n },\n prices: {\n input_mtok: 0.4,\n cache_read_mtok: 0.09,\n output_mtok: 1.9\n }\n },\n {\n id: \"moonshotai/kimi-k2.6\",\n name: \"Kimi K2.6\",\n match: {\n equals: \"moonshotai/kimi-k2.6\"\n },\n prices: {\n input_mtok: 0.68,\n cache_read_mtok: 0.34,\n output_mtok: 3.41\n }\n },\n {\n id: \"moonshotai/kimi-k2.6:free\",\n name: \"Kimi K2.6 (free)\",\n match: {\n equals: \"moonshotai/kimi-k2.6:free\"\n },\n prices: {}\n },\n {\n id: \"moonshotai/kimi-k2.7-code\",\n name: \"Kimi K2.7 Code\",\n match: {\n or: [\n {\n equals: \"moonshotai/kimi-k2.7-code\"\n },\n {\n equals: \"moonshotai/kimi-k2.7-code-20260612\"\n }\n ]\n },\n context_window: 262144,\n price_comments: \"Ref: https://openrouter.ai/api/v1/models\",\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.16,\n output_mtok: 3.5\n }\n },\n {\n id: \"moonshotai/kimi-k3\",\n name: \"Kimi K3\",\n match: {\n or: [\n {\n equals: \"moonshotai/kimi-k3\"\n },\n {\n equals: \"moonshotai/kimi-k3-20260715\"\n }\n ]\n },\n context_window: 1048576,\n price_comments: \"Ref: https://openrouter.ai/api/v1/models\",\n prices: {\n input_mtok: 3,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n id: \"moonshotai/kimi-vl-a3b-thinking:free\",\n match: {\n equals: \"moonshotai/kimi-vl-a3b-thinking:free\"\n },\n prices: {}\n },\n {\n id: \"moonshotai/moonlight-16b-a3b-instruct:free\",\n match: {\n equals: \"moonshotai/moonlight-16b-a3b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"morph/morph-v3-fast\",\n name: \"Morph V3 Fast\",\n match: {\n equals: \"morph/morph-v3-fast\"\n },\n context_window: 81920,\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"morph/morph-v3-large\",\n name: \"Morph V3 Large\",\n match: {\n equals: \"morph/morph-v3-large\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.9,\n output_mtok: 1.9\n }\n },\n {\n id: \"mythalion-13b\",\n name: \"Mythalion 13B\",\n match: {\n equals: \"mythalion-13b\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"neversleep/llama-3-lumimaid-70b\",\n match: {\n equals: \"neversleep/llama-3-lumimaid-70b\"\n },\n prices: {\n input_mtok: 3.375,\n output_mtok: 4.5\n }\n },\n {\n id: \"neversleep/llama-3-lumimaid-8b\",\n match: {\n or: [\n {\n equals: \"neversleep/llama-3-lumimaid-8b\"\n },\n {\n equals: \"neversleep/llama-3-lumimaid-8b:extended\"\n }\n ]\n },\n prices: {\n input_mtok: 0.09375,\n output_mtok: 0.75\n }\n },\n {\n id: \"neversleep/llama-3.1-lumimaid-70b\",\n match: {\n equals: \"neversleep/llama-3.1-lumimaid-70b\"\n },\n prices: {\n input_mtok: 1.5,\n output_mtok: 2.25\n }\n },\n {\n id: \"neversleep/llama-3.1-lumimaid-8b\",\n match: {\n equals: \"neversleep/llama-3.1-lumimaid-8b\"\n },\n prices: {\n input_mtok: 0.09375,\n output_mtok: 0.75\n }\n },\n {\n id: \"neversleep/noromaid-20b\",\n match: {\n equals: \"neversleep/noromaid-20b\"\n },\n prices: {\n input_mtok: 0.75,\n output_mtok: 1.5\n }\n },\n {\n id: \"nex-agi/nex-n2-pro:free\",\n name: \"Nex-N2-Pro (free)\",\n match: {\n equals: \"nex-agi/nex-n2-pro:free\"\n },\n prices: {}\n },\n {\n id: \"noromaid-20b\",\n name: \"Noromaid 20B\",\n match: {\n equals: \"noromaid-20b\"\n },\n prices: {\n input_mtok: 1.25,\n output_mtok: 2\n }\n },\n {\n id: \"nothingiisreal/mn-celeste-12b\",\n match: {\n equals: \"nothingiisreal/mn-celeste-12b\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"nous-hermes-2-mixtral-8x7b-dpo\",\n name: \"Hermes 2 Mixtral 8x7B DPO\",\n match: {\n equals: \"nous-hermes-2-mixtral-8x7b-dpo\"\n },\n prices: {\n input_mtok: 0.6,\n output_mtok: 0.6\n }\n },\n {\n id: \"nousresearch/deephermes-3-llama-3-8b-preview:free\",\n match: {\n equals: \"nousresearch/deephermes-3-llama-3-8b-preview:free\"\n },\n prices: {}\n },\n {\n id: \"nousresearch/hermes-2-pro-llama-3-8b\",\n match: {\n equals: \"nousresearch/hermes-2-pro-llama-3-8b\"\n },\n prices: {\n input_mtok: 0.025,\n output_mtok: 0.04\n }\n },\n {\n id: \"nousresearch/hermes-3-llama-3.1-405b\",\n name: \"Hermes 3 405B Instruct\",\n match: {\n equals: \"nousresearch/hermes-3-llama-3.1-405b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.7,\n output_mtok: 0.8\n }\n },\n {\n id: \"nousresearch/hermes-3-llama-3.1-405b:free\",\n name: \"Hermes 3 405B Instruct (free)\",\n match: {\n equals: \"nousresearch/hermes-3-llama-3.1-405b:free\"\n },\n prices: {}\n },\n {\n id: \"nousresearch/hermes-3-llama-3.1-70b\",\n name: \"Hermes 3 70B Instruct\",\n match: {\n equals: \"nousresearch/hermes-3-llama-3.1-70b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.12,\n output_mtok: 0.3\n }\n },\n {\n id: \"nousresearch/hermes-4-405b\",\n name: \"Hermes 4 405B\",\n match: {\n equals: \"nousresearch/hermes-4-405b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 1,\n output_mtok: 3\n }\n },\n {\n id: \"nousresearch/hermes-4-70b\",\n name: \"Hermes 4 70B\",\n match: {\n equals: \"nousresearch/hermes-4-70b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.13,\n output_mtok: 0.4\n }\n },\n {\n id: \"nousresearch/nous-hermes-2-mixtral-8x7b-dpo\",\n match: {\n equals: \"nousresearch/nous-hermes-2-mixtral-8x7b-dpo\"\n },\n prices: {\n input_mtok: 0.6,\n output_mtok: 0.6\n }\n },\n {\n id: \"nousresearch/nous-hermes-llama2-13b\",\n match: {\n equals: \"nousresearch/nous-hermes-llama2-13b\"\n },\n prices: {\n input_mtok: 0.18,\n output_mtok: 0.18\n }\n },\n {\n id: \"nvidia/llama-3.1-nemotron-70b-instruct\",\n match: {\n equals: \"nvidia/llama-3.1-nemotron-70b-instruct\"\n },\n prices: {\n input_mtok: 0.12,\n output_mtok: 0.3\n }\n },\n {\n id: \"nvidia/llama-3.1-nemotron-70b-instruct:free\",\n match: {\n equals: \"nvidia/llama-3.1-nemotron-70b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"nvidia/llama-3.1-nemotron-nano-8b-v1:free\",\n match: {\n equals: \"nvidia/llama-3.1-nemotron-nano-8b-v1:free\"\n },\n prices: {}\n },\n {\n id: \"nvidia/llama-3.1-nemotron-ultra-253b-v1:free\",\n match: {\n equals: \"nvidia/llama-3.1-nemotron-ultra-253b-v1:free\"\n },\n prices: {}\n },\n {\n id: \"nvidia/llama-3.3-nemotron-super-49b-v1.5\",\n name: \"Llama 3.3 Nemotron Super 49B V1.5\",\n match: {\n equals: \"nvidia/llama-3.3-nemotron-super-49b-v1.5\"\n },\n prices: {\n input_mtok: 0.4,\n output_mtok: 0.4\n }\n },\n {\n id: \"nvidia/llama-3.3-nemotron-super-49b-v1:free\",\n match: {\n equals: \"nvidia/llama-3.3-nemotron-super-49b-v1:free\"\n },\n prices: {}\n },\n {\n id: \"nvidia/nemotron-3-nano-30b-a3b\",\n name: \"Nemotron 3 Nano 30B A3B\",\n match: {\n equals: \"nvidia/nemotron-3-nano-30b-a3b\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.2\n }\n },\n {\n id: \"nvidia/nemotron-3-nano-30b-a3b:free\",\n name: \"Nemotron 3 Nano 30B A3B (free)\",\n match: {\n equals: \"nvidia/nemotron-3-nano-30b-a3b:free\"\n },\n prices: {}\n },\n {\n id: \"nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free\",\n name: \"Nemotron 3 Nano Omni (free)\",\n match: {\n equals: \"nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free\"\n },\n context_window: 256000,\n prices: {}\n },\n {\n id: \"nvidia/nemotron-3-super-120b-a12b\",\n name: \"Nemotron 3 Super\",\n match: {\n equals: \"nvidia/nemotron-3-super-120b-a12b\"\n },\n prices: {\n input_mtok: 0.09,\n output_mtok: 0.45\n }\n },\n {\n id: \"nvidia/nemotron-3-super-120b-a12b:free\",\n name: \"Nemotron 3 Super (free)\",\n match: {\n equals: \"nvidia/nemotron-3-super-120b-a12b:free\"\n },\n context_window: 262144,\n prices: {}\n },\n {\n id: \"nvidia/nemotron-3-ultra-550b-a55b\",\n name: \"Nemotron 3 Ultra\",\n match: {\n equals: \"nvidia/nemotron-3-ultra-550b-a55b\"\n },\n prices: {\n input_mtok: 0.5,\n cache_read_mtok: 0.15,\n output_mtok: 2.5\n }\n },\n {\n id: \"nvidia/nemotron-3-ultra-550b-a55b:free\",\n name: \"Nemotron 3 Ultra (free)\",\n match: {\n equals: \"nvidia/nemotron-3-ultra-550b-a55b:free\"\n },\n context_window: 1e6,\n prices: {}\n },\n {\n id: \"nvidia/nemotron-3.5-content-safety:free\",\n name: \"Nemotron 3.5 Content Safety (free)\",\n match: {\n equals: \"nvidia/nemotron-3.5-content-safety:free\"\n },\n context_window: 128000,\n prices: {}\n },\n {\n id: \"nvidia/nemotron-nano-12b-v2-vl:free\",\n name: \"Nemotron Nano 12B 2 VL (free)\",\n match: {\n equals: \"nvidia/nemotron-nano-12b-v2-vl:free\"\n },\n prices: {}\n },\n {\n id: \"nvidia/nemotron-nano-9b-v2\",\n name: \"Nemotron Nano 9B V2\",\n match: {\n equals: \"nvidia/nemotron-nano-9b-v2\"\n },\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.16\n }\n },\n {\n id: \"nvidia/nemotron-nano-9b-v2:free\",\n name: \"Nemotron Nano 9B V2 (free)\",\n match: {\n equals: \"nvidia/nemotron-nano-9b-v2:free\"\n },\n prices: {}\n },\n {\n id: \"o1-mini\",\n name: \"o1-mini\",\n match: {\n or: [\n {\n equals: \"o1-mini\"\n },\n {\n equals: \"o1-mini-2024-09-12\"\n }\n ]\n },\n prices: {\n input_mtok: 1.1,\n cache_read_mtok: 0.55,\n output_mtok: 4.4\n }\n },\n {\n id: \"open-r1/olympiccoder-32b:free\",\n match: {\n equals: \"open-r1/olympiccoder-32b:free\"\n },\n prices: {}\n },\n {\n id: \"open-r1/olympiccoder-7b:free\",\n match: {\n equals: \"open-r1/olympiccoder-7b:free\"\n },\n prices: {}\n },\n {\n id: \"openai/chatgpt-4o-latest\",\n match: {\n equals: \"openai/chatgpt-4o-latest\"\n },\n prices: {\n input_mtok: 5,\n output_mtok: 15\n }\n },\n {\n id: \"openai/codex-mini\",\n match: {\n equals: \"openai/codex-mini\"\n },\n prices: {\n input_mtok: 1.5,\n cache_read_mtok: 0.375,\n output_mtok: 6\n }\n },\n {\n id: \"openai/gpt-3.5-turbo\",\n name: \"GPT-3.5 Turbo\",\n match: {\n or: [\n {\n equals: \"openai/gpt-3.5-turbo\"\n },\n {\n equals: \"gpt-3.5-turbo-0125\"\n }\n ]\n },\n context_window: 16385,\n prices: {\n input_mtok: 0.5,\n output_mtok: 1.5\n }\n },\n {\n id: \"openai/gpt-3.5-turbo-0613\",\n name: \"GPT-3.5 Turbo (older v0613)\",\n match: {\n equals: \"openai/gpt-3.5-turbo-0613\"\n },\n context_window: 4095,\n prices: {\n input_mtok: 1,\n output_mtok: 2\n }\n },\n {\n id: \"openai/gpt-3.5-turbo-1106\",\n match: {\n equals: \"openai/gpt-3.5-turbo-1106\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 2\n }\n },\n {\n id: \"openai/gpt-3.5-turbo-16k\",\n name: \"GPT-3.5 Turbo 16k\",\n match: {\n equals: \"openai/gpt-3.5-turbo-16k\"\n },\n context_window: 16385,\n prices: {\n input_mtok: 3,\n output_mtok: 4\n }\n },\n {\n id: \"openai/gpt-3.5-turbo-instruct\",\n name: \"GPT-3.5 Turbo Instruct\",\n match: {\n equals: \"openai/gpt-3.5-turbo-instruct\"\n },\n context_window: 4095,\n prices: {\n input_mtok: 1.5,\n output_mtok: 2\n }\n },\n {\n id: \"openai/gpt-4\",\n name: \"GPT-4\",\n match: {\n or: [\n {\n equals: \"openai/gpt-4\"\n },\n {\n equals: \"gpt-4-0314\"\n }\n ]\n },\n context_window: 8191,\n prices: {\n input_mtok: 30,\n output_mtok: 60\n }\n },\n {\n id: \"openai/gpt-4-1106-preview\",\n match: {\n equals: \"openai/gpt-4-1106-preview\"\n },\n prices: {\n input_mtok: 10,\n output_mtok: 30\n }\n },\n {\n id: \"openai/gpt-4-32k\",\n match: {\n or: [\n {\n equals: \"openai/gpt-4-32k\"\n },\n {\n equals: \"openai/gpt-4-32k-0314\"\n }\n ]\n },\n prices: {\n input_mtok: 60,\n output_mtok: 120\n }\n },\n {\n id: \"openai/gpt-4-turbo\",\n name: \"GPT-4 Turbo\",\n match: {\n or: [\n {\n equals: \"openai/gpt-4-turbo\"\n },\n {\n equals: \"openai/gpt-4-turbo-preview\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 10,\n output_mtok: 30\n }\n },\n {\n id: \"openai/gpt-4.1\",\n name: \"GPT-4.1\",\n match: {\n equals: \"openai/gpt-4.1\"\n },\n context_window: 1047576,\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.5,\n output_mtok: 8\n }\n },\n {\n id: \"openai/gpt-4.1-mini\",\n name: \"GPT-4.1 Mini\",\n match: {\n or: [\n {\n equals: \"openai/gpt-4.1-mini\"\n },\n {\n equals: \"openai/gpt-4.1-mini-2025-04-14\"\n }\n ]\n },\n context_window: 1047576,\n prices: {\n input_mtok: 0.4,\n cache_read_mtok: 0.1,\n output_mtok: 1.6\n }\n },\n {\n id: \"openai/gpt-4.1-nano\",\n name: \"GPT-4.1 Nano\",\n match: {\n equals: \"openai/gpt-4.1-nano\"\n },\n context_window: 1047576,\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.025,\n output_mtok: 0.4\n }\n },\n {\n id: \"openai/gpt-4.5-preview\",\n match: {\n equals: \"openai/gpt-4.5-preview\"\n },\n prices: {\n input_mtok: 75,\n output_mtok: 150\n }\n },\n {\n id: \"openai/gpt-4o\",\n match: {\n or: [\n {\n equals: \"openai/gpt-4o\"\n },\n {\n equals: \"openai/gpt-4o-2024-08-06\"\n },\n {\n equals: \"openai/gpt-4o-2024-11-20\"\n },\n {\n equals: \"openai/gpt-4o-audio-preview\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 2.5,\n output_mtok: 10\n }\n },\n {\n id: \"openai/gpt-4o-2024-05-13\",\n name: \"GPT-4o (2024-05-13)\",\n match: {\n equals: \"openai/gpt-4o-2024-05-13\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 5,\n output_mtok: 15\n }\n },\n {\n id: \"openai/gpt-4o-mini\",\n name: \"GPT-4o-mini\",\n match: {\n or: [\n {\n equals: \"openai/gpt-4o-mini\"\n },\n {\n equals: \"openai/gpt-4o-mini-2024-07-18\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.075,\n output_mtok: 0.6\n }\n },\n {\n id: \"openai/gpt-4o-mini-search-preview\",\n name: \"GPT-4o-mini Search Preview\",\n match: {\n equals: \"openai/gpt-4o-mini-search-preview\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.6\n }\n },\n {\n id: \"openai/gpt-4o-search-preview\",\n name: \"GPT-4o Search Preview\",\n match: {\n equals: \"openai/gpt-4o-search-preview\"\n },\n prices: {\n input_mtok: 2.5,\n output_mtok: 10\n }\n },\n {\n id: \"openai/gpt-4o:extended\",\n match: {\n equals: \"openai/gpt-4o:extended\"\n },\n prices: {\n input_mtok: 6,\n output_mtok: 18\n }\n },\n {\n id: \"openai/gpt-5\",\n name: \"GPT-5\",\n match: {\n or: [\n {\n equals: \"openai/gpt-5\"\n },\n {\n equals: \"openai/gpt-5-2025-08-07\"\n },\n {\n equals: \"openai/gpt-5-chat\"\n },\n {\n equals: \"openai/gpt-5-codex\"\n },\n {\n equals: \"openai/gpt-5.1-codex-max\"\n }\n ]\n },\n context_window: 400000,\n prices: {\n input_mtok: 1.25,\n cache_read_mtok: 0.125,\n output_mtok: 10\n }\n },\n {\n id: \"openai/gpt-5-image\",\n name: \"GPT-5 Image\",\n match: {\n equals: \"openai/gpt-5-image\"\n },\n context_window: 400000,\n prices: {\n input_mtok: 10,\n cache_read_mtok: 1.25,\n output_mtok: 10\n }\n },\n {\n id: \"openai/gpt-5-image-mini\",\n name: \"GPT-5 Image Mini\",\n match: {\n equals: \"openai/gpt-5-image-mini\"\n },\n context_window: 400000,\n prices: {\n input_mtok: 2.5,\n cache_read_mtok: 0.25,\n output_mtok: 2\n }\n },\n {\n id: \"openai/gpt-5-mini\",\n name: \"GPT-5 Mini\",\n match: {\n or: [\n {\n equals: \"openai/gpt-5-mini\"\n },\n {\n equals: \"openai/gpt-5-mini-2025-08-07\"\n }\n ]\n },\n context_window: 400000,\n prices: {\n input_mtok: 0.25,\n cache_read_mtok: 0.025,\n output_mtok: 2\n }\n },\n {\n id: \"openai/gpt-5-nano\",\n name: \"GPT-5 Nano\",\n match: {\n equals: \"openai/gpt-5-nano\"\n },\n context_window: 400000,\n prices: {\n input_mtok: 0.05,\n cache_read_mtok: 0.01,\n output_mtok: 0.4\n }\n },\n {\n id: \"openai/gpt-5-pro\",\n name: \"GPT-5 Pro\",\n match: {\n equals: \"openai/gpt-5-pro\"\n },\n context_window: 400000,\n prices: {\n input_mtok: 15,\n output_mtok: 120\n }\n },\n {\n id: \"openai/gpt-5.1\",\n name: \"GPT-5.1\",\n match: {\n or: [\n {\n equals: \"openai/gpt-5.1\"\n },\n {\n equals: \"openai/gpt-5.1-chat\"\n },\n {\n equals: \"openai/gpt-5.1-codex\"\n }\n ]\n },\n context_window: 400000,\n prices: {\n input_mtok: 1.25,\n cache_read_mtok: 0.13,\n output_mtok: 10\n }\n },\n {\n id: \"openai/gpt-5.1-codex-mini\",\n name: \"GPT-5.1-Codex-Mini\",\n match: {\n or: [\n {\n equals: \"openai/gpt-5.1-codex-mini\"\n },\n {\n equals: \"openai/gpt-5.1-codex-mini-20251113\"\n }\n ]\n },\n context_window: 400000,\n prices: {\n input_mtok: 0.25,\n cache_read_mtok: 0.025,\n output_mtok: 2\n }\n },\n {\n id: \"openai/gpt-5.2\",\n name: \"GPT-5.2\",\n match: {\n or: [\n {\n equals: \"openai/gpt-5.2\"\n },\n {\n equals: \"openai/gpt-5.2-20251211\"\n },\n {\n equals: \"openai/gpt-5.2-chat\"\n },\n {\n equals: \"openai/gpt-5.2-codex\"\n }\n ]\n },\n context_window: 400000,\n prices: {\n input_mtok: 1.75,\n cache_read_mtok: 0.175,\n output_mtok: 14\n }\n },\n {\n id: \"openai/gpt-5.2-pro\",\n name: \"GPT-5.2 Pro\",\n match: {\n or: [\n {\n equals: \"openai/gpt-5.2-pro\"\n },\n {\n equals: \"openai/gpt-5.2-pro-20251211\"\n }\n ]\n },\n context_window: 400000,\n prices: {\n input_mtok: 21,\n output_mtok: 168\n }\n },\n {\n id: \"openai/gpt-5.3-chat\",\n name: \"GPT-5.3 Chat\",\n match: {\n equals: \"openai/gpt-5.3-chat\"\n },\n prices: {\n input_mtok: 1.75,\n cache_read_mtok: 0.175,\n output_mtok: 14\n }\n },\n {\n id: \"openai/gpt-5.3-codex\",\n name: \"GPT-5.3-Codex\",\n match: {\n or: [\n {\n equals: \"openai/gpt-5.3-codex\"\n },\n {\n equals: \"openai/gpt-5.3-codex-20260224\"\n }\n ]\n },\n context_window: 400000,\n prices: {\n input_mtok: 1.75,\n cache_read_mtok: 0.175,\n output_mtok: 14\n }\n },\n {\n id: \"openai/gpt-5.4\",\n name: \"GPT-5.4\",\n match: {\n or: [\n {\n equals: \"openai/gpt-5.4\"\n },\n {\n equals: \"openai/gpt-5.4-20260305\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"Long-context tier (>272K prompt tokens) is 2x input and 1.5x output, matching OpenAI. Ref: https://openrouter.ai/api/v1/models (pricing.overrides).\",\n prices: {\n input_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 272000,\n price: 0.5\n }\n ]\n },\n output_mtok: {\n base: 15,\n tiers: [\n {\n start: 272000,\n price: 22.5\n }\n ]\n }\n }\n },\n {\n id: \"openai/gpt-5.4-image-2\",\n name: \"GPT-5.4 Image 2\",\n match: {\n equals: \"openai/gpt-5.4-image-2\"\n },\n context_window: 272000,\n prices: {\n input_mtok: 8,\n cache_read_mtok: 2,\n output_mtok: 15\n }\n },\n {\n id: \"openai/gpt-5.4-mini\",\n name: \"GPT-5.4 Mini\",\n match: {\n equals: \"openai/gpt-5.4-mini\"\n },\n context_window: 400000,\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 4.5\n }\n },\n {\n id: \"openai/gpt-5.4-nano\",\n name: \"GPT-5.4 Nano\",\n match: {\n equals: \"openai/gpt-5.4-nano\"\n },\n context_window: 400000,\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.02,\n output_mtok: 1.25\n }\n },\n {\n id: \"openai/gpt-5.4-pro\",\n name: \"GPT-5.4 Pro\",\n match: {\n equals: \"openai/gpt-5.4-pro\"\n },\n context_window: 1050000,\n price_comments: \"Long-context tier (>272K prompt tokens) is 2x input and 1.5x output, matching OpenAI. Ref: https://openrouter.ai/api/v1/models (pricing.overrides).\",\n prices: {\n input_mtok: {\n base: 30,\n tiers: [\n {\n start: 272000,\n price: 60\n }\n ]\n },\n output_mtok: {\n base: 180,\n tiers: [\n {\n start: 272000,\n price: 270\n }\n ]\n }\n }\n },\n {\n id: \"openai/gpt-5.5\",\n name: \"GPT-5.5\",\n match: {\n equals: \"openai/gpt-5.5\"\n },\n context_window: 1050000,\n price_comments: \"Long-context tier (>272K prompt tokens) is 2x input and 1.5x output, matching OpenAI. Ref: https://openrouter.ai/api/v1/models (pricing.overrides).\",\n prices: {\n input_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 10\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 272000,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 30,\n tiers: [\n {\n start: 272000,\n price: 45\n }\n ]\n }\n }\n },\n {\n id: \"openai/gpt-5.5-pro\",\n name: \"GPT-5.5 Pro\",\n match: {\n equals: \"openai/gpt-5.5-pro\"\n },\n context_window: 1050000,\n price_comments: \"Long-context tier (>272K prompt tokens) is 2x input and 1.5x output, matching OpenAI. Ref: https://openrouter.ai/api/v1/models (pricing.overrides).\",\n prices: {\n input_mtok: {\n base: 30,\n tiers: [\n {\n start: 272000,\n price: 60\n }\n ]\n },\n output_mtok: {\n base: 180,\n tiers: [\n {\n start: 272000,\n price: 270\n }\n ]\n }\n }\n },\n {\n id: \"openai/gpt-5.6-luna\",\n name: \"GPT-5.6 Luna\",\n match: {\n or: [\n {\n equals: \"openai/gpt-5.6-luna\"\n },\n {\n regex: \"^openai/gpt-5\\\\.6-luna-\\\\d{8}$\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"Cache writes are billed at 1.25x the uncached input rate. Long-context tier (>272K prompt tokens) is 2x input and 1.5x output, matching OpenAI. Ref: https://openrouter.ai/api/v1/models (pricing.overrides).\",\n prices: {\n input_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n cache_write_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 272000,\n price: 0.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.02,\n tiers: [\n {\n start: 272000,\n price: 0.04\n }\n ]\n },\n output_mtok: {\n base: 1.2,\n tiers: [\n {\n start: 272000,\n price: 1.8\n }\n ]\n }\n }\n },\n {\n id: \"openai/gpt-5.6-sol\",\n name: \"GPT-5.6 Sol\",\n match: {\n or: [\n {\n equals: \"openai/gpt-5.6-sol\"\n },\n {\n regex: \"^openai/gpt-5\\\\.6-sol-\\\\d{8}$\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"Cache writes are billed at 1.25x the uncached input rate. Long-context tier (>272K prompt tokens) is 2x input and 1.5x output, matching OpenAI. Ref: https://openrouter.ai/api/v1/models (pricing.overrides).\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 15\n }\n ]\n }\n }\n },\n {\n id: \"openai/gpt-5.6-terra\",\n name: \"GPT-5.6 Terra\",\n match: {\n or: [\n {\n equals: \"openai/gpt-5.6-terra\"\n },\n {\n regex: \"^openai/gpt-5\\\\.6-terra-\\\\d{8}$\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"Cache writes are billed at 1.25x the uncached input rate. Long-context tier (>272K prompt tokens) is 2x input and 1.5x output, matching OpenAI. Ref: https://openrouter.ai/api/v1/models (pricing.overrides).\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 12,\n tiers: [\n {\n start: 272000,\n price: 18\n }\n ]\n }\n }\n },\n {\n id: \"openai/gpt-6-luna\",\n name: \"GPT-6 Luna\",\n match: {\n or: [\n {\n equals: \"openai/gpt-6-luna\"\n },\n {\n equals: \"openai/gpt-6-luna-pro\"\n },\n {\n regex: \"^openai/gpt-6-luna-\\\\d{8}$\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"OpenRouter lists the base and pro routes at the same rates. Long-context tier (>272K prompt tokens) is 2x input and cache rates and 1.5x output. Ref: https://openrouter.ai/api/v1/models (pricing.overrides).\",\n prices: {\n input_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n cache_write_mtok: {\n base: 0.125,\n tiers: [\n {\n start: 272000,\n price: 0.25\n }\n ]\n },\n cache_read_mtok: {\n base: 0.01,\n tiers: [\n {\n start: 272000,\n price: 0.02\n }\n ]\n },\n output_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 272000,\n price: 0.75\n }\n ]\n },\n web_searches_kcount: 10\n }\n },\n {\n id: \"openai/gpt-6-luna:batch\",\n name: \"GPT-6 Luna Batch\",\n match: {\n or: [\n {\n equals: \"openai/gpt-6-luna:batch\"\n },\n {\n equals: \"openai/gpt-6-luna-pro:batch\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"OpenRouter's batch routes bill input, cache, and output tokens at half the standard rates; web searches retain their $0.01 per-call rate. Ref: https://openrouter.ai/api/v1/models.\",\n prices: {\n input_mtok: {\n base: 0.05,\n tiers: [\n {\n start: 272000,\n price: 0.1\n }\n ]\n },\n cache_write_mtok: {\n base: 0.0625,\n tiers: [\n {\n start: 272000,\n price: 0.125\n }\n ]\n },\n cache_read_mtok: {\n base: 0.005,\n tiers: [\n {\n start: 272000,\n price: 0.01\n }\n ]\n },\n output_mtok: {\n base: 0.25,\n tiers: [\n {\n start: 272000,\n price: 0.375\n }\n ]\n },\n web_searches_kcount: 10\n }\n },\n {\n id: \"openai/gpt-6-sol\",\n name: \"GPT-6 Sol\",\n match: {\n or: [\n {\n equals: \"openai/gpt-6-sol\"\n },\n {\n equals: \"openai/gpt-6-sol-pro\"\n },\n {\n regex: \"^openai/gpt-6-sol-\\\\d{8}$\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"OpenRouter lists the base and pro routes at the same rates. Long-context tier (>272K prompt tokens) is 2x input and cache rates and 1.5x output. Ref: https://openrouter.ai/api/v1/models (pricing.overrides).\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 272000,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 15\n }\n ]\n },\n web_searches_kcount: 10\n }\n },\n {\n id: \"openai/gpt-6-sol:batch\",\n name: \"GPT-6 Sol Batch\",\n match: {\n or: [\n {\n equals: \"openai/gpt-6-sol:batch\"\n },\n {\n equals: \"openai/gpt-6-sol-pro:batch\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"OpenRouter's batch routes bill input, cache, and output tokens at half the standard rates; web searches retain their $0.01 per-call rate. Ref: https://openrouter.ai/api/v1/models.\",\n prices: {\n input_mtok: {\n base: 1,\n tiers: [\n {\n start: 272000,\n price: 2\n }\n ]\n },\n cache_write_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 272000,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n output_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 7.5\n }\n ]\n },\n web_searches_kcount: 10\n }\n },\n {\n id: \"openai/gpt-6.1-sol\",\n name: \"GPT-6.1 Sol\",\n match: {\n or: [\n {\n equals: \"openai/gpt-6.1-sol\"\n },\n {\n equals: \"openai/gpt-6.1-sol-pro\"\n },\n {\n regex: \"^openai/gpt-6\\\\.1-sol-\\\\d{8}$\"\n },\n {\n regex: \"^openai/gpt-6\\\\.1-sol-pro-\\\\d{8}$\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"OpenRouter lists the base and pro routes at the same rates. Long-context tier (>272K prompt tokens) is 2x input and cache rates and 1.5x output. Ref: https://openrouter.ai/api/v1/models (pricing.overrides).\",\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 272000,\n price: 4\n }\n ]\n },\n cache_write_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 272000,\n price: 5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 272000,\n price: 0.2\n }\n ]\n },\n output_mtok: {\n base: 10,\n tiers: [\n {\n start: 272000,\n price: 15\n }\n ]\n },\n web_searches_kcount: 10\n }\n },\n {\n id: \"openai/gpt-6.1-sol:batch\",\n name: \"GPT-6.1 Sol Batch\",\n match: {\n or: [\n {\n equals: \"openai/gpt-6.1-sol:batch\"\n },\n {\n equals: \"openai/gpt-6.1-sol-pro:batch\"\n }\n ]\n },\n context_window: 1050000,\n price_comments: \"OpenRouter's batch routes bill input, cache, and output tokens at half the standard rates; web searches retain their $0.01 per-call rate. Ref: https://openrouter.ai/api/v1/models.\",\n prices: {\n input_mtok: {\n base: 1,\n tiers: [\n {\n start: 272000,\n price: 2\n }\n ]\n },\n cache_write_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 272000,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.05,\n tiers: [\n {\n start: 272000,\n price: 0.1\n }\n ]\n },\n output_mtok: {\n base: 5,\n tiers: [\n {\n start: 272000,\n price: 7.5\n }\n ]\n },\n web_searches_kcount: 10\n }\n },\n {\n id: \"openai/gpt-audio\",\n name: \"GPT Audio\",\n match: {\n equals: \"openai/gpt-audio\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 2.5,\n output_mtok: 10\n }\n },\n {\n id: \"openai/gpt-audio-mini\",\n name: \"GPT Audio Mini\",\n match: {\n equals: \"openai/gpt-audio-mini\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.6,\n output_mtok: 2.4\n }\n },\n {\n id: \"openai/gpt-chat-latest\",\n name: \"GPT Chat Latest\",\n match: {\n equals: \"openai/gpt-chat-latest\"\n },\n context_window: 400000,\n prices: {\n input_mtok: 5,\n cache_read_mtok: 0.5,\n output_mtok: 30\n }\n },\n {\n id: \"openai/gpt-oss-120b\",\n name: \"gpt-oss-120b\",\n match: {\n equals: \"openai/gpt-oss-120b\"\n },\n prices: {\n input_mtok: 0.039,\n output_mtok: 0.18\n }\n },\n {\n id: \"openai/gpt-oss-120b:free\",\n name: \"gpt-oss-120b (free)\",\n match: {\n equals: \"openai/gpt-oss-120b:free\"\n },\n prices: {}\n },\n {\n id: \"openai/gpt-oss-20b\",\n name: \"gpt-oss-20b\",\n match: {\n equals: \"openai/gpt-oss-20b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.029,\n output_mtok: 0.14\n }\n },\n {\n id: \"openai/gpt-oss-20b:free\",\n name: \"gpt-oss-20b (free)\",\n match: {\n equals: \"openai/gpt-oss-20b:free\"\n },\n prices: {}\n },\n {\n id: \"openai/gpt-oss-safeguard-20b\",\n name: \"gpt-oss-safeguard-20b\",\n match: {\n equals: \"openai/gpt-oss-safeguard-20b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.075,\n cache_read_mtok: 0.037,\n output_mtok: 0.3\n }\n },\n {\n id: \"openai/o1\",\n name: \"o1\",\n match: {\n or: [\n {\n equals: \"openai/o1\"\n },\n {\n equals: \"o1-preview\"\n },\n {\n equals: \"o1-preview-2024-09-12\"\n }\n ]\n },\n prices: {\n input_mtok: 15,\n cache_read_mtok: 7.5,\n output_mtok: 60\n }\n },\n {\n id: \"openai/o1-mini\",\n match: {\n or: [\n {\n equals: \"openai/o1-mini\"\n },\n {\n equals: \"openai/o1-mini-2024-09-12\"\n }\n ]\n },\n prices: {\n input_mtok: 1.1,\n output_mtok: 4.4\n }\n },\n {\n id: \"openai/o1-pro\",\n name: \"o1-pro\",\n match: {\n equals: \"openai/o1-pro\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 150,\n output_mtok: 600\n }\n },\n {\n id: \"openai/o3\",\n name: \"o3\",\n match: {\n equals: \"openai/o3\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.5,\n output_mtok: 8\n }\n },\n {\n id: \"openai/o3-deep-research\",\n name: \"o3 Deep Research\",\n match: {\n equals: \"openai/o3-deep-research\"\n },\n prices: {\n input_mtok: 10,\n cache_read_mtok: 2.5,\n output_mtok: 40\n }\n },\n {\n id: \"openai/o3-mini\",\n name: \"o3 Mini\",\n match: {\n or: [\n {\n equals: \"openai/o3-mini\"\n },\n {\n equals: \"openai/o3-mini-high\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 1.1,\n cache_read_mtok: 0.55,\n output_mtok: 4.4\n }\n },\n {\n id: \"openai/o3-pro\",\n name: \"o3 Pro\",\n match: {\n equals: \"openai/o3-pro\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 20,\n output_mtok: 80\n }\n },\n {\n id: \"openai/o4-mini\",\n name: \"o4 Mini\",\n match: {\n or: [\n {\n equals: \"openai/o4-mini\"\n },\n {\n equals: \"openai/o4-mini-high\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 1.1,\n cache_read_mtok: 0.275,\n output_mtok: 4.4\n }\n },\n {\n id: \"openai/o4-mini-deep-research\",\n name: \"o4 Mini Deep Research\",\n match: {\n equals: \"openai/o4-mini-deep-research\"\n },\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.5,\n output_mtok: 8\n }\n },\n {\n id: \"openchat/openchat-7b\",\n match: {\n equals: \"openchat/openchat-7b\"\n },\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.07\n }\n },\n {\n id: \"openhands-lm-32b-v0.1\",\n name: \"OpenHands LM 32B V0.1\",\n match: {\n equals: \"openhands-lm-32b-v0.1\"\n },\n prices: {\n input_mtok: 2.6,\n output_mtok: 3.4\n }\n },\n {\n id: \"perceptron/perceptron-mk1\",\n name: \"Perceptron Mk1\",\n match: {\n equals: \"perceptron/perceptron-mk1\"\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.15,\n output_mtok: 1.5\n }\n },\n {\n id: \"perplexity/llama-3.1-sonar-large-128k-online\",\n match: {\n equals: \"perplexity/llama-3.1-sonar-large-128k-online\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 1\n }\n },\n {\n id: \"perplexity/llama-3.1-sonar-small-128k-online\",\n match: {\n equals: \"perplexity/llama-3.1-sonar-small-128k-online\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"perplexity/pplx-decider-v1-27b\",\n name: \"Decider V1 27B\",\n match: {\n equals: \"perplexity/pplx-decider-v1-27b\"\n },\n context_window: 262144,\n price_comments: \"OpenRouter bills this id at $0.04 per million input tokens. Perplexity's own API bills the same id at $0.02. https://openrouter.ai/api/v1/models/perplexity/pplx-decider-v1-27b/endpoints\",\n prices: {\n input_mtok: 0.04\n }\n },\n {\n id: \"perplexity/pplx-decider-v1.1-27b\",\n name: \"Decider V1.1 27B\",\n match: {\n equals: \"perplexity/pplx-decider-v1.1-27b\"\n },\n context_window: 262144,\n price_comments: \"Same $0.02 input rate as Perplexity's API. Output is free. https://openrouter.ai/api/v1/models/perplexity/pplx-decider-v1.1-27b/endpoints\",\n prices: {\n input_mtok: 0.02\n }\n },\n {\n id: \"perplexity/r1-1776\",\n match: {\n equals: \"perplexity/r1-1776\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 8\n }\n },\n {\n id: \"perplexity/sonar\",\n match: {\n equals: \"perplexity/sonar\"\n },\n context_window: 127072,\n prices: {\n input_mtok: 1,\n output_mtok: 1\n }\n },\n {\n id: \"perplexity/sonar-deep-research\",\n match: {\n equals: \"perplexity/sonar-deep-research\"\n },\n context_window: 128000,\n price_comments: \"OpenRouter reports internal reasoning as a distinct $3 per million-token charge. Ref: https://openrouter.ai/perplexity/sonar-deep-research/pricing\",\n prices: {\n input_mtok: 2,\n output_mtok: 8,\n output_reasoning_mtok: 3\n }\n },\n {\n id: \"perplexity/sonar-pro\",\n match: {\n equals: \"perplexity/sonar-pro\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 3,\n output_mtok: 15\n }\n },\n {\n id: \"perplexity/sonar-reasoning\",\n match: {\n equals: \"perplexity/sonar-reasoning\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 5\n }\n },\n {\n id: \"perplexity/sonar-reasoning-pro\",\n match: {\n equals: \"perplexity/sonar-reasoning-pro\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 2,\n output_mtok: 8\n }\n },\n {\n id: \"phi-3-medium-128k-instruct\",\n name: \"Phi-3 Medium 128K Instruct\",\n match: {\n equals: \"phi-3-medium-128k-instruct\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 1\n }\n },\n {\n id: \"phi-3-mini-128k-instruct\",\n name: \"Phi-3 Mini 128K Instruct\",\n match: {\n equals: \"phi-3-mini-128k-instruct\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"phi-3.5-mini-128k-instruct\",\n name: \"Phi-3.5 Mini 128K Instruct\",\n match: {\n equals: \"phi-3.5-mini-128k-instruct\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"phi-4-multimodal-instruct\",\n name: \"Phi 4 Multimodal Instruct\",\n match: {\n equals: \"phi-4-multimodal-instruct\"\n },\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.1\n }\n },\n {\n id: \"phi-4-reasoning-plus\",\n name: \"Phi 4 Reasoning Plus\",\n match: {\n equals: \"phi-4-reasoning-plus\"\n },\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.35\n }\n },\n {\n id: \"phi-4-reasoning-plus:free\",\n name: \"Phi 4 Reasoning Plus (free)\",\n match: {\n equals: \"phi-4-reasoning-plus:free\"\n },\n prices: {}\n },\n {\n id: \"phi-4-reasoning:free\",\n name: \"Phi 4 Reasoning (free)\",\n match: {\n equals: \"phi-4-reasoning:free\"\n },\n prices: {}\n },\n {\n id: \"pixtral-12b\",\n name: \"Pixtral 12B\",\n match: {\n equals: \"pixtral-12b\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"pixtral-large-2411\",\n name: \"Pixtral Large 2411\",\n match: {\n equals: \"pixtral-large-2411\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 6\n }\n },\n {\n id: \"poolside/laguna-m.1:free\",\n name: \"Laguna M.1 (free)\",\n match: {\n equals: \"poolside/laguna-m.1:free\"\n },\n prices: {}\n },\n {\n id: \"poolside/laguna-xs.2:free\",\n name: \"Laguna XS.2 (free)\",\n match: {\n equals: \"poolside/laguna-xs.2:free\"\n },\n prices: {}\n },\n {\n id: \"prime-intellect/intellect-3\",\n name: \"INTELLECT-3\",\n match: {\n equals: \"prime-intellect/intellect-3\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 1.1\n }\n },\n {\n id: \"pygmalionai/mythalion-13b\",\n match: {\n equals: \"pygmalionai/mythalion-13b\"\n },\n prices: {\n input_mtok: 0.5625,\n output_mtok: 1.125\n }\n },\n {\n id: \"qwen-2-72b-instruct\",\n name: \"Qwen 2 72B Instruct\",\n match: {\n equals: \"qwen-2-72b-instruct\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"qwen-2.5-72b-instruct:free\",\n name: \"Qwen2.5 72B Instruct (free)\",\n match: {\n equals: \"qwen-2.5-72b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"qwen-2.5-coder-32b-instruct:free\",\n name: \"Qwen2.5 Coder 32B Instruct (free)\",\n match: {\n equals: \"qwen-2.5-coder-32b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"qwen-2.5-vl-7b-instruct\",\n name: \"Qwen2.5-VL 7B Instruct\",\n match: {\n equals: \"qwen-2.5-vl-7b-instruct\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"qwen-max\",\n name: \"Qwen-Max\",\n match: {\n equals: \"qwen-max\"\n },\n prices: {\n input_mtok: 1.6,\n cache_read_mtok: 0.64,\n output_mtok: 6.4\n }\n },\n {\n id: \"qwen-turbo\",\n name: \"Qwen-Turbo\",\n match: {\n equals: \"qwen-turbo\"\n },\n prices: {\n input_mtok: 0.05,\n cache_read_mtok: 0.02,\n output_mtok: 0.2\n }\n },\n {\n id: \"qwen-vl-max\",\n name: \"Qwen VL Max\",\n match: {\n equals: \"qwen-vl-max\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 3.2\n }\n },\n {\n id: \"qwen-vl-plus\",\n name: \"Qwen VL Plus\",\n match: {\n equals: \"qwen-vl-plus\"\n },\n prices: {\n input_mtok: 0.21,\n output_mtok: 0.63\n }\n },\n {\n id: \"qwen/qwen-2-72b-instruct\",\n match: {\n equals: \"qwen/qwen-2-72b-instruct\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"qwen/qwen-2.5-72b-instruct\",\n name: \"Qwen2.5 72B Instruct\",\n match: {\n equals: \"qwen/qwen-2.5-72b-instruct\"\n },\n prices: {\n input_mtok: 0.12,\n output_mtok: 0.39\n }\n },\n {\n id: \"qwen/qwen-2.5-72b-instruct:free\",\n match: {\n equals: \"qwen/qwen-2.5-72b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"qwen/qwen-2.5-7b-instruct\",\n name: \"Qwen2.5 7B Instruct\",\n match: {\n equals: \"qwen/qwen-2.5-7b-instruct\"\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.04,\n output_mtok: 0.1\n }\n },\n {\n id: \"qwen/qwen-2.5-7b-instruct:free\",\n match: {\n equals: \"qwen/qwen-2.5-7b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"qwen/qwen-2.5-coder-32b-instruct\",\n name: \"Qwen2.5 Coder 32B Instruct\",\n match: {\n equals: \"qwen/qwen-2.5-coder-32b-instruct\"\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.15\n }\n },\n {\n id: \"qwen/qwen-2.5-coder-32b-instruct:free\",\n match: {\n equals: \"qwen/qwen-2.5-coder-32b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"qwen/qwen-2.5-vl-72b-instruct\",\n match: {\n equals: \"qwen/qwen-2.5-vl-72b-instruct\"\n },\n prices: {\n input_mtok: 0.6,\n output_mtok: 0.6\n }\n },\n {\n id: \"qwen/qwen-2.5-vl-7b-instruct\",\n match: {\n equals: \"qwen/qwen-2.5-vl-7b-instruct\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"qwen/qwen-2.5-vl-7b-instruct:free\",\n match: {\n equals: \"qwen/qwen-2.5-vl-7b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"qwen/qwen-max\",\n match: {\n equals: \"qwen/qwen-max\"\n },\n prices: {\n input_mtok: 1.6,\n output_mtok: 6.4\n }\n },\n {\n id: \"qwen/qwen-plus\",\n name: \"Qwen-Plus\",\n match: {\n equals: \"qwen/qwen-plus\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.4,\n cache_read_mtok: 0.16,\n output_mtok: 1.2\n }\n },\n {\n id: \"qwen/qwen-plus-2025-07-28\",\n name: \"Qwen Plus 0728\",\n match: {\n equals: \"qwen/qwen-plus-2025-07-28\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.26,\n output_mtok: 0.78\n }\n },\n {\n id: \"qwen/qwen-plus-2025-07-28:thinking\",\n name: \"Qwen Plus 0728 (thinking)\",\n match: {\n equals: \"qwen/qwen-plus-2025-07-28:thinking\"\n },\n prices: {\n input_mtok: 0.26,\n cache_write_mtok: 0.325,\n output_mtok: 0.78\n }\n },\n {\n id: \"qwen/qwen-turbo\",\n match: {\n equals: \"qwen/qwen-turbo\"\n },\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.2\n }\n },\n {\n id: \"qwen/qwen-vl-max\",\n match: {\n equals: \"qwen/qwen-vl-max\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 3.2\n }\n },\n {\n id: \"qwen/qwen-vl-plus\",\n match: {\n equals: \"qwen/qwen-vl-plus\"\n },\n prices: {\n input_mtok: 0.21,\n output_mtok: 0.63\n }\n },\n {\n id: \"qwen/qwen2.5-coder-7b-instruct\",\n match: {\n equals: \"qwen/qwen2.5-coder-7b-instruct\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"qwen/qwen2.5-vl-32b-instruct\",\n match: {\n equals: \"qwen/qwen2.5-vl-32b-instruct\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"qwen/qwen2.5-vl-32b-instruct:free\",\n match: {\n equals: \"qwen/qwen2.5-vl-32b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"qwen/qwen2.5-vl-3b-instruct:free\",\n match: {\n equals: \"qwen/qwen2.5-vl-3b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"qwen/qwen2.5-vl-72b-instruct\",\n match: {\n equals: \"qwen/qwen2.5-vl-72b-instruct\"\n },\n prices: {\n input_mtok: 0.7,\n output_mtok: 0.7\n }\n },\n {\n id: \"qwen/qwen2.5-vl-72b-instruct:free\",\n match: {\n equals: \"qwen/qwen2.5-vl-72b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"qwen/qwen3-14b\",\n name: \"Qwen3 14B\",\n match: {\n equals: \"qwen/qwen3-14b\"\n },\n prices: {\n input_mtok: 0.06,\n output_mtok: 0.24\n }\n },\n {\n id: \"qwen/qwen3-235b-a22b\",\n name: \"Qwen3 235B A22B\",\n match: {\n equals: \"qwen/qwen3-235b-a22b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.13,\n output_mtok: 0.6\n }\n },\n {\n id: \"qwen/qwen3-235b-a22b-2507\",\n name: \"Qwen3 235B A22B Instruct 2507\",\n match: {\n equals: \"qwen/qwen3-235b-a22b-2507\"\n },\n prices: {\n input_mtok: 0.09,\n output_mtok: 0.1\n }\n },\n {\n id: \"qwen/qwen3-235b-a22b-thinking-2507\",\n name: \"Qwen3 235B A22B Thinking 2507\",\n match: {\n equals: \"qwen/qwen3-235b-a22b-thinking-2507\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"qwen/qwen3-30b-a3b\",\n name: \"Qwen3 30B A3B\",\n match: {\n equals: \"qwen/qwen3-30b-a3b\"\n },\n prices: {\n input_mtok: 0.08,\n output_mtok: 0.29\n }\n },\n {\n id: \"qwen/qwen3-30b-a3b-instruct-2507\",\n name: \"Qwen3 30B A3B Instruct 2507\",\n match: {\n equals: \"qwen/qwen3-30b-a3b-instruct-2507\"\n },\n prices: {\n input_mtok: 0.04815,\n output_mtok: 0.19305\n }\n },\n {\n id: \"qwen/qwen3-30b-a3b-thinking-2507\",\n name: \"Qwen3 30B A3B Thinking 2507\",\n match: {\n equals: \"qwen/qwen3-30b-a3b-thinking-2507\"\n },\n context_window: 81920,\n prices: {\n input_mtok: 0.08,\n output_mtok: 0.4\n }\n },\n {\n id: \"qwen/qwen3-32b\",\n name: \"Qwen3 32B\",\n match: {\n equals: \"qwen/qwen3-32b\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.3\n }\n },\n {\n id: \"qwen/qwen3-8b\",\n name: \"Qwen3 8B\",\n match: {\n equals: \"qwen/qwen3-8b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.035,\n output_mtok: 0.138\n }\n },\n {\n id: \"qwen/qwen3-coder\",\n name: \"Qwen3 Coder 480B A35B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-coder\"\n },\n {\n equals: \"qwen/qwen3-coder-480b-a35b-07-25\"\n }\n ]\n },\n prices: {\n input_mtok: 0.22,\n output_mtok: 1.8\n }\n },\n {\n id: \"qwen/qwen3-coder-30b-a3b-instruct\",\n name: \"Qwen3 Coder 30B A3B Instruct\",\n match: {\n equals: \"qwen/qwen3-coder-30b-a3b-instruct\"\n },\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.27\n }\n },\n {\n id: \"qwen/qwen3-coder-flash\",\n name: \"Qwen3 Coder Flash\",\n match: {\n equals: \"qwen/qwen3-coder-flash\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.195,\n cache_write_mtok: 0.24375,\n cache_read_mtok: 0.039,\n output_mtok: 0.975\n }\n },\n {\n id: \"qwen/qwen3-coder-next\",\n name: \"Qwen3 Coder Next\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-coder-next\"\n },\n {\n equals: \"qwen/qwen3-coder-next-2025-02-03\"\n }\n ]\n },\n prices: {\n input_mtok: 0.11,\n cache_read_mtok: 0.07,\n output_mtok: 0.8\n }\n },\n {\n id: \"qwen/qwen3-coder-plus\",\n name: \"Qwen3 Coder Plus\",\n match: {\n equals: \"qwen/qwen3-coder-plus\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.65,\n cache_write_mtok: 0.8125,\n cache_read_mtok: 0.13,\n output_mtok: 3.25\n }\n },\n {\n id: \"qwen/qwen3-coder:free\",\n name: \"Qwen3 Coder 480B A35B (free)\",\n match: {\n equals: \"qwen/qwen3-coder:free\"\n },\n prices: {}\n },\n {\n id: \"qwen/qwen3-max\",\n name: \"Qwen 3 Max\",\n match: {\n equals: \"qwen/qwen3-max\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 1.2,\n output_mtok: 6\n }\n },\n {\n id: \"qwen/qwen3-max-thinking\",\n name: \"Qwen3 Max Thinking\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-max-thinking\"\n },\n {\n equals: \"qwen/qwen3-max-thinking-20260123\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.78,\n output_mtok: 3.9\n }\n },\n {\n id: \"qwen/qwen3-next-80b-a3b-instruct\",\n name: \"Qwen3 Next 80B A3B Instruct\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-next-80b-a3b-instruct\"\n },\n {\n equals: \"qwen/qwen3-next-80b-a3b-instruct-2509\"\n }\n ]\n },\n prices: {\n input_mtok: 0.09,\n output_mtok: 1.1\n }\n },\n {\n id: \"qwen/qwen3-next-80b-a3b-instruct:free\",\n name: \"Qwen3 Next 80B A3B Instruct (free)\",\n match: {\n equals: \"qwen/qwen3-next-80b-a3b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"qwen/qwen3-next-80b-a3b-thinking\",\n name: \"Qwen3 Next 80B A3B Thinking\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3-next-80b-a3b-thinking\"\n },\n {\n equals: \"qwen/qwen3-next-80b-a3b-thinking-2509\"\n }\n ]\n },\n prices: {\n input_mtok: 0.0975,\n output_mtok: 0.78\n }\n },\n {\n id: \"qwen/qwen3-vl-235b-a22b-instruct\",\n name: \"Qwen3 VL 235B A22B Instruct\",\n match: {\n equals: \"qwen/qwen3-vl-235b-a22b-instruct\"\n },\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.11,\n output_mtok: 0.88\n }\n },\n {\n id: \"qwen/qwen3-vl-235b-a22b-thinking\",\n name: \"Qwen3 VL 235B A22B Thinking\",\n match: {\n equals: \"qwen/qwen3-vl-235b-a22b-thinking\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.26,\n output_mtok: 2.6\n }\n },\n {\n id: \"qwen/qwen3-vl-30b-a3b-instruct\",\n name: \"Qwen3 VL 30B A3B Instruct\",\n match: {\n equals: \"qwen/qwen3-vl-30b-a3b-instruct\"\n },\n prices: {\n input_mtok: 0.13,\n output_mtok: 0.52\n }\n },\n {\n id: \"qwen/qwen3-vl-30b-a3b-thinking\",\n name: \"Qwen3 VL 30B A3B Thinking\",\n match: {\n equals: \"qwen/qwen3-vl-30b-a3b-thinking\"\n },\n prices: {\n input_mtok: 0.13,\n output_mtok: 1.56\n }\n },\n {\n id: \"qwen/qwen3-vl-32b-instruct\",\n name: \"Qwen3 VL 32B Instruct\",\n match: {\n equals: \"qwen/qwen3-vl-32b-instruct\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.104,\n output_mtok: 0.416\n }\n },\n {\n id: \"qwen/qwen3-vl-8b-instruct\",\n name: \"Qwen3 VL 8B Instruct\",\n match: {\n equals: \"qwen/qwen3-vl-8b-instruct\"\n },\n prices: {\n input_mtok: 0.08,\n output_mtok: 0.5\n }\n },\n {\n id: \"qwen/qwen3-vl-8b-thinking\",\n name: \"Qwen3 VL 8B Thinking\",\n match: {\n equals: \"qwen/qwen3-vl-8b-thinking\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.117,\n output_mtok: 1.365\n }\n },\n {\n id: \"qwen/qwen3.5-122b-a10b\",\n name: \"Qwen3.5-122B-A10B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.5-122b-a10b\"\n },\n {\n equals: \"qwen/qwen3.5-122b-a10b-20260224\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.26,\n output_mtok: 2.08\n }\n },\n {\n id: \"qwen/qwen3.5-27b\",\n name: \"Qwen3.5-27B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.5-27b\"\n },\n {\n equals: \"qwen/qwen3.5-27b-20260224\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.195,\n output_mtok: 1.56\n }\n },\n {\n id: \"qwen/qwen3.5-35b-a3b\",\n name: \"Qwen3.5-35B-A3B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.5-35b-a3b\"\n },\n {\n equals: \"qwen/qwen3.5-35b-a3b-20260224\"\n }\n ]\n },\n prices: {\n input_mtok: 0.14,\n cache_read_mtok: 0.05,\n output_mtok: 1\n }\n },\n {\n id: \"qwen/qwen3.5-397b-a17b\",\n name: \"Qwen3.5 397B A17B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.5-397b-a17b\"\n },\n {\n equals: \"qwen/qwen3.5-397b-a17b-20260216\"\n }\n ]\n },\n prices: {\n input_mtok: 0.39,\n output_mtok: 2.34\n }\n },\n {\n id: \"qwen/qwen3.5-9b\",\n name: \"Qwen3.5-9B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.5-9b\"\n },\n {\n equals: \"qwen/qwen3.5-9b-20260310\"\n }\n ]\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.15\n }\n },\n {\n id: \"qwen/qwen3.5-flash-02-23\",\n name: \"Qwen3.5-Flash\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.5-flash-02-23\"\n },\n {\n equals: \"qwen/qwen3.5-flash-20260224\"\n }\n ]\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.065,\n output_mtok: 0.26\n }\n },\n {\n id: \"qwen/qwen3.5-plus-02-15\",\n name: \"Qwen3.5 plus-02-15\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.5-plus-02-15\"\n },\n {\n equals: \"qwen/qwen3.5-plus-20260216\"\n }\n ]\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.4,\n output_mtok: 2.4\n }\n },\n {\n id: \"qwen/qwen3.5-plus-20260420\",\n name: \"Qwen3.5 Plus 2026-04-20\",\n match: {\n equals: \"qwen/qwen3.5-plus-20260420\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.3,\n cache_write_mtok: 0.375,\n output_mtok: 1.8\n }\n },\n {\n id: \"qwen/qwen3.6-27b\",\n name: \"Qwen3.6 27B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.6-27b\"\n },\n {\n equals: \"qwen/qwen3.6-27b-20260422\"\n }\n ]\n },\n prices: {\n input_mtok: 0.289,\n output_mtok: 2.4\n }\n },\n {\n id: \"qwen/qwen3.6-35b-a3b\",\n name: \"Qwen3.6 35B A3B\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.6-35b-a3b\"\n },\n {\n equals: \"qwen/qwen3.6-35b-a3b-20260415\"\n }\n ]\n },\n prices: {\n input_mtok: 0.14,\n output_mtok: 1\n }\n },\n {\n id: \"qwen/qwen3.6-flash\",\n name: \"Qwen3.6 Flash\",\n match: {\n equals: \"qwen/qwen3.6-flash\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.1875,\n cache_write_mtok: 0.234375,\n output_mtok: 1.125\n }\n },\n {\n id: \"qwen/qwen3.6-max-preview\",\n name: \"Qwen3.6 Max Preview\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.6-max-preview\"\n },\n {\n equals: \"qwen/qwen3.6-max-preview-20260420\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 1.04,\n cache_write_mtok: 1.3,\n output_mtok: 6.24\n }\n },\n {\n id: \"qwen/qwen3.6-plus\",\n name: \"Qwen3.6 Plus\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.6-plus\"\n },\n {\n equals: \"qwen/qwen3.6-plus-04-02\"\n }\n ]\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.325,\n cache_write_mtok: 0.40625,\n output_mtok: 1.95\n }\n },\n {\n id: \"qwen/qwen3.7-max\",\n name: \"Qwen3.7 Max\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.7-max\"\n },\n {\n equals: \"qwen/qwen3.7-max-20260520\"\n }\n ]\n },\n context_window: 1e6,\n prices: {\n input_mtok: 1.25,\n cache_write_mtok: 1.5625,\n cache_read_mtok: 0.25,\n output_mtok: 3.75\n }\n },\n {\n id: \"qwen/qwen3.7-plus\",\n name: \"Qwen3.7 Plus\",\n match: {\n or: [\n {\n equals: \"qwen/qwen3.7-plus\"\n },\n {\n equals: \"qwen/qwen3.7-plus-20260602\"\n }\n ]\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.4,\n cache_write_mtok: 0.5,\n cache_read_mtok: 0.08,\n output_mtok: 1.6\n }\n },\n {\n id: \"qwen/qwq-32b\",\n match: {\n equals: \"qwen/qwq-32b\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.2\n }\n },\n {\n id: \"qwen/qwq-32b-preview\",\n match: {\n equals: \"qwen/qwq-32b-preview\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"qwen/qwq-32b-preview:free\",\n match: {\n equals: \"qwen/qwq-32b-preview:free\"\n },\n prices: {}\n },\n {\n id: \"qwen/qwq-32b:free\",\n match: {\n equals: \"qwen/qwq-32b:free\"\n },\n prices: {}\n },\n {\n id: \"qwen2.5-vl-32b-instruct\",\n name: \"Qwen2.5 VL 32B Instruct\",\n match: {\n equals: \"qwen2.5-vl-32b-instruct\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"qwen2.5-vl-32b-instruct:free\",\n name: \"Qwen2.5 VL 32B Instruct (free)\",\n match: {\n equals: \"qwen2.5-vl-32b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"qwen2.5-vl-72b-instruct:free\",\n name: \"Qwen2.5 VL 72B Instruct (free)\",\n match: {\n equals: \"qwen2.5-vl-72b-instruct:free\"\n },\n prices: {}\n },\n {\n id: \"qwen3-14b:free\",\n name: \"Qwen3 14B (free)\",\n match: {\n equals: \"qwen3-14b:free\"\n },\n prices: {}\n },\n {\n id: \"qwen3-235b-a22b:free\",\n name: \"Qwen3 235B A22B (free)\",\n match: {\n equals: \"qwen3-235b-a22b:free\"\n },\n prices: {}\n },\n {\n id: \"qwen3-30b-a3b:free\",\n name: \"Qwen3 30B A3B (free)\",\n match: {\n equals: \"qwen3-30b-a3b:free\"\n },\n prices: {}\n },\n {\n id: \"qwen3-32b:free\",\n name: \"Qwen3 32B (free)\",\n match: {\n equals: \"qwen3-32b:free\"\n },\n prices: {}\n },\n {\n id: \"qwen3-8b:free\",\n name: \"Qwen3 8B (free)\",\n match: {\n equals: \"qwen3-8b:free\"\n },\n prices: {}\n },\n {\n id: \"qwerky-72b:free\",\n name: \"Qwerky 72B (free)\",\n match: {\n equals: \"qwerky-72b:free\"\n },\n prices: {}\n },\n {\n id: \"qwq-32b\",\n name: \"QwQ 32B\",\n match: {\n equals: \"qwq-32b\"\n },\n prices: {\n input_mtok: 0.15,\n output_mtok: 0.2\n }\n },\n {\n id: \"qwq-32b-arliai-rpr-v1:free\",\n name: \"QwQ 32B RpR v1 (free)\",\n match: {\n equals: \"qwq-32b-arliai-rpr-v1:free\"\n },\n prices: {}\n },\n {\n id: \"qwq-32b-preview\",\n name: \"QwQ 32B Preview\",\n match: {\n equals: \"qwq-32b-preview\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"qwq-32b:free\",\n name: \"QwQ 32B (free)\",\n match: {\n equals: \"qwq-32b:free\"\n },\n prices: {}\n },\n {\n id: \"r1-1776\",\n name: \"R1 1776\",\n match: {\n equals: \"r1-1776\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 8\n }\n },\n {\n id: \"raifle/sorcererlm-8x22b\",\n match: {\n equals: \"raifle/sorcererlm-8x22b\"\n },\n prices: {\n input_mtok: 4.5,\n output_mtok: 4.5\n }\n },\n {\n id: \"reka-flash-3:free\",\n name: \"Flash 3 (free)\",\n match: {\n equals: \"reka-flash-3:free\"\n },\n prices: {}\n },\n {\n id: \"rekaai/reka-edge\",\n name: \"Reka Edge\",\n match: {\n equals: \"rekaai/reka-edge\"\n },\n context_window: 16384,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"rekaai/reka-flash-3\",\n name: \"Reka Flash 3\",\n match: {\n equals: \"rekaai/reka-flash-3\"\n },\n context_window: 65536,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.2\n }\n },\n {\n id: \"rekaai/reka-flash-3:free\",\n match: {\n equals: \"rekaai/reka-flash-3:free\"\n },\n prices: {}\n },\n {\n id: \"relace/relace-apply-3\",\n name: \"Relace Apply 3\",\n match: {\n equals: \"relace/relace-apply-3\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.85,\n output_mtok: 1.25\n }\n },\n {\n id: \"relace/relace-search\",\n name: \"Relace Search\",\n match: {\n equals: \"relace/relace-search\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 1,\n output_mtok: 3\n }\n },\n {\n id: \"sao10k/fimbulvetr-11b-v2\",\n match: {\n equals: \"sao10k/fimbulvetr-11b-v2\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"sao10k/l3-euryale-70b\",\n match: {\n equals: \"sao10k/l3-euryale-70b\"\n },\n prices: {\n input_mtok: 1.48,\n output_mtok: 1.48\n }\n },\n {\n id: \"sao10k/l3-lunaris-8b\",\n name: \"Llama 3 8B Lunaris\",\n match: {\n equals: \"sao10k/l3-lunaris-8b\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.02,\n output_mtok: 0.05\n }\n },\n {\n id: \"sao10k/l3.1-70b-hanami-x1\",\n name: \"Llama 3.1 70B Hanami x1\",\n match: {\n equals: \"sao10k/l3.1-70b-hanami-x1\"\n },\n prices: {\n input_mtok: 3,\n output_mtok: 3\n }\n },\n {\n id: \"sao10k/l3.1-euryale-70b\",\n name: \"Llama 3.1 Euryale 70B v2.2\",\n match: {\n equals: \"sao10k/l3.1-euryale-70b\"\n },\n prices: {\n input_mtok: 0.7,\n output_mtok: 0.8\n }\n },\n {\n id: \"sao10k/l3.3-euryale-70b\",\n name: \"Llama 3.3 Euryale 70B\",\n match: {\n equals: \"sao10k/l3.3-euryale-70b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.7,\n output_mtok: 0.8\n }\n },\n {\n id: \"sarvam-m:free\",\n name: \"Sarvam-M (free)\",\n match: {\n equals: \"sarvam-m:free\"\n },\n prices: {}\n },\n {\n id: \"scb10x/llama3.1-typhoon2-70b-instruct\",\n match: {\n equals: \"scb10x/llama3.1-typhoon2-70b-instruct\"\n },\n prices: {\n input_mtok: 0.88,\n output_mtok: 0.88\n }\n },\n {\n id: \"scb10x/llama3.1-typhoon2-8b-instruct\",\n match: {\n equals: \"scb10x/llama3.1-typhoon2-8b-instruct\"\n },\n prices: {\n input_mtok: 0.18,\n output_mtok: 0.18\n }\n },\n {\n id: \"shisa-ai/shisa-v2-llama3.3-70b:free\",\n match: {\n equals: \"shisa-ai/shisa-v2-llama3.3-70b:free\"\n },\n prices: {}\n },\n {\n id: \"shisa-v2-llama3.3-70b:free\",\n name: \"Shisa V2 Llama 3.3 70B (free)\",\n match: {\n equals: \"shisa-v2-llama3.3-70b:free\"\n },\n prices: {}\n },\n {\n id: \"sonar-reasoning\",\n name: \"Sonar Reasoning\",\n match: {\n equals: \"sonar-reasoning\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 5\n }\n },\n {\n id: \"sophosympatheia/midnight-rose-70b\",\n match: {\n equals: \"sophosympatheia/midnight-rose-70b\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 0.8\n }\n },\n {\n id: \"sophosympatheia/rogue-rose-103b-v0.2:free\",\n match: {\n equals: \"sophosympatheia/rogue-rose-103b-v0.2:free\"\n },\n prices: {}\n },\n {\n id: \"sorcererlm-8x22b\",\n name: \"SorcererLM 8x22B\",\n match: {\n equals: \"sorcererlm-8x22b\"\n },\n prices: {\n input_mtok: 4.5,\n output_mtok: 4.5\n }\n },\n {\n id: \"spotlight\",\n name: \"Spotlight\",\n match: {\n equals: \"spotlight\"\n },\n prices: {\n input_mtok: 0.18,\n output_mtok: 0.18\n }\n },\n {\n id: \"steelskull/l3.3-electra-r1-70b\",\n match: {\n equals: \"steelskull/l3.3-electra-r1-70b\"\n },\n prices: {\n input_mtok: 0.7,\n output_mtok: 0.95\n }\n },\n {\n id: \"stepfun/step-3.5-flash\",\n name: \"Step 3.5 Flash\",\n match: {\n equals: \"stepfun/step-3.5-flash\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.09,\n cache_read_mtok: 0.02,\n output_mtok: 0.3\n }\n },\n {\n id: \"stepfun/step-3.7-flash\",\n name: \"Step 3.7 Flash\",\n match: {\n equals: \"stepfun/step-3.7-flash\"\n },\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.04,\n output_mtok: 1.15\n }\n },\n {\n id: \"switchpoint/router\",\n name: \"Switchpoint Router\",\n match: {\n equals: \"switchpoint/router\"\n },\n prices: {\n input_mtok: 0.85,\n output_mtok: 3.4\n }\n },\n {\n id: \"tencent/hunyuan-a13b-instruct\",\n name: \"Hunyuan A13B Instruct\",\n match: {\n equals: \"tencent/hunyuan-a13b-instruct\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.14,\n output_mtok: 0.57\n }\n },\n {\n id: \"tencent/hy3-preview\",\n name: \"Hy3 preview\",\n match: {\n equals: \"tencent/hy3-preview\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.063,\n cache_read_mtok: 0.021,\n output_mtok: 0.21\n }\n },\n {\n id: \"thedrummer/anubis-pro-105b-v1\",\n match: {\n equals: \"thedrummer/anubis-pro-105b-v1\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1\n }\n },\n {\n id: \"thedrummer/cydonia-24b-v4.1\",\n name: \"Cydonia 24B V4.1\",\n match: {\n equals: \"thedrummer/cydonia-24b-v4.1\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.15,\n output_mtok: 0.5\n }\n },\n {\n id: \"thedrummer/rocinante-12b\",\n name: \"Rocinante 12B\",\n match: {\n equals: \"thedrummer/rocinante-12b\"\n },\n prices: {\n input_mtok: 0.25,\n output_mtok: 0.5\n }\n },\n {\n id: \"thedrummer/skyfall-36b-v2\",\n name: \"Skyfall 36B V2\",\n match: {\n equals: \"thedrummer/skyfall-36b-v2\"\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.5,\n output_mtok: 0.8\n }\n },\n {\n id: \"thedrummer/unslopnemo-12b\",\n match: {\n equals: \"thedrummer/unslopnemo-12b\"\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 0.5\n }\n },\n {\n id: \"thudm/glm-4-32b:free\",\n match: {\n equals: \"thudm/glm-4-32b:free\"\n },\n prices: {}\n },\n {\n id: \"thudm/glm-z1-32b:free\",\n match: {\n equals: \"thudm/glm-z1-32b:free\"\n },\n prices: {}\n },\n {\n id: \"toppy-m-7b\",\n name: \"Toppy M 7B\",\n match: {\n equals: \"toppy-m-7b\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"typesafe/jev-1.13\",\n name: \"Jev 1.13\",\n match: {\n or: [\n {\n equals: \"typesafe/jev-1.13\"\n },\n {\n equals: \"~typesafe/jev-latest\"\n },\n {\n regex: \"^typesafe/jev-1\\\\.13-\\\\d{8}$\"\n }\n ]\n },\n context_window: 32000,\n price_comments: \"Same $0.042 per million input tokens as TypeSafe. Output is free. Decisions responses name a dated id such as typesafe/jev-1.13-20260917. ~typesafe/jev-latest redirects to the current Jev release. https://openrouter.ai/api/v1/models/typesafe/jev-1.13/endpoints\",\n prices: {\n input_mtok: 0.042\n }\n },\n {\n id: \"undi95/remm-slerp-l2-13b\",\n name: \"ReMM SLERP 13B\",\n match: {\n equals: \"undi95/remm-slerp-l2-13b\"\n },\n context_window: 6144,\n prices: {\n input_mtok: 0.8,\n output_mtok: 1.2\n }\n },\n {\n id: \"undi95/toppy-m-7b\",\n match: {\n equals: \"undi95/toppy-m-7b\"\n },\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.07\n }\n },\n {\n id: \"upstage/solar-pro-3\",\n name: \"Solar Pro 3\",\n match: {\n equals: \"upstage/solar-pro-3\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.015,\n output_mtok: 0.6\n }\n },\n {\n id: \"valkyrie-49b-v1\",\n name: \"Valkyrie 49B V1\",\n match: {\n equals: \"valkyrie-49b-v1\"\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 0.8\n }\n },\n {\n id: \"virtuoso-medium-v2\",\n name: \"Virtuoso Medium V2\",\n match: {\n equals: \"virtuoso-medium-v2\"\n },\n prices: {\n input_mtok: 0.5,\n output_mtok: 0.8\n }\n },\n {\n id: \"writer/palmyra-x5\",\n name: \"Palmyra X5\",\n match: {\n equals: \"writer/palmyra-x5\"\n },\n context_window: 1040000,\n prices: {\n input_mtok: 0.6,\n output_mtok: 6\n }\n },\n {\n id: \"x-ai/grok-2-1212\",\n match: {\n equals: \"x-ai/grok-2-1212\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 10\n }\n },\n {\n id: \"x-ai/grok-2-vision-1212\",\n match: {\n equals: \"x-ai/grok-2-vision-1212\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 10\n }\n },\n {\n id: \"x-ai/grok-3-beta\",\n match: {\n equals: \"x-ai/grok-3-beta\"\n },\n prices: {\n input_mtok: 3,\n output_mtok: 15\n }\n },\n {\n id: \"x-ai/grok-3-mini-beta\",\n match: {\n equals: \"x-ai/grok-3-mini-beta\"\n },\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.5\n }\n },\n {\n id: \"x-ai/grok-4-fast\",\n match: {\n equals: \"x-ai/grok-4-fast\"\n },\n context_window: 2000000,\n prices: {\n input_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 128000,\n price: 0.4\n }\n ]\n },\n cache_read_mtok: 0.05,\n output_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 128000,\n price: 1\n }\n ]\n }\n }\n },\n {\n id: \"x-ai/grok-4.1-fast:free\",\n match: {\n equals: \"x-ai/grok-4.1-fast:free\"\n },\n context_window: 2000000,\n prices: {}\n },\n {\n id: \"x-ai/grok-4.20\",\n name: \"Grok 4.20\",\n match: {\n equals: \"x-ai/grok-4.20\"\n },\n context_window: 2000000,\n prices: {\n input_mtok: 1.25,\n cache_read_mtok: 0.2,\n output_mtok: 2.5\n }\n },\n {\n id: \"x-ai/grok-4.20-multi-agent\",\n name: \"Grok 4.20 Multi-Agent\",\n match: {\n equals: \"x-ai/grok-4.20-multi-agent\"\n },\n context_window: 2000000,\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.2,\n output_mtok: 6\n }\n },\n {\n id: \"x-ai/grok-4.3\",\n name: \"Grok 4.3\",\n match: {\n or: [\n {\n equals: \"x-ai/grok-4.3\"\n },\n {\n regex: \"^x-ai/grok-4\\\\.3-\\\\d{8}$\"\n }\n ]\n },\n context_window: 1e6,\n prices: {\n input_mtok: 1.25,\n cache_read_mtok: 0.2,\n output_mtok: 2.5\n }\n },\n {\n id: \"x-ai/grok-beta\",\n match: {\n equals: \"x-ai/grok-beta\"\n },\n prices: {\n input_mtok: 5,\n output_mtok: 15\n }\n },\n {\n id: \"x-ai/grok-build-0.1\",\n name: \"Grok Build 0.1\",\n match: {\n equals: \"x-ai/grok-build-0.1\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 1,\n cache_read_mtok: 0.2,\n output_mtok: 2\n }\n },\n {\n id: \"x-ai/grok-code-fast-1\",\n match: {\n equals: \"x-ai/grok-code-fast-1\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.02,\n output_mtok: 1.5\n }\n },\n {\n id: \"x-ai/grok-vision-beta\",\n match: {\n equals: \"x-ai/grok-vision-beta\"\n },\n prices: {\n input_mtok: 5,\n output_mtok: 15\n }\n },\n {\n id: \"xiaomi/mimo-v2-flash\",\n name: \"MiMo-V2-Flash\",\n match: {\n equals: \"xiaomi/mimo-v2-flash\"\n },\n prices: {\n input_mtok: 0.1,\n cache_read_mtok: 0.01,\n output_mtok: 0.3\n }\n },\n {\n id: \"xiaomi/mimo-v2.5\",\n name: \"MiMo-V2.5\",\n match: {\n equals: \"xiaomi/mimo-v2.5\"\n },\n prices: {\n input_mtok: 0.14,\n cache_read_mtok: 0.0028,\n output_mtok: 0.28\n }\n },\n {\n id: \"xiaomi/mimo-v2.5-pro\",\n name: \"MiMo-V2.5-Pro\",\n match: {\n equals: \"xiaomi/mimo-v2.5-pro\"\n },\n prices: {\n input_mtok: 0.435,\n cache_read_mtok: 0.0036,\n output_mtok: 0.87\n }\n },\n {\n id: \"xwin-lm/xwin-lm-70b\",\n match: {\n equals: \"xwin-lm/xwin-lm-70b\"\n },\n prices: {\n input_mtok: 3.75,\n output_mtok: 3.75\n }\n },\n {\n id: \"yi-large\",\n name: \"Yi Large\",\n match: {\n equals: \"yi-large\"\n },\n prices: {\n input_mtok: 3,\n output_mtok: 3\n }\n },\n {\n id: \"z-ai/glm-4.5\",\n name: \"GLM 4.5\",\n match: {\n equals: \"z-ai/glm-4.5\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.11,\n output_mtok: 2.2\n }\n },\n {\n id: \"z-ai/glm-4.5-air\",\n name: \"GLM 4.5 Air\",\n match: {\n equals: \"z-ai/glm-4.5-air\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.125,\n cache_read_mtok: 0.06,\n output_mtok: 0.85\n }\n },\n {\n id: \"z-ai/glm-4.5v\",\n name: \"GLM 4.5V\",\n match: {\n equals: \"z-ai/glm-4.5v\"\n },\n context_window: 65536,\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.11,\n output_mtok: 1.8\n }\n },\n {\n id: \"z-ai/glm-4.6\",\n name: \"GLM 4.6\",\n match: {\n equals: \"z-ai/glm-4.6\"\n },\n prices: {\n input_mtok: 0.43,\n cache_read_mtok: 0.08,\n output_mtok: 1.74\n }\n },\n {\n id: \"z-ai/glm-4.6v\",\n name: \"GLM 4.6V\",\n match: {\n equals: \"z-ai/glm-4.6v\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.05,\n output_mtok: 0.9\n }\n },\n {\n id: \"z-ai/glm-4.7\",\n name: \"GLM 4.7\",\n match: {\n equals: \"z-ai/glm-4.7\"\n },\n prices: {\n input_mtok: 0.4,\n cache_read_mtok: 0.08,\n output_mtok: 1.75\n }\n },\n {\n id: \"z-ai/glm-4.7-flash\",\n name: \"GLM 4.7 Flash\",\n match: {\n or: [\n {\n equals: \"z-ai/glm-4.7-flash\"\n },\n {\n equals: \"z-ai/glm-4.7-flash-20260119\"\n }\n ]\n },\n prices: {\n input_mtok: 0.06,\n cache_read_mtok: 0.01,\n output_mtok: 0.4\n }\n },\n {\n id: \"z-ai/glm-5\",\n name: \"GLM 5\",\n match: {\n or: [\n {\n equals: \"z-ai/glm-5\"\n },\n {\n equals: \"z-ai/glm-5-20260211\"\n }\n ]\n },\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.12,\n output_mtok: 1.92\n }\n },\n {\n id: \"z-ai/glm-5-turbo\",\n name: \"GLM 5 Turbo\",\n match: {\n or: [\n {\n equals: \"z-ai/glm-5-turbo\"\n },\n {\n equals: \"z-ai/glm-5-turbo-20260315\"\n }\n ]\n },\n context_window: 202752,\n prices: {\n input_mtok: 1.2,\n cache_read_mtok: 0.24,\n output_mtok: 4\n }\n },\n {\n id: \"z-ai/glm-5.1\",\n name: \"GLM 5.1\",\n match: {\n or: [\n {\n equals: \"z-ai/glm-5.1\"\n },\n {\n equals: \"z-ai/glm-5.1-20260406\"\n }\n ]\n },\n prices: {\n input_mtok: 0.98,\n cache_read_mtok: 0.182,\n output_mtok: 3.08\n }\n },\n {\n id: \"z-ai/glm-5.2\",\n name: \"GLM 5.2\",\n match: {\n or: [\n {\n equals: \"z-ai/glm-5.2\"\n },\n {\n equals: \"z-ai/glm-5.2-20260616\"\n }\n ]\n },\n context_window: 1048576,\n prices: {\n input_mtok: 1.4,\n cache_read_mtok: 0.26,\n output_mtok: 4.4\n }\n },\n {\n id: \"z-ai/glm-5.3\",\n name: \"GLM 5.3\",\n match: {\n or: [\n {\n equals: \"z-ai/glm-5.3\"\n },\n {\n equals: \"z-ai/glm-5.3-20260816\"\n }\n ]\n },\n context_window: 1048576,\n prices: {\n input_mtok: 1.4,\n cache_read_mtok: 0.26,\n output_mtok: 4.4\n }\n },\n {\n id: \"z-ai/glm-5.3-flash\",\n name: \"GLM 5.3 Flash\",\n match: {\n equals: \"z-ai/glm-5.3-flash\"\n },\n context_window: 1310720,\n price_comments: \"A 50% promotion ran through September 9, 2026 at 16:00 UTC. `start_date` is a UTC date, so the list prices start on 2026-09-10 and requests from 16:00 to 24:00 UTC on September 9, after the promotion ended, stay at the promotional rate.\",\n prices: [\n {\n prices: {\n input_mtok: 0.075,\n cache_read_mtok: 0.015,\n output_mtok: 0.25\n }\n },\n {\n constraint: {\n start_date: \"2026-09-10\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.03,\n output_mtok: 0.5\n }\n }\n ]\n },\n {\n id: \"~anthropic/claude-fable-latest\",\n name: \"Claude Fable Latest\",\n match: {\n equals: \"~anthropic/claude-fable-latest\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 10,\n cache_write_mtok: 12.5,\n cache_read_mtok: 1,\n output_mtok: 50\n }\n },\n {\n id: \"~anthropic/claude-haiku-latest\",\n name: \"Anthropic Claude Haiku Latest\",\n match: {\n equals: \"~anthropic/claude-haiku-latest\"\n },\n context_window: 1e6,\n prices: [\n {\n prices: {\n input_mtok: 1,\n cache_write_mtok: 1.25,\n cache_read_mtok: 0.1,\n output_mtok: 5\n }\n },\n {\n constraint: {\n start_date: \"2026-10-07\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: {\n base: 0.1,\n tiers: [\n {\n start: 1e5,\n price: 0.5\n }\n ]\n },\n cache_write_mtok: {\n base: 0.125,\n tiers: [\n {\n start: 1e5,\n price: 0.625\n }\n ]\n },\n cache_read_mtok: {\n base: 0.01,\n tiers: [\n {\n start: 1e5,\n price: 0.05\n }\n ]\n },\n output_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 1e5,\n price: 2.5\n }\n ]\n },\n cache_write_1h_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 1e5,\n price: 1\n }\n ]\n },\n web_searches_kcount: 10\n }\n }\n ]\n },\n {\n id: \"~anthropic/claude-opus-latest\",\n name: \"Claude Opus Latest\",\n match: {\n equals: \"~anthropic/claude-opus-latest\"\n },\n context_window: 1e6,\n prices: [\n {\n prices: {\n input_mtok: 5,\n cache_write_mtok: 6.25,\n cache_read_mtok: 0.5,\n output_mtok: 25\n }\n },\n {\n constraint: {\n start_date: \"2026-09-22\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 4,\n cache_write_mtok: 5,\n cache_read_mtok: 0.2,\n output_mtok: 20\n }\n }\n ]\n },\n {\n id: \"~anthropic/claude-sonnet-latest\",\n name: \"Anthropic Claude Sonnet Latest\",\n match: {\n equals: \"~anthropic/claude-sonnet-latest\"\n },\n context_window: 1e6,\n prices: [\n {\n prices: {\n input_mtok: 3,\n cache_write_mtok: 3.75,\n cache_read_mtok: 0.3,\n output_mtok: 15\n }\n },\n {\n constraint: {\n start_date: \"2026-09-28\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 2,\n cache_write_mtok: 2.5,\n cache_read_mtok: 0.2,\n output_mtok: 10\n }\n }\n ]\n },\n {\n id: \"~google/gemini-flash-latest\",\n name: \"Google Gemini Flash Latest\",\n match: {\n equals: \"~google/gemini-flash-latest\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 1.5,\n cache_write_mtok: 0.08333333333333334,\n cache_read_mtok: 0.15,\n output_mtok: 9\n }\n },\n {\n id: \"~google/gemini-pro-latest\",\n name: \"Google Gemini Pro Latest\",\n match: {\n equals: \"~google/gemini-pro-latest\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 2,\n cache_write_mtok: 0.375,\n cache_read_mtok: 0.2,\n output_mtok: 12\n }\n },\n {\n id: \"~moonshotai/kimi-latest\",\n name: \"MoonshotAI Kimi Latest\",\n match: {\n equals: \"~moonshotai/kimi-latest\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.68,\n cache_read_mtok: 0.34,\n output_mtok: 3.41\n }\n },\n {\n id: \"~openai/gpt-latest\",\n name: \"OpenAI GPT Latest\",\n match: {\n equals: \"~openai/gpt-latest\"\n },\n context_window: 1050000,\n prices: {\n input_mtok: 5,\n cache_read_mtok: 0.5,\n output_mtok: 30\n }\n },\n {\n id: \"~openai/gpt-mini-latest\",\n name: \"OpenAI GPT Mini Latest\",\n match: {\n equals: \"~openai/gpt-mini-latest\"\n },\n context_window: 400000,\n prices: {\n input_mtok: 0.75,\n cache_read_mtok: 0.075,\n output_mtok: 4.5\n }\n }\n ]\n },\n {\n id: \"ovhcloud\",\n name: \"OVHcloud AI Endpoints\",\n pricing_urls: [\"https://oai.endpoints.kepler.ai.cloud.ovh.net/v1/models\"],\n api_pattern: \"https://oai\\\\.endpoints\\\\.kepler\\\\.ai\\\\.cloud\\\\.ovh\\\\.net\",\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"DeepSeek-R1-Distill-Llama-70B\",\n name: \"DeepSeek-R1-Distill-Llama-70B\",\n match: {\n or: [\n {\n equals: \"DeepSeek-R1-Distill-Llama-70B\"\n },\n {\n equals: \"deepseek-r1-distill-llama-70b\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.74,\n output_mtok: 0.74\n }\n },\n {\n id: \"Llama-3.1-8B-Instruct\",\n name: \"Llama-3.1-8B-Instruct\",\n match: {\n or: [\n {\n equals: \"Llama-3.1-8B-Instruct\"\n },\n {\n equals: \"llama-3.1-8b-instruct\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.11,\n output_mtok: 0.11\n }\n },\n {\n id: \"Meta-Llama-3_3-70B-Instruct\",\n name: \"Meta-Llama-3_3-70B-Instruct\",\n match: {\n or: [\n {\n equals: \"Meta-Llama-3_3-70B-Instruct\"\n },\n {\n equals: \"meta-llama-3_3-70b-instruct\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.74,\n output_mtok: 0.74\n }\n },\n {\n id: \"Mistral-7B-Instruct-v0.3\",\n name: \"Mistral-7B-Instruct-v0.3\",\n match: {\n or: [\n {\n equals: \"Mistral-7B-Instruct-v0.3\"\n },\n {\n equals: \"mistral-7b-instruct-v0.3\"\n }\n ]\n },\n context_window: 65536,\n prices: {\n input_mtok: 0.11,\n output_mtok: 0.11\n }\n },\n {\n id: \"Mistral-Nemo-Instruct-2407\",\n name: \"Mistral-Nemo-Instruct-2407\",\n match: {\n or: [\n {\n equals: \"Mistral-Nemo-Instruct-2407\"\n },\n {\n equals: \"mistral-nemo-instruct-2407\"\n }\n ]\n },\n context_window: 65536,\n prices: {\n input_mtok: 0.14,\n output_mtok: 0.14\n }\n },\n {\n id: \"Mistral-Small-3.2-24B-Instruct-2506\",\n name: \"Mistral-Small-3.2-24B-Instruct-2506\",\n match: {\n or: [\n {\n equals: \"Mistral-Small-3.2-24B-Instruct-2506\"\n },\n {\n equals: \"mistral-small-3.2-24b-instruct-2506\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.31\n }\n },\n {\n id: \"Mixtral-8x7B-Instruct-v0.1\",\n name: \"Mixtral-8x7B-Instruct-v0.1\",\n match: {\n or: [\n {\n equals: \"Mixtral-8x7B-Instruct-v0.1\"\n },\n {\n equals: \"mixtral-8x7b-instruct-v0.1\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.7,\n output_mtok: 0.7\n }\n },\n {\n id: \"Qwen2.5-VL-72B-Instruct\",\n name: \"Qwen2.5-VL-72B-Instruct\",\n match: {\n or: [\n {\n equals: \"Qwen2.5-VL-72B-Instruct\"\n },\n {\n equals: \"qwen2.5-vl-72b-instruct\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 1.01,\n output_mtok: 1.01\n }\n },\n {\n id: \"Qwen3-32B\",\n name: \"Qwen3-32B\",\n match: {\n or: [\n {\n equals: \"Qwen3-32B\"\n },\n {\n equals: \"qwen3-32b\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 0.09,\n output_mtok: 0.25\n }\n },\n {\n id: \"Qwen3-Coder-30B-A3B-Instruct\",\n name: \"Qwen3-Coder-30B-A3B-Instruct\",\n match: {\n or: [\n {\n equals: \"Qwen3-Coder-30B-A3B-Instruct\"\n },\n {\n equals: \"qwen3-coder-30b-a3b-instruct\"\n }\n ]\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.07,\n output_mtok: 0.26\n }\n },\n {\n id: \"bge-base-en-v1.5\",\n name: \"bge-base-en-v1.5\",\n match: {\n equals: \"bge-base-en-v1.5\"\n },\n context_window: 512,\n prices: {\n input_mtok: 0.01\n }\n },\n {\n id: \"bge-m3\",\n name: \"bge-m3\",\n match: {\n equals: \"bge-m3\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.01\n }\n },\n {\n id: \"bge-multilingual-gemma2\",\n name: \"bge-multilingual-gemma2\",\n match: {\n equals: \"bge-multilingual-gemma2\"\n },\n context_window: 8192,\n prices: {\n input_mtok: 0.01\n }\n },\n {\n id: \"gpt-oss-120b\",\n name: \"gpt-oss-120b\",\n match: {\n equals: \"gpt-oss-120b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.09,\n output_mtok: 0.47\n }\n },\n {\n id: \"gpt-oss-20b\",\n name: \"gpt-oss-20b\",\n match: {\n equals: \"gpt-oss-20b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.05,\n output_mtok: 0.18\n }\n }\n ]\n },\n {\n id: \"perplexity\",\n name: \"Perplexity\",\n pricing_urls: [\"https://docs.perplexity.ai/docs/getting-started/pricing\"],\n api_pattern: \"https://api\\\\.perplexity\\\\.ai\",\n price_comments: \"Prices per request vary based on usage, this is not represented here, instead we just take the highest price shown for `requests_kcount`.\",\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"reasoning_tokens\",\n dest: \"output_tokens\",\n required: false\n },\n {\n path: \"reasoning_tokens\",\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"citation_tokens\",\n dest: \"output_tokens\",\n required: false\n },\n {\n path: \"citation_tokens\",\n dest: \"output_citation_tokens\",\n required: false\n },\n {\n path: \"num_search_queries\",\n dest: \"web_searches\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"decisions\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"llama-3.1-sonar-large-128k-online\",\n name: \"Llama 3.1 Sonar 70B Online\",\n description: \"Llama 3.1 Sonar is Perplexity's latest model family. It surpasses their earlier Sonar models in cost-efficiency, speed, and performance.\",\n match: {\n equals: \"llama-3.1-sonar-large-128k-online\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 1\n }\n },\n {\n id: \"llama-3.1-sonar-small-128k-online\",\n name: \"Llama 3.1 Sonar 8B Online\",\n description: \"Llama 3.1 Sonar is Perplexity's latest model family. It surpasses their earlier Sonar models in cost-efficiency, speed, and performance.\",\n match: {\n equals: \"llama-3.1-sonar-small-128k-online\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"pplx-decider-v1-27b\",\n name: \"Decider V1 27B\",\n match: {\n equals: \"pplx-decider-v1-27b\"\n },\n context_window: 262144,\n price_comments: \"Decisions API: $0.02 per million input tokens, output free, no per-request fee. Input stays under 262,144 tokens. https://docs.perplexity.ai/docs/decisions/quickstart\",\n prices: {\n input_mtok: 0.02\n }\n },\n {\n id: \"pplx-decider-v1.1-27b\",\n name: \"Decider V1.1 27B\",\n match: {\n equals: \"pplx-decider-v1.1-27b\"\n },\n context_window: 262144,\n price_comments: \"Decisions API: $0.02 per million input tokens, output free, no per-request fee. Input stays under 262,144 tokens. https://docs.perplexity.ai/docs/decisions/quickstart\",\n prices: {\n input_mtok: 0.02\n }\n },\n {\n id: \"r1-1776\",\n name: \"R1 1776\",\n description: \"R1 1776 is a version of DeepSeek-R1 that has been post-trained to remove censorship constraints related to topics restricted by the Chinese government. The model retains its original reasoning capabilities while providing direct responses to a wider range of queries. R1 1776 is an offline chat model that does not use the perplexity search subsystem.\",\n match: {\n equals: \"r1-1776\"\n },\n prices: {\n input_mtok: 2,\n output_mtok: 8\n }\n },\n {\n id: \"sonar\",\n name: \"Sonar\",\n description: \"Sonar is lightweight, affordable, fast, and simple to use \\u2014 now featuring citations and the ability to customize sources. It is designed for companies seeking to integrate lightweight question-and-answer features optimized for speed.\",\n match: {\n equals: \"sonar\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 1,\n output_mtok: 1,\n requests_kcount: 12\n }\n },\n {\n id: \"sonar-deep-research\",\n name: \"Sonar Deep Research\",\n description: \"Sonar Deep Research is a research-focused model designed for multi-step retrieval, synthesis, and reasoning across complex topics. It autonomously searches, reads, and evaluates sources, refining its approach as it gathers information. This enables comprehensive report generation across domains like finance, technology, health, and current events.\",\n match: {\n equals: \"sonar-deep-research\"\n },\n context_window: 128000,\n price_comments: \"Perplexity lists reasoning tokens separately at $3 per million tokens and citation tokens at $2 per million tokens. Search queries cost $5 per thousand.\",\n prices: {\n input_mtok: 2,\n output_mtok: 8,\n output_reasoning_mtok: 3,\n output_citation_mtok: 2,\n web_searches_kcount: 5\n }\n },\n {\n id: \"sonar-pro\",\n name: \"Sonar Pro\",\n description: \"Note: Sonar Pro pricing includes Perplexity search pricing. See details here\",\n match: {\n equals: \"sonar-pro\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 3,\n output_mtok: 15,\n requests_kcount: 14\n }\n },\n {\n id: \"sonar-pro-search\",\n name: \"Sonar Pro Search\",\n description: \"Sonar Pro Search is Perplexity's advanced agentic search system for deeper reasoning and analysis.\",\n match: {\n equals: \"sonar-pro-search\"\n },\n price_comments: \"Imported from OpenRouter pricing; verify against Perplexity pricing when native API pricing is published.\",\n prices: {\n input_mtok: 3,\n output_mtok: 15\n }\n },\n {\n id: \"sonar-reasoning\",\n name: \"Sonar Reasoning\",\n description: \"Sonar Reasoning is a reasoning model provided by Perplexity based on DeepSeek R1.\",\n match: {\n equals: \"sonar-reasoning\"\n },\n prices: {\n input_mtok: 1,\n output_mtok: 5,\n requests_kcount: 12\n }\n },\n {\n id: \"sonar-reasoning-pro\",\n name: \"Sonar Reasoning Pro\",\n description: \"Sonar Pro pricing includes Perplexity search pricing.\",\n match: {\n equals: \"sonar-reasoning-pro\"\n },\n context_window: 128000,\n prices: {\n input_mtok: 2,\n output_mtok: 8,\n requests_kcount: 14\n }\n }\n ]\n },\n {\n id: \"quicksilverpro\",\n name: \"QuickSilver Pro\",\n pricing_urls: [\"https://quicksilverpro.io/#pricing\", \"https://quicksilverpro.io/pricing.json\"],\n api_pattern: \"https://api\\\\.quicksilverpro\\\\.io\",\n description: \"OpenAI-compatible inference gateway serving frontier and open models via one endpoint.\",\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"prompt_tokens_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"responses\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"input_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"input_tokens_details\", \"cache_write_tokens\"],\n dest: \"cache_write_tokens\",\n required: false\n },\n {\n path: [\"output_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"claude-fable-5\",\n name: \"Claude Fable 5\",\n match: {\n equals: \"claude-fable-5\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 8,\n output_mtok: 40\n }\n },\n {\n id: \"claude-haiku-4-5\",\n name: \"Claude Haiku 4.5\",\n match: {\n equals: \"claude-haiku-4-5\"\n },\n context_window: 200000,\n prices: {\n input_mtok: 0.8,\n output_mtok: 4\n }\n },\n {\n id: \"claude-opus-4-6\",\n name: \"Claude Opus 4.6\",\n match: {\n equals: \"claude-opus-4-6\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 4,\n output_mtok: 20\n }\n },\n {\n id: \"claude-opus-4-8\",\n name: \"Claude Opus 4.8\",\n match: {\n equals: \"claude-opus-4-8\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 4,\n output_mtok: 20\n }\n },\n {\n id: \"claude-opus-5\",\n name: \"Claude Opus 5\",\n match: {\n equals: \"claude-opus-5\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 4,\n output_mtok: 20\n }\n },\n {\n id: \"claude-sonnet-4-6\",\n name: \"Claude Sonnet 4.6\",\n match: {\n equals: \"claude-sonnet-4-6\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 2.4,\n output_mtok: 12\n }\n },\n {\n id: \"claude-sonnet-5\",\n name: \"Claude Sonnet 5\",\n match: {\n equals: \"claude-sonnet-5\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 2,\n output_mtok: 10\n }\n },\n {\n id: \"deepseek-v4-flash\",\n name: \"DeepSeek V4 Flash\",\n match: {\n equals: \"deepseek-v4-flash\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.112,\n cache_read_mtok: 0.0144,\n output_mtok: 0.224\n }\n },\n {\n id: \"deepseek-v4-pro\",\n name: \"DeepSeek V4 Pro\",\n match: {\n equals: \"deepseek-v4-pro\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.435,\n cache_read_mtok: 0.003625,\n output_mtok: 0.87\n }\n },\n {\n id: \"gemini-3-flash-preview\",\n name: \"Gemini 3 Flash Preview\",\n match: {\n equals: \"gemini-3-flash-preview\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.425,\n output_mtok: 2.55\n },\n deprecated: true\n },\n {\n id: \"gemini-3-pro-image\",\n name: \"Gemini 3 Pro Image\",\n match: {\n equals: \"gemini-3-pro-image\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 1.7,\n output_mtok: 10.2,\n output_image_mtok: 102\n }\n },\n {\n id: \"gemini-3.1-flash-lite\",\n name: \"Gemini 3.1 Flash Lite\",\n match: {\n equals: \"gemini-3.1-flash-lite\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.2125,\n output_mtok: 1.275\n },\n deprecated: true\n },\n {\n id: \"gemini-3.1-pro-preview\",\n name: \"Gemini 3.1 Pro Preview\",\n match: {\n equals: \"gemini-3.1-pro-preview\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 1.7,\n output_mtok: 10.2\n }\n },\n {\n id: \"gemini-3.5-flash\",\n name: \"Gemini 3.5 Flash\",\n match: {\n equals: \"gemini-3.5-flash\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 1.275,\n output_mtok: 7.65\n }\n },\n {\n id: \"gemini-3.5-flash-lite\",\n name: \"Gemini 3.5 Flash-Lite\",\n match: {\n equals: \"gemini-3.5-flash-lite\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.255,\n output_mtok: 2.125\n }\n },\n {\n id: \"gemini-3.6-flash\",\n name: \"Gemini 3.6 Flash\",\n match: {\n equals: \"gemini-3.6-flash\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 1.275,\n output_mtok: 6.375\n }\n },\n {\n id: \"gemini-3.7-flash\",\n name: \"Gemini 3.7 Flash\",\n match: {\n equals: \"gemini-3.7-flash\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.6375,\n cache_read_mtok: 0.06375,\n output_mtok: 3.1875\n }\n },\n {\n id: \"glm-5.2\",\n name: \"GLM 5.2\",\n match: {\n equals: \"glm-5.2\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 1.12,\n cache_read_mtok: 0.208,\n output_mtok: 3.52\n }\n },\n {\n id: \"glm-5.3\",\n name: \"GLM 5.3\",\n match: {\n equals: \"glm-5.3\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 1.12,\n cache_read_mtok: 0.208,\n output_mtok: 3.52\n }\n },\n {\n id: \"glm-5.3-flash\",\n name: \"GLM 5.3 Flash\",\n match: {\n equals: \"glm-5.3-flash\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.06,\n cache_read_mtok: 0.012,\n output_mtok: 0.2\n }\n },\n {\n id: \"gpt-5.6-luna\",\n name: \"GPT-5.6 Luna\",\n match: {\n equals: \"gpt-5.6-luna\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.08,\n cache_write_mtok: 0.1,\n cache_read_mtok: 0.008,\n output_mtok: 0.48\n }\n },\n {\n id: \"gpt-5.6-sol\",\n name: \"GPT-5.6 Sol\",\n match: {\n equals: \"gpt-5.6-sol\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 4,\n cache_write_mtok: 5,\n cache_read_mtok: 0.4,\n output_mtok: 24\n }\n },\n {\n id: \"gpt-5.6-terra\",\n name: \"GPT-5.6 Terra\",\n match: {\n equals: \"gpt-5.6-terra\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.8,\n cache_write_mtok: 1,\n cache_read_mtok: 0.08,\n output_mtok: 4.8\n }\n },\n {\n id: \"gpt-oss-120b\",\n name: \"GPT-OSS 120B\",\n match: {\n equals: \"gpt-oss-120b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.12,\n cache_read_mtok: 0.06,\n output_mtok: 0.48\n }\n },\n {\n id: \"grok-4.5\",\n name: \"Grok 4.5\",\n match: {\n equals: \"grok-4.5\"\n },\n context_window: 500000,\n prices: {\n input_mtok: 1.6,\n cache_read_mtok: 0.4,\n output_mtok: 4.8\n }\n },\n {\n id: \"grok-4.6\",\n name: \"Grok 4.6\",\n match: {\n equals: \"grok-4.6\"\n },\n context_window: 500000,\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.5,\n output_mtok: 6\n }\n },\n {\n id: \"hy3\",\n name: \"Hy3\",\n match: {\n equals: \"hy3\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.1056,\n cache_read_mtok: 0.0264,\n output_mtok: 0.4224\n }\n },\n {\n id: \"kimi-k2.6\",\n name: \"Kimi K2.6\",\n match: {\n equals: \"kimi-k2.6\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.5472,\n cache_read_mtok: 0.292,\n output_mtok: 2.728\n }\n },\n {\n id: \"kimi-k2.7-code\",\n name: \"Kimi K2.7 Code\",\n match: {\n equals: \"kimi-k2.7-code\"\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.584,\n cache_read_mtok: 0.1278,\n output_mtok: 2.8\n }\n },\n {\n id: \"kimi-k3\",\n name: \"Kimi K3\",\n match: {\n equals: \"kimi-k3\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 2.4,\n cache_read_mtok: 0.24,\n output_mtok: 12\n }\n },\n {\n id: \"mimo-v2.5\",\n name: \"MiMo-V2.5\",\n match: {\n equals: \"mimo-v2.5\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.112,\n cache_read_mtok: 0.00224,\n output_mtok: 0.224\n }\n },\n {\n id: \"minimax-m3\",\n name: \"MiniMax M3\",\n match: {\n equals: \"minimax-m3\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.24,\n cache_read_mtok: 0.048,\n output_mtok: 0.96\n }\n },\n {\n id: \"muse-glimmer-30b\",\n name: \"Muse Glimmer 30B\",\n match: {\n equals: \"muse-glimmer-30b\"\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.28,\n cache_read_mtok: 0.032,\n output_mtok: 1.2\n }\n },\n {\n id: \"muse-spark-1.2\",\n name: \"Muse Spark 1.2\",\n match: {\n equals: \"muse-spark-1.2\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 1,\n cache_read_mtok: 0.12,\n output_mtok: 3.4\n }\n },\n {\n id: \"nemotron-3.5-lightning\",\n name: \"Nemotron 3.5 Lightning\",\n match: {\n equals: \"nemotron-3.5-lightning\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.08,\n cache_read_mtok: 0.04,\n output_mtok: 0.2\n }\n },\n {\n id: \"qwen3.6-35b\",\n name: \"Qwen3.6-35B-A3B\",\n match: {\n equals: \"qwen3.6-35b\"\n },\n context_window: 262144,\n prices: {\n input_mtok: 0.112,\n cache_read_mtok: 0.04,\n output_mtok: 0.8\n }\n },\n {\n id: \"qwen3.6-plus\",\n name: \"Qwen3.6 Plus\",\n match: {\n equals: \"qwen3.6-plus\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.26,\n cache_write_mtok: 0.325,\n output_mtok: 1.56\n }\n },\n {\n id: \"qwen3.7-flash\",\n name: \"Qwen3.7 Flash\",\n match: {\n equals: \"qwen3.7-flash\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.024,\n cache_read_mtok: 0.0048,\n output_mtok: 0.104\n }\n },\n {\n id: \"qwen3.7-max\",\n name: \"Qwen3.7 Max\",\n match: {\n equals: \"qwen3.7-max\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 1.25,\n cache_write_mtok: 1.5625,\n cache_read_mtok: 0.25,\n output_mtok: 3.75\n }\n },\n {\n id: \"qwen3.7-plus\",\n name: \"Qwen3.7 Plus\",\n match: {\n equals: \"qwen3.7-plus\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 0.256,\n cache_write_mtok: 0.32,\n cache_read_mtok: 0.0512,\n output_mtok: 1.024\n }\n },\n {\n id: \"qwen3.8-27b\",\n name: \"Qwen3.8 27B\",\n match: {\n equals: \"qwen3.8-27b\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.34,\n cache_write_mtok: 0.425,\n cache_read_mtok: 0.068,\n output_mtok: 2.04\n }\n },\n {\n id: \"qwen3.8-max\",\n name: \"Qwen3.8 Max\",\n match: {\n equals: \"qwen3.8-max\"\n },\n context_window: 1048576,\n prices: {\n input_mtok: 2,\n cache_write_mtok: 2.5,\n cache_read_mtok: 0.25,\n output_mtok: 6\n }\n }\n ]\n },\n {\n id: \"together\",\n name: \"Together AI\",\n pricing_urls: [\"https://www.together.ai/pricing\"],\n api_pattern: \"https://api\\\\.together\\\\.xyz\",\n provider_match: {\n or: [\n {\n equals: \"together-ai\"\n },\n {\n equals: \"together_ai\"\n }\n ]\n },\n models: [\n {\n id: \"Austism/chronos-hermes-13b\",\n match: {\n equals: \"Austism/chronos-hermes-13b\"\n },\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.3\n }\n },\n {\n id: \"Gryphe/MythoMax-L2-13b\",\n match: {\n equals: \"Gryphe/MythoMax-L2-13b\"\n },\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.3\n }\n },\n {\n id: \"Nexusflow/NexusRaven-V2-13B\",\n match: {\n equals: \"Nexusflow/NexusRaven-V2-13B\"\n },\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.3\n }\n },\n {\n id: \"NousResearch/Nous-Capybara-7B-V1p9\",\n match: {\n equals: \"NousResearch/Nous-Capybara-7B-V1p9\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO\",\n match: {\n equals: \"NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"NousResearch/Nous-Hermes-2-Mixtral-8x7B-SFT\",\n match: {\n equals: \"NousResearch/Nous-Hermes-2-Mixtral-8x7B-SFT\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"NousResearch/Nous-Hermes-2-Yi-34B\",\n match: {\n equals: \"NousResearch/Nous-Hermes-2-Yi-34B\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 0.8\n }\n },\n {\n id: \"NousResearch/Nous-Hermes-Llama2-13b\",\n match: {\n equals: \"NousResearch/Nous-Hermes-Llama2-13b\"\n },\n prices: {\n input_mtok: 0.225,\n output_mtok: 0.225\n }\n },\n {\n id: \"NousResearch/Nous-Hermes-llama-2-7b\",\n match: {\n equals: \"NousResearch/Nous-Hermes-llama-2-7b\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"Open-Orca/Mistral-7B-OpenOrca\",\n match: {\n equals: \"Open-Orca/Mistral-7B-OpenOrca\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"Qwen/Qwen1.5-0.5B\",\n match: {\n or: [\n {\n equals: \"Qwen/Qwen1.5-0.5B\"\n },\n {\n equals: \"Qwen/Qwen1.5-0.5B-Chat\"\n }\n ]\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"Qwen/Qwen1.5-1.8B\",\n match: {\n or: [\n {\n equals: \"Qwen/Qwen1.5-1.8B\"\n },\n {\n equals: \"Qwen/Qwen1.5-1.8B-Chat\"\n }\n ]\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"Qwen/Qwen1.5-14B\",\n match: {\n or: [\n {\n equals: \"Qwen/Qwen1.5-14B\"\n },\n {\n equals: \"Qwen/Qwen1.5-14B-Chat\"\n }\n ]\n },\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.3\n }\n },\n {\n id: \"Qwen/Qwen1.5-4B\",\n match: {\n or: [\n {\n equals: \"Qwen/Qwen1.5-4B\"\n },\n {\n equals: \"Qwen/Qwen1.5-4B-Chat\"\n }\n ]\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"Qwen/Qwen1.5-72B\",\n match: {\n equals: \"Qwen/Qwen1.5-72B\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"Qwen/Qwen1.5-7B\",\n match: {\n or: [\n {\n equals: \"Qwen/Qwen1.5-7B\"\n },\n {\n equals: \"Qwen/Qwen1.5-7B-Chat\"\n }\n ]\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"Undi95/ReMM-SLERP-L2-13B\",\n match: {\n equals: \"Undi95/ReMM-SLERP-L2-13B\"\n },\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.3\n }\n },\n {\n id: \"Undi95/Toppy-M-7B\",\n match: {\n equals: \"Undi95/Toppy-M-7B\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"WizardLM/WizardLM-13B-V1.2\",\n match: {\n equals: \"WizardLM/WizardLM-13B-V1.2\"\n },\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.3\n }\n },\n {\n id: \"allenai/OLMo-7B\",\n match: {\n or: [\n {\n equals: \"allenai/OLMo-7B\"\n },\n {\n equals: \"allenai/OLMo-7B-Instruct\"\n },\n {\n equals: \"allenai/OLMo-7B-Twin-2T\"\n }\n ]\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"codellama/CodeLlama-13b-Instruct-hf\",\n match: {\n equals: \"codellama/CodeLlama-13b-Instruct-hf\"\n },\n prices: {\n input_mtok: 0.225,\n output_mtok: 0.225\n }\n },\n {\n id: \"codellama/CodeLlama-34b-Instruct-hf\",\n match: {\n equals: \"codellama/CodeLlama-34b-Instruct-hf\"\n },\n prices: {\n input_mtok: 0.776,\n output_mtok: 0.776\n }\n },\n {\n id: \"codellama/CodeLlama-70b-Instruct-hf\",\n match: {\n equals: \"codellama/CodeLlama-70b-Instruct-hf\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"codellama/CodeLlama-7b-Instruct-hf\",\n match: {\n equals: \"codellama/CodeLlama-7b-Instruct-hf\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"deepseek-ai/deepseek-coder-33b-instruct\",\n match: {\n equals: \"deepseek-ai/deepseek-coder-33b-instruct\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 0.8\n }\n },\n {\n id: \"garage-bAInd/Platypus2-70B-instruct\",\n match: {\n equals: \"garage-bAInd/Platypus2-70B-instruct\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"google/gemma-2b\",\n match: {\n or: [\n {\n equals: \"google/gemma-2b\"\n },\n {\n equals: \"google/gemma-2b-it\"\n }\n ]\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"google/gemma-7b\",\n match: {\n or: [\n {\n equals: \"google/gemma-7b\"\n },\n {\n equals: \"google/gemma-7b-it\"\n }\n ]\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"lmsys/vicuna-13b-v1.5\",\n match: {\n equals: \"lmsys/vicuna-13b-v1.5\"\n },\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.3\n }\n },\n {\n id: \"lmsys/vicuna-7b-v1.5\",\n match: {\n equals: \"lmsys/vicuna-7b-v1.5\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"meta-llama/Llama-2-13b-chat-hf\",\n match: {\n equals: \"meta-llama/Llama-2-13b-chat-hf\"\n },\n prices: {\n input_mtok: 0.225,\n output_mtok: 0.225\n }\n },\n {\n id: \"meta-llama/Llama-2-70b-chat-hf\",\n match: {\n equals: \"meta-llama/Llama-2-70b-chat-hf\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"meta-llama/Llama-2-7b-chat-hf\",\n match: {\n equals: \"meta-llama/Llama-2-7b-chat-hf\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"meta-llama/Llama-3-70b-chat-hf\",\n match: {\n equals: \"meta-llama/Llama-3-70b-chat-hf\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"meta-llama/Llama-3-8b-chat-hf\",\n match: {\n equals: \"meta-llama/Llama-3-8b-chat-hf\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"meta-llama/Llama-3.3-70B-Instruct-Turbo\",\n match: {\n equals: \"meta-llama/Llama-3.3-70B-Instruct-Turbo\"\n },\n prices: {\n input_mtok: 0.88,\n output_mtok: 0.88\n }\n },\n {\n id: \"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8\",\n match: {\n equals: \"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8\"\n },\n prices: {\n input_mtok: 0.27,\n output_mtok: 0.85\n }\n },\n {\n id: \"meta-llama/Llama-4-Scout-17B-16E-Instruct\",\n match: {\n equals: \"meta-llama/Llama-4-Scout-17B-16E-Instruct\"\n },\n prices: {\n input_mtok: 0.18,\n output_mtok: 0.59\n }\n },\n {\n id: \"meta-llama/Meta-Llama-3-70B-Instruct-Lite\",\n match: {\n equals: \"meta-llama/Meta-Llama-3-70B-Instruct-Lite\"\n },\n prices: {\n input_mtok: 0.54,\n output_mtok: 0.54\n }\n },\n {\n id: \"meta-llama/Meta-Llama-3-70B-Instruct-Turbo\",\n match: {\n equals: \"meta-llama/Meta-Llama-3-70B-Instruct-Turbo\"\n },\n prices: {\n input_mtok: 0.88,\n output_mtok: 0.88\n }\n },\n {\n id: \"meta-llama/Meta-Llama-3-8B-Instruct-Lite\",\n match: {\n equals: \"meta-llama/Meta-Llama-3-8B-Instruct-Lite\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"meta-llama/Meta-Llama-3-8B-Instruct-Turbo\",\n match: {\n equals: \"meta-llama/Meta-Llama-3-8B-Instruct-Turbo\"\n },\n prices: {\n input_mtok: 0.18,\n output_mtok: 0.18\n }\n },\n {\n id: \"meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo\",\n match: {\n equals: \"meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo\"\n },\n prices: {\n input_mtok: 3.5,\n output_mtok: 3.5\n }\n },\n {\n id: \"meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo\",\n match: {\n equals: \"meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo\"\n },\n prices: {\n input_mtok: 0.88,\n output_mtok: 0.88\n }\n },\n {\n id: \"meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo\",\n match: {\n equals: \"meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo\"\n },\n prices: {\n input_mtok: 0.18,\n output_mtok: 0.18\n }\n },\n {\n id: \"meta-llama/Meta-Llama-3.3-70B-Instruct-Turbo\",\n match: {\n equals: \"meta-llama/Meta-Llama-3.3-70B-Instruct-Turbo\"\n },\n prices: {\n input_mtok: 0.88,\n output_mtok: 0.88\n }\n },\n {\n id: \"microsoft/WizardLM-2-8x22B\",\n match: {\n equals: \"microsoft/WizardLM-2-8x22B\"\n },\n prices: {\n input_mtok: 1.2,\n output_mtok: 1.2\n }\n },\n {\n id: \"microsoft/phi-2\",\n match: {\n equals: \"microsoft/phi-2\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"mistralai/Mistral-7B-Instruct-v0.1\",\n match: {\n equals: \"mistralai/Mistral-7B-Instruct-v0.1\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"mistralai/Mistral-7B-Instruct-v0.2\",\n match: {\n equals: \"mistralai/Mistral-7B-Instruct-v0.2\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"mistralai/Mistral-7B-v0.1\",\n match: {\n equals: \"mistralai/Mistral-7B-v0.1\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"mistralai/Mixtral-8x22B-Instruct-v0.1\",\n match: {\n equals: \"mistralai/Mixtral-8x22B-Instruct-v0.1\"\n },\n prices: {\n input_mtok: 2.4,\n output_mtok: 2.4\n }\n },\n {\n id: \"mistralai/Mixtral-8x7B-Instruct-v0.1\",\n match: {\n equals: \"mistralai/Mixtral-8x7B-Instruct-v0.1\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"mistralai/Mixtral-8x7B-v0.1\",\n match: {\n equals: \"mistralai/Mixtral-8x7B-v0.1\"\n },\n prices: {\n input_mtok: 0.9,\n output_mtok: 0.9\n }\n },\n {\n id: \"openchat/openchat-3.5-1210\",\n match: {\n equals: \"openchat/openchat-3.5-1210\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"snorkelai/Snorkel-Mistral-PairRM-DPO\",\n match: {\n equals: \"snorkelai/Snorkel-Mistral-PairRM-DPO\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"teknium/OpenHermes-2-Mistral-7B\",\n match: {\n equals: \"teknium/OpenHermes-2-Mistral-7B\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"teknium/OpenHermes-2p5-Mistral-7B\",\n match: {\n equals: \"teknium/OpenHermes-2p5-Mistral-7B\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"together/Tev1-4B-experimental\",\n name: \"Tev1 4B Experimental\",\n match: {\n equals: \"together/Tev1-4B-experimental\"\n },\n context_window: 32768,\n price_comments: \"The model page and https://www.together.ai/pricing list $0.04 per million input tokens and free output. The serverless catalog still lists $0.042: https://docs.together.ai/docs/serverless/models\",\n prices: {\n input_mtok: 0.04\n }\n },\n {\n id: \"togethercomputer/GPT-JT-Moderation-6B\",\n match: {\n equals: \"togethercomputer/GPT-JT-Moderation-6B\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"togethercomputer/Llama-2-7B-32K-Instruct\",\n match: {\n equals: \"togethercomputer/Llama-2-7B-32K-Instruct\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"togethercomputer/RedPajama-INCITE-7B-Base\",\n match: {\n equals: \"togethercomputer/RedPajama-INCITE-7B-Base\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"togethercomputer/RedPajama-INCITE-7B-Chat\",\n match: {\n equals: \"togethercomputer/RedPajama-INCITE-7B-Chat\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"togethercomputer/RedPajama-INCITE-7B-Instruct\",\n match: {\n equals: \"togethercomputer/RedPajama-INCITE-7B-Instruct\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"togethercomputer/RedPajama-INCITE-Base-3B-v1\",\n match: {\n equals: \"togethercomputer/RedPajama-INCITE-Base-3B-v1\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"togethercomputer/RedPajama-INCITE-Chat-3B-v1\",\n match: {\n equals: \"togethercomputer/RedPajama-INCITE-Chat-3B-v1\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"togethercomputer/RedPajama-INCITE-Instruct-3B-v1\",\n match: {\n equals: \"togethercomputer/RedPajama-INCITE-Instruct-3B-v1\"\n },\n prices: {\n input_mtok: 0.1,\n output_mtok: 0.1\n }\n },\n {\n id: \"togethercomputer/StripedHyena-Hessian-7B\",\n match: {\n equals: \"togethercomputer/StripedHyena-Hessian-7B\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"togethercomputer/StripedHyena-Nous-7B\",\n match: {\n equals: \"togethercomputer/StripedHyena-Nous-7B\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"togethercomputer/alpaca-7b\",\n match: {\n equals: \"togethercomputer/alpaca-7b\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n },\n {\n id: \"upstage/SOLAR-10.7B-Instruct-v1.0\",\n match: {\n equals: \"upstage/SOLAR-10.7B-Instruct-v1.0\"\n },\n prices: {\n input_mtok: 0.3,\n output_mtok: 0.3\n }\n },\n {\n id: \"zero-one-ai/Yi-34B\",\n match: {\n equals: \"zero-one-ai/Yi-34B\"\n },\n prices: {\n input_mtok: 0.8,\n output_mtok: 0.8\n }\n },\n {\n id: \"zero-one-ai/Yi-6B\",\n match: {\n equals: \"zero-one-ai/Yi-6B\"\n },\n prices: {\n input_mtok: 0.2,\n output_mtok: 0.2\n }\n }\n ]\n },\n {\n id: \"typesafe\",\n name: \"TypeSafe\",\n pricing_urls: [\"https://docs.typesafe.ai/models\"],\n api_pattern: \"https://api\\\\.typesafe\\\\.ai\",\n price_comments: \"TypeSafe bills Jev per input token only; output tokens are free. `jev-latest` and `jev-preview` are aliases that move with releases and currently point at `jev-1.13.0`; a versioned id is billed the same.\",\n model_match: {\n starts_with: \"jev-\"\n },\n provider_match: {\n contains: \"typesafe\"\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"jev-1.13.0\",\n name: \"Jev 1.13.0\",\n match: {\n or: [\n {\n equals: \"jev-1.13.0\"\n },\n {\n equals: \"jev-latest\"\n },\n {\n equals: \"jev-preview\"\n }\n ]\n },\n context_window: 32000,\n prices: {\n input_mtok: 0.042\n }\n }\n ]\n },\n {\n id: \"voyageai\",\n name: \"Voyage AI\",\n pricing_urls: [\"https://docs.voyageai.com/docs/pricing\"],\n api_pattern: \"https://api\\\\.voyageai\\\\.com\",\n price_comments: \"Voyage AI bills per input token only; embedding models produce vectors rather than completion tokens, so there is no output price. The Batch API offers a 33% discount. This file covers text embedding models only; rerankers and multimodal embedding models are not included.\",\n model_match: {\n starts_with: \"voyage-\"\n },\n provider_match: {\n contains: \"voyage\"\n },\n models: [\n {\n id: \"voyage-01\",\n name: \"Voyage 01\",\n match: {\n equals: \"voyage-01\"\n },\n prices: {\n input_mtok: 0.1\n },\n deprecated: true\n },\n {\n id: \"voyage-02\",\n name: \"Voyage 02\",\n match: {\n equals: \"voyage-02\"\n },\n prices: {\n input_mtok: 0.1\n },\n deprecated: true\n },\n {\n id: \"voyage-2\",\n name: \"Voyage 2\",\n match: {\n equals: \"voyage-2\"\n },\n prices: {\n input_mtok: 0.1\n }\n },\n {\n id: \"voyage-3\",\n name: \"Voyage 3\",\n description: \"General-purpose text embedding model optimized for retrieval quality and cost.\",\n match: {\n equals: \"voyage-3\"\n },\n prices: {\n input_mtok: 0.06\n }\n },\n {\n id: \"voyage-3-large\",\n name: \"Voyage 3 Large\",\n match: {\n equals: \"voyage-3-large\"\n },\n prices: {\n input_mtok: 0.18\n }\n },\n {\n id: \"voyage-3-lite\",\n name: \"Voyage 3 Lite\",\n match: {\n equals: \"voyage-3-lite\"\n },\n prices: {\n input_mtok: 0.02\n }\n },\n {\n id: \"voyage-3.5\",\n name: \"Voyage 3.5\",\n description: \"General-purpose text embedding model optimized for retrieval quality and cost.\",\n match: {\n equals: \"voyage-3.5\"\n },\n prices: {\n input_mtok: 0.06\n }\n },\n {\n id: \"voyage-3.5-lite\",\n name: \"Voyage 3.5 Lite\",\n description: \"Latency- and cost-optimized variant of voyage-3.5.\",\n match: {\n equals: \"voyage-3.5-lite\"\n },\n prices: {\n input_mtok: 0.02\n }\n },\n {\n id: \"voyage-4\",\n name: \"Voyage 4\",\n description: \"General-purpose text embedding model balancing retrieval quality and cost.\",\n match: {\n equals: \"voyage-4\"\n },\n prices: {\n input_mtok: 0.06\n }\n },\n {\n id: \"voyage-4-large\",\n name: \"Voyage 4 Large\",\n description: \"Highest-quality general-purpose text embedding model in the Voyage 4 family.\",\n match: {\n equals: \"voyage-4-large\"\n },\n prices: {\n input_mtok: 0.12\n }\n },\n {\n id: \"voyage-4-lite\",\n name: \"Voyage 4 Lite\",\n description: \"Latency- and cost-optimized text embedding model in the Voyage 4 family.\",\n match: {\n equals: \"voyage-4-lite\"\n },\n prices: {\n input_mtok: 0.02\n }\n },\n {\n id: \"voyage-code-2\",\n name: \"Voyage Code 2\",\n description: \"Embedding model optimized for code retrieval.\",\n match: {\n equals: \"voyage-code-2\"\n },\n prices: {\n input_mtok: 0.12\n }\n },\n {\n id: \"voyage-code-3\",\n name: \"Voyage Code 3\",\n description: \"Embedding model optimized for code retrieval.\",\n match: {\n equals: \"voyage-code-3\"\n },\n prices: {\n input_mtok: 0.18\n }\n },\n {\n id: \"voyage-code-4\",\n name: \"Voyage Code 4\",\n description: \"Embedding model optimized for code retrieval.\",\n match: {\n equals: \"voyage-code-4\"\n },\n prices: {\n input_mtok: 0.12\n }\n },\n {\n id: \"voyage-context-3\",\n name: \"Voyage Context 3\",\n description: \"Contextualized chunk embedding model that encodes chunks together with full-document context.\",\n match: {\n equals: \"voyage-context-3\"\n },\n prices: {\n input_mtok: 0.18\n }\n },\n {\n id: \"voyage-context-4\",\n name: \"Voyage Context 4\",\n description: \"Contextualized chunk embedding model that encodes chunks together with full-document context.\",\n match: {\n equals: \"voyage-context-4\"\n },\n prices: {\n input_mtok: 0.12\n }\n },\n {\n id: \"voyage-finance-2\",\n name: \"Voyage Finance 2\",\n description: \"Embedding model optimized for finance-domain retrieval.\",\n match: {\n equals: \"voyage-finance-2\"\n },\n prices: {\n input_mtok: 0.12\n }\n },\n {\n id: \"voyage-large-2\",\n name: \"Voyage Large 2\",\n match: {\n equals: \"voyage-large-2\"\n },\n prices: {\n input_mtok: 0.12\n }\n },\n {\n id: \"voyage-large-2-instruct\",\n name: \"Voyage Large 2 Instruct\",\n match: {\n equals: \"voyage-large-2-instruct\"\n },\n prices: {\n input_mtok: 0.12\n }\n },\n {\n id: \"voyage-law-2\",\n name: \"Voyage Law 2\",\n description: \"Embedding model optimized for legal-domain retrieval.\",\n match: {\n equals: \"voyage-law-2\"\n },\n prices: {\n input_mtok: 0.12\n }\n },\n {\n id: \"voyage-lite-01\",\n name: \"Voyage Lite 01\",\n match: {\n equals: \"voyage-lite-01\"\n },\n prices: {\n input_mtok: 0.1\n },\n deprecated: true\n },\n {\n id: \"voyage-lite-01-instruct\",\n name: \"Voyage Lite 01 Instruct\",\n match: {\n equals: \"voyage-lite-01-instruct\"\n },\n prices: {\n input_mtok: 0.1\n },\n deprecated: true\n },\n {\n id: \"voyage-lite-02-instruct\",\n name: \"Voyage Lite 02 Instruct\",\n match: {\n equals: \"voyage-lite-02-instruct\"\n },\n prices: {\n input_mtok: 0.1\n },\n deprecated: true\n },\n {\n id: \"voyage-multilingual-2\",\n name: \"Voyage Multilingual 2\",\n match: {\n equals: \"voyage-multilingual-2\"\n },\n prices: {\n input_mtok: 0.12\n }\n }\n ]\n },\n {\n id: \"x-ai\",\n name: \"X AI\",\n pricing_urls: [\"https://docs.x.ai/docs/models\"],\n api_pattern: \"https://api\\\\.x\\\\.ai\",\n model_match: {\n contains: \"grok\"\n },\n provider_match: {\n equals: \"xai\"\n },\n extractors: [\n {\n api_flavor: \"default\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: \"cached_prompt_text_tokens\",\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: \"reasoning_tokens\",\n dest: \"output_tokens\",\n required: false\n },\n {\n path: \"reasoning_tokens\",\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n },\n {\n api_flavor: \"realtime\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"input_tokens\",\n dest: \"input_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"text_tokens\"],\n dest: \"input_text_tokens\",\n required: false\n },\n {\n path: [\"input_token_details\", \"audio_tokens\"],\n dest: \"input_audio_tokens\",\n required: false\n },\n {\n path: \"output_tokens\",\n dest: \"output_tokens\",\n required: false\n },\n {\n path: [\"output_token_details\", \"text_tokens\"],\n dest: \"output_text_tokens\",\n required: false\n },\n {\n path: [\"output_token_details\", \"audio_tokens\"],\n dest: \"output_audio_tokens\",\n required: false\n },\n {\n path: \"billable_audio_seconds\",\n dest: \"audio_seconds\",\n required: true\n },\n {\n path: \"input_text_messages\",\n dest: \"input_text_messages\",\n required: false\n }\n ]\n }\n ],\n models: [\n {\n id: \"grok-2-1212\",\n name: \"Grok 2 1212\",\n description: \"(deprecated) Grok 2 1212 introduces significant enhancements to accuracy, instruction adherence, and multilingual support, making it a powerful and flexible choice for developers seeking a highly steerable, intelligent model.\",\n match: {\n or: [\n {\n equals: \"grok-2-1212\"\n },\n {\n equals: \"grok-2\"\n },\n {\n equals: \"grok-2-latest\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 2,\n output_mtok: 10\n },\n deprecated: true\n },\n {\n id: \"grok-2-vision-1212\",\n name: \"Grok 2 Vision 1212\",\n description: \"Our multimodal model that processes documents, diagrams, charts, screenshots, and photographs.\",\n match: {\n or: [\n {\n equals: \"grok-2-vision-1212\"\n },\n {\n equals: \"grok-2-vision\"\n },\n {\n equals: \"grok-2-vision-latest\"\n }\n ]\n },\n context_window: 32768,\n prices: {\n input_mtok: 2,\n output_mtok: 10\n }\n },\n {\n id: \"grok-3\",\n name: \"Grok 3\",\n description: \"Flagship model that excels at enterprise use cases like data extraction, coding, and text summarization. Possesses deep domain knowledge in finance, healthcare, law, and science.\",\n match: {\n or: [\n {\n equals: \"grok-3\"\n },\n {\n equals: \"grok-3-latest\"\n },\n {\n equals: \"grok-3-beta\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 3,\n cache_read_mtok: 0.75,\n output_mtok: 15\n }\n },\n {\n id: \"grok-3-fast\",\n name: \"Grok 3 Fast\",\n description: \"Excels at enterprise use cases like data extraction, coding, and text summarization. Possesses deep domain knowledge in finance, healthcare, law, and science.\",\n match: {\n or: [\n {\n equals: \"grok-3-fast\"\n },\n {\n equals: \"grok-3-fast-latest\"\n },\n {\n equals: \"grok-3-fast-beta\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 5,\n cache_read_mtok: 1.25,\n output_mtok: 25\n }\n },\n {\n id: \"grok-3-mini\",\n name: \"Grok 3 Mini\",\n description: \"A lightweight model that thinks before responding. Fast, smart, and great for logic-based tasks that do not require deep domain knowledge. The raw thinking traces are accessible.\",\n match: {\n or: [\n {\n equals: \"grok-3-mini\"\n },\n {\n equals: \"grok-3-mini-beta\"\n },\n {\n equals: \"grok-3-mini-latest\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.075,\n output_mtok: 0.5\n }\n },\n {\n id: \"grok-3-mini-fast\",\n name: \"Grok 3 Mini Fast\",\n description: \"A lightweight model that thinks before responding. Fast, smart, and great for logic-based tasks that do not require deep domain knowledge. The raw thinking traces are accessible.\",\n match: {\n or: [\n {\n equals: \"grok-3-mini-fast\"\n },\n {\n equals: \"grok-3-mini-fast-beta\"\n },\n {\n equals: \"grok-3-mini-fast-latest\"\n }\n ]\n },\n context_window: 131072,\n prices: {\n input_mtok: 0.6,\n cache_read_mtok: 0.15,\n output_mtok: 4\n }\n },\n {\n id: \"grok-4-0709\",\n name: \"Grok 4\",\n description: \"A flagship model, offering unparalleled performance in natural language, math and reasoning - the perfect jack of all trades.\",\n match: {\n or: [\n {\n equals: \"grok-4-0709\"\n },\n {\n equals: \"grok-4\"\n },\n {\n equals: \"grok-4-latest\"\n }\n ]\n },\n context_window: 256000,\n prices: {\n input_mtok: 3,\n cache_read_mtok: 0.75,\n output_mtok: 15\n }\n },\n {\n id: \"grok-4-1-fast-non-reasoning\",\n name: \"Grok 4.1 Fast Non-Reasoning\",\n description: \"A frontier multimodal model optimized specifically for high-performance agentic tool calling.\",\n match: {\n or: [\n {\n equals: \"grok-4-1-fast-non-reasoning\"\n },\n {\n equals: \"grok-4-1-fast-non-reasoning-latest\"\n }\n ]\n },\n context_window: 2000000,\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.05,\n output_mtok: 0.5\n }\n },\n {\n id: \"grok-4-1-fast-reasoning\",\n name: \"Grok 4.1 Fast Reasoning\",\n description: \"A frontier multimodal model optimized specifically for high-performance agentic tool calling.\",\n match: {\n or: [\n {\n equals: \"grok-4-1-fast\"\n },\n {\n equals: \"grok-4-1-fast-reasoning\"\n },\n {\n equals: \"grok-4-1-fast-reasoning-latest\"\n }\n ]\n },\n context_window: 2000000,\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.05,\n output_mtok: 0.5\n }\n },\n {\n id: \"grok-4-fast-non-reasoning\",\n name: \"Grok 4 Fast Non-Reasoning\",\n description: \"A frontier multimodal model optimized specifically for high-performance agentic tool calling.\",\n match: {\n or: [\n {\n equals: \"grok-4-fast-non-reasoning\"\n },\n {\n equals: \"grok-4-fast-non-reasoning-latest\"\n }\n ]\n },\n context_window: 2000000,\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.05,\n output_mtok: 0.5\n }\n },\n {\n id: \"grok-4-fast-reasoning\",\n name: \"Grok 4 Fast Reasoning\",\n description: \"A frontier multimodal model optimized specifically for high-performance agentic tool calling.\",\n match: {\n or: [\n {\n equals: \"grok-4-fast\"\n },\n {\n equals: \"grok-4-fast-reasoning\"\n },\n {\n equals: \"grok-4-fast-reasoning-latest\"\n }\n ]\n },\n context_window: 2000000,\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.05,\n output_mtok: 0.5\n }\n },\n {\n id: \"grok-4.20\",\n name: \"Grok 4.20\",\n description: \"Grok 4.20 is a reasoning model from xAI with industry-leading speed and agentic tool calling capabilities. It combines low hallucination rates with strict prompt adherence.\",\n match: {\n equals: \"grok-4.20\"\n },\n context_window: 1e6,\n price_comments: 'Prompts at or above 200k tokens are billed at the higher rate for every token in the request, not just the tokens past the threshold. xAI\\'s boundary is inclusive -- its table columns read \"< 200k prompt tokens\" and \">= 200k prompt tokens\" -- while a tier here fires on `tokens > start`, so the start is 199999 and a 200000-token prompt lands on the higher rate. Ref: https://docs.x.ai/docs/models The Batch API is 20% off every token type, cached tokens included, on both context tiers. Ref: https://docs.x.ai/developers/pricing#batch-api-pricing',\n prices: {\n input_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 199999,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 199999,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 199999,\n price: 5\n }\n ]\n }\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 1,\n tiers: [\n {\n start: 199999,\n price: 2\n }\n ]\n },\n cache_read_mtok: {\n base: 0.16,\n tiers: [\n {\n start: 199999,\n price: 0.32\n }\n ]\n },\n output_mtok: {\n base: 2,\n tiers: [\n {\n start: 199999,\n price: 4\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"grok-4.20-multi-agent\",\n name: \"Grok 4.20 Multi-Agent\",\n description: \"Grok 4.20 Multi-Agent is a variant of xAI's Grok 4.20 designed for collaborative, agent-based workflows. Multiple agents operate in parallel to conduct deep research, coordinate tool use, and synthesize information.\",\n match: {\n equals: \"grok-4.20-multi-agent\"\n },\n context_window: 1e6,\n prices: {\n input_mtok: 2,\n cache_read_mtok: 0.2,\n output_mtok: 6\n }\n },\n {\n id: \"grok-4.3\",\n name: \"Grok 4.3\",\n description: \"Most advanced flagship model, leading the industry in non-hallucination rate, agentic tool calling, and instruction following capabilities. Supports text and image inputs with text outputs, function calling, structured outputs, and reasoning.\",\n match: {\n or: [\n {\n equals: \"grok-4.3\"\n },\n {\n regex: \"^grok-4\\\\.3-\\\\d{8}$\"\n },\n {\n equals: \"x-ai/grok-4.3\"\n },\n {\n regex: \"^x-ai/grok-4\\\\.3-\\\\d{8}$\"\n },\n {\n equals: \"grok-4.3-latest\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: 'Prompts at or above 200k tokens are billed at the higher rate for every token in the request, not just the tokens past the threshold. xAI\\'s boundary is inclusive -- its table columns read \"< 200k prompt tokens\" and \">= 200k prompt tokens\" -- while a tier here fires on `tokens > start`, so the start is 199999 and a 200000-token prompt lands on the higher rate. Ref: https://docs.x.ai/docs/models The Batch API is 20% off every token type, cached tokens included, on both context tiers. Ref: https://docs.x.ai/developers/pricing#batch-api-pricing',\n prices: {\n input_mtok: {\n base: 1.25,\n tiers: [\n {\n start: 199999,\n price: 2.5\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 199999,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 2.5,\n tiers: [\n {\n start: 199999,\n price: 5\n }\n ]\n }\n },\n price_variants: [\n {\n when: {\n processing: \"batch\"\n },\n prices: {\n input_mtok: {\n base: 1,\n tiers: [\n {\n start: 199999,\n price: 2\n }\n ]\n },\n cache_read_mtok: {\n base: 0.16,\n tiers: [\n {\n start: 199999,\n price: 0.32\n }\n ]\n },\n output_mtok: {\n base: 2,\n tiers: [\n {\n start: 199999,\n price: 4\n }\n ]\n }\n },\n constraint: {\n start_date: \"2026-10-14\",\n type: \"start_date\"\n }\n }\n ]\n },\n {\n id: \"grok-4.5\",\n name: \"Grok 4.5\",\n description: \"xAI's most intelligent and fastest flagship model, well-suited for general-purpose use including coding and chat. Supports text and image inputs with text outputs, function calling, structured outputs, and reasoning.\",\n match: {\n or: [\n {\n equals: \"grok-4.5\"\n },\n {\n regex: \"^grok-4\\\\.5-\\\\d{8}$\"\n },\n {\n equals: \"x-ai/grok-4.5\"\n },\n {\n regex: \"^x-ai/grok-4\\\\.5-\\\\d{8}$\"\n },\n {\n equals: \"grok-4.5-latest\"\n },\n {\n equals: \"grok-latest\"\n }\n ]\n },\n context_window: 500000,\n price_comments: 'Prompts at or above 200k tokens are billed at the higher rate for every token in the request, not just the tokens past the threshold. xAI\\'s boundary is inclusive -- its table columns read \"< 200k prompt tokens\" and \">= 200k prompt tokens\" -- while a tier here fires on `tokens > start`, so the start is 199999 and a 200000-token prompt lands on the higher rate. The base cache-read price is 0.30, not 0.50 - 0.50 is Grok 4.6\\'s. Ref: https://docs.x.ai/docs/models/grok-4.5',\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 199999,\n price: 4\n }\n ]\n },\n cache_read_mtok: {\n base: 0.3,\n tiers: [\n {\n start: 199999,\n price: 0.6\n }\n ]\n },\n output_mtok: {\n base: 6,\n tiers: [\n {\n start: 199999,\n price: 12\n }\n ]\n }\n }\n },\n {\n id: \"grok-4.6\",\n name: \"Grok 4.6\",\n description: \"xAI's latest flagship model, released 2026-08-12. Supports text and image inputs with text outputs, function calling, structured outputs, and reasoning, over a 500k-token context window.\",\n match: {\n or: [\n {\n equals: \"grok-4.6\"\n },\n {\n regex: \"^grok-4\\\\.6-\\\\d{8}$\"\n },\n {\n equals: \"x-ai/grok-4.6\"\n },\n {\n regex: \"^x-ai/grok-4\\\\.6-\\\\d{8}$\"\n },\n {\n equals: \"grok-4.6-latest\"\n }\n ]\n },\n context_window: 500000,\n price_comments: 'Prompts at or above 200k tokens are billed at the higher rate for every token in the request, not just the tokens past the threshold. xAI\\'s boundary is inclusive -- its table columns read \"< 200k prompt tokens\" and \">= 200k prompt tokens\" -- while a tier here fires on `tokens > start`, so the start is 199999 and a 200000-token prompt lands on the higher rate. Ref: https://docs.x.ai/docs/models/grok-4.6',\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 199999,\n price: 4\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 199999,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 6,\n tiers: [\n {\n start: 199999,\n price: 12\n }\n ]\n }\n }\n },\n {\n id: \"grok-4.7\",\n name: \"Grok 4.7\",\n description: \"xAI's flagship model for coding, agentic tasks, and knowledge work. Supports text and image inputs with text outputs, function calling, structured outputs, and configurable reasoning, over a 500k-token context window.\",\n match: {\n or: [\n {\n equals: \"grok-4.7\"\n },\n {\n regex: \"^grok-4\\\\.7-\\\\d{8}$\"\n },\n {\n equals: \"x-ai/grok-4.7\"\n },\n {\n regex: \"^x-ai/grok-4\\\\.7-\\\\d{8}$\"\n },\n {\n equals: \"grok-4.7-latest\"\n }\n ]\n },\n context_window: 500000,\n price_comments: 'Prompts at or above 200k tokens are billed at the higher rate for every token in the request, not just the tokens past the threshold. xAI\\'s pricing table labels the long-context column \"Long context >= 200k tokens\", while a tier here fires on `tokens > start`, so the start is 199999 and a 200000-token prompt lands on the higher rate. Refs: https://docs.x.ai/developers/pricing, https://docs.x.ai/developers/models/grok-4.7',\n prices: {\n input_mtok: {\n base: 2,\n tiers: [\n {\n start: 199999,\n price: 4\n }\n ]\n },\n cache_read_mtok: {\n base: 0.5,\n tiers: [\n {\n start: 199999,\n price: 1\n }\n ]\n },\n output_mtok: {\n base: 6,\n tiers: [\n {\n start: 199999,\n price: 12\n }\n ]\n }\n }\n },\n {\n id: \"grok-build-0.1\",\n name: \"Grok Build 0.1\",\n description: \"Grok Build 0.1 is xAI's fast coding model trained specifically for agentic software engineering workflows. It supports text and image inputs with text output, and is optimized for interactive coding.\",\n match: {\n equals: \"grok-build-0.1\"\n },\n context_window: 256000,\n price_comments: 'Prompts at or above 200k tokens are billed at the higher rate for every token in the request, not just the tokens past the threshold. xAI\\'s boundary is inclusive -- its table columns read \"< 200k prompt tokens\" and \">= 200k prompt tokens\" -- while a tier here fires on `tokens > start`, so the start is 199999 and a 200000-token prompt lands on the higher rate. Ref: https://docs.x.ai/docs/models',\n prices: {\n input_mtok: {\n base: 1,\n tiers: [\n {\n start: 199999,\n price: 2\n }\n ]\n },\n cache_read_mtok: {\n base: 0.2,\n tiers: [\n {\n start: 199999,\n price: 0.4\n }\n ]\n },\n output_mtok: {\n base: 2,\n tiers: [\n {\n start: 199999,\n price: 4\n }\n ]\n }\n }\n },\n {\n id: \"grok-code-fast-1\",\n name: \"Grok Code Fast 1\",\n description: \"A speedy and economical reasoning model that excels at agentic coding.\",\n match: {\n or: [\n {\n equals: \"grok-code-fast\"\n },\n {\n equals: \"grok-code-fast-1\"\n },\n {\n equals: \"grok-code-fast-1-0825\"\n }\n ]\n },\n context_window: 256000,\n prices: {\n input_mtok: 0.2,\n cache_read_mtok: 0.02,\n output_mtok: 1.5\n }\n },\n {\n id: \"grok-voice-latest\",\n name: \"Grok Voice\",\n match: {\n equals: \"grok-voice-latest\"\n },\n price_comments: \"See https://docs.x.ai/developers/release-notes and https://docs.x.ai/developers/pricing#voice-pricing.\",\n prices: [\n {\n prices: {\n audio_hours: 3,\n input_text_messages_kcount: 4\n }\n },\n {\n constraint: {\n start_date: \"2026-08-05\",\n type: \"start_date\"\n },\n prices: {\n audio_hours: 4.8,\n input_text_messages_kcount: 4\n }\n }\n ]\n },\n {\n id: \"grok-voice-think-fast-1.0\",\n name: \"Grok Voice Think Fast 1.0\",\n match: {\n equals: \"grok-voice-think-fast-1.0\"\n },\n price_comments: \"See https://docs.x.ai/developers/pricing#voice-pricing.\",\n prices: {\n audio_hours: 3,\n input_text_messages_kcount: 4\n },\n deprecated: true\n },\n {\n id: \"grok-voice-think-fast-2.0\",\n name: \"Grok Voice Think Fast 2.0\",\n match: {\n equals: \"grok-voice-think-fast-2.0\"\n },\n price_comments: \"See https://docs.x.ai/developers/pricing#voice-pricing.\",\n prices: {\n audio_hours: 4.8,\n input_text_messages_kcount: 4\n }\n }\n ]\n },\n {\n id: \"zai\",\n name: \"Z.AI\",\n pricing_urls: [\"https://docs.z.ai/guides/overview/pricing\"],\n api_pattern: \"https://api\\\\.z\\\\.ai\",\n price_comments: \"USD prices from the Z.AI pricing page. The API pattern covers both the standard and Coding Plan endpoints, with Coding Plan usage valued at the published API rates.\",\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"GLM-4.6V\",\n name: \"GLM-4.6V\",\n description: \"Z.AI vision model with video, image, text and file input and native tool use.\",\n match: {\n or: [\n {\n equals: \"GLM-4.6V\"\n },\n {\n equals: \"glm-4.6v\"\n }\n ]\n },\n context_window: 128000,\n price_comments: \"Context length is 128K per https://docs.z.ai/guides/vlm/glm-4.6v.\",\n prices: {\n input_mtok: 0.3,\n cache_read_mtok: 0.05,\n output_mtok: 0.9\n }\n },\n {\n id: \"GLM-5.2\",\n name: \"GLM-5.2\",\n description: \"Z.AI flagship model with a 1,000,000 token context window, context caching, structured output, and function calling.\",\n match: {\n or: [\n {\n equals: \"GLM-5.2\"\n },\n {\n equals: \"glm-5.2\"\n }\n ]\n },\n context_window: 1e6,\n prices: {\n input_mtok: 1.4,\n cache_read_mtok: 0.26,\n output_mtok: 4.4\n }\n },\n {\n id: \"GLM-5.3\",\n name: \"GLM-5.3\",\n description: \"Z.AI flagship model with a 1,000,000 token context window, context caching, structured output, and function calling. Reasoning is always enabled, with low, high and max effort levels.\",\n match: {\n or: [\n {\n equals: \"GLM-5.3\"\n },\n {\n equals: \"glm-5.3\"\n }\n ]\n },\n context_window: 1e6,\n prices: {\n input_mtok: 1.4,\n cache_read_mtok: 0.26,\n output_mtok: 4.4\n }\n },\n {\n id: \"GLM-5.3-Flash\",\n name: \"GLM-5.3-Flash\",\n description: \"Z.AI native multimodal model with a 1,000,000 token context window, context caching, function calling, and always-on reasoning.\",\n match: {\n or: [\n {\n equals: \"GLM-5.3-Flash\"\n },\n {\n equals: \"glm-5.3-flash\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"The 50% launch promotion ended at 24:00 on September 9, 2026 (UTC+8), which is 16:00 UTC. `start_date` is a UTC date, so the list prices start on 2026-09-10 and requests from 16:00 to 24:00 UTC on September 9, after the promotion ended, stay at the promotional rate.\",\n prices: [\n {\n prices: {\n input_mtok: 0.075,\n cache_read_mtok: 0.015,\n output_mtok: 0.25\n }\n },\n {\n constraint: {\n start_date: \"2026-09-10\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 0.15,\n cache_read_mtok: 0.03,\n output_mtok: 0.5\n }\n }\n ]\n }\n ]\n },\n {\n id: \"zhipuai\",\n name: \"Zhipu AI\",\n pricing_urls: [\"https://open.bigmodel.cn/pricing\", \"https://docs.bigmodel.cn/cn/guide/start/model-overview\"],\n api_pattern: \"https://open\\\\.bigmodel\\\\.cn\",\n price_comments: \"Prices sourced from Zhipu AI open platform pricing (CNY, open.bigmodel.cn/pricing), converted to USD at 1 USD = 7.25 CNY (May/June 2026). Zhipu AI does not publish USD prices; CNY is the only billing currency. Flagship models (GLM-4.5-Air, GLM-4.7, GLM-5 series) have tiered pricing by input/output length; prices shown are for the cheapest tier ([0, 32k) input / [0, 0.2k) output where applicable). GLM-4 standard inference models (GLM-4-Air, GLM-4-Plus, etc.) bill input and output tokens at the same per-token rate per their pricing page. Cache write is temporarily free for flagship models (limited-time promotion, not included).\",\n model_match: {\n or: [\n {\n starts_with: \"GLM-\"\n },\n {\n starts_with: \"glm-\"\n }\n ]\n },\n extractors: [\n {\n api_flavor: \"chat\",\n root: \"usage\",\n model_path: \"model\",\n mappings: [\n {\n path: \"prompt_tokens\",\n dest: \"input_tokens\",\n required: true\n },\n {\n path: [\"prompt_tokens_details\", \"cached_tokens\"],\n dest: \"cache_read_tokens\",\n required: false\n },\n {\n path: [\"completion_tokens_details\", \"reasoning_tokens\"],\n dest: \"output_reasoning_tokens\",\n required: false\n },\n {\n path: \"completion_tokens\",\n dest: \"output_tokens\",\n required: true\n }\n ]\n }\n ],\n models: [\n {\n id: \"GLM-4-Air\",\n name: \"GLM-4-Air\",\n description: \"High-performance GLM-4 model with context caching. 128,000 token context window. Input and output billed at the same per-token rate.\",\n match: {\n or: [\n {\n equals: \"GLM-4-Air\"\n },\n {\n equals: \"glm-4-air\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.069,\n cache_read_mtok: 0.034,\n output_mtok: 0.069\n }\n },\n {\n id: \"GLM-4-AirX\",\n name: \"GLM-4-AirX\",\n description: \"Fastest GLM-4 model. 8,000 token context window. Does not support context caching. Input and output billed at the same per-token rate.\",\n match: {\n or: [\n {\n equals: \"GLM-4-AirX\"\n },\n {\n equals: \"glm-4-airx\"\n }\n ]\n },\n context_window: 8000,\n prices: {\n input_mtok: 1.379,\n output_mtok: 1.379\n }\n },\n {\n id: \"GLM-4-Assistant\",\n name: \"GLM-4-Assistant\",\n description: \"GLM-4 agent/assistant model. 128,000 token context window. Does not support context caching. Input and output billed at the same per-token rate.\",\n match: {\n or: [\n {\n equals: \"GLM-4-Assistant\"\n },\n {\n equals: \"glm-4-assistant\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.69,\n output_mtok: 0.69\n }\n },\n {\n id: \"GLM-4-FlashX-250414\",\n name: \"GLM-4-FlashX-250414\",\n description: \"Fast and cheap GLM-4 model with context caching. 128,000 token context window. Input and output billed at the same per-token rate.\",\n match: {\n or: [\n {\n equals: \"GLM-4-FlashX-250414\"\n },\n {\n equals: \"glm-4-flashx-250414\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.014,\n cache_read_mtok: 0.007,\n output_mtok: 0.014\n }\n },\n {\n id: \"GLM-4-Long\",\n name: \"GLM-4-Long\",\n description: \"GLM-4 model optimized for long inputs with context caching. 1,000,000 token context window. Input and output billed at the same per-token rate.\",\n match: {\n or: [\n {\n equals: \"GLM-4-Long\"\n },\n {\n equals: \"glm-4-long\"\n }\n ]\n },\n context_window: 1e6,\n prices: {\n input_mtok: 0.138,\n cache_read_mtok: 0.069,\n output_mtok: 0.138\n }\n },\n {\n id: \"GLM-4-Plus\",\n name: \"GLM-4-Plus\",\n description: \"Flagship GLM-4 model with context caching. 128,000 token context window. Input and output billed at the same per-token rate.\",\n match: {\n or: [\n {\n equals: \"GLM-4-Plus\"\n },\n {\n equals: \"glm-4-plus\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.69,\n cache_read_mtok: 0.345,\n output_mtok: 0.69\n }\n },\n {\n id: \"GLM-4.5-Air\",\n name: \"GLM-4.5-Air\",\n description: \"Zhipu AI's GLM-4.5-Air flagship model with context caching. 128,000 token context window. Tiered pricing; prices shown for [0, 32k) input / [0, 0.2k) output tier.\",\n match: {\n or: [\n {\n equals: \"GLM-4.5-Air\"\n },\n {\n equals: \"glm-4.5-air\"\n }\n ]\n },\n context_window: 128000,\n prices: {\n input_mtok: 0.11,\n cache_read_mtok: 0.022,\n output_mtok: 0.276\n }\n },\n {\n id: \"GLM-4.7\",\n name: \"GLM-4.7\",\n description: \"Zhipu AI's GLM-4.7 flagship model with context caching. 200,000 token context window. Tiered pricing; prices shown for [0, 32k) input / [0, 0.2k) output tier.\",\n match: {\n or: [\n {\n equals: \"GLM-4.7\"\n },\n {\n equals: \"glm-4.7\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 0.276,\n cache_read_mtok: 0.055,\n output_mtok: 1.103\n }\n },\n {\n id: \"GLM-4.7-FlashX\",\n name: \"GLM-4.7-FlashX\",\n description: \"Fast and affordable GLM-4.7 model with context caching. 200,000 token context window.\",\n match: {\n or: [\n {\n equals: \"GLM-4.7-FlashX\"\n },\n {\n equals: \"glm-4.7-flashx\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 0.069,\n cache_read_mtok: 0.014,\n output_mtok: 0.414\n }\n },\n {\n id: \"GLM-5\",\n name: \"GLM-5\",\n description: \"Zhipu AI GLM-5 model with context caching. 200,000 token context window. Tiered pricing; prices shown for [0, 32k) input tier.\",\n match: {\n or: [\n {\n equals: \"GLM-5\"\n },\n {\n equals: \"glm-5\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 0.552,\n cache_read_mtok: 0.138,\n output_mtok: 2.483\n }\n },\n {\n id: \"GLM-5-Turbo\",\n name: \"GLM-5-Turbo\",\n description: \"Zhipu AI GLM-5 Turbo model with context caching. 200,000 token context window. Tiered pricing; prices shown for [0, 32k) input tier.\",\n match: {\n or: [\n {\n equals: \"GLM-5-Turbo\"\n },\n {\n equals: \"glm-5-turbo\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 0.69,\n cache_read_mtok: 0.166,\n output_mtok: 3.034\n }\n },\n {\n id: \"GLM-5.1\",\n name: \"GLM-5.1\",\n description: \"Zhipu AI flagship model supporting long-horizon tasks, structured output, function calling, and context caching. 200,000 token context window. Tiered pricing; prices shown for [0, 32k) input tier.\",\n match: {\n or: [\n {\n equals: \"GLM-5.1\"\n },\n {\n equals: \"glm-5.1\"\n },\n {\n equals: \"GLM-5.1-20260406\"\n },\n {\n equals: \"glm-5.1-20260406\"\n }\n ]\n },\n context_window: 200000,\n prices: {\n input_mtok: 0.828,\n cache_read_mtok: 0.179,\n output_mtok: 3.31\n }\n },\n {\n id: \"GLM-5.2\",\n name: \"GLM-5.2\",\n description: \"Zhipu AI's latest flagship model supporting 1,000,000 token context, long-horizon coding tasks, structured output, function calling, and context caching.\",\n match: {\n or: [\n {\n equals: \"GLM-5.2\"\n },\n {\n equals: \"glm-5.2\"\n }\n ]\n },\n context_window: 1e6,\n prices: {\n input_mtok: 1.103,\n cache_read_mtok: 0.276,\n output_mtok: 3.862\n }\n },\n {\n id: \"GLM-5.3\",\n name: \"GLM-5.3\",\n description: \"Zhipu AI flagship model supporting 1,000,000 token context, long-horizon coding tasks, structured output, function calling, and context caching.\",\n match: {\n or: [\n {\n equals: \"GLM-5.3\"\n },\n {\n equals: \"glm-5.3\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Standard API rates are 8 CNY input, 2 CNY cached input, and 28 CNY output per million tokens, converted to USD at 1 USD = 7.25 CNY.\",\n prices: {\n input_mtok: 1.103,\n cache_read_mtok: 0.276,\n output_mtok: 3.862\n }\n },\n {\n id: \"GLM-5.3-Flash\",\n name: \"GLM-5.3-Flash\",\n description: \"Zhipu AI's native multimodal GLM-5.3 model with a 1,000,000 token context window, function calling, always-on reasoning, and context caching.\",\n match: {\n or: [\n {\n equals: \"GLM-5.3-Flash\"\n },\n {\n equals: \"glm-5.3-flash\"\n }\n ]\n },\n context_window: 1e6,\n price_comments: \"Launched with a two-week 50% promotion of 0.4 CNY input, 0.115 CNY cached input, and 1.4 CNY output per million tokens. List prices are 0.8 CNY input, 0.23 CNY cached input, and 2.8 CNY output. Converted to USD at 1 USD = 7.25 CNY. Zhipu's page does not state the end date; the list prices start on 2026-09-10, the first UTC day after Z.AI's promotion for the same model ended at 24:00 on September 9, 2026 (UTC+8).\",\n prices: [\n {\n prices: {\n input_mtok: 0.055,\n cache_read_mtok: 0.016,\n output_mtok: 0.193\n }\n },\n {\n constraint: {\n start_date: \"2026-09-10\",\n type: \"start_date\"\n },\n prices: {\n input_mtok: 0.11,\n cache_read_mtok: 0.032,\n output_mtok: 0.386\n }\n }\n ]\n }\n ]\n }\n];\nvar ee = {\n audio_seconds: {\n dimensions: {\n family: \"durations\",\n modality: \"audio\"\n },\n per: 3600,\n price_key: \"audio_hours\"\n },\n cache_audio_read_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"audio\",\n token_type: \"cache_read\"\n },\n per: 1e6,\n price_key: \"cache_audio_read_mtok\"\n },\n cache_audio_write_1h_tokens: {\n dimensions: {\n cache_ttl: \"1h\",\n direction: \"input\",\n family: \"tokens\",\n modality: \"audio\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_audio_write_1h_mtok\"\n },\n cache_audio_write_5m_tokens: {\n dimensions: {\n cache_ttl: \"5m\",\n direction: \"input\",\n family: \"tokens\",\n modality: \"audio\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_audio_write_5m_mtok\"\n },\n cache_audio_write_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"audio\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_audio_write_mtok\"\n },\n cache_image_read_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"image\",\n token_type: \"cache_read\"\n },\n per: 1e6,\n price_key: \"cache_image_read_mtok\"\n },\n cache_image_write_1h_tokens: {\n dimensions: {\n cache_ttl: \"1h\",\n direction: \"input\",\n family: \"tokens\",\n modality: \"image\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_image_write_1h_mtok\"\n },\n cache_image_write_5m_tokens: {\n dimensions: {\n cache_ttl: \"5m\",\n direction: \"input\",\n family: \"tokens\",\n modality: \"image\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_image_write_5m_mtok\"\n },\n cache_image_write_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"image\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_image_write_mtok\"\n },\n cache_read_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n token_type: \"cache_read\"\n },\n per: 1e6,\n price_key: \"cache_read_mtok\"\n },\n cache_text_read_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"text\",\n token_type: \"cache_read\"\n },\n per: 1e6,\n price_key: \"cache_text_read_mtok\"\n },\n cache_text_write_1h_tokens: {\n dimensions: {\n cache_ttl: \"1h\",\n direction: \"input\",\n family: \"tokens\",\n modality: \"text\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_text_write_1h_mtok\"\n },\n cache_text_write_5m_tokens: {\n dimensions: {\n cache_ttl: \"5m\",\n direction: \"input\",\n family: \"tokens\",\n modality: \"text\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_text_write_5m_mtok\"\n },\n cache_text_write_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"text\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_text_write_mtok\"\n },\n cache_video_read_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"video\",\n token_type: \"cache_read\"\n },\n per: 1e6,\n price_key: \"cache_video_read_mtok\"\n },\n cache_video_write_1h_tokens: {\n dimensions: {\n cache_ttl: \"1h\",\n direction: \"input\",\n family: \"tokens\",\n modality: \"video\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_video_write_1h_mtok\"\n },\n cache_video_write_5m_tokens: {\n dimensions: {\n cache_ttl: \"5m\",\n direction: \"input\",\n family: \"tokens\",\n modality: \"video\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_video_write_5m_mtok\"\n },\n cache_video_write_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"video\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_video_write_mtok\"\n },\n cache_write_1h_tokens: {\n dimensions: {\n cache_ttl: \"1h\",\n direction: \"input\",\n family: \"tokens\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_write_1h_mtok\"\n },\n cache_write_5m_tokens: {\n dimensions: {\n cache_ttl: \"5m\",\n direction: \"input\",\n family: \"tokens\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_write_5m_mtok\"\n },\n cache_write_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n token_type: \"cache_write\"\n },\n per: 1e6,\n price_key: \"cache_write_mtok\"\n },\n code_executions: {\n dimensions: {\n family: \"tool_calls\",\n tool_type: \"code_execution\"\n },\n per: 1000,\n price_key: \"code_executions_kcount\"\n },\n input_annotated_document_pages: {\n dimensions: {\n direction: \"input\",\n family: \"document_pages\",\n page_type: \"annotated\"\n },\n per: 1000,\n price_key: \"input_annotated_document_kpages\"\n },\n input_audio_seconds: {\n dimensions: {\n direction: \"input\",\n family: \"durations\",\n modality: \"audio\"\n },\n per: 3600,\n price_key: \"input_audio_hours\"\n },\n input_audio_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"audio\"\n },\n per: 1e6,\n price_key: \"input_audio_mtok\"\n },\n input_audio_tool_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"audio\",\n token_type: \"tool\"\n },\n per: 1e6,\n price_key: \"input_audio_tool_mtok\"\n },\n input_characters: {\n dimensions: {\n direction: \"input\",\n family: \"characters\"\n },\n per: 1e6,\n price_key: \"input_mchars\"\n },\n input_document_pages: {\n dimensions: {\n direction: \"input\",\n family: \"document_pages\"\n },\n per: 1000,\n price_key: \"input_document_kpages\"\n },\n input_image_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"image\"\n },\n per: 1e6,\n price_key: \"input_image_mtok\"\n },\n input_image_tool_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"image\",\n token_type: \"tool\"\n },\n per: 1e6,\n price_key: \"input_image_tool_mtok\"\n },\n input_pixels: {\n dimensions: {\n direction: \"input\",\n family: \"pixels\"\n },\n per: 1e9,\n price_key: \"input_gpixels\"\n },\n input_text_messages: {\n dimensions: {\n direction: \"input\",\n family: \"messages\",\n modality: \"text\"\n },\n per: 1000,\n price_key: \"input_text_messages_kcount\"\n },\n input_text_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"text\"\n },\n per: 1e6,\n price_key: \"input_text_mtok\"\n },\n input_text_tool_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"text\",\n token_type: \"tool\"\n },\n per: 1e6,\n price_key: \"input_text_tool_mtok\"\n },\n input_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\"\n },\n per: 1e6,\n price_key: \"input_mtok\"\n },\n input_tool_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n token_type: \"tool\"\n },\n per: 1e6,\n price_key: \"input_tool_mtok\"\n },\n input_video_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"video\"\n },\n per: 1e6,\n price_key: \"input_video_mtok\"\n },\n input_video_tool_tokens: {\n dimensions: {\n direction: \"input\",\n family: \"tokens\",\n modality: \"video\",\n token_type: \"tool\"\n },\n per: 1e6,\n price_key: \"input_video_tool_mtok\"\n },\n output_audio_citation_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\",\n modality: \"audio\",\n token_type: \"citation\"\n },\n per: 1e6,\n price_key: \"output_audio_citation_mtok\"\n },\n output_audio_reasoning_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\",\n modality: \"audio\",\n token_type: \"reasoning\"\n },\n per: 1e6,\n price_key: \"output_audio_reasoning_mtok\"\n },\n output_audio_seconds: {\n dimensions: {\n direction: \"output\",\n family: \"durations\",\n modality: \"audio\"\n },\n per: 3600,\n price_key: \"output_audio_hours\"\n },\n output_audio_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\",\n modality: \"audio\"\n },\n per: 1e6,\n price_key: \"output_audio_mtok\"\n },\n output_citation_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\",\n token_type: \"citation\"\n },\n per: 1e6,\n price_key: \"output_citation_mtok\"\n },\n output_image_citation_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\",\n modality: \"image\",\n token_type: \"citation\"\n },\n per: 1e6,\n price_key: \"output_image_citation_mtok\"\n },\n output_image_reasoning_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\",\n modality: \"image\",\n token_type: \"reasoning\"\n },\n per: 1e6,\n price_key: \"output_image_reasoning_mtok\"\n },\n output_image_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\",\n modality: \"image\"\n },\n per: 1e6,\n price_key: \"output_image_mtok\"\n },\n output_reasoning_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\",\n token_type: \"reasoning\"\n },\n per: 1e6,\n price_key: \"output_reasoning_mtok\"\n },\n output_text_citation_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\",\n modality: \"text\",\n token_type: \"citation\"\n },\n per: 1e6,\n price_key: \"output_text_citation_mtok\"\n },\n output_text_reasoning_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\",\n modality: \"text\",\n token_type: \"reasoning\"\n },\n per: 1e6,\n price_key: \"output_text_reasoning_mtok\"\n },\n output_text_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\",\n modality: \"text\"\n },\n per: 1e6,\n price_key: \"output_text_mtok\"\n },\n output_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\"\n },\n per: 1e6,\n price_key: \"output_mtok\"\n },\n output_video_citation_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\",\n modality: \"video\",\n token_type: \"citation\"\n },\n per: 1e6,\n price_key: \"output_video_citation_mtok\"\n },\n output_video_reasoning_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\",\n modality: \"video\",\n token_type: \"reasoning\"\n },\n per: 1e6,\n price_key: \"output_video_reasoning_mtok\"\n },\n output_video_tokens: {\n dimensions: {\n direction: \"output\",\n family: \"tokens\",\n modality: \"video\"\n },\n per: 1e6,\n price_key: \"output_video_mtok\"\n },\n requests: {\n dimensions: {\n family: \"requests\"\n },\n per: 1000,\n price_key: \"requests_kcount\"\n },\n rerank_searches: {\n dimensions: {\n family: \"rerank\"\n },\n per: 1000,\n price_key: \"rerank_searches_kcount\"\n },\n social_searches: {\n dimensions: {\n family: \"tool_calls\",\n tool_type: \"social_search\"\n },\n per: 1000,\n price_key: \"social_searches_kcount\"\n },\n storage_searches: {\n dimensions: {\n family: \"tool_calls\",\n tool_type: \"storage_search\"\n },\n per: 1000,\n price_key: \"storage_searches_kcount\"\n },\n web_searches: {\n dimensions: {\n family: \"tool_calls\",\n tool_type: \"web_search\"\n },\n per: 1000,\n price_key: \"web_searches_kcount\"\n }\n};\nclass te {\n #a;\n #i;\n #o;\n #t;\n #e;\n #n;\n #s;\n constructor(t) {\n this.#o = /* @__PURE__ */ new Map, this.#a = /* @__PURE__ */ new Set, this.#i = /* @__PURE__ */ new Set, this.#t = /* @__PURE__ */ new Set, this.#e = /* @__PURE__ */ new Map, this.#n = /* @__PURE__ */ new Map, this.#s = /* @__PURE__ */ new Map;\n for (const [a, i] of Object.entries(t)) {\n const o = i.price_key ?? a, n = Object.freeze({\n dimensions: Object.freeze({ ...i.dimensions }),\n per: i.per,\n priceKey: o,\n usageKey: a\n });\n this.#a.add(o), this.#i.add(a), this.#e.set(a, n), this.#s.set(o, n), this.#n.set(D(n.dimensions), n);\n }\n for (const [a, i] of this.#e)\n this.#o.set(a, new Set([...this.#e.values()].filter((o) => o !== i && H(o, i)).map((o) => o.usageKey))), a !== \"requests\" && this.#t.add(a);\n }\n ancestorUsageKeys(t) {\n const a = this.#o.get(t);\n if (!a)\n throw new Error(`Unknown unit usage key: ${t}`);\n return new Set(a);\n }\n findJoin(t, a) {\n if (z(t, a))\n return this.#n.get(D({ ...t.dimensions, ...a.dimensions }));\n }\n getAllPriceKeys() {\n return new Set(this.#a);\n }\n getAllUsageKeys() {\n return new Set(this.#i);\n }\n getUnit(t) {\n return this.#e.get(t);\n }\n getUnitForPriceKey(t) {\n return this.#s.get(t);\n }\n isReportedUsageKey(t) {\n return this.#t.has(t);\n }\n reportedUsageKeys() {\n return this.#t.values();\n }\n}\nvar ae = new te(ee);\nfunction g() {\n return ae;\n}\nfunction D(e) {\n return JSON.stringify(Object.entries(e).sort(([t], [a]) => t.localeCompare(a)));\n}\nfunction H(e, t) {\n return Object.entries(e.dimensions).every(([a, i]) => t.dimensions[a] === i);\n}\nfunction Z(e, t) {\n return H(e, t);\n}\nfunction z(e, t) {\n return Object.entries(e.dimensions).every(([a, i]) => t.dimensions[a] === undefined || t.dimensions[a] === i);\n}\nvar O = /* @__PURE__ */ new WeakMap;\nfunction ie(e, t = g()) {\n const a = O.get(e), i = Object.keys(e).filter((o) => !t.isReportedUsageKey(o) && !a?.has(o)).sort();\n if (i.length) {\n const o = a ?? /* @__PURE__ */ new Set;\n for (const n of i)\n o.add(n);\n O.set(e, o), console.warn(`Unsupported usage key for standard pricing: ${i.join(\", \")}`);\n }\n}\nfunction oe(e, t = g()) {\n if (!ne(e))\n return {};\n const a = {};\n for (const i of t.reportedUsageKeys()) {\n const o = e[i];\n typeof o == \"number\" && (a[i] = q(i, o));\n }\n return a;\n}\nfunction M(e, t, a = g()) {\n const i = re(a, t), o = I(t, e[t]);\n if (o !== undefined)\n return o;\n for (const [s, r] of Object.entries(e)) {\n const c = V(a, s);\n if (!(!c || (I(s, r) ?? 0) <= 0) && s !== t && a.ancestorUsageKeys(c.usageKey).has(t))\n throw new Error(`Missing usage value for ${t} with positive reported descendant ${s}`);\n }\n const n = Object.entries(e).map(([s, r]) => {\n const c = V(a, s);\n return c && (I(s, r) ?? 0) > 0 ? c : undefined;\n }).filter((s) => s !== undefined);\n for (let s = 0;s < n.length; s++)\n for (let r = s + 1;r < n.length; r++) {\n const c = n[s], p = n[r];\n if (!(!c || !p || !z(c, p) || se(a, c, p)) && a.findJoin(c, p) === i)\n throw new Error(`Missing usage value for ${t} with positive reported overlap ${c.usageKey} and ${p.usageKey}`);\n }\n return 0;\n}\nfunction I(e, t) {\n if (t !== undefined) {\n if (typeof t != \"number\")\n throw new Error(`Invalid usage value for ${e}: expected a finite non-negative number`);\n return q(e, t);\n }\n}\nfunction q(e, t) {\n if (!Number.isFinite(t) || t < 0)\n throw new Error(`Invalid usage value for ${e}: expected a finite non-negative number`);\n return t;\n}\nfunction ne(e) {\n return typeof e == \"object\" && e !== null && !Array.isArray(e);\n}\nfunction se(e, t, a) {\n return t === a || e.ancestorUsageKeys(a.usageKey).has(t.usageKey) || e.ancestorUsageKeys(t.usageKey).has(a.usageKey);\n}\nfunction re(e, t) {\n const a = e.getUnit(t);\n if (!a)\n throw new Error(`Unknown unit usage key: ${t}`);\n if (!e.isReportedUsageKey(t))\n throw new Error(`Unsupported usage key for standard pricing: ${t}`);\n return a;\n}\nfunction V(e, t) {\n return e.isReportedUsageKey(t) ? e.getUnit(t) : undefined;\n}\nfunction ce(e, t, a = g()) {\n const i = [...e].map((n) => a.getUnit(n)).filter((n) => n !== undefined).sort((n, s) => Object.keys(s.dimensions).length - Object.keys(n.dimensions).length || n.usageKey.localeCompare(s.usageKey)), o = {};\n for (const n of i) {\n const s = M(t, n.usageKey, a);\n let r = 0;\n for (const m of i) {\n if (m === n || !Z(n, m))\n continue;\n const l = o[m.usageKey];\n if (l === undefined)\n throw new Error(`Missing computed leaf value for ${m.usageKey}`);\n r += l;\n }\n let c = s - r;\n const p = Number.EPSILON * Math.max(1, Math.abs(s), Math.abs(r)) * (i.length + 1);\n if (c < 0 && Math.abs(c) <= p && (c = 0), c < 0)\n throw new Error(pe(n, i, t, c, a));\n o[n.usageKey] = c;\n }\n return o;\n}\nfunction pe(e, t, a, i, o) {\n const n = M(a, e.usageKey, o), s = t.filter((p) => p !== e && Z(e, p)).map((p) => ({\n unit: p,\n value: M(a, p.usageKey, o)\n })).filter(({ value: p }) => p > 0);\n for (const p of s)\n if (p.value > n)\n return `Invalid usage data: ${p.unit.usageKey} (${p.value.toString()}) cannot exceed ${e.usageKey} (${n.toString()})`;\n const r = s.map(({ unit: p }) => p.usageKey).join(\", \"), c = n - i;\n return `Invalid usage data: more-specific usage for ${r} totals ${c.toString()}, which exceeds ${e.usageKey} (${n.toString()})`;\n}\nfunction S(e) {\n const t = /^(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?(Z|([+-])(\\d{2}):(\\d{2}))$/.exec(e);\n if (!t)\n throw new Error(`Invalid time-of-day constraint: ${e}`);\n const [, a, i, o, n = \"\", s, r, c = \"00\", p = \"00\"] = t, m = Number(a), l = Number(i), u = Number(o), d = (Number(c) * 60 + Number(p)) * 60;\n if (m > 23 || l > 59 || u > 59 || c > \"23\" || p > \"59\")\n throw new Error(`Invalid time-of-day constraint: ${e}`);\n const _ = m * 3600 + l * 60 + u + +`0.${n}`, x = s === \"Z\" ? 0 : r === \"+\" ? d : -d;\n return (_ - x + 86400) % 86400;\n}\n\nclass me {\n base;\n tiers;\n constructor(t) {\n this.base = t.base, this.tiers = [...t.tiers].sort((a, i) => a.start - i.start);\n }\n}\nvar A = /* @__PURE__ */ new WeakMap;\nfunction ue(e, t) {\n const a = new Set(e.map((i) => i.priceKey));\n for (const i of e) {\n const o = t.ancestorUsageKeys(i.usageKey);\n for (const n of o) {\n const s = t.getUnit(n);\n if (s && !a.has(s.priceKey))\n throw new Error(`Missing ancestor price key ${s.priceKey} for ${i.priceKey}`);\n }\n }\n}\nfunction de(e, t) {\n const a = new Set(e.map((i) => i.priceKey));\n for (let i = 0;i < e.length; i++)\n for (let o = i + 1;o < e.length; o++) {\n const n = e[i], s = e[o];\n if (!n || !s || !z(n, s))\n continue;\n const r = t.findJoin(n, s);\n if (!r)\n throw new Error(`Missing registered join unit for ${n.priceKey} and ${s.priceKey}`);\n if (!a.has(r.priceKey))\n throw new Error(`Missing join price key ${r.priceKey} for ${n.priceKey} and ${s.priceKey}`);\n }\n}\nfunction le(e, t) {\n ue(e, t), de(e, t);\n}\nfunction J(e, t = g()) {\n const a = /* @__PURE__ */ new Set;\n for (const i of e)\n for (const o of i.extractors ?? [])\n for (const n of o.mappings)\n if (!t.isReportedUsageKey(n.dest) && !A.get(i)?.has(n.dest)) {\n a.add(n.dest);\n const s = A.get(i) ?? /* @__PURE__ */ new Set;\n s.add(n.dest), A.set(i, s);\n }\n a.size && console.warn(`Unsupported extractor destination for standard extraction: ${[...a].sort().join(\", \")}`);\n}\nfunction _e(e, t) {\n const a = [], i = [];\n for (const [o, n] of Object.entries(e)) {\n if (n === undefined)\n continue;\n const s = t.getUnitForPriceKey(o);\n if (!s) {\n i.push(o);\n continue;\n }\n a.push({ price: he(o, n), unit: s });\n }\n return i.length && console.warn(`Unsupported price key for standard pricing: ${i.sort().join(\", \")}`), a;\n}\nfunction he(e, t) {\n if (T(t))\n return t;\n if (!K(t) || !T(t.base) || !Array.isArray(t.tiers))\n throw L(e);\n const a = [];\n for (const i of t.tiers) {\n if (!K(i))\n throw L(e);\n const { price: o, start: n } = i;\n if (typeof n != \"number\" || !Number.isSafeInteger(n) || n < 0 || !T(o))\n throw L(e);\n a.push({ price: o, start: n });\n }\n return new me({ base: t.base, tiers: a });\n}\nfunction L(e) {\n return new Error(`Invalid price value for ${e}: expected a finite non-negative number or valid tiered prices`);\n}\nfunction K(e) {\n return typeof e == \"object\" && e !== null && !Array.isArray(e);\n}\nfunction T(e) {\n return typeof e == \"number\" && Number.isFinite(e) && e >= 0;\n}\nfunction ke(e, t, a) {\n if (t <= 0)\n return 0;\n let i = e.base;\n for (const o of e.tiers)\n a > o.start && (i = o.price);\n return i * t / 1e6;\n}\nfunction ge(e, t, a, i) {\n return e === undefined || t === undefined ? 0 : typeof e == \"number\" ? e * t / i : ke(e, t, a) * 1e6 / i;\n}\nfunction we(e, t, a = g()) {\n ie(e, a);\n const i = _e(t, a);\n le(i.map(({ unit: u }) => u), a);\n let o = 0, n = 0, s = 0;\n const c = i.some(({ price: u }) => fe(u)) ? M(e, \"input_tokens\", a) : 0, p = new Set(i.filter(({ unit: u }) => u.usageKey !== \"requests\").map(({ unit: u }) => u.usageKey)), m = ce(p, e, a);\n i.some(({ unit: u }) => u.usageKey === \"requests\") && (m.requests = 1);\n for (const { price: u, unit: d } of i) {\n const _ = ge(u, m[d.usageKey] ?? 0, c, d.per);\n d.dimensions.direction === \"input\" ? o += _ : d.dimensions.direction === \"output\" ? n += _ : s += _;\n }\n const l = o + n + s;\n return {\n input_price: o,\n output_price: n,\n total_price: l\n };\n}\nfunction fe(e) {\n return typeof e == \"object\";\n}\nfunction be(e) {\n return typeof e == \"object\" && e !== null && \"type\" in e && e.type === \"time_of_date\";\n}\nfunction N(e, t, a) {\n if (e === undefined)\n return true;\n if (Number.isNaN(t.getTime()))\n throw new RangeError(\"Invalid time value\");\n if (e.type === \"start_date\")\n return t >= new Date(e.start_date);\n if (!be(e))\n throw new Error(`Unknown price constraint for model '${a}': ${JSON.stringify(e)}`);\n const i = t.getUTCHours() * 3600 + t.getUTCMinutes() * 60 + t.getUTCSeconds() + t.getUTCMilliseconds() / 1000, o = S(e.start_time), n = S(e.end_time);\n return n < o ? i >= o || i < n : i >= o && i < n;\n}\nfunction qe(e, t, a) {\n let i;\n if (Array.isArray(e.prices)) {\n if (Number.isNaN(t.getTime()))\n throw new RangeError(\"Invalid time value\");\n i = e.prices[0].prices;\n for (const n of [...e.prices].reverse())\n if (N(n.constraint, t, e.id)) {\n i = n.prices;\n break;\n }\n } else\n i = e.prices;\n if (!a || !Object.keys(a).length)\n return i;\n const o = /* @__PURE__ */ Object.create(null);\n for (const n of e.price_variants ?? [])\n if (N(n.constraint, t, e.id) && Object.entries(n.when).every(([s, r]) => Object.prototype.hasOwnProperty.call(a, s) && a[s] === r))\n for (const [s, r] of Object.entries(n.prices))\n r != null && !Object.prototype.hasOwnProperty.call(o, s) && (o[s] = r);\n return { ...i, ...o };\n}\nfunction b(e, t) {\n return \"or\" in e ? e.or.some((a) => b(a, t)) : (\"and\" in e) ? e.and.every((a) => b(a, t)) : (\"equals\" in e) ? t.toLowerCase() === e.equals.toLowerCase() : (\"starts_with\" in e) ? t.toLowerCase().startsWith(e.starts_with.toLowerCase()) : (\"ends_with\" in e) ? t.toLowerCase().endsWith(e.ends_with.toLowerCase()) : (\"contains\" in e) ? t.toLowerCase().includes(e.contains.toLowerCase()) : (\"regex\" in e) ? new RegExp(e.regex).test(t) : false;\n}\nfunction ve(e, t) {\n const a = t.toLowerCase().trim(), i = e.find((o) => o.id === a);\n return i || e.find((o) => o.provider_match && b(o.provider_match, a));\n}\nfunction C(e, { modelId: t, providerApiUrl: a, providerId: i }) {\n if (i) {\n const o = ve(e, i);\n if (o || i.toLowerCase() !== \"litellm\")\n return o;\n }\n if (a)\n return e.find((o) => new RegExp(`^(?:${o.api_pattern})`).test(a));\n if (t)\n return e.find((o) => o.model_match && b(o.model_match, t));\n}\nfunction xe(e, t) {\n return e.find((a) => b(a.match, t));\n}\nvar ye = /(-)(20\\d{2})(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])(?=-|:|$)/g;\nfunction Be(e) {\n return e.replace(ye, (t, a, i, o, n) => {\n const s = new Date(Date.UTC(Number(i), Number(o) - 1, Number(n)));\n return s.getUTCFullYear() !== Number(i) || s.getUTCMonth() !== Number(o) - 1 || s.getUTCDate() !== Number(n) ? t : `${a}${i}-${o}-${n}`;\n });\n}\nfunction Me(e, t, a) {\n const i = R(e, t, a);\n if (i)\n return i;\n const o = Be(t);\n if (o !== t)\n return R(e, o, a);\n}\nfunction R(e, t, a) {\n const i = xe(e.models, t);\n if (i)\n return i;\n if (e.fallback_model_providers && a)\n for (const o of e.fallback_model_providers) {\n const n = a.find((s) => s.id === o);\n if (n) {\n const s = R(n, t);\n if (s)\n return s;\n }\n }\n}\nvar k = W;\nvar f = Promise.resolve(W);\nvar Q = null;\nfunction X(e) {\n return {\n ...e,\n models: e.models.map((t) => ({\n ...t,\n price_variants: t.price_variants?.map((a) => {\n if (!F(a.prices))\n throw new Error(\"Price variant prices must be an object\");\n if (!F(a.when) || !Object.keys(a.when).length || Object.values(a.when).some((i) => typeof i != \"string\"))\n throw new Error(\"Price variant when must contain at least one string value\");\n return {\n ...$({ ...a, constraint: a.constraint ?? undefined }, e.id, t.id),\n when: a.when\n };\n }),\n prices: Array.isArray(t.prices) ? t.prices.map((a) => $(a, e.id, t.id)) : t.prices\n }))\n };\n}\nfunction $(e, t, a) {\n const i = e.constraint;\n if (i === undefined)\n return { prices: e.prices };\n if (!F(i))\n throw P(i, t, a);\n if (i.type !== undefined) {\n if (i.type === \"start_date\" && y(i, [\"start_date\", \"type\"]) && U(i.start_date) || i.type === \"time_of_date\" && y(i, [\"end_time\", \"start_time\", \"type\"]) && B(i.start_time) && B(i.end_time))\n return e;\n throw P(i, t, a);\n }\n if (y(i, [\"start_date\"]) && U(i.start_date))\n return {\n constraint: { start_date: i.start_date, type: \"start_date\" },\n prices: e.prices\n };\n if (y(i, [\"end_time\", \"start_time\"]) && B(i.start_time) && B(i.end_time))\n return {\n constraint: {\n end_time: i.end_time,\n start_time: i.start_time,\n type: \"time_of_date\"\n },\n prices: e.prices\n };\n throw P(i, t, a);\n}\nfunction P(e, t, a) {\n return new Error(`Expected a start-date or time-of-day price constraint for provider '${t}' model '${a}', got: ${JSON.stringify(e)}`);\n}\nfunction U(e) {\n if (typeof e != \"string\" || !/^\\d{4}-\\d{2}-\\d{2}$/.test(e))\n return false;\n const t = /* @__PURE__ */ new Date(`${e}T00:00:00Z`);\n return !Number.isNaN(t.getTime()) && t.getUTCFullYear() >= 1 && t.toISOString().startsWith(e);\n}\nfunction y(e, t) {\n const a = Object.keys(e);\n return a.length === t.length && a.every((i) => t.includes(i));\n}\nfunction B(e) {\n if (typeof e != \"string\")\n return false;\n try {\n return S(e), true;\n } catch {\n return false;\n }\n}\nfunction F(e) {\n return typeof e == \"object\" && e !== null && !Array.isArray(e);\n}\nfunction Re(e, t, a) {\n Q?.();\n let i = t.toLowerCase().trim(), o = a?.providerId;\n if (o && o.toLowerCase() === \"litellm\" && i.includes(\"/\")) {\n const u = i.indexOf(\"/\"), d = i.slice(0, u), _ = i.slice(u + 1);\n d && _ && C(k, { providerId: d }) && (o = d, i = _);\n }\n const n = a?.provider ? X(a.provider) : C(k, { modelId: i, providerApiUrl: a?.providerApiUrl, providerId: o });\n if (!n)\n return null;\n const s = Me(n, i, k);\n if (!s)\n return null;\n const r = a?.timestamp ?? /* @__PURE__ */ new Date, c = n.models.includes(s), p = qe(s, r, c ? a?.priceContext : undefined);\n let m = e;\n if (n.id === \"groq\" && (s.id === \"whisper-large-v3\" || s.id === \"whisper-large-v3-turbo\")) {\n m = { ...e };\n const u = m.audio_seconds, d = m.input_audio_seconds;\n u !== undefined && q(\"audio_seconds\", u), d !== undefined && q(\"input_audio_seconds\", d);\n const _ = u === 0 ? d : u ?? d;\n if (_ !== undefined) {\n const x = _ > 0 ? Math.max(_, 10) : 0;\n m.audio_seconds = x, m.input_audio_seconds = x;\n }\n }\n const l = we(m, p);\n return {\n auto_update_timestamp: undefined,\n model: s,\n model_price: p,\n provider: n,\n ...l\n };\n}\nfunction Fe(e) {\n return Q?.(), C(k, e);\n}\nfunction ze(e, t, a) {\n a = a ?? \"default\";\n const i = g();\n if (!e.extractors)\n throw new Error(\"No extraction logic defined for this provider\");\n J([e], i);\n const o = e.extractors.find((m) => m.api_flavor === a);\n if (!o) {\n const m = e.extractors.map((l) => l.api_flavor).join(\", \");\n throw new Error(`Unknown apiFlavor '${a}', allowed values: ${m}`);\n }\n if (!v.guard(t))\n throw new Error(`Expected response data to be a mapping object, got ${h(t)}`);\n const n = G(o.model_path, t, Pe, false, []), s = Y(o.root), r = G(s, t, v, true, []), c = {};\n let p = 0;\n for (const m of o.mappings) {\n if (!i.isReportedUsageKey(m.dest))\n continue;\n p += 1;\n const l = G(m.path, r, Ge, m.required, s);\n if (l !== null) {\n const u = q(m.dest, l), d = c[m.dest] ?? 0;\n c[m.dest] = d + u;\n }\n }\n if (p && !Object.keys(c).length)\n throw new Error(`No usage information found at ${JSON.stringify(o.root)}`);\n return { model: n, usage: oe(c, i) };\n}\nfunction G(e, t, a, i, o) {\n const [n, ...s] = Y(e).reverse();\n if (typeof n != \"string\")\n throw new Error(`Expected last step of path to be a string, got ${h(n)}`);\n s.reverse();\n let r = t;\n const c = [];\n for (const m of s) {\n if (c.push(m), typeof m == \"object\")\n if (Array.isArray(r))\n r = Te(m, r);\n else {\n if (i)\n throw new Error(`Expected \\`${w(o, c)}\\` value to be a mapping, got ${h(r)}`);\n return null;\n }\n else if (v.guard(r))\n r = r[m];\n else {\n if (i)\n throw new Error(`Expected \\`${w(o, c)}\\` value to be a mapping, got ${h(r)}`);\n return null;\n }\n if (typeof r > \"u\")\n if (i) {\n const l = typeof m == \"object\" ? \"Unable to find item\" : \"Missing value\";\n throw new Error(`${l} at \\`${w(o, c)}\\``);\n } else\n return null;\n }\n if (!v.guard(r)) {\n if (i)\n throw new Error(`Expected \\`${w(o, c)}\\` value to be a mapping, got ${h(r)}`);\n return null;\n }\n const p = r[n];\n if (typeof p > \"u\") {\n if (i)\n throw c.push(n), new Error(`Missing value at \\`${w(o, c)}\\``);\n return null;\n }\n if (a.guard(p))\n return p;\n if (i)\n throw c.push(n), new Error(`Expected \\`${w(o, c)}\\` value to be a ${a.name}, got ${h(p)}`);\n return null;\n}\nfunction Te(e, t) {\n for (const a of t)\n if (v.guard(a)) {\n const i = a[e.field];\n if (typeof i == \"string\" && b(e.match, i))\n return a;\n }\n}\nfunction Y(e) {\n return Array.isArray(e) ? [...e] : [e];\n}\nfunction h(e) {\n return e === null ? \"null\" : Array.isArray(e) ? \"array\" : typeof e == \"object\" ? \"mapping\" : typeof e;\n}\nvar v = {\n guard: (e) => h(e) === \"mapping\",\n name: \"mapping\"\n};\nvar Pe = {\n guard: (e) => typeof e == \"string\",\n name: \"string\"\n};\nvar Ge = {\n guard: (e) => typeof e == \"number\",\n name: \"number\"\n};\nvar w = (e, t) => [...e.map(j), ...t.map(j)].join(\".\");\nvar j = (e) => typeof e == \"string\" ? e : JSON.stringify(e);\n\n// agent/session/usage.ts\nvar READ_BYTES = 1024 * 1024;\nvar NEWLINE2 = 10;\nasync function* jsonLines(files, path) {\n const size = await files.size(path);\n const decoder = new TextDecoder;\n let carry = new Uint8Array(0);\n for (let position = 0;position < size; position += READ_BYTES) {\n const read = await files.readFileRange(path, position, Math.min(size, position + READ_BYTES));\n const bytes = new Uint8Array(carry.length + read.length);\n bytes.set(carry);\n bytes.set(read, carry.length);\n const end = bytes.lastIndexOf(NEWLINE2);\n if (end < 0) {\n carry = bytes;\n continue;\n }\n carry = bytes.slice(end + 1);\n for (const line of decoder.decode(bytes.subarray(0, end)).split(`\n`)) {\n const parsed = parsedLine(line);\n if (parsed !== undefined)\n yield parsed;\n }\n }\n const last = parsedLine(decoder.decode(carry));\n if (last !== undefined)\n yield last;\n}\nfunction parsedLine(line) {\n if (line.trim() === \"\")\n return;\n try {\n return JSON.parse(line);\n } catch {\n return;\n }\n}\nfunction usageTurns(provider) {\n const priced = Fe({ providerId: provider });\n const turns = [];\n function turnAt(at) {\n if (at === null)\n return turns.at(-1);\n for (let index = turns.length - 1;index >= 0; index--)\n if (turns[index].startedAt <= at)\n return turns[index];\n return;\n }\n function start(id, at) {\n turns.push({ id, startedAt: at, endedAt: at, models: new Map });\n }\n function seen(at) {\n const turn = turnAt(at);\n if (turn && at > turn.endedAt)\n turn.endedAt = at;\n }\n function add(call, at) {\n const turn = turnAt(at);\n if (!turn)\n return;\n if (at !== null)\n seen(at);\n const totals = turn.models.get(call.model) ?? { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costUsd: 0 };\n const cacheRead = call.usage.cache_read_tokens ?? 0;\n const cacheWrite = call.usage.cache_write_tokens ?? 0;\n totals.inputTokens += Math.max(0, (call.usage.input_tokens ?? 0) - cacheRead - cacheWrite);\n totals.outputTokens += call.usage.output_tokens ?? 0;\n totals.cacheReadTokens += cacheRead;\n totals.cacheWriteTokens += cacheWrite;\n const cost = priceOf(priced, call);\n totals.costUsd = totals.costUsd === null || cost === null ? null : totals.costUsd + cost;\n turn.models.set(call.model, totals);\n }\n function result() {\n return turns.filter((turn) => turn.models.size > 0).map((turn) => {\n const models = [...turn.models].map(([model, totals]) => ({ model, ...totals }));\n return {\n id: turn.id,\n startedAt: new Date(turn.startedAt).toISOString(),\n endedAt: new Date(turn.endedAt).toISOString(),\n models,\n costUsd: models.reduce((sum, model) => sum === null || model.costUsd === null ? null : sum + model.costUsd, 0)\n };\n });\n }\n return { start, seen, add, turns: result };\n}\nfunction priceOf(provider, call) {\n if (!provider)\n return null;\n try {\n return Re(call.usage, call.model, { provider })?.total_price ?? null;\n } catch {\n return null;\n }\n}\nfunction extractedCall(providerId, response, flavor) {\n const provider = Fe({ providerId });\n if (!provider)\n return null;\n try {\n const { model, usage } = ze(provider, response, flavor);\n return model ? { model, usage } : null;\n } catch {\n return null;\n }\n}\nfunction timeOf(value) {\n const time = typeof value === \"string\" ? Date.parse(value) : NaN;\n return Number.isNaN(time) ? null : time;\n}\n\n// agent/session/claude/usage.ts\nvar SUBAGENT_FILE2 = /^agent-.+\\.jsonl$/;\nvar SYNTHETIC_MODEL = \"<synthetic>\";\nasync function claudeUsage({ nativeId, env, files }) {\n const main = await locateTranscript(files, claudeHome(env), CLAUDE_TRANSCRIPT_LAYOUT, nativeId);\n if (!main)\n return [];\n const turns = usageTurns(\"anthropic\");\n const prompts = new Set;\n const calls = new Set;\n function call(line, at) {\n const message = line.message;\n if (line.type !== \"assistant\" || !message?.usage || message.model === SYNTHETIC_MODEL)\n return;\n if (message.id !== undefined) {\n if (calls.has(message.id))\n return;\n calls.add(message.id);\n }\n const extracted = extractedCall(\"anthropic\", message);\n if (extracted)\n turns.add(extracted, at);\n }\n for await (const entry of jsonLines(files, main)) {\n const line = entry;\n const at = timeOf(line.timestamp);\n if (isPrompt(line) && at !== null) {\n const prompt = line.promptId ?? line.uuid;\n if (!prompts.has(prompt)) {\n prompts.add(prompt);\n turns.start(prompt, at);\n }\n continue;\n }\n if (line.isSidechain || line.isMeta || line.type !== \"user\" && line.type !== \"assistant\")\n continue;\n if (at !== null)\n turns.seen(at);\n call(line, at);\n }\n const subagents = `${main.slice(0, -\".jsonl\".length)}/subagents`;\n for (const name of await directoryNames(files, subagents) ?? []) {\n if (!SUBAGENT_FILE2.test(name))\n continue;\n for await (const entry of jsonLines(files, `${subagents}/${name}`)) {\n const line = entry;\n const at = timeOf(line.timestamp);\n if (at !== null)\n call(line, at);\n }\n }\n return turns.turns();\n}\nfunction isPrompt(line) {\n if (line.type !== \"user\" || line.isSidechain || line.isMeta || line.isCompactSummary || !line.uuid)\n return false;\n const content = line.message?.content;\n if (typeof content === \"string\")\n return true;\n return Array.isArray(content) && !content.some((block) => block.type === \"tool_result\");\n}\n\n// agent/session/claude/index.ts\nvar claudeBackend = {\n install: installClaude,\n installed: installedClaude,\n auth: claudeAuth,\n transcript: claudeTranscript,\n findSubagent: claudeFindSubagent,\n subagentTranscript: claudeSubagentTranscript,\n usage: claudeUsage,\n homeEnvironment: claudeHomeEnvironment,\n async start(context) {\n const prompts = createPromptQueue();\n const questions = claudeQuestions(context);\n const backgroundTasks = claudeBackgroundTasks();\n const status = claudeStatus(() => context.activeChanged());\n const queue = claudeMessageQueue();\n const permissions = claudePermissions(context, questions);\n const query = await startClaudeStream(context, prompts, permissions, questions, backgroundTasks, status, queue);\n return {\n sendMessage: claudeInput(prompts, queue),\n active: status.active,\n getLastUserMessages: queue.lastUserMessages,\n cancel: claudeInterrupt(query),\n ...claudeModel(query),\n setPermissionMode: (mode) => permissions.setMode(query, mode),\n listBackgroundTasks: backgroundTasks.list,\n stopTask: claudeStopTask(query, backgroundTasks),\n async close() {\n prompts.close();\n query.close();\n }\n };\n }\n};\n\n// agent/session/codex/provision.ts\nvar CODEX_PACKAGE = \"@openai/codex\";\nvar CODEX_VERSION = () => pinnedVersion(\"codex\", CODEX_PACKAGE, \"@openai/codex-sdk\");\nfunction installCodex(installDir) {\n return installPackage(installDir, CODEX_PACKAGE, CODEX_VERSION(), () => codexExecutable(installDir).path);\n}\nfunction installedCodex(installDir) {\n return installedPackage(installDir, CODEX_PACKAGE, CODEX_VERSION(), () => codexExecutable(installDir).path);\n}\nvar LINUX_TARGETS = {\n x64: { pkg: \"@openai/codex-linux-x64\", triple: \"x86_64-unknown-linux-musl\" },\n arm64: { pkg: \"@openai/codex-linux-arm64\", triple: \"aarch64-unknown-linux-musl\" }\n};\nfunction codexExecutable(installDir) {\n const target = LINUX_TARGETS[process.arch];\n if (process.platform !== \"linux\" || !target)\n throw new Error(`codex: unsupported platform ${process.platform}/${process.arch}`);\n return {\n path: `${installDir}/node_modules/${target.pkg}/vendor/${target.triple}/bin/codex`,\n env: { CODEX_MANAGED_PACKAGE_ROOT: `${installDir}/node_modules/${CODEX_PACKAGE}`, CODEX_MANAGED_BY_BUN: \"1\" }\n };\n}\n\n// agent/session/codex/auth.ts\nfunction run({ installDir, env, cwd }, args) {\n const executable = codexExecutable(installDir);\n return { cmd: [executable.path, ...args], env: { ...env, ...executable.env }, cwd };\n}\nvar codexAuth = {\n async signedIn(environment) {\n const { cmd, env, cwd } = run(environment, [\"login\", \"status\"]);\n const proc = Bun.spawn(cmd, { env, cwd, stdout: \"ignore\", stderr: \"ignore\" });\n return await proc.exited === 0;\n },\n login(environment) {\n const { cmd, env, cwd } = run(environment, [\"login\", \"--device-auth\"]);\n return spawnLogin(cmd, { env, cwd }, (output) => {\n const url = /https:\\/\\/\\S+\\/device\\S*/.exec(output)?.[0];\n const userCode = /^\\s*([A-Z0-9]{4}-[A-Z0-9]{4,})\\s*$/m.exec(output)?.[1];\n return url && userCode ? { url, userCode, needsCode: false } : null;\n });\n }\n};\n\n// agent/session/codex/backgroundTasks.ts\nimport { readdirSync, readFileSync } from \"fs\";\nvar STOP_GRACE_MS = 1000;\nfunction codexBackgroundTasks() {\n const running = new Map;\n const background = new Map;\n function observe(notification) {\n switch (notification.method) {\n case \"item/started\": {\n const { item } = notification.params;\n if (item.type === \"commandExecution\")\n running.set(item.id, { task_id: item.id, task_type: \"local_bash\", description: item.command });\n return;\n }\n case \"item/completed\": {\n const { item } = notification.params;\n if (item.type !== \"commandExecution\")\n return;\n running.delete(item.id);\n background.delete(item.id);\n return;\n }\n case \"turn/completed\":\n for (const [id, task] of running)\n background.set(id, task);\n running.clear();\n return;\n }\n }\n async function stop(taskId, appServerPid) {\n const task = background.get(taskId);\n if (!task)\n throw new AgentNotFoundError(`no background task ${taskId}`);\n const targets = commandProcesses(appServerPid, task.description);\n signal(targets, \"SIGTERM\");\n const deadline = Date.now() + STOP_GRACE_MS;\n while (targets.some(alive) && Date.now() < deadline)\n await Bun.sleep(50);\n signal(targets.filter(alive), \"SIGKILL\");\n background.delete(taskId);\n }\n return { observe, list: async () => [...background.values()], stop };\n}\nfunction processes() {\n const entries = [];\n for (const name of readdirSync(\"/proc\")) {\n if (!/^\\d+$/.test(name))\n continue;\n try {\n const stat = readFileSync(`/proc/${name}/stat`, \"utf8\");\n const ppid = Number(stat.slice(stat.lastIndexOf(\")\") + 2).split(\" \")[1]);\n const argv = readFileSync(`/proc/${name}/cmdline`, \"utf8\").split(\"\\x00\").filter(Boolean);\n entries.push({ pid: Number(name), ppid, argv });\n } catch {}\n }\n return entries;\n}\nfunction commandProcesses(root, command) {\n const all = processes();\n const children = new Map;\n for (const entry of all)\n children.set(entry.ppid, [...children.get(entry.ppid) ?? [], entry.pid]);\n const under = (pid) => (children.get(pid) ?? []).flatMap((child) => [child, ...under(child)]);\n const descendants = new Set(under(root));\n const shells = all.filter((entry) => descendants.has(entry.pid) && entry.argv.length >= 3 && [\"-c\", \"-lc\"].includes(entry.argv.at(-2)) && command.includes(entry.argv.at(-1)));\n return [...new Set(shells.flatMap((shell) => [shell.pid, ...under(shell.pid)]))];\n}\nfunction signal(pids, name) {\n for (const pid of pids) {\n try {\n process.kill(pid, name);\n } catch {}\n }\n}\nfunction alive(pid) {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n// agent/session/codex/input.ts\nfunction codexApprovalPolicy(mode) {\n return mode === \"auto\" ? \"never\" : \"untrusted\";\n}\nfunction createCodexInput(thread, queue, initialModel, initialMode) {\n let model = initialModel;\n let mode = initialMode;\n async function sendMessage(message) {\n if (!message.uuid)\n throw new Error(\"a message sent to codex needs a uuid to be known by\");\n const { rpc, threadId } = thread();\n await rpc.request(\"turn/start\", {\n threadId,\n input: [{ type: \"text\", text: userMessageText(message), text_elements: [] }],\n clientUserMessageId: message.uuid,\n model,\n approvalPolicy: codexApprovalPolicy(mode)\n });\n queue.sent(message, message.uuid);\n }\n function setModel(next) {\n model = next;\n }\n async function setPermissionMode(next) {\n mode = next;\n }\n return { sendMessage, setModel, setPermissionMode };\n}\n\n// agent/session/codex/messageQueue.ts\nfunction codexMessageQueue() {\n const queued = new Map;\n let taken = null;\n return {\n observe(notification) {\n if (notification.method !== \"item/started\")\n return;\n const { item } = notification.params;\n if (item.type !== \"userMessage\" || !item.clientId)\n return;\n const message = queued.get(item.clientId);\n if (!message)\n return;\n queued.delete(item.clientId);\n taken = message;\n },\n sent(message, clientId) {\n queued.set(clientId, message);\n },\n async lastUserMessages() {\n return [...taken ? [{ message: taken, queued: false }] : [], ...[...queued.values()].map((message) => ({ message, queued: true }))];\n }\n };\n}\n\n// agent/session/codex/status.ts\nfunction codexStatus(changed) {\n let active = false;\n return {\n observe(notification) {\n if (notification.method !== \"thread/status/changed\")\n return;\n const next = notification.params.status.type === \"active\";\n if (next === active)\n return;\n active = next;\n changed();\n },\n active: () => active\n };\n}\n\n// agent/session/codex/interrupt.ts\nfunction codexInterrupt(thread) {\n return async () => {\n const { rpc, threadId, activeTurn } = thread();\n const turnId = activeTurn();\n if (!turnId)\n return;\n await rpc.request(\"turn/interrupt\", { threadId, turnId });\n };\n}\n\n// agent/session/codex/model.ts\nfunction codexModel(thread, input) {\n async function listModels() {\n const { data } = await thread().rpc.request(\"model/list\", {});\n return data.map((model) => ({ value: model.model, displayName: model.displayName, description: model.description }));\n }\n async function setModel(model) {\n input.setModel(model);\n }\n return { listModels, setModel };\n}\n\n// agent/session/codex/permissions.ts\nfunction codexPermissions(context) {\n function ask(request) {\n return context.requestPermission(request);\n }\n async function answer(request, rpc) {\n switch (request.method) {\n case \"item/commandExecution/requestApproval\": {\n const { params } = request;\n const decision = await ask({\n tool_name: \"commandExecution\",\n input: { command: params.command ?? \"\", cwd: params.cwd },\n tool_use_id: params.itemId,\n decision_reason: params.reason ?? undefined\n });\n const response = { decision: decision.behavior === \"allow\" ? \"accept\" : \"decline\" };\n return rpc.respond(request.id, response);\n }\n case \"item/fileChange/requestApproval\": {\n const { params } = request;\n const decision = await ask({\n tool_name: \"fileChange\",\n input: { grantRoot: params.grantRoot ?? null },\n tool_use_id: params.itemId,\n decision_reason: params.reason ?? undefined\n });\n const response = { decision: decision.behavior === \"allow\" ? \"accept\" : \"decline\" };\n return rpc.respond(request.id, response);\n }\n case \"execCommandApproval\": {\n const { params } = request;\n const decision = await ask({\n tool_name: \"commandExecution\",\n input: { command: params.command.join(\" \"), cwd: params.cwd },\n tool_use_id: params.callId,\n decision_reason: params.reason ?? undefined\n });\n const response = {\n decision: decision.behavior === \"allow\" ? \"approved\" : { denied: { rejection: decision.message } }\n };\n return rpc.respond(request.id, response);\n }\n case \"applyPatchApproval\": {\n const { params } = request;\n const decision = await ask({\n tool_name: \"fileChange\",\n input: { changes: params.fileChanges, grantRoot: params.grantRoot },\n tool_use_id: params.callId,\n decision_reason: params.reason ?? undefined\n });\n const response = {\n decision: decision.behavior === \"allow\" ? \"approved\" : { denied: { rejection: decision.message } }\n };\n return rpc.respond(request.id, response);\n }\n default:\n return rpc.respondError(request.id, `${request.method} is not supported by hyperbrane`);\n }\n }\n return (request, rpc) => {\n answer(request, rpc).catch((error) => {\n console.error(`answering codex's ${request.method} failed:`, error);\n rpc.respondError(request.id, error.message);\n });\n };\n}\n\n// agent/session/codex/questions.ts\nfunction codexQuestions(context, thread) {\n return async (params) => {\n const questions = params.questions.map((question) => ({\n question: question.question,\n header: question.header,\n options: question.options ?? [],\n multiSelect: false\n }));\n thread().emitToolUse(params.itemId, ASK_USER_QUESTION, { questions });\n const response = await context.askQuestions({ tool_use_id: params.itemId, questions });\n if (!response)\n thread().emitToolResult(params.itemId, \"The user dismissed the questions.\", true);\n else\n thread().emitToolResult(params.itemId, answersText(response.answers), false);\n const answers = {};\n for (const question of params.questions) {\n const answer = response?.answers[question.question];\n answers[question.id] = { answers: answer === undefined ? [] : [answer] };\n }\n return { answers };\n };\n}\nfunction answersText(answers) {\n const pairs = Object.entries(answers).map(([question, answer]) => `\"${question}\"=\"${answer}\"`);\n return `User has answered your questions: ${pairs.join(\", \")}.`;\n}\n\n// agent/session/codex/home.ts\nfunction codexHomeEnvironment(home) {\n return { CODEX_HOME: home };\n}\n\n// lib/version.ts\nvar HYPERBRANE_VERSION = \"v1\";\n\n// agent/session/codex/messages.ts\nfunction backendId(id) {\n return id;\n}\nfunction toolResultId(itemId) {\n return `${itemId}:result`;\n}\nfunction assistantMessage(uuid, content, threadId, model, extras = {}) {\n const usage = {\n ...resultUsage(),\n cache_creation: null,\n server_tool_use: null,\n service_tier: null,\n fallback_credit: null,\n inference_geo: null,\n iterations: null,\n output_tokens_details: null,\n speed: null\n };\n return {\n type: \"assistant\",\n message: {\n id: uuid,\n type: \"message\",\n role: \"assistant\",\n model,\n content,\n container: null,\n context_management: null,\n stop_reason: null,\n stop_sequence: null,\n stop_details: null,\n diagnostics: null,\n usage\n },\n parent_tool_use_id: null,\n uuid: backendId(uuid),\n session_id: threadId,\n ...extras\n };\n}\nfunction toolResultMessage(itemId, content, isError, threadId, output) {\n return {\n type: \"user\",\n message: { role: \"user\", content: [{ type: \"tool_result\", tool_use_id: itemId, content, is_error: isError }] },\n parent_tool_use_id: null,\n uuid: backendId(toolResultId(itemId)),\n session_id: threadId,\n ...output === undefined ? {} : { tool_use_result: output }\n };\n}\nfunction eventMessage(id, event, threadId) {\n return { type: \"system\", subtype: \"session_event\", event, uuid: id, session_id: threadId };\n}\nfunction itemMessages(item, threadId, model, finished = true) {\n const call = toolCallOf(item);\n if (call) {\n const messages = [assistantMessage(item.id, [call], threadId, model)];\n const result = finished ? toolResultOf(item) : null;\n if (result)\n messages.push(toolResultMessage(item.id, result.content, result.isError, threadId, item));\n return messages;\n }\n switch (item.type) {\n case \"userMessage\":\n return [{ type: \"user\", message: { role: \"user\", content: userContent(item.content) }, parent_tool_use_id: null, uuid: backendId(item.clientId ?? item.id), session_id: threadId }];\n case \"hookPrompt\":\n return [\n {\n type: \"user\",\n message: { role: \"user\", content: item.fragments.map((fragment) => fragment.text).join(`\n`) },\n parent_tool_use_id: null,\n uuid: backendId(item.id),\n session_id: threadId\n }\n ];\n case \"agentMessage\": {\n const citations = item.memoryCitation?.entries.map((entry, index) => ({\n type: \"search_result_location\",\n source: entry.path,\n title: entry.note,\n cited_text: \"\",\n search_result_index: index,\n start_block_index: entry.lineStart,\n end_block_index: entry.lineEnd\n }));\n const content = [{ type: \"text\", text: item.text, citations: citations?.length ? citations : null }];\n if (item.questions?.length) {\n const questions = item.questions.map((question) => ({\n question: question.title,\n header: \"\",\n options: (question.options ?? []).map((label) => ({ label, description: \"\" })),\n multiSelect: false\n }));\n content.push({ type: \"tool_use\", id: `${item.id}:questions`, name: ASK_USER_QUESTION, input: { questions } });\n }\n const extras = {\n ...item.phase ? { phase: item.phase === \"final_answer\" ? \"final\" : \"commentary\" } : {},\n ...item.delivery === \"async\" ? { async: true } : {}\n };\n return [assistantMessage(item.id, content, threadId, model, extras)];\n }\n case \"reasoning\":\n return [assistantMessage(item.id, [{ type: \"thinking\", thinking: [...item.summary, ...item.content].join(`\n\n`), signature: \"\" }], threadId, model)];\n case \"contextCompaction\":\n return [{ type: \"system\", subtype: \"compact_boundary\", uuid: backendId(item.id), session_id: threadId }];\n case \"enteredReviewMode\":\n return [eventMessage(item.id, { kind: \"review\", state: \"entered\", review: item.review }, threadId)];\n case \"exitedReviewMode\":\n return [eventMessage(item.id, { kind: \"review\", state: \"exited\", review: item.review }, threadId)];\n case \"subAgentActivity\":\n return [eventMessage(item.id, { kind: \"subagent\", activity: item.kind, agent: item.agentThreadId, path: item.agentPath }, threadId)];\n default:\n return [];\n }\n}\nfunction entryMessages({ item, startedAtMs, completedAtMs }, threadId, model, parentToolUseId = null) {\n const started = startedAtMs ?? completedAtMs;\n const completed = completedAtMs ?? startedAtMs;\n const at = (time) => time === null ? {} : { timestamp: new Date(time).toISOString() };\n const messages = itemMessages(item, threadId, model, completedAtMs !== null).map((message) => underTool(message, parentToolUseId));\n return messages.map((message, index) => ({ ...message, ...at(index === 0 && toolCallOf(item) ? started : completed) }));\n}\nfunction underTool(message, parentToolUseId) {\n return message.type === \"system\" || parentToolUseId === null ? message : { ...message, parent_tool_use_id: parentToolUseId };\n}\nfunction toolCallOf(item) {\n const call = (input, name = item.type) => ({ type: \"tool_use\", id: item.id, name, input });\n switch (item.type) {\n case \"commandExecution\":\n return call({ command: item.command, cwd: item.cwd });\n case \"fileChange\":\n return call({ changes: item.changes });\n case \"mcpToolCall\":\n return { type: \"mcp_tool_use\", id: item.id, name: item.tool, server_name: item.server, input: item.arguments };\n case \"dynamicToolCall\":\n return call(item.arguments, item.tool);\n case \"functionCallOutput\":\n return call({}, item.name);\n case \"collabAgentToolCall\":\n return call({ prompt: item.prompt, model: item.model, reasoningEffort: item.reasoningEffort, receiverThreadIds: item.receiverThreadIds }, item.tool);\n case \"webSearch\":\n return call({ query: item.query, action: item.action });\n case \"imageView\":\n return call({ path: item.path });\n case \"imageGeneration\":\n return call({ revisedPrompt: item.revisedPrompt });\n case \"sleep\":\n return call({ durationMs: item.durationMs });\n case \"plan\":\n return call({ text: item.text });\n default:\n return null;\n }\n}\nfunction toolResultOf(item) {\n switch (item.type) {\n case \"commandExecution\":\n return { content: item.aggregatedOutput ?? \"\", isError: item.status !== \"completed\" || (item.exitCode ?? 0) !== 0 };\n case \"fileChange\":\n return { content: item.status, isError: item.status !== \"completed\" };\n case \"mcpToolCall\":\n return { content: item.error ? item.error.message : mcpContent(item.result?.content ?? []), isError: item.status !== \"completed\" };\n case \"dynamicToolCall\":\n return { content: inputContent(item.contentItems ?? []), isError: item.success === false };\n case \"functionCallOutput\":\n return { content: typeof item.output === \"string\" ? item.output : functionOutputContent(item.output), isError: false };\n case \"collabAgentToolCall\":\n return { content: JSON.stringify(item.agentsStates), isError: item.status === \"failed\" };\n case \"webSearch\":\n return { content: item.results ? JSON.stringify(item.results) : \"\", isError: false };\n case \"imageView\":\n case \"sleep\":\n return { content: \"\", isError: false };\n case \"imageGeneration\": {\n const image = base64Image(item.result);\n return { content: image ? [image] : \"\", isError: item.failure !== null };\n }\n case \"plan\":\n return { content: item.text, isError: false };\n default:\n return null;\n }\n}\nfunction userContent(inputs) {\n if (inputs.every((input) => input.type === \"text\"))\n return inputs.map((input) => input.text).join(\"\");\n return inputs.flatMap((input) => {\n if (input.type === \"text\")\n return [{ type: \"text\", text: input.text }];\n if (input.type === \"image\" && \"url\" in input)\n return [imageBlock(input.url)];\n return [];\n });\n}\nfunction mcpContent(content) {\n const texts = content.map((block) => block.type === \"text\" ? block.text : null);\n if (texts.every((text) => text !== null))\n return texts.join(`\n`);\n return JSON.stringify(content);\n}\nfunction inputContent(items) {\n return items.flatMap((item) => {\n if (item.type === \"inputText\")\n return [{ type: \"text\", text: item.text }];\n if (item.type === \"inputImage\")\n return [imageBlock(item.imageUrl)];\n return [];\n });\n}\nfunction functionOutputContent(items) {\n return items.flatMap((item) => {\n if (item.type === \"input_text\")\n return [{ type: \"text\", text: item.text }];\n if (item.type === \"input_image\" && \"image_url\" in item)\n return [imageBlock(item.image_url)];\n return [];\n });\n}\nfunction imageBlock(url) {\n const data = /^data:(image\\/(?:png|jpeg|gif|webp));base64,(.*)$/s.exec(url);\n if (data)\n return { type: \"image\", source: { type: \"base64\", media_type: data[1], data: data[2] } };\n return { type: \"image\", source: { type: \"url\", url } };\n}\nvar IMAGE_SIGNATURES = [\n [\"iVBORw0KGgo\", \"image/png\"],\n [\"/9j/\", \"image/jpeg\"],\n [\"R0lGOD\", \"image/gif\"],\n [\"UklGR\", \"image/webp\"]\n];\nfunction base64Image(data) {\n const media = IMAGE_SIGNATURES.find(([signature]) => data.startsWith(signature))?.[1];\n return media ? { type: \"image\", source: { type: \"base64\", media_type: media, data } } : null;\n}\n\n// agent/session/codex/rpc.ts\nvar METHOD_NOT_FOUND = -32601;\nfunction spawnCodexAppServer(executable, { cwd, env }, handlers) {\n const args = [\"app-server\", \"-c\", \"tools.experimental_request_user_input.enabled=true\", \"--enable\", \"default_mode_request_user_input\"];\n const proc = Bun.spawn([executable.path, ...args], {\n cwd,\n env: { ...env, ...executable.env },\n stdin: \"pipe\",\n stdout: \"pipe\",\n stderr: \"pipe\"\n });\n const pending = new Map;\n let nextId = 1;\n function send(message) {\n proc.stdin.write(JSON.stringify(message) + `\n`);\n proc.stdin.flush();\n }\n function dispatch(message) {\n if (message.id !== undefined && message.method === undefined) {\n const waiting = pending.get(message.id);\n pending.delete(message.id);\n if (message.error)\n waiting?.reject(new Error(`codex: ${message.error.message ?? JSON.stringify(message.error)}`));\n else\n waiting?.resolve(message.result);\n return;\n }\n if (message.id !== undefined)\n handlers.onRequest(message);\n else\n handlers.onNotification(message);\n }\n async function readLines() {\n const decoder = new TextDecoder;\n let buffered = \"\";\n for await (const chunk of proc.stdout) {\n buffered += decoder.decode(chunk, { stream: true });\n let newline;\n while ((newline = buffered.indexOf(`\n`)) >= 0) {\n const line = buffered.slice(0, newline).trim();\n buffered = buffered.slice(newline + 1);\n if (line)\n dispatch(JSON.parse(line));\n }\n }\n }\n async function forwardStderr() {\n for await (const chunk of proc.stderr)\n process.stderr.write(chunk);\n }\n forwardStderr().catch(() => {});\n readLines().then(() => proc.exited).then(() => {\n const exit = { code: proc.exitCode, signal: proc.signalCode === null ? null : String(proc.signalCode) };\n finish(exit, exit.code === 0 ? undefined : new Error(`codex app-server exited with ${exit.signal ?? `code ${exit.code}`}`));\n }, (error) => finish(null, error));\n function finish(exit, error) {\n for (const waiting of pending.values())\n waiting.reject(error ?? new Error(\"codex app-server exited\"));\n pending.clear();\n handlers.onExit(exit, error);\n }\n function request(method, params) {\n const id = nextId++;\n return new Promise((resolve, reject) => {\n pending.set(id, { resolve: (result) => resolve(result), reject });\n send({ id, method, params });\n });\n }\n function notify(method) {\n send({ method });\n }\n function respond(id, result) {\n send({ id, result });\n }\n function respondError(id, message) {\n send({ id, error: { code: METHOD_NOT_FOUND, message } });\n }\n function close() {\n proc.stdin.end();\n proc.kill();\n }\n return { pid: proc.pid, request, notify, respond, respondError, close };\n}\n\n// agent/session/codex/transcript.ts\nvar ITEMS_PAGE = 100;\nvar CURSORS_KEPT = 1000;\nvar THREADS_SEARCHED = 50;\nvar cursors = new Map;\nfunction codexSubagentTranscript(source, subagentThreadId, toolUseId) {\n return codexTranscript({ ...source, nativeId: subagentThreadId }, toolUseId);\n}\nasync function codexFindSubagent(source, toolUseId) {\n const threads = [source.nativeId];\n const seen = new Set(threads);\n for (let thread = threads.shift();thread !== undefined && seen.size <= THREADS_SEARCHED; thread = threads.shift()) {\n const found = await codexReading({ ...source, nativeId: thread }, async (list) => {\n for (let cursor = null;; ) {\n const response = await list(cursor);\n for (const { item } of response.data) {\n if (item.type !== \"collabAgentToolCall\")\n continue;\n if (item.id === toolUseId)\n return item.receiverThreadIds[0] ?? null;\n for (const receiver of item.receiverThreadIds) {\n if (seen.has(receiver))\n continue;\n seen.add(receiver);\n threads.push(receiver);\n }\n }\n if (!response.nextCursor)\n return;\n cursor = response.nextCursor;\n }\n });\n if (found !== undefined)\n return found;\n }\n return null;\n}\nasync function codexReading({ nativeId: threadId, env, installDir }, read) {\n let rpc = null;\n rpc = spawnCodexAppServer(codexExecutable(installDir), { env }, {\n onNotification: () => {},\n onRequest: (request) => rpc?.respondError(request.id, \"this app-server only reads thread history\"),\n onExit: () => {}\n });\n const reader = rpc;\n try {\n await reader.request(\"initialize\", {\n clientInfo: { name: \"hyperbrane\", title: \"hyperbrane\", version: HYPERBRANE_VERSION },\n capabilities: { experimentalApi: true, requestAttestation: false }\n });\n reader.notify(\"initialized\");\n return await read((cursor) => reader.request(\"thread/items/list\", { threadId, cursor, limit: ITEMS_PAGE, sortDirection: \"desc\" }));\n } finally {\n reader.close();\n }\n}\nfunction codexTranscript(source, parentToolUseId = null) {\n const threadId = source.nativeId;\n const reading = (read) => codexReading(source, read);\n const messagesOf = (entry) => entryMessages(entry, threadId, \"\", parentToolUseId);\n const timeOf = (entry) => entry.startedAtMs ?? entry.completedAtMs ?? 0;\n const cursorKey = (time) => `${threadId} ${time}`;\n function keepCursor(time, cursor) {\n cursors.delete(cursorKey(time));\n cursors.set(cursorKey(time), cursor);\n if (cursors.size > CURSORS_KEPT)\n cursors.delete(cursors.keys().next().value);\n }\n const page = (before, limit) => reading((list) => pageOf(list, before, limit));\n const replay = (afterId, emit) => reading((list) => replayOf(list, afterId, emit));\n async function pageOf(list, before, limit) {\n let cursor = before === null ? null : cursors.get(cursorKey(before)) ?? null;\n const taken = [];\n let count = 0;\n let boundary = Infinity;\n let boundaryCursor = null;\n let older = false;\n pages:\n for (;; ) {\n const response = await list(cursor);\n for (const entry of response.data) {\n const time = timeOf(entry);\n if (before !== null && time >= before)\n continue;\n if (count >= limit && time < boundary) {\n older = true;\n break pages;\n }\n const messages = messagesOf(entry);\n if (messages.length === 0)\n continue;\n taken.push(messages);\n count += messages.length;\n if (count >= limit && boundary === Infinity) {\n boundary = time;\n boundaryCursor = cursor;\n }\n }\n if (!response.nextCursor)\n break;\n cursor = response.nextCursor;\n }\n if (older)\n keepCursor(boundary, boundaryCursor);\n return { messages: taken.reverse().flat(), nextBefore: older ? new Date(boundary).toISOString() : null };\n }\n async function replayOf(list, afterId, emit) {\n const newestFirst = [];\n let found = afterId === undefined;\n let cursor = null;\n scan:\n for (;; ) {\n const response = await list(cursor);\n for (const entry of response.data) {\n const messages = messagesOf(entry);\n newestFirst.push(messages);\n if (messages.some((message) => message.uuid === afterId)) {\n found = true;\n break scan;\n }\n }\n if (!response.nextCursor)\n break;\n cursor = response.nextCursor;\n }\n if (!found)\n return false;\n let passed = afterId === undefined;\n for (const messages of newestFirst.reverse()) {\n for (const message of messages) {\n if (passed)\n emit(message);\n else if (message.uuid === afterId)\n passed = true;\n }\n }\n return true;\n }\n return { page, replay };\n}\n\n// agent/session/codex/usage.ts\nvar CODEX_ROLLOUT_LAYOUT = { dir: \"sessions\", depth: 4, name: \"rollout-*-{id}.jsonl\", time: \"timestamp\" };\nasync function codexUsage({ nativeId, env, files }) {\n const home = env.CODEX_HOME ?? `${env.HOME}/.codex`;\n const rollout = await locateTranscript(files, home, CODEX_ROLLOUT_LAYOUT, nativeId);\n if (!rollout)\n return [];\n const turns = usageTurns(\"openai\");\n let model = null;\n let counted = 0;\n for await (const entry of jsonLines(files, rollout)) {\n const line = entry;\n const at = timeOf(line.timestamp);\n const payload = line.payload;\n if (line.type === \"turn_context\" && payload?.model)\n model = payload.model;\n if (line.type === \"event_msg\" && payload?.type === \"task_started\" && payload.turn_id && at !== null) {\n turns.start(payload.turn_id, at);\n continue;\n }\n const ends = line.type === \"response_item\" || line.type === \"event_msg\" && (payload?.type === \"task_complete\" || payload?.type === \"turn_aborted\");\n if (ends && at !== null)\n turns.seen(at);\n if (line.type !== \"event_msg\" || payload?.type !== \"token_count\" || !model)\n continue;\n const total = payload.info?.total_token_usage?.total_tokens ?? 0;\n const last = payload.info?.last_token_usage;\n if (!last || total <= counted)\n continue;\n counted = total;\n const call = extractedCall(\"openai\", responseOf(model, last), \"responses\");\n if (call)\n turns.add(call, at);\n }\n return turns.turns();\n}\nfunction responseOf(model, tokens) {\n return {\n model,\n usage: {\n input_tokens: tokens.input_tokens ?? 0,\n input_tokens_details: { cached_tokens: tokens.cached_input_tokens ?? 0, cache_write_tokens: tokens.cache_write_input_tokens ?? 0 },\n output_tokens: tokens.output_tokens ?? 0,\n output_tokens_details: { reasoning_tokens: tokens.reasoning_output_tokens ?? 0 }\n }\n };\n}\n\n// agent/session/codex/stream.ts\nasync function startCodexStream(context, hooks) {\n let threadId = context.nativeId ?? \"\";\n let model = context.model ?? \"\";\n let turn = null;\n const rpc = spawnCodexAppServer(codexExecutable(context.installDir), { cwd: context.cwd, env: context.env }, {\n onNotification,\n onRequest: (request) => hooks.onServerRequest(request, rpc),\n onExit: (exit, error) => context.exited(exit, error)\n });\n function base(parent = null, session = threadId) {\n return { parent_tool_use_id: parent, uuid: crypto.randomUUID(), session_id: session };\n }\n const subagentThreads = new Map;\n function noteSubagents(item) {\n if (item.type !== \"collabAgentToolCall\")\n return;\n for (const thread of item.receiverThreadIds) {\n if (subagentThreads.has(thread))\n continue;\n subagentThreads.set(thread, item.id);\n context.subagentStarted(item.id, thread);\n }\n }\n function delta(notification, parent, session = threadId) {\n switch (notification.method) {\n case \"item/agentMessage/delta\":\n return { type: \"stream_event\", event: { type: \"content_block_delta\", index: 0, delta: { type: \"text_delta\", text: notification.params.delta } }, ...base(parent, session) };\n case \"item/reasoning/summaryTextDelta\":\n case \"item/reasoning/textDelta\":\n return {\n type: \"stream_event\",\n event: { type: \"content_block_delta\", index: 0, delta: { type: \"thinking_delta\", thinking: notification.params.delta, estimated_tokens: null } },\n ...base(parent, session)\n };\n default:\n return null;\n }\n }\n function onSubagentNotification(notification, thread) {\n const parent = subagentThreads.get(thread);\n if (!parent)\n return;\n const streamed = delta(notification, parent, thread);\n if (streamed)\n return context.emit(streamed);\n if (notification.method === \"item/started\") {\n const { item } = notification.params;\n noteSubagents(item);\n const call = toolCallOf(item);\n if (call)\n context.emit({ ...underTool(assistantMessage(item.id, [call], thread, model), parent), timestamp: new Date(notification.params.startedAtMs).toISOString() });\n }\n if (notification.method === \"item/completed\") {\n const { item } = notification.params;\n noteSubagents(item);\n const messages = itemMessages(item, thread, model).map((message) => underTool(message, parent));\n const timestamp = new Date(notification.params.completedAtMs).toISOString();\n for (const message of toolCallOf(item) ? messages.slice(1) : messages)\n context.emit({ ...message, timestamp });\n }\n }\n function currentTurn(id) {\n turn ??= { id, startedAt: Date.now(), lastText: \"\", usage: null, errors: [] };\n turn.id ??= id;\n return turn;\n }\n function onNotification(notification) {\n const thread = notification.params?.threadId;\n if (typeof thread === \"string\" && threadId !== \"\" && thread !== threadId)\n return onSubagentNotification(notification, thread);\n for (const observer of hooks.observers)\n observer.observe(notification);\n const streamed = delta(notification, null);\n if (streamed)\n return context.emit(streamed);\n switch (notification.method) {\n case \"turn/started\":\n currentTurn(notification.params.turn.id);\n context.setNativeId(threadId);\n return;\n case \"item/started\": {\n const { item } = notification.params;\n noteSubagents(item);\n const call = toolCallOf(item);\n if (call)\n context.emit({ ...assistantMessage(item.id, [call], threadId, model), timestamp: new Date(notification.params.startedAtMs).toISOString() });\n return;\n }\n case \"item/completed\": {\n const { item } = notification.params;\n noteSubagents(item);\n if (item.type === \"agentMessage\")\n currentTurn(notification.params.turnId).lastText = item.text;\n if (item.type === \"userMessage\")\n return;\n const messages = itemMessages(item, threadId, model);\n const timestamp = new Date(notification.params.completedAtMs).toISOString();\n for (const message of toolCallOf(item) ? messages.slice(1) : messages)\n context.emit({ ...message, timestamp });\n return;\n }\n case \"thread/tokenUsage/updated\":\n currentTurn(notification.params.turnId).usage = notification.params.tokenUsage.last;\n return;\n case \"error\":\n if (!notification.params.willRetry)\n currentTurn(notification.params.turnId).errors.push(notification.params.error.message);\n return;\n case \"turn/completed\":\n context.emit(resultOf(currentTurn(notification.params.turn.id), notification.params.turn));\n turn = null;\n return;\n }\n }\n function resultOf(state, finished) {\n const duration = finished.durationMs ?? Date.now() - state.startedAt;\n const usage = resultUsage({\n input: state.usage?.inputTokens,\n output: state.usage?.outputTokens,\n cacheRead: state.usage?.cachedInputTokens,\n cacheWrite: state.usage?.cacheWriteInputTokens,\n thinking: state.usage?.reasoningOutputTokens\n });\n const common = {\n duration_ms: duration,\n duration_api_ms: duration,\n num_turns: 1,\n total_cost_usd: 0,\n usage,\n modelUsage: {},\n permission_denials: [],\n uuid: crypto.randomUUID(),\n session_id: threadId\n };\n if (finished.status === \"completed\") {\n return { type: \"result\", subtype: \"success\", is_error: false, result: state.lastText, stop_reason: \"end_turn\", ...common };\n }\n const errors = [...state.errors];\n if (finished.error)\n errors.push(finished.error.message);\n if (errors.length === 0)\n errors.push(`turn ${finished.status}`);\n return { type: \"result\", subtype: \"error_during_execution\", is_error: true, stop_reason: null, errors, ...common };\n }\n await rpc.request(\"initialize\", {\n clientInfo: { name: \"hyperbrane\", title: \"hyperbrane\", version: HYPERBRANE_VERSION },\n capabilities: { experimentalApi: true, requestAttestation: false }\n });\n rpc.notify(\"initialized\");\n const settings = {\n cwd: context.spec.cwd ?? null,\n model: context.model,\n approvalPolicy: codexApprovalPolicy(context.permissionMode),\n sandbox: \"danger-full-access\"\n };\n const started = threadId ? await rpc.request(\"thread/resume\", { threadId, ...settings }) : await rpc.request(\"thread/start\", settings);\n threadId = started.thread.id;\n model = started.model;\n return {\n rpc,\n threadId,\n model,\n activeTurn: () => turn?.id ?? null,\n emitToolUse: (id, name, input) => context.emit(assistantMessage(id, [{ type: \"tool_use\", id, name, input }], threadId, model)),\n emitToolResult: (id, content, isError) => context.emit(toolResultMessage(id, content, isError, threadId))\n };\n}\n\n// agent/session/codex/index.ts\nvar codexBackend = {\n install: installCodex,\n installed: installedCodex,\n auth: codexAuth,\n transcript: (source) => codexTranscript(source),\n findSubagent: codexFindSubagent,\n subagentTranscript: codexSubagentTranscript,\n usage: codexUsage,\n homeEnvironment: codexHomeEnvironment,\n async start(context) {\n let thread;\n const current = () => thread;\n const queue = codexMessageQueue();\n const status = codexStatus(() => context.activeChanged());\n const input = createCodexInput(current, queue, context.model, context.permissionMode);\n const permissions = codexPermissions(context);\n const questions = codexQuestions(context, current);\n function onServerRequest(request, rpc) {\n if (request.method !== \"item/tool/requestUserInput\")\n return permissions(request, rpc);\n questions(request.params).then((response) => rpc.respond(request.id, response), (error) => rpc.respondError(request.id, error.message));\n }\n const backgroundTasks = codexBackgroundTasks();\n thread = await startCodexStream(context, { onServerRequest, observers: [backgroundTasks, status, queue] });\n return {\n sendMessage: input.sendMessage,\n active: status.active,\n getLastUserMessages: queue.lastUserMessages,\n cancel: codexInterrupt(current),\n ...codexModel(current, input),\n setPermissionMode: input.setPermissionMode,\n listBackgroundTasks: backgroundTasks.list,\n stopTask: (taskId) => backgroundTasks.stop(taskId, current().rpc.pid),\n async close() {\n current().rpc.close();\n }\n };\n }\n};\n\n// agent/session/backends.ts\nvar SESSION_BACKENDS = {\n claude: claudeBackend,\n codex: codexBackend\n};\n\n// dimension/paths.ts\nfunction containerName(dimensionName) {\n return `hyb-${dimensionName}`;\n}\nvar WORKSPACE_PATH = \"/workspace\";\nvar RUN_PATH = \"/run/hyperbrane\";\nvar STATE_PATH = \"/var/lib/hyperbrane\";\nvar CA_BUNDLE_PATH = \"/etc/ssl/certs/ca-certificates.crt\";\nvar TRANSPORT_SOCKET = \".transport.sock\";\nfunction dimensionRunDirectory(dataDirectory, dimensionName) {\n return `${dataDirectory}/dimension-run/${dimensionName}`;\n}\nfunction dimensionStateDirectory(dataDirectory, dimensionName) {\n return `${dataDirectory}/dimension-state/${dimensionName}`;\n}\nvar dimensionLayout = {\n containerName,\n workspacePath: () => WORKSPACE_PATH,\n runPath: () => RUN_PATH,\n statePath: () => STATE_PATH,\n caBundlePath: () => CA_BUNDLE_PATH,\n runDirectory: dimensionRunDirectory,\n stateDirectory: dimensionStateDirectory,\n transportSocketPath: (dataDirectory, dimensionName) => `${dimensionRunDirectory(dataDirectory, dimensionName)}/${TRANSPORT_SOCKET}`\n};\n\n// dimension/api.ts\nvar MEMORY_UNITS = {\n \"\": 1,\n K: 1024,\n M: 1024 ** 2,\n G: 1024 ** 3,\n T: 1024 ** 4\n};\n\n// agent/session/sessionStorage.ts\nfunction workerSocketPath(runDirectory, agentName) {\n return `${runDirectory}/${agentName}.sock`;\n}\nfunction workerReadyPath(runDirectory, agentName) {\n return `${runDirectory}/${agentName}.ready`;\n}\nfunction sessionsDirectory(stateDirectory, agentName) {\n return `${stateDirectory}/agents/${agentName}/sessions`;\n}\nfunction backendHomePath(stateDirectory, backend) {\n return `${stateDirectory}/backends/${backend}`;\n}\nfunction createSessionStorage(sandbox, agentName, { stateDirectory = dimensionLayout.statePath(), runDirectory = dimensionLayout.runPath() } = {}) {\n const sessionsDir = sessionsDirectory(stateDirectory, agentName);\n const sessions = createJsonDirectory(sandbox, async () => sessionsDir);\n const sessionDir = (sessionName) => `${sessionsDir}/${checkName(sessionName)}`;\n const permissionRequests = (sessionName) => createJsonDirectory(sandbox, async () => `${sessionDir(sessionName)}/permission-requests`);\n const questionRequests = (sessionName) => createJsonDirectory(sandbox, async () => `${sessionDir(sessionName)}/question-requests`);\n const sessionFiles = (sessionName) => createJsonDirectory(sandbox, async () => sessionDir(sessionName));\n const statusFiles = (sessionName) => createJsonDirectory(sandbox, async () => sessionDir(sessionName));\n async function oldestFirst(stored) {\n return (await stored).sort((a, b) => a.at - b.at).map((entry) => entry.request);\n }\n async function getSessions() {\n return sessions.list();\n }\n async function insertSession(record) {\n return sessions.insert(record.spec.name, record);\n }\n async function saveSession(record) {\n await sessions.save(record.spec.name, record);\n }\n async function removeSession(sessionName) {\n await sandbox.removeDirectory(sessionDir(sessionName));\n await sessions.remove(sessionName);\n }\n async function getPermissionRequests(sessionName) {\n return oldestFirst(permissionRequests(sessionName).list());\n }\n async function savePermissionRequest(sessionName, request) {\n await permissionRequests(sessionName).save(request.id, { request, at: Date.now() });\n }\n async function removePermissionRequest(sessionName, requestId) {\n await permissionRequests(sessionName).remove(requestId);\n }\n async function getQuestionRequests(sessionName) {\n return oldestFirst(questionRequests(sessionName).list());\n }\n async function saveQuestionRequest(sessionName, request) {\n await questionRequests(sessionName).save(request.id, { request, at: Date.now() });\n }\n async function removeQuestionRequest(sessionName, requestId) {\n await questionRequests(sessionName).remove(requestId);\n }\n async function readAuth(sessionName) {\n return sessionFiles(sessionName).read(\"auth\");\n }\n async function saveAuth(sessionName, record) {\n await sessionFiles(sessionName).save(\"auth\", record);\n }\n async function saveStatus(sessionName, status) {\n await statusFiles(sessionName).save(\"status\", { status });\n }\n async function socketPath() {\n return workerSocketPath(runDirectory, agentName);\n }\n async function backendHome(backend) {\n const path = backendHomePath(stateDirectory, backend);\n await sandbox.createDirectory(path);\n return path;\n }\n return {\n getSessions,\n insertSession,\n saveSession,\n removeSession,\n getPermissionRequests,\n savePermissionRequest,\n removePermissionRequest,\n getQuestionRequests,\n saveQuestionRequest,\n removeQuestionRequest,\n readAuth,\n saveAuth,\n saveStatus,\n socketPath,\n backendHome\n };\n}\n\n// agent/session/worker/protocol.ts\nvar WORKER_PROTOCOL = 6;\nvar WorkerErrorCode = {\n methodNotFound: -32601,\n invalidParams: -32602,\n internalError: -32603,\n notFound: -32004,\n conflict: -32009,\n backendNotInstalled: -32010,\n protocolMismatch: -32011\n};\n\nclass WorkerMethodNotFoundError extends Error {\n name = \"WorkerMethodNotFoundError\";\n}\n\nclass WorkerProtocolMismatchError extends Error {\n name = \"WorkerProtocolMismatchError\";\n}\nfunction encodeWorkerError(error) {\n const message = error instanceof Error ? error.message : String(error);\n if (error instanceof AgentInvalidError)\n return { code: WorkerErrorCode.invalidParams, message };\n if (error instanceof AgentNotFoundError)\n return { code: WorkerErrorCode.notFound, message };\n if (error instanceof AgentBackendNotInstalledError)\n return { code: WorkerErrorCode.backendNotInstalled, message };\n if (error instanceof AgentConflictError)\n return { code: WorkerErrorCode.conflict, message };\n if (error instanceof WorkerMethodNotFoundError)\n return { code: WorkerErrorCode.methodNotFound, message };\n if (error instanceof WorkerProtocolMismatchError)\n return { code: WorkerErrorCode.protocolMismatch, message };\n return { code: WorkerErrorCode.internalError, message };\n}\nfunction decodeWorkerError({ code, message }) {\n if (code === WorkerErrorCode.invalidParams)\n return new AgentInvalidError(message);\n if (code === WorkerErrorCode.notFound)\n return new AgentNotFoundError(message);\n if (code === WorkerErrorCode.conflict)\n return new AgentConflictError(message);\n if (code === WorkerErrorCode.backendNotInstalled)\n return new AgentBackendNotInstalledError(message);\n if (code === WorkerErrorCode.methodNotFound)\n return new WorkerMethodNotFoundError(`worker: ${message}`);\n if (code === WorkerErrorCode.protocolMismatch)\n return new WorkerProtocolMismatchError(`worker: ${message}`);\n return new Error(`worker: ${message}`);\n}\n\n// agent/session/worker/workerServer.ts\nimport { rm as rm2 } from \"fs/promises\";\n\n// agent/session/worker/jsonRpc.ts\nvar PARSE_ERROR = -32700;\nfunction createJsonRpcPeer(write, options) {\n const pending = new Map;\n let nextId = 1;\n let buffered = \"\";\n let closedWith;\n function send(message) {\n if (closedWith)\n return;\n write(JSON.stringify({ jsonrpc: \"2.0\", ...message }) + `\n`);\n }\n function handle(message) {\n if (message.method === undefined) {\n if (message.id === undefined || message.id === null)\n return;\n const waiting = pending.get(message.id);\n pending.delete(message.id);\n if (message.error)\n waiting?.reject(options.decodeError(message.error));\n else\n waiting?.resolve(message.result);\n return;\n }\n if (message.id === undefined || message.id === null) {\n options.onNotification?.(message.method, message.params);\n return;\n }\n const id = message.id;\n options.onRequest(message.method, message.params, message.protocol).then((result) => send({ id, result: result ?? null }), (error) => send({ id, error: options.encodeError(error) }));\n }\n function receive(text) {\n buffered += text;\n let newline;\n while ((newline = buffered.indexOf(`\n`)) >= 0) {\n const line = buffered.slice(0, newline).trim();\n buffered = buffered.slice(newline + 1);\n if (!line)\n continue;\n let message;\n try {\n message = JSON.parse(line);\n } catch {\n send({ id: null, error: { code: PARSE_ERROR, message: \"not JSON\" } });\n continue;\n }\n handle(message);\n }\n }\n function request(method, params) {\n if (closedWith)\n return Promise.reject(closedWith);\n const id = nextId++;\n return new Promise((resolve, reject) => {\n pending.set(id, { resolve, reject });\n send({ id, method, params, ...options.protocol === undefined ? {} : { protocol: options.protocol } });\n });\n }\n function notify(method, params) {\n send({ method, params });\n }\n function close(error) {\n closedWith ??= error;\n for (const waiting of pending.values())\n waiting.reject(error);\n pending.clear();\n }\n return { receive, request, notify, close };\n}\nfunction createSocketWriter(socket) {\n const encoder = new TextEncoder;\n let backlog = null;\n function write(text) {\n const bytes = encoder.encode(text);\n if (backlog) {\n const joined = new Uint8Array(backlog.length + bytes.length);\n joined.set(backlog);\n joined.set(bytes, backlog.length);\n backlog = joined;\n return;\n }\n const written = Math.max(socket.write(bytes), 0);\n if (written < bytes.length)\n backlog = bytes.subarray(written);\n }\n function drain() {\n if (!backlog)\n return;\n const written = Math.max(socket.write(backlog), 0);\n backlog = written < backlog.length ? backlog.subarray(written) : null;\n }\n return { write, drain };\n}\n\n// agent/session/worker/workerParams.ts\nvar WORKER_PARAMS = {\n \"worker/health\": () => ({}),\n \"worker/shutdown\": () => ({}),\n \"backend/install\": (params) => ({ backend: backend(params.backend) }),\n \"backend/list\": () => ({}),\n \"session/create\": (params) => ({ session: session(object(params.session, \"session\")) }),\n \"session/list\": () => ({}),\n \"session/update\": (params) => ({ session: name(params), update: sessionUpdate(object(params.update, \"update\")) }),\n \"session/stop\": (params) => ({ session: name(params) }),\n \"session/send\": (params) => ({ session: name(params), message: userMessage(params.message) }),\n \"session/interrupt\": (params) => ({ session: name(params) }),\n \"session/status\": (params) => ({ session: name(params) }),\n \"session/lastUserMessages\": (params) => ({ session: name(params) }),\n \"session/subscribe\": (params) => ({\n session: name(params),\n subscription: text(params.subscription, \"subscription\"),\n after_id: optional(params.after_id, (value) => text(value, \"after_id\"))\n }),\n \"session/unsubscribe\": (params) => ({ subscription: text(params.subscription, \"subscription\") }),\n \"session/transcript\": (params) => ({\n session: name(params),\n limit: optional(params.limit, (value) => number(value, \"limit\")),\n before: params.before === undefined ? optional(params.cursor, (value) => text(value, \"cursor\")) : text(params.before, \"before\")\n }),\n \"session/subagentTranscript\": (params) => ({\n session: name(params),\n toolUseId: text(params.toolUseId, \"toolUseId\"),\n limit: optional(params.limit, (value) => number(value, \"limit\")),\n before: optional(params.before, (value) => text(value, \"before\"))\n }),\n \"session/usage\": (params) => ({ session: name(params) }),\n \"permission/list\": (params) => ({ session: name(params) }),\n \"permission/respond\": (params) => ({\n session: name(params),\n request: text(params.request, \"request\"),\n result: permissionResult(params.result)\n }),\n \"question/list\": (params) => ({ session: name(params) }),\n \"question/respond\": (params) => ({\n session: name(params),\n request: text(params.request, \"request\"),\n response: questionResponse(params.response)\n }),\n \"model/list\": (params) => ({ session: name(params) }),\n \"model/set\": (params) => ({\n session: name(params),\n model: params.model === null ? null : text(params.model, \"the model (a model name, or null for the backend's default)\")\n }),\n \"permission/mode\": (params) => ({ session: name(params), mode: permissionMode(params.mode) }),\n \"task/list\": (params) => ({ session: name(params) }),\n \"task/stop\": (params) => ({ session: name(params), task: text(params.task, \"task\") }),\n \"auth/status\": (params) => ({ session: name(params) }),\n \"auth/login\": (params) => ({\n session: name(params),\n gatewaySignedIn: optional(params.gatewaySignedIn, (value) => boolean(value, \"gatewaySignedIn\")) ?? false\n }),\n \"auth/code\": (params) => ({ session: name(params), code: text(params.code, \"the code (the one the sign-in page showed)\").trim() })\n};\nfunction invalid2(message) {\n throw new AgentInvalidError(message);\n}\nfunction name(params) {\n return text(params.session, \"session\");\n}\nfunction object(value, what) {\n if (value === null || typeof value !== \"object\" || Array.isArray(value))\n invalid2(`${what} must be an object`);\n return value;\n}\nfunction text(value, what) {\n if (typeof value !== \"string\" || value.trim() === \"\")\n invalid2(`${what} must be a non-empty string`);\n return value;\n}\nfunction number(value, what) {\n if (typeof value !== \"number\" || !Number.isFinite(value))\n invalid2(`${what} must be a number`);\n return value;\n}\nfunction boolean(value, what) {\n if (typeof value !== \"boolean\")\n invalid2(`${what} must be true or false`);\n return value;\n}\nfunction optional(value, parse) {\n return value === undefined ? undefined : parse(value);\n}\nfunction permissionMode(value) {\n const known = AGENT_PERMISSION_MODES.find((mode) => mode === value);\n if (!known)\n invalid2(`unknown permission mode \"${String(value)}\"; known: ${AGENT_PERMISSION_MODES.join(\", \")}`);\n return known;\n}\nfunction backend(value) {\n const known = AGENT_BACKENDS.find((name) => name === value);\n if (!known)\n invalid2(`unknown backend \"${String(value)}\"; known: ${AGENT_BACKENDS.join(\", \")}`);\n return known;\n}\nfunction memoryLimit(value) {\n const bytes = number(value, \"memoryLimit\");\n if (!(bytes > 0))\n invalid2(\"memoryLimit must be positive\");\n return bytes;\n}\nfunction cpuLimit(value) {\n const cores = number(value, \"cpuLimit\");\n if (!(cores > 0))\n invalid2(\"cpuLimit must be positive\");\n return cores;\n}\nfunction environment(value) {\n const fields = object(value, \"env\");\n for (const [key, entry] of Object.entries(fields))\n if (typeof entry !== \"string\")\n invalid2(`env.${key} must be a string`);\n return fields;\n}\nfunction session(fields) {\n return {\n name: text(fields.name, \"the session's name\"),\n backend: backend(fields.backend),\n memoryLimit: memoryLimit(fields.memoryLimit),\n cpuLimit: cpuLimit(fields.cpuLimit),\n cwd: optional(fields.cwd, (value) => text(value, \"cwd\")),\n env: optional(fields.env, environment)\n };\n}\nfunction sessionUpdate(fields) {\n return {\n backend: optional(fields.backend, backend),\n memoryLimit: optional(fields.memoryLimit, memoryLimit),\n cpuLimit: optional(fields.cpuLimit, cpuLimit),\n cwd: optional(fields.cwd, (value) => text(value, \"cwd\")),\n env: optional(fields.env, environment)\n };\n}\nfunction userMessage(value) {\n const fields = object(value, 'a message (an SDK user message: {type: \"user\", message})');\n if (fields.type !== \"user\")\n invalid2('a message is an SDK user message: {type: \"user\", message}');\n const message = object(fields.message, \"message.message\");\n if (message.role !== \"user\")\n invalid2('message.message.role must be \"user\"');\n if (typeof message.content !== \"string\" && !Array.isArray(message.content))\n invalid2(\"message.message.content must be a string or an array of content blocks\");\n return value;\n}\nfunction permissionResult(value) {\n const fields = object(value, \"result\");\n if (fields.behavior === \"allow\") {\n return { behavior: \"allow\", updatedInput: optional(fields.updatedInput, (input) => object(input, \"updatedInput\")) };\n }\n if (fields.behavior === \"deny\") {\n return {\n behavior: \"deny\",\n message: typeof fields.message === \"string\" ? fields.message : invalid2(\"a denial needs a message\"),\n interrupt: optional(fields.interrupt, (interrupt) => boolean(interrupt, \"interrupt\"))\n };\n }\n return invalid2('a permission result is {behavior: \"allow\"} or {behavior: \"deny\", message}');\n}\nfunction questionResponse(value) {\n const answers = object(object(value, \"response\").answers, \"a question response's answers ({<question>: <answer>})\");\n for (const [question, answer] of Object.entries(answers))\n if (typeof answer !== \"string\")\n invalid2(`the answer to \"${question}\" must be a string`);\n return { answers };\n}\n\n// agent/session/worker/workerServer.ts\nvar ANY_PROTOCOL = new Set([\"worker/health\", \"worker/shutdown\"]);\nfunction createWorkerServer(service, options) {\n let listener;\n function ref(session) {\n return { agentName: options.health.agentName, sessionName: session };\n }\n const handlers = {\n \"worker/health\": async () => options.health,\n \"worker/shutdown\": async () => {\n setTimeout(options.onShutdown, 0);\n return null;\n },\n \"backend/install\": async ({ backend }) => nothing(await service.install(options.health.agentName, backend)),\n \"backend/list\": () => service.listBackends(options.health.agentName),\n \"session/create\": ({ session }) => service.createSession(options.health.agentName, session),\n \"session/list\": () => service.listSessions(),\n \"session/update\": ({ session, update }) => service.updateSession(ref(session), update),\n \"session/stop\": async ({ session }) => nothing(await service.stopSession(ref(session))),\n \"session/send\": async ({ session, message }) => nothing(await service.sendMessage(ref(session), message)),\n \"session/interrupt\": async ({ session }) => nothing(await service.cancel(ref(session))),\n \"session/status\": ({ session }) => service.getSessionStatus(ref(session)),\n \"session/lastUserMessages\": ({ session }) => service.getLastUserMessages(ref(session)),\n \"session/subscribe\": async ({ session, subscription, after_id }, connection) => {\n if (connection.subscriptions.has(subscription))\n throw new AgentConflictError(`subscription ${subscription} already exists`);\n let unsubscribe = null;\n let cancelled = false;\n connection.subscriptions.set(subscription, () => {\n cancelled = true;\n unsubscribe?.();\n });\n try {\n unsubscribe = await service.subscribe(ref(session), after_id, (message) => {\n const notification = { subscription, message };\n connection.peer.notify(\"session/message\", notification);\n });\n } catch (error) {\n connection.subscriptions.delete(subscription);\n throw error;\n }\n if (cancelled)\n unsubscribe();\n return null;\n },\n \"session/unsubscribe\": async ({ subscription }, connection) => {\n connection.subscriptions.get(subscription)?.();\n connection.subscriptions.delete(subscription);\n return null;\n },\n \"session/transcript\": ({ session, limit, before }) => service.getTranscript(ref(session), { limit, before }),\n \"session/subagentTranscript\": ({ session, toolUseId, limit, before }) => service.getTranscript(ref(session), { limit, before, toolUseId }),\n \"session/usage\": ({ session }) => service.getUsage(ref(session)),\n \"permission/list\": ({ session }) => service.listPermissionRequests(ref(session)),\n \"permission/respond\": async ({ session, request, result }) => nothing(await service.respondPermissionRequest(ref(session), request, result)),\n \"question/list\": ({ session }) => service.listQuestionRequests(ref(session)),\n \"question/respond\": async ({ session, request, response }) => nothing(await service.respondQuestionRequest(ref(session), request, response)),\n \"model/list\": ({ session }) => service.listModels(ref(session)),\n \"model/set\": async ({ session, model }) => nothing(await service.setModel(ref(session), model)),\n \"permission/mode\": async ({ session, mode }) => nothing(await service.setPermissionMode(ref(session), mode)),\n \"task/list\": ({ session }) => service.listBackgroundTasks(ref(session)),\n \"task/stop\": async ({ session, task }) => nothing(await service.stopTask(ref(session), task)),\n \"auth/status\": ({ session }) => service.getAuthStatus(ref(session)),\n \"auth/login\": ({ session, gatewaySignedIn }) => service.login(ref(session), gatewaySignedIn),\n \"auth/code\": ({ session, code }) => service.submitAuthCode(ref(session), code)\n };\n function handle(method, params, connection) {\n return handlers[method](WORKER_PARAMS[method](params), connection);\n }\n async function dispatch(method, params, connection) {\n if (!isWorkerMethod(method))\n throw new WorkerMethodNotFoundError(`no method ${method}`);\n const fields = params ?? {};\n if (typeof fields !== \"object\" || Array.isArray(fields))\n throw new AgentInvalidError(`${method} takes an object of params`);\n return handle(method, fields, connection);\n }\n function openConnection(socket) {\n const writer = createSocketWriter(socket);\n const peer = createJsonRpcPeer(writer.write, {\n onRequest: (method, params, protocol) => {\n if (protocol !== undefined && protocol > options.health.protocol && !ANY_PROTOCOL.has(method)) {\n return Promise.reject(new WorkerProtocolMismatchError(`this worker speaks protocol ${options.health.protocol}, not ${protocol}`));\n }\n return dispatch(method, params, connection);\n },\n encodeError: (error) => {\n const encoded = encodeWorkerError(error);\n if (encoded.code === WorkerErrorCode.internalError)\n console.error(error);\n return encoded;\n },\n decodeError: decodeWorkerError\n });\n const connection = { peer, writer, subscriptions: new Map, decoder: new TextDecoder };\n return connection;\n }\n async function start() {\n await rm2(options.socketPath, { force: true });\n listener = Bun.listen({\n unix: options.socketPath,\n socket: {\n open(socket) {\n socket.data = openConnection(socket);\n },\n data(socket, data) {\n socket.data.peer.receive(socket.data.decoder.decode(data, { stream: true }));\n },\n drain(socket) {\n socket.data.writer.drain();\n },\n close(socket) {\n for (const unsubscribe of socket.data.subscriptions.values())\n unsubscribe();\n socket.data.subscriptions.clear();\n socket.data.peer.close(new Error(\"the connection closed\"));\n },\n error(_socket, error) {\n console.error(\"worker connection failed:\", error);\n }\n }\n });\n }\n function stop() {\n listener?.stop(true);\n listener = undefined;\n }\n return { start, stop };\n}\nfunction isWorkerMethod(method) {\n return Object.hasOwn(WORKER_PARAMS, method);\n}\nfunction nothing(_done) {\n return null;\n}\n\n// agent/session/workerService.ts\nimport { statSync } from \"fs\";\n\n// agent/session/pending.ts\nfunction createPendingRequests(store, fallback) {\n const waiting = new Map;\n function add(request, signal) {\n const id = crypto.randomUUID();\n return new Promise((resolve) => {\n let settled = false;\n const settle = (response) => {\n if (settled)\n return;\n settled = true;\n waiting.delete(id);\n signal?.removeEventListener(\"abort\", aborted);\n store.remove(id).catch((error) => console.error(`removing request ${id} failed:`, error));\n resolve(response);\n };\n const aborted = () => settle(fallback(\"the request was cancelled\"));\n if (signal?.aborted)\n return aborted();\n signal?.addEventListener(\"abort\", aborted);\n waiting.set(id, settle);\n store.save({ ...request, id }).catch((error) => {\n console.error(`keeping request ${id} failed:`, error);\n settle(fallback(\"the request couldn't be kept\"));\n });\n });\n }\n async function list() {\n return (await store.list()).filter((request) => waiting.has(request.id));\n }\n async function answer(id, response) {\n const settle = waiting.get(id);\n if (!settle)\n return false;\n settle(response);\n return true;\n }\n async function dropAll(reason) {\n for (const settle of [...waiting.values()])\n settle(fallback(reason));\n for (const request of await store.list())\n if (!waiting.has(request.id))\n await store.remove(request.id);\n }\n return { add, list, count: () => waiting.size, answer, dropAll };\n}\n\n// agent/session/workerService.ts\nfunction createWorkerService(config) {\n const sessions = new Map;\n let installing = Promise.resolve();\n const installs = new Map;\n async function loadSessions() {\n for (const record of await config.storage.getSessions()) {\n const state = await newState(record);\n sessions.set(record.spec.name, state);\n noteStatus(state);\n await dropRequests(state, \"the worker restarted\");\n const auth = await config.storage.readAuth(record.spec.name);\n if (auth?.login)\n await config.storage.saveAuth(record.spec.name, { login: null, error: auth.error });\n }\n }\n async function newState(record) {\n return {\n ...record,\n listeners: new Set,\n running: null,\n startedAt: 0,\n generation: 0,\n session: null,\n login: null,\n accepted: null,\n sending: new Map,\n pending: [],\n draining: null,\n handing: null,\n exit: null,\n failure: null,\n tools: new Map,\n subagents: new Map,\n permissionMode: record.permissionMode ?? \"ask\",\n turnOpen: false,\n status: null,\n statusSaving: Promise.resolve(),\n permissionRequests: createPendingRequests({\n list: () => config.storage.getPermissionRequests(record.spec.name),\n save: (request) => config.storage.savePermissionRequest(record.spec.name, request),\n remove: (id) => config.storage.removePermissionRequest(record.spec.name, id)\n }, (message) => ({ behavior: \"deny\", message })),\n questionRequests: createPendingRequests({\n list: () => config.storage.getQuestionRequests(record.spec.name),\n save: (request) => config.storage.saveQuestionRequest(record.spec.name, request),\n remove: (id) => config.storage.removeQuestionRequest(record.spec.name, id)\n }, () => null)\n };\n }\n async function saveRecord(state) {\n await config.storage.saveSession({ spec: state.spec, nativeId: state.nativeId, model: state.model, permissionMode: state.permissionMode });\n }\n async function info(state) {\n return { ...state.spec, agentName: config.agentName, status: currentStatus(state), nativeId: state.nativeId, model: state.model, permissionMode: state.permissionMode };\n }\n function statusOf(state) {\n if (!state.running) {\n const exited = { state: \"exited\", code: state.exit?.code ?? null, signal: state.exit?.signal ?? null };\n return state.failure ? { ...exited, error: state.failure } : exited;\n }\n if (!state.session) {\n const install = installs.get(state.spec.backend);\n return install ? { state: \"installing\", backend: state.spec.backend, version: install.version } : { state: \"starting\" };\n }\n if (state.permissionRequests.count() + state.questionRequests.count() > 0)\n return { state: \"waiting\" };\n if (!state.session.active())\n return { state: \"idle\" };\n if (state.tools.size > 0)\n return { state: \"tool\", tools: [...state.tools.values()] };\n return { state: \"thinking\" };\n }\n async function lastStoredPrompt(state) {\n const transcript = await transcriptOf(state);\n let before = null;\n while (transcript) {\n const page = await transcript.page(before, LAST_PROMPT_PAGE_SIZE);\n const prompt = page.messages.findLast((message) => message.type === \"user\" && isPrompt2(message.message.content));\n if (prompt?.type === \"user\")\n return { message: prompt, queued: false };\n if (!page.nextBefore)\n return null;\n before = Date.parse(page.nextBefore);\n }\n return null;\n }\n function trustChangedSince(time) {\n if (!config.trustBundle)\n return false;\n try {\n return statSync(config.trustBundle).mtimeMs > time;\n } catch {\n return false;\n }\n }\n function requireAgent(agentName) {\n if (agentName !== config.agentName)\n throw new AgentNotFoundError(`this worker runs agent ${config.agentName}, not ${agentName}`);\n }\n function requireSession({ agentName, sessionName }) {\n requireAgent(agentName);\n const state = sessions.get(sessionName);\n if (!state)\n throw new AgentNotFoundError(`session ${sessionName} does not exist`);\n return state;\n }\n async function dropRequests(state, reason) {\n await Promise.all([state.permissionRequests.dropAll(reason), state.questionRequests.dropAll(reason)]);\n }\n function broadcast(state, message) {\n if (message.type === \"result\")\n state.turnOpen = false;\n else if (message.type === \"user\" || message.type === \"assistant\")\n state.turnOpen = true;\n noteToolCalls(state, message);\n for (const listener of state.listeners)\n listener(message);\n if (message.type !== \"stream_event\")\n noteStatus(state);\n }\n function noteToolCalls(state, message) {\n if (message.type === \"assistant\" && message.parent_tool_use_id === null) {\n for (const block of message.message.content)\n if (block.type === \"tool_use\" || block.type === \"mcp_tool_use\")\n state.tools.set(block.id, block.name);\n }\n if (message.type === \"user\" && Array.isArray(message.message.content)) {\n for (const block of message.message.content)\n if (block.type === \"tool_result\")\n state.tools.delete(block.tool_use_id);\n }\n }\n function noteStatus(state) {\n const computed = statusOf(state);\n if (state.status && sameStatus(computed, state.status))\n return;\n const status = { ...computed, since: Date.now() };\n state.status = status;\n for (const listener of state.listeners)\n listener({ type: \"session_status\", status });\n state.statusSaving = state.statusSaving.then(async () => {\n if (sessions.get(state.spec.name) !== state)\n return;\n await config.storage.saveStatus(state.spec.name, status);\n }).catch((error) => console.error(`saving session ${state.spec.name}'s status failed:`, error));\n }\n function currentStatus(state) {\n noteStatus(state);\n return state.status;\n }\n async function transcriptSource(state) {\n if (!state.nativeId)\n return null;\n return { nativeId: state.nativeId, env: await environmentOf(state), files: config.files, installDir: config.installDir };\n }\n async function transcriptOf(state) {\n const source = await transcriptSource(state);\n return source && config.backends[state.spec.backend].transcript(source);\n }\n async function subagentTranscriptOf(state, toolUseId) {\n const source = await transcriptSource(state);\n if (!source)\n return null;\n const backend = config.backends[state.spec.backend];\n const subagent = state.subagents.get(toolUseId) ?? await backend.findSubagent(source, toolUseId);\n return subagent ? backend.subagentTranscript(source, subagent, toolUseId) : null;\n }\n async function environmentOf(state) {\n const env = {};\n for (const [key, value] of Object.entries(process.env))\n if (value !== undefined)\n env[key] = value;\n const backend = config.backends[state.spec.backend];\n return { ...env, ...backend.homeEnvironment(await config.storage.backendHome(state.spec.backend)), ...state.spec.env, HYB_SESSION: state.spec.name };\n }\n async function authEnvironment(state) {\n requireCwd(state.spec);\n return { installDir: config.installDir, env: await environmentOf(state), cwd: state.spec.cwd ?? process.cwd() };\n }\n function runInstall(backend, version) {\n const underway = installs.get(backend);\n if (underway)\n return underway.done;\n const run = installing.then(() => config.backends[backend].install(config.installDir));\n installing = run.catch(() => {});\n const done = run.finally(() => {\n installs.delete(backend);\n noteBackendStatus(backend);\n });\n installs.set(backend, { version, done });\n noteBackendStatus(backend);\n return done;\n }\n function noteBackendStatus(backend) {\n for (const state of sessions.values())\n if (state.spec.backend === backend)\n noteStatus(state);\n }\n async function ensureBackend(state) {\n const backend = state.spec.backend;\n const installed = await config.backends[backend].installed(config.installDir);\n if (installed.ready && installed.version === installed.pinned)\n return;\n try {\n await runInstall(backend, installed.pinned);\n } catch (error) {\n const reason = error.message;\n if (!installed.ready)\n throw new AgentBackendNotInstalledError(`installing ${backend} ${installed.pinned} failed: ${reason}`);\n console.error(`updating ${backend} to ${installed.pinned} failed:`, error);\n broadcast(state, { type: \"session_notice\", text: `updating ${backend} from ${installed.version} to ${installed.pinned} failed, so ${installed.version} runs: ${reason}` });\n }\n }\n async function authStatus(state) {\n const auth = await config.storage.readAuth(state.spec.name);\n if (state.login && auth?.login)\n return auth.login;\n const backend = state.spec.backend;\n if (!(await config.backends[backend].installed(config.installDir)).ready) {\n return { state: \"signedOut\", error: `${backend} isn't installed yet: it installs when the session's CLI first starts` };\n }\n if (await config.backends[state.spec.backend].auth.signedIn(await authEnvironment(state)))\n return { state: \"signedIn\" };\n return auth?.error ? { state: \"signedOut\", error: auth.error } : { state: \"signedOut\" };\n }\n function saveAuth(state, record) {\n return config.storage.saveAuth(state.spec.name, record);\n }\n function watchLogin(state, login) {\n return login.done.then(async () => {\n if (state.login?.process !== login)\n return;\n state.login = null;\n await saveAuth(state, { login: null, error: null });\n if (statusOf(state).state === \"idle\")\n await closeSession(state);\n }, async (error) => {\n if (state.login?.process !== login)\n return;\n state.login = null;\n await saveAuth(state, { login: null, error: error.message });\n });\n }\n async function settleLogin(state, settled, timeoutMs) {\n await Promise.race([settled, Bun.sleep(timeoutMs)]);\n return authStatus(state);\n }\n function contextFor(state, generation, env) {\n const current = () => state.generation === generation;\n return {\n spec: state.spec,\n nativeId: state.nativeId,\n cwd: state.spec.cwd ?? process.cwd(),\n model: state.model,\n permissionMode: state.permissionMode,\n installDir: config.installDir,\n env,\n emit(message) {\n if (!current())\n return;\n broadcast(state, message);\n },\n setNativeId(id) {\n if (!current() || state.nativeId === id)\n return;\n state.nativeId = id;\n saveRecord(state).catch((error) => console.error(`saving session ${state.spec.name} failed:`, error));\n },\n async requestPermission(request, signal) {\n if (!current())\n return { behavior: \"deny\", message: \"the session stopped\" };\n if (state.permissionMode === \"auto\")\n return { behavior: \"allow\", updatedInput: request.input };\n const answer = state.permissionRequests.add(request, signal);\n noteStatus(state);\n return answer.finally(() => noteStatus(state));\n },\n async askQuestions(request, signal) {\n if (!current())\n return null;\n const answer = state.questionRequests.add(request, signal);\n noteStatus(state);\n return answer.finally(() => noteStatus(state));\n },\n subagentStarted(toolUseId, subagentId) {\n if (!current())\n return;\n state.subagents.set(toolUseId, subagentId);\n if (state.subagents.size > SUBAGENTS_KEPT)\n state.subagents.delete(state.subagents.keys().next().value);\n },\n activeChanged() {\n if (!current())\n return;\n if (!state.session?.active())\n state.tools.clear();\n noteStatus(state);\n },\n exited(exit, error) {\n if (!current())\n return;\n state.running = null;\n state.session = null;\n state.exit = exit;\n state.failure = error ? `${state.spec.backend} exited: ${error.message}` : null;\n state.tools.clear();\n if (error)\n console.error(`session ${state.spec.name} exited:`, error);\n if (state.turnOpen) {\n broadcast(state, {\n type: \"result\",\n subtype: \"error_during_execution\",\n duration_ms: 0,\n duration_api_ms: 0,\n is_error: true,\n num_turns: 0,\n stop_reason: null,\n total_cost_usd: 0,\n usage: resultUsage(),\n modelUsage: {},\n permission_denials: [],\n errors: [`the agent exited${error ? `: ${error.message}` : \"\"}`],\n uuid: crypto.randomUUID(),\n session_id: state.nativeId ?? \"\"\n });\n }\n noteStatus(state);\n dropRequests(state, \"the agent exited\").catch((error) => console.error(`dropping ${state.spec.name}'s requests failed:`, error));\n }\n };\n }\n function ensureRunning(state) {\n if (state.running)\n return state.running;\n const generation = ++state.generation;\n state.startedAt = Date.now();\n state.failure = null;\n const backend = state.spec.backend;\n const starting = ensureBackend(state).then(() => requireCwd(state.spec)).then(() => environmentOf(state)).then((env) => config.backends[backend].start(contextFor(state, generation, env)).catch((error) => {\n throw new Error(`${backend} didn't start: ${error.message}`);\n }));\n state.running = starting;\n noteStatus(state);\n starting.then((session) => {\n if (state.generation === generation)\n state.session = session;\n noteStatus(state);\n }, (error) => {\n if (state.generation === generation) {\n state.running = null;\n state.failure = error.message;\n }\n noteStatus(state);\n });\n return starting;\n }\n async function closeSession(state) {\n const running = state.running;\n state.generation++;\n state.running = null;\n state.session = null;\n state.exit = null;\n state.failure = null;\n state.tools.clear();\n state.turnOpen = false;\n noteStatus(state);\n await dropRequests(state, \"the session stopped\");\n if (running)\n await (await running.catch(() => null))?.close();\n }\n async function cancelLogin(state) {\n if (!state.login)\n return;\n state.login.process.cancel();\n state.login = null;\n const auth = await config.storage.readAuth(state.spec.name);\n await saveAuth(state, { login: null, error: auth?.error ?? null });\n }\n async function listBackends(agentName) {\n requireAgent(agentName);\n return Promise.all(AGENT_BACKENDS.map(async (backend) => ({ backend, ...await config.backends[backend].installed(config.installDir) })));\n }\n async function install(agentName, backend) {\n requireAgent(agentName);\n await runInstall(backend, (await config.backends[backend].installed(config.installDir)).pinned);\n }\n async function createSession(agentName, session) {\n requireAgent(agentName);\n const spec = validSession(session, config.workspace ?? dimensionLayout.workspacePath());\n const existing = sessions.get(spec.name);\n if (existing) {\n const differing = differences(existing.spec, spec);\n if (differing.length > 0)\n throw new AgentConflictError(conflictMessage(spec.name, differing));\n return info(existing);\n }\n requireCwd(spec);\n const record = await config.storage.insertSession({ spec, nativeId: null, model: null });\n const state = await newState(record);\n sessions.set(spec.name, state);\n noteStatus(state);\n return info(state);\n }\n async function listSessions() {\n return Promise.all([...sessions.values()].map(info));\n }\n async function updateSession(ref, update) {\n const state = requireSession(ref);\n if (update.backend !== undefined && update.backend !== state.spec.backend) {\n throw new AgentConflictError(`session ${ref.sessionName}'s backend can't change: its conversation is ${state.spec.backend}'s; stop the session and create it again with ${update.backend}`);\n }\n const spec = {\n ...state.spec,\n memoryLimit: update.memoryLimit ?? state.spec.memoryLimit,\n cpuLimit: update.cpuLimit ?? state.spec.cpuLimit,\n cwd: update.cwd ?? state.spec.cwd,\n env: update.env ?? state.spec.env\n };\n const restarts = spec.cwd !== state.spec.cwd || JSON.stringify(spec.env ?? {}) !== JSON.stringify(state.spec.env ?? {});\n if (restarts)\n requireCwd(spec);\n state.spec = spec;\n await saveRecord(state);\n if (restarts)\n await closeSession(state);\n return info(state);\n }\n async function stopSession(ref) {\n requireAgent(ref.agentName);\n const state = sessions.get(ref.sessionName);\n if (!state)\n return;\n failPending(state, \"the session stopped\");\n sessions.delete(ref.sessionName);\n await closeSession(state);\n await state.statusSaving;\n state.login?.process.cancel();\n state.login = null;\n state.listeners.clear();\n await config.storage.removeSession(state.spec.name);\n }\n async function sendMessage(ref, sent) {\n const state = requireSession(ref);\n if (!sent.uuid)\n return deliver(state, sent);\n const uuid = sent.uuid;\n const underway = state.sending.get(uuid);\n if (underway)\n return underway;\n const sending = (async () => {\n const accepted = await acceptedOf(state);\n if (accepted.has(uuid))\n return;\n await deliver(state, sent);\n accepted.add(uuid);\n if (accepted.size > ACCEPTED_KEPT)\n accepted.delete(accepted.values().next().value);\n })();\n state.sending.set(uuid, sending);\n try {\n await sending;\n } finally {\n state.sending.delete(uuid);\n }\n }\n async function acceptedOf(state) {\n if (state.accepted)\n return state.accepted;\n const seeded = new Set;\n const transcript = await transcriptOf(state);\n const page = transcript ? await transcript.page(null, ACCEPTED_SEED_PAGE_SIZE) : null;\n for (const message of page?.messages ?? [])\n if (message.type === \"user\" && isPrompt2(message.message.content))\n seeded.add(message.uuid);\n state.accepted ??= seeded;\n return state.accepted;\n }\n async function deliver(state, sent) {\n if (statusOf(state).state === \"idle\" && trustChangedSince(state.startedAt))\n await closeSession(state);\n state.pending.push({ ...sent, uuid: sent.uuid ?? crypto.randomUUID(), session_id: state.nativeId ?? \"\" });\n drain(state);\n }\n function drain(state) {\n if (state.draining)\n return;\n const current = () => sessions.get(state.spec.name) === state;\n state.draining = (async () => {\n while (state.pending.length > 0 && current()) {\n let running;\n try {\n running = await ensureRunning(state);\n } catch (error) {\n failPending(state, error.message);\n return;\n }\n if (state.session !== running)\n continue;\n const message = state.pending.shift();\n if (!message)\n return;\n state.handing = message;\n try {\n await running.sendMessage(message);\n } catch (error) {\n failPrompt(state, message, error.message);\n continue;\n } finally {\n state.handing = null;\n }\n broadcast(state, { ...message, timestamp: message.timestamp ?? new Date().toISOString() });\n }\n })().finally(() => {\n state.draining = null;\n if (state.pending.length > 0 && current())\n drain(state);\n });\n }\n function failPrompt(state, message, error) {\n state.accepted?.delete(message.uuid);\n broadcast(state, { type: \"prompt_failed\", uuid: message.uuid, error });\n }\n function failPending(state, error) {\n for (const message of state.pending.splice(0))\n failPrompt(state, message, error);\n }\n async function cancel(ref) {\n const state = requireSession(ref);\n failPending(state, \"the turn was interrupted\");\n await dropRequests(state, \"the turn was interrupted\");\n if (state.session)\n await state.session.cancel();\n }\n async function getSessionStatus(ref) {\n return currentStatus(requireSession(ref));\n }\n async function getLastUserMessages(ref) {\n const state = requireSession(ref);\n const waiting = [...state.handing ? [state.handing] : [], ...state.pending];\n const listed = state.session ? await state.session.getLastUserMessages() : await lastStoredPrompt(state).then((stored) => stored ? [stored] : []);\n const known = new Set(listed.map(({ message }) => message.uuid));\n return [...listed, ...waiting.filter((message) => !known.has(message.uuid)).map((message) => ({ message, queued: true }))];\n }\n async function subscribe(ref, afterId, emit) {\n const state = requireSession(ref);\n let held = [];\n const listener = (message) => held ? held.push(message) : emit(message);\n const status = currentStatus(state);\n state.listeners.add(listener);\n emit({ type: \"session_status\", status });\n const unsubscribe = () => void state.listeners.delete(listener);\n try {\n const replayed = new Set;\n const transcript = await transcriptOf(state);\n const found = transcript ? await transcript.replay(afterId, (message) => {\n replayed.add(message.uuid);\n emit(message);\n }) : afterId === undefined;\n if (!found) {\n throw new AgentInvalidError(`no message ${afterId} in session ${ref.sessionName}: after_id is the uuid of a user or assistant message (partial output and results aren't stored)`);\n }\n for (const message of held)\n if (!(\"uuid\" in message) || !replayed.has(message.uuid))\n emit(message);\n held = null;\n } catch (error) {\n unsubscribe();\n throw error;\n }\n return unsubscribe;\n }\n async function getTranscript(ref, request = {}) {\n const state = requireSession(ref);\n if (request.toolUseId !== undefined)\n return transcriptPage(await subagentTranscriptOf(state, request.toolUseId), request);\n return transcriptPage(await transcriptOf(state), request);\n }\n async function getUsage(ref) {\n const state = requireSession(ref);\n const source = await transcriptSource(state);\n return source ? config.backends[state.spec.backend].usage(source) : [];\n }\n async function listPermissionRequests(ref) {\n return requireSession(ref).permissionRequests.list();\n }\n async function respondPermissionRequest(ref, requestId, result) {\n const state = requireSession(ref);\n if (!await state.permissionRequests.answer(requestId, result))\n throw new AgentNotFoundError(`no pending permission request ${requestId}`);\n }\n async function listQuestionRequests(ref) {\n return requireSession(ref).questionRequests.list();\n }\n async function respondQuestionRequest(ref, requestId, response) {\n const state = requireSession(ref);\n if (!await state.questionRequests.answer(requestId, response)) {\n throw new AgentNotFoundError(`no pending question request ${requestId}`);\n }\n }\n async function listModels(ref) {\n return (await ensureRunning(requireSession(ref))).listModels();\n }\n async function setModel(ref, model) {\n const state = requireSession(ref);\n state.model = model;\n await saveRecord(state);\n if (state.running)\n await (await state.running).setModel(model);\n }\n async function setPermissionMode(ref, mode) {\n const state = requireSession(ref);\n state.permissionMode = mode;\n await saveRecord(state);\n if (state.running)\n await (await state.running).setPermissionMode(mode);\n if (mode !== \"auto\")\n return;\n for (const request of await state.permissionRequests.list())\n await state.permissionRequests.answer(request.id, { behavior: \"allow\", updatedInput: request.input });\n }\n async function listBackgroundTasks(ref) {\n const state = requireSession(ref);\n return state.running ? (await state.running).listBackgroundTasks() : [];\n }\n async function stopTask(ref, taskId) {\n const state = requireSession(ref);\n if (!state.running)\n throw new AgentNotFoundError(`no background task ${taskId}: session ${ref.sessionName}'s CLI isn't running`);\n await (await state.running).stopTask(taskId);\n }\n async function login(ref, gatewaySignedIn) {\n const state = requireSession(ref);\n if (state.login)\n return authStatus(state);\n await ensureBackend(state);\n if (await config.backends[state.spec.backend].auth.signedIn(await authEnvironment(state)))\n return { state: \"signedIn\" };\n const login = config.backends[state.spec.backend].auth.login(await authEnvironment(state), gatewaySignedIn);\n const entry = { process: login, settled: Promise.resolve() };\n state.login = entry;\n await saveAuth(state, { login: null, error: null });\n entry.settled = watchLogin(state, login);\n try {\n const prompt = await Promise.race([\n login.prompt,\n Bun.sleep(LOGIN_PROMPT_TIMEOUT_MS).then(() => Promise.reject(new Error(\"the login didn't show where to sign in\")))\n ]);\n if (state.login === entry)\n await saveAuth(state, { login: { state: \"pending\", ...prompt }, error: null });\n } catch (error) {\n if (state.login === entry)\n await cancelLogin(state);\n await saveAuth(state, { login: null, error: error.message });\n return authStatus(state);\n }\n return settleLogin(state, entry.settled, gatewaySignedIn ? LOGIN_COMPLETION_TIMEOUT_MS : 0);\n }\n async function submitAuthCode(ref, code) {\n const state = requireSession(ref);\n const login = state.login;\n const prompt = (await config.storage.readAuth(ref.sessionName))?.login;\n if (!login || !prompt)\n throw new AgentInvalidError(`session ${ref.sessionName} has no sign-in waiting`);\n if (!prompt.needsCode)\n throw new AgentInvalidError(`session ${ref.sessionName}'s sign-in takes no code: finish it at ${prompt.url}`);\n login.process.submitCode(code);\n return settleLogin(state, login.settled, LOGIN_COMPLETION_TIMEOUT_MS);\n }\n async function close() {\n for (const state of sessions.values())\n failPending(state, \"the agent's worker stopped\");\n await Promise.all([...sessions.values()].map(closeSession));\n }\n return {\n load: loadSessions,\n close,\n install,\n listBackends,\n createSession,\n listSessions,\n updateSession,\n stopSession,\n sendMessage,\n cancel,\n getSessionStatus,\n getLastUserMessages,\n subscribe,\n getTranscript,\n getUsage,\n listPermissionRequests,\n respondPermissionRequest,\n listQuestionRequests,\n respondQuestionRequest,\n listModels,\n setModel,\n setPermissionMode,\n listBackgroundTasks,\n stopTask,\n getAuthStatus: async (ref) => authStatus(requireSession(ref)),\n login,\n submitAuthCode\n };\n}\nvar LAST_PROMPT_PAGE_SIZE = 50;\nvar SUBAGENTS_KEPT = 1000;\nvar ACCEPTED_KEPT = 500;\nvar ACCEPTED_SEED_PAGE_SIZE = 100;\nfunction isPrompt2(content) {\n if (typeof content === \"string\")\n return true;\n return Array.isArray(content) && content.some((block) => block.type === \"text\");\n}\nvar LOGIN_PROMPT_TIMEOUT_MS = 30000;\nvar LOGIN_COMPLETION_TIMEOUT_MS = 60000;\nfunction sameStatus(a, b) {\n const { since: _a, ...first } = a;\n const { since: _b, ...second } = b;\n return JSON.stringify(first) === JSON.stringify(second);\n}\nfunction validSession(spec, workspace) {\n try {\n checkName(spec.name);\n } catch (error) {\n if (error instanceof InvalidNameError)\n throw new AgentInvalidError(`invalid session name: ${error.message}`);\n throw error;\n }\n return { ...spec, cwd: spec.cwd ?? workspace };\n}\nfunction requireCwd(spec) {\n const cwd = spec.cwd ?? process.cwd();\n let directory = false;\n try {\n directory = statSync(cwd).isDirectory();\n } catch {}\n if (!directory)\n throw new AgentInvalidError(`session ${spec.name}'s cwd ${cwd} isn't a directory in the dimension`);\n}\nfunction differences(a, b) {\n const fields = [\"backend\", \"memoryLimit\", \"cpuLimit\", \"cwd\", \"env\"];\n return fields.filter((field) => JSON.stringify(a[field] ?? (field === \"env\" ? {} : null)) !== JSON.stringify(b[field] ?? (field === \"env\" ? {} : null)));\n}\nfunction conflictMessage(sessionName, differing) {\n const updatable = differing.filter((field) => field !== \"backend\");\n const how = [\n ...updatable.length > 0 ? [`updateSession changes its ${updatable.join(\", \")}`] : [],\n ...differing.includes(\"backend\") ? [\"its backend changes only by stopping the session and creating it again\"] : []\n ];\n return `session ${sessionName} already exists with a different ${differing.join(\", \")}: ${how.join(\"; \")}`;\n}\n\n// agent/session/main.ts\nfunction required(name) {\n const value = process.env[name];\n if (!value)\n throw new Error(`${name} is not set`);\n return value;\n}\nvar agentName = required(\"HYB_AGENT\");\nvar build = required(\"HYB_WORKER_BUILD\");\nvar protocol = Number(process.env.HYB_WORKER_PROTOCOL ?? WORKER_PROTOCOL);\nvar files = createLocalService({ kind: \"local\" });\nvar storage = createSessionStorage(files, agentName);\nvar service = createWorkerService({\n agentName,\n storage,\n installDir: required(\"HYB_INSTALL_DIR\"),\n files: sandboxFiles(files),\n trustBundle: process.env.NODE_EXTRA_CA_CERTS,\n backends: SESSION_BACKENDS\n});\nvar socketPath = await storage.socketPath();\nvar readyPath = workerReadyPath(dirname2(socketPath), agentName);\nvar server = createWorkerServer(service, { socketPath, health: { agentName, build, protocol }, onShutdown: shutdown });\nfunction shutdown() {\n server.stop();\n rm3(readyPath, { force: true }).catch(() => {});\n service.close().catch((error) => console.error(\"stopping the sessions failed:\", error)).finally(() => process.exit(0));\n}\nfor (const signal of [\"SIGTERM\", \"SIGINT\"])\n process.once(signal, shutdown);\nawait service.load();\nawait server.start();\nvar socketOwner = process.env.HYB_SOCKET_OWNER;\nif (socketOwner) {\n const [uid, gid] = socketOwner.split(\":\").map(Number);\n await chown(socketPath, uid, gid);\n}\nawait writeFile3(readyPath, `${process.pid} ${build} ${protocol}\n`);\nconsole.log(`session worker for agent ${agentName} (build ${build}) listening on ${socketPath}`);\n";
|