@oh-my-pi/pi-catalog 18.4.3 → 18.4.4

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.
@@ -71,6 +71,7 @@ const codexModelEntrySchema = type({
71
71
  "use_responses_lite?": "unknown",
72
72
  "tool_mode?": "unknown",
73
73
  "available_access_programs?": "unknown",
74
+ "service_tiers?": "unknown",
74
75
  });
75
76
 
76
77
  const codexModelsResponseSchema = type({
@@ -303,6 +304,8 @@ interface ParsedCodexModelEntry {
303
304
  useResponsesLite: boolean;
304
305
  toolMode: boolean;
305
306
  priority: number;
307
+ /** Advertised tier ids; `undefined` when the entry has no `service_tiers` array. */
308
+ serviceTiers: string[] | undefined;
306
309
  }
307
310
 
308
311
  function parseCodexModelEntry(entry: unknown): ParsedCodexModelEntry | null {
@@ -332,6 +335,17 @@ function parseCodexModelEntry(entry: unknown): ParsedCodexModelEntry | null {
332
335
  }
333
336
  }
334
337
 
338
+ // codex-rs `ModelServiceTier { id, name, description }`; only the id reaches the wire.
339
+ // An explicit empty array is kept: it means the model offers no optional tier.
340
+ let serviceTiers: string[] | undefined;
341
+ if (Array.isArray(payload.service_tiers)) {
342
+ serviceTiers = [];
343
+ for (const tier of payload.service_tiers) {
344
+ const id = tier !== null && typeof tier === "object" && "id" in tier ? toNonEmptyString(tier.id) : null;
345
+ if (id && !serviceTiers.includes(id)) serviceTiers.push(id);
346
+ }
347
+ }
348
+
335
349
  return {
336
350
  slug,
337
351
  cyberPrograms,
@@ -344,6 +358,7 @@ function parseCodexModelEntry(entry: unknown): ParsedCodexModelEntry | null {
344
358
  useResponsesLite: toBoolean(payload.use_responses_lite) === true,
345
359
  toolMode: payload.tool_mode === "code_mode_only",
346
360
  priority: toFiniteNumber(payload.priority) ?? Number.MAX_SAFE_INTEGER,
361
+ serviceTiers,
347
362
  };
348
363
  }
349
364
 
@@ -408,6 +423,7 @@ function buildNormalizedCodexModel(
408
423
  ...(parsed.preferWebsockets ? { preferWebsockets: true } : {}),
409
424
  ...(parsed.useResponsesLite ? { useResponsesLite: true } : {}),
410
425
  ...(parsed.toolMode ? { toolMode: "code_mode_only" as const } : {}),
426
+ ...(parsed.serviceTiers !== undefined ? { serviceTiers: parsed.serviceTiers } : {}),
411
427
  ...(parsed.priority !== Number.MAX_SAFE_INTEGER ? { priority: parsed.priority } : {}),
412
428
  },
413
429
  };
package/src/hosts.ts CHANGED
@@ -169,6 +169,53 @@ export function isAzureDeploymentsUrl(baseUrl: string): boolean {
169
169
  return baseUrl.includes("/deployments/");
170
170
  }
171
171
 
172
+ // Bedrock inference endpoints, per AWS's endpoint and PrivateLink docs:
173
+ // - bedrock-runtime: `bedrock-runtime.<region>.amazonaws.com`, FIPS `bedrock-runtime-fips.<region>.amazonaws.com`
174
+ // - bedrock-mantle: `bedrock-mantle.<region>.api.aws`; the bundled provider keeps a `{region}`
175
+ // template until request preparation fills it in
176
+ // - PrivateLink endpoint-specific names for either service, Regional or zonal:
177
+ // `<vpce-id>[-<az>].<service>.<region>.vpce.amazonaws.com`
178
+ // With private DNS enabled, a VPC endpoint answers on the public names above.
179
+ const BEDROCK_PUBLIC_HOST =
180
+ /^(?:(?<runtime>bedrock-runtime(?:-fips)?)\.[a-z0-9-]+\.amazonaws\.com|bedrock-mantle\.(?:[a-z0-9-]+|\{region\})\.api\.aws)$/;
181
+ const BEDROCK_PRIVATELINK_HOST =
182
+ /^vpce-[a-z0-9-]+\.(?:(?<runtime>bedrock-runtime(?:-fips)?)|bedrock-mantle)\.[a-z0-9-]+\.vpce\.amazonaws\.com$/;
183
+
184
+ function hasPathPrefix(pathname: string, prefix: string): boolean {
185
+ return pathname === prefix || pathname.startsWith(`${prefix}/`);
186
+ }
187
+
188
+ /**
189
+ * Amazon Bedrock API route on a bedrock-runtime or bedrock-mantle endpoint
190
+ * (public, FIPS, or PrivateLink hostname):
191
+ * - `anthropic`: the Anthropic Messages API under `/anthropic`.
192
+ * - `openai`: the OpenAI-compatible APIs under `/openai`, plus Mantle's
193
+ * documented `/v1` base (`https://bedrock-mantle.<region>.api.aws/v1`).
194
+ *
195
+ * Hostnames are parsed strictly so proxies that embed these hosts in a path
196
+ * do not match.
197
+ */
198
+ export function isBedrockRouteUrl(baseUrl: string | undefined, route: "openai" | "anthropic"): boolean {
199
+ if (!baseUrl) return false;
200
+ let url: URL;
201
+ try {
202
+ url = new URL(baseUrl);
203
+ } catch {
204
+ return false;
205
+ }
206
+ if (url.protocol !== "https:") return false;
207
+ const host = BEDROCK_PUBLIC_HOST.exec(url.hostname) ?? BEDROCK_PRIVATELINK_HOST.exec(url.hostname);
208
+ if (!host) return false;
209
+ if (route === "anthropic") return hasPathPrefix(url.pathname, "/anthropic");
210
+ const isMantle = host.groups?.runtime === undefined;
211
+ return hasPathPrefix(url.pathname, "/openai") || (isMantle && hasPathPrefix(url.pathname, "/v1"));
212
+ }
213
+
214
+ /** Amazon Bedrock's OpenAI-compatible routes; see {@link isBedrockRouteUrl}. */
215
+ export function isBedrockOpenAIUrl(baseUrl: string | undefined): boolean {
216
+ return isBedrockRouteUrl(baseUrl, "openai");
217
+ }
218
+
172
219
  /** Alibaba DashScope consumer `compatible-mode` endpoint (rejects multimodal arrays for some text-only SKUs). */
173
220
  export function isDashscopeCompatibleModeUrl(baseUrl: string): boolean {
174
221
  const normalized = baseUrl.toLowerCase();