@@ -1184,6 +1184,76 @@ def model_token_limits(model_id: str) -> dict[str, int] | None:
11841184 return None
11851185
11861186
1187+ # Pi treats every custom model without explicit metadata as 128k context / 4k
1188+ # output. Gateway ids are custom ids (not Pi's built-ins), so preserve the
1189+ # upstream windows explicitly. Entries are ordered most-specific first after
1190+ # normalizing dotted OpenAI ids and hyphenated Databricks ids to one form.
1191+ # GPT-5.6 Sol/Terra/Luna support the opt-in 1.05M window; 272k is merely Pi's
1192+ # built-in short-context pricing default, not the model's hard context limit.
1193+ _GPT_TOKEN_LIMITS : tuple [tuple [str , dict [str , int ]], ...] = (
1194+ ("gpt-5-6-sol" , {"context" : 1_050_000 , "output" : 128_000 }),
1195+ ("gpt-5-6-terra" , {"context" : 1_050_000 , "output" : 128_000 }),
1196+ ("gpt-5-6-luna" , {"context" : 1_050_000 , "output" : 128_000 }),
1197+ ("gpt-5-5-pro" , {"context" : 1_050_000 , "output" : 128_000 }),
1198+ ("gpt-5-4-pro" , {"context" : 1_050_000 , "output" : 128_000 }),
1199+ ("gpt-5-5" , {"context" : 272_000 , "output" : 128_000 }),
1200+ ("gpt-5-4-mini" , {"context" : 400_000 , "output" : 128_000 }),
1201+ ("gpt-5-4-nano" , {"context" : 400_000 , "output" : 128_000 }),
1202+ ("gpt-5-4" , {"context" : 272_000 , "output" : 128_000 }),
1203+ ("gpt-5" , {"context" : 400_000 , "output" : 128_000 }),
1204+ ("gpt-4-1" , {"context" : 1_047_576 , "output" : 32_768 }),
1205+ ("gpt-4o" , {"context" : 128_000 , "output" : 16_384 }),
1206+ ("gpt-4-turbo" , {"context" : 128_000 , "output" : 4_096 }),
1207+ ("gpt-4" , {"context" : 8_192 , "output" : 8_192 }),
1208+ )
1209+ _GPT_FALLBACK_LIMITS = {"context" : 128_000 , "output" : 16_384 }
1210+
1211+
1212+ def _normalized_foundation_model_id (model_id : str ) -> str :
1213+ """Strip route prefixes and normalize dotted versions to hyphens."""
1214+ tail = model_id .split ("/" )[- 1 ]
1215+ if tail .startswith ("system.ai." ):
1216+ tail = tail [len ("system.ai." ) :]
1217+ if tail .startswith ("databricks-" ):
1218+ tail = tail [len ("databricks-" ) :]
1219+ return tail .lower ().replace ("." , "-" )
1220+
1221+
1222+ def gpt_model_token_limits (model_id : str ) -> dict [str , int ]:
1223+ """Return Pi metadata limits for a GPT (codex/openai) gateway model."""
1224+ tail = _normalized_foundation_model_id (model_id )
1225+ for family , limits in _GPT_TOKEN_LIMITS :
1226+ if tail .startswith (family ):
1227+ return dict (limits )
1228+ return dict (_GPT_FALLBACK_LIMITS )
1229+
1230+
1231+ _CLAUDE_FALLBACK_LIMITS = {"context" : 200_000 , "output" : 64_000 }
1232+
1233+
1234+ def claude_model_token_limits (model_id : str ) -> dict [str , int ]:
1235+ """Return Pi metadata limits for a Claude gateway model.
1236+
1237+ Claude gateway ids are custom to Pi, so omitting these fields silently
1238+ applies Pi's 128k/4k custom-model defaults. Current 1M families mirror
1239+ Anthropic's model metadata: Opus >=4.6 and Sonnet >=4.5. Sonnet 4.5 has a
1240+ 64k output cap; later 1M models have 128k output.
1241+ """
1242+ tail = _normalized_foundation_model_id (model_id )
1243+ match = re .match (r"claude-(opus|sonnet|haiku)-(\d+)-(\d+)" , tail )
1244+ if not match :
1245+ return dict (_CLAUDE_FALLBACK_LIMITS )
1246+ family , major_raw , minor_raw = match .groups ()
1247+ version = (int (major_raw ), int (minor_raw ))
1248+ if family == "opus" and version >= (4 , 6 ):
1249+ return {"context" : 1_000_000 , "output" : 128_000 }
1250+ if family == "sonnet" and version >= (4 , 6 ):
1251+ return {"context" : 1_000_000 , "output" : 128_000 }
1252+ if family == "sonnet" and version >= (4 , 5 ):
1253+ return {"context" : 1_000_000 , "output" : 64_000 }
1254+ return dict (_CLAUDE_FALLBACK_LIMITS )
1255+
1256+
11871257def _model_service_id (service : dict ) -> str | None :
11881258 """Extract the `system.ai.<model-name>` id from one model-service entry.
11891259
0 commit comments