@@ -1226,6 +1226,76 @@ def model_token_limits(model_id: str) -> dict[str, int] | None:
12261226 return None
12271227
12281228
1229+ # Pi treats every custom model without explicit metadata as 128k context / 4k
1230+ # output. Gateway ids are custom ids (not Pi's built-ins), so preserve the
1231+ # upstream windows explicitly. Entries are ordered most-specific first after
1232+ # normalizing dotted OpenAI ids and hyphenated Databricks ids to one form.
1233+ # GPT-5.6 Sol/Terra/Luna support the opt-in 1.05M window; 272k is merely Pi's
1234+ # built-in short-context pricing default, not the model's hard context limit.
1235+ _GPT_TOKEN_LIMITS : tuple [tuple [str , dict [str , int ]], ...] = (
1236+ ("gpt-5-6-sol" , {"context" : 1_050_000 , "output" : 128_000 }),
1237+ ("gpt-5-6-terra" , {"context" : 1_050_000 , "output" : 128_000 }),
1238+ ("gpt-5-6-luna" , {"context" : 1_050_000 , "output" : 128_000 }),
1239+ ("gpt-5-5-pro" , {"context" : 1_050_000 , "output" : 128_000 }),
1240+ ("gpt-5-4-pro" , {"context" : 1_050_000 , "output" : 128_000 }),
1241+ ("gpt-5-5" , {"context" : 272_000 , "output" : 128_000 }),
1242+ ("gpt-5-4-mini" , {"context" : 400_000 , "output" : 128_000 }),
1243+ ("gpt-5-4-nano" , {"context" : 400_000 , "output" : 128_000 }),
1244+ ("gpt-5-4" , {"context" : 272_000 , "output" : 128_000 }),
1245+ ("gpt-5" , {"context" : 400_000 , "output" : 128_000 }),
1246+ ("gpt-4-1" , {"context" : 1_047_576 , "output" : 32_768 }),
1247+ ("gpt-4o" , {"context" : 128_000 , "output" : 16_384 }),
1248+ ("gpt-4-turbo" , {"context" : 128_000 , "output" : 4_096 }),
1249+ ("gpt-4" , {"context" : 8_192 , "output" : 8_192 }),
1250+ )
1251+ _GPT_FALLBACK_LIMITS = {"context" : 128_000 , "output" : 16_384 }
1252+
1253+
1254+ def _normalized_foundation_model_id (model_id : str ) -> str :
1255+ """Strip route prefixes and normalize dotted versions to hyphens."""
1256+ tail = model_id .split ("/" )[- 1 ]
1257+ if tail .startswith ("system.ai." ):
1258+ tail = tail [len ("system.ai." ) :]
1259+ if tail .startswith ("databricks-" ):
1260+ tail = tail [len ("databricks-" ) :]
1261+ return tail .lower ().replace ("." , "-" )
1262+
1263+
1264+ def gpt_model_token_limits (model_id : str ) -> dict [str , int ]:
1265+ """Return Pi metadata limits for a GPT (codex/openai) gateway model."""
1266+ tail = _normalized_foundation_model_id (model_id )
1267+ for family , limits in _GPT_TOKEN_LIMITS :
1268+ if tail .startswith (family ):
1269+ return dict (limits )
1270+ return dict (_GPT_FALLBACK_LIMITS )
1271+
1272+
1273+ _CLAUDE_FALLBACK_LIMITS = {"context" : 200_000 , "output" : 64_000 }
1274+
1275+
1276+ def claude_model_token_limits (model_id : str ) -> dict [str , int ]:
1277+ """Return Pi metadata limits for a Claude gateway model.
1278+
1279+ Claude gateway ids are custom to Pi, so omitting these fields silently
1280+ applies Pi's 128k/4k custom-model defaults. Current 1M families mirror
1281+ Anthropic's model metadata: Opus >=4.6 and Sonnet >=4.5. Sonnet 4.5 has a
1282+ 64k output cap; later 1M models have 128k output.
1283+ """
1284+ tail = _normalized_foundation_model_id (model_id )
1285+ match = re .match (r"claude-(opus|sonnet|haiku)-(\d+)-(\d+)" , tail )
1286+ if not match :
1287+ return dict (_CLAUDE_FALLBACK_LIMITS )
1288+ family , major_raw , minor_raw = match .groups ()
1289+ version = (int (major_raw ), int (minor_raw ))
1290+ if family == "opus" and version >= (4 , 6 ):
1291+ return {"context" : 1_000_000 , "output" : 128_000 }
1292+ if family == "sonnet" and version >= (4 , 6 ):
1293+ return {"context" : 1_000_000 , "output" : 128_000 }
1294+ if family == "sonnet" and version >= (4 , 5 ):
1295+ return {"context" : 1_000_000 , "output" : 64_000 }
1296+ return dict (_CLAUDE_FALLBACK_LIMITS )
1297+
1298+
12291299def _model_service_id (service : dict ) -> str | None :
12301300 """Extract the `system.ai.<model-name>` id from one model-service entry.
12311301
0 commit comments