mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-21 06:12:17 +03:00
fix(sse): retire Microsoft Designer Web runtime (#11720)
Merged via /merge-batch (v3.8.51 provenance sweep). Boarded and validated together with the batch's other retirement PRs in a combined worktree — full gate suite green. This PR's own conflicts (against #11711's EdgeTTS retirement, both touching test-masking-allowlist.json and the "Image / video / audio generation" README bullet) were reconciled additively/subtractively (both retirements now correctly reflected), re-validated with this PR's own 62 focused tests, and pushed before merge. Thank you.
This commit is contained in:
committed by
GitHub
parent
dd8575eb2c
commit
890cbfbfde
@@ -8,8 +8,8 @@ import { getWebSessionCredentialRequirement } from "@/shared/providers/webSessio
|
||||
* `validateWebCookieProvider` probe (`src/lib/providers/validation/webCookie.ts`).
|
||||
*
|
||||
* `WEB_SESSION_CREDENTIAL_REQUIREMENTS` currently marks more providers as `kind: "token"`
|
||||
* than have a matching validator (e.g. hailuo-web, microsoft-designer-web, t3-chat-web,
|
||||
* promptql). Those fall through to `validateWebCookieProvider`'s generic probe, which
|
||||
* than have a matching validator (e.g. hailuo-web, t3-chat-web, promptql). Those fall
|
||||
* through to `validateWebCookieProvider`'s generic probe, which
|
||||
* sends the stored credential as a `Cookie` header and treats most non-401/403 responses
|
||||
* as valid — the wrong wire format for a token-authenticated provider, so an invalid
|
||||
* token can be reported as a healthy connection. Keep this set in sync with
|
||||
@@ -30,7 +30,6 @@ export function shouldUseApiKeyConnectionTest(authType: unknown, providerId: unk
|
||||
if (authType !== "cookie") return false;
|
||||
if (getWebSessionCredentialRequirement(providerId)?.kind !== "token") return false;
|
||||
return (
|
||||
typeof providerId === "string" &&
|
||||
TOKEN_AWARE_VALIDATED_WEB_SESSION_PROVIDERS.has(providerId)
|
||||
typeof providerId === "string" && TOKEN_AWARE_VALIDATED_WEB_SESSION_PROVIDERS.has(providerId)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
extractImageEditInputFromJson,
|
||||
validateCodexImageEditReferences,
|
||||
} from "@/lib/images/imageRouteModel";
|
||||
import { isMicrosoftDesignerWebProviderRetiredError } from "@/shared/constants/designerWebRetirement";
|
||||
import { resolveProxyForConnection } from "@/lib/localDb";
|
||||
import { runWithProxyContext } from "@omniroute/open-sse/utils/proxyFetch.ts";
|
||||
import { isCodexFreePlan } from "@omniroute/open-sse/executors/codex/tools.ts";
|
||||
@@ -363,7 +364,15 @@ async function postHandler(request: Request, _context?: unknown) {
|
||||
|
||||
// Resolve combo/alias, custom-provider prefix, and built-in ids consistently with
|
||||
// /v1/images/generations (#3215).
|
||||
const resolvedModel = await resolveImageRouteModel(fullModel);
|
||||
let resolvedModel: string;
|
||||
try {
|
||||
resolvedModel = await resolveImageRouteModel(fullModel);
|
||||
} catch (error) {
|
||||
if (isMicrosoftDesignerWebProviderRetiredError(error)) {
|
||||
return errorResponse(HTTP_STATUS.GONE, error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
const parsed = parseImageModel(resolvedModel);
|
||||
const providerConfig = parsed.provider ? getImageProvider(parsed.provider) : null;
|
||||
// Firefly nano/gpt-image accept multiple reference blobs; other non-Codex stay at 1.
|
||||
|
||||
@@ -23,6 +23,11 @@ import { getComboByName } from "@/lib/db/combos";
|
||||
import { getAllCustomModels } from "@/lib/db/models";
|
||||
import { resolveProxyForConnection } from "@/lib/db/settings";
|
||||
import { resolveImageRouteModel } from "@/lib/images/imageRouteModel";
|
||||
import {
|
||||
isMicrosoftDesignerWebProviderRetiredError,
|
||||
isMicrosoftDesignerWebRetiredProviderId,
|
||||
MICROSOFT_DESIGNER_WEB_RETIRED_MESSAGE,
|
||||
} from "@/shared/constants/designerWebRetirement";
|
||||
import {
|
||||
resolveLocalSyncedEndpointRoute,
|
||||
type LocalSyncedEndpointRoute,
|
||||
@@ -122,21 +127,20 @@ async function postHandler(request, context) {
|
||||
const policy = await enforceApiKeyPolicy(request, body.model);
|
||||
if (policy.rejection) return policy.rejection;
|
||||
|
||||
const modelPrefix = body.model.includes("/")
|
||||
? body.model.slice(0, body.model.indexOf("/"))
|
||||
: body.model;
|
||||
if (isMicrosoftDesignerWebRetiredProviderId(modelPrefix)) {
|
||||
return errorResponse(HTTP_STATUS.GONE, MICROSOFT_DESIGNER_WEB_RETIRED_MESSAGE);
|
||||
}
|
||||
|
||||
// #9239: Detect combo name and divert to full image combo execution.
|
||||
// Checks before resolveImageRouteModel so we skip single-target flattening.
|
||||
if (body.model && typeof body.model === "string" && !body.model.includes("/")) {
|
||||
const combo = await getComboByName(body.model as string);
|
||||
if (combo) {
|
||||
const { executeImageCombo } = await import(
|
||||
"@omniroute/open-sse/services/imageCombo"
|
||||
);
|
||||
return executeImageCombo(
|
||||
body.model as string,
|
||||
body,
|
||||
{ request, policy },
|
||||
startTime,
|
||||
log
|
||||
);
|
||||
const { executeImageCombo } = await import("@omniroute/open-sse/services/imageCombo");
|
||||
return executeImageCombo(body.model as string, body, { request, policy }, startTime, log);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +148,14 @@ async function postHandler(request, context) {
|
||||
// model (`myImg/gpt-image-2`) to its internal `<nodeId>/<model>` form so the
|
||||
// custom-model lookup and handler's resolvedProvider extraction resolve correctly.
|
||||
// Built-in and already-internal ids pass through unchanged. Shared with /images/edits.
|
||||
body.model = await resolveImageRouteModel(body.model);
|
||||
try {
|
||||
body.model = await resolveImageRouteModel(body.model);
|
||||
} catch (error) {
|
||||
if (isMicrosoftDesignerWebProviderRetiredError(error)) {
|
||||
return errorResponse(HTTP_STATUS.GONE, error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Parse model to get provider
|
||||
let { provider, model: requestedModel } = parseImageModel(body.model);
|
||||
@@ -246,7 +257,8 @@ async function postHandler(request, context) {
|
||||
provider,
|
||||
null,
|
||||
syncedEndpointRoute?.connectionIds ?? null,
|
||||
requestedModel );
|
||||
requestedModel
|
||||
);
|
||||
if (!credentials) {
|
||||
return errorResponse(
|
||||
HTTP_STATUS.BAD_REQUEST,
|
||||
@@ -346,7 +358,10 @@ async function postHandler(request, context) {
|
||||
});
|
||||
}
|
||||
|
||||
const errorPayload = toJsonErrorPayload((result as any).error, "Image generation provider error") as {
|
||||
const errorPayload = toJsonErrorPayload(
|
||||
(result as any).error,
|
||||
"Image generation provider error"
|
||||
) as {
|
||||
error?: { message?: string };
|
||||
};
|
||||
const message =
|
||||
|
||||
122
src/lib/db/migrations/164_retire_microsoft_designer_web.sql
Normal file
122
src/lib/db/migrations/164_retire_microsoft_designer_web.sql
Normal file
@@ -0,0 +1,122 @@
|
||||
-- Permanently retire the Microsoft Designer Web reverse-engineered integration.
|
||||
-- Keep connection rows, encrypted credentials, allowlists, and historical usage intact.
|
||||
|
||||
UPDATE provider_connections
|
||||
SET is_active = 0,
|
||||
test_status = 'unavailable',
|
||||
last_error = 'Provider retired from OmniRoute runtime.',
|
||||
last_error_at = COALESCE(last_error_at, CURRENT_TIMESTAMP),
|
||||
last_error_type = 'provider_retired',
|
||||
last_error_source = 'migration:retire-microsoft-designer-web'
|
||||
WHERE lower(trim(provider, ' ' || char(9) || char(10) || char(11) || char(12) || char(13)))
|
||||
IN ('microsoft-designer-web', 'msdesigner');
|
||||
|
||||
UPDATE exclusive_connection_leases
|
||||
SET state = 'INVALIDATED',
|
||||
ended_at = COALESCE(ended_at, CURRENT_TIMESTAMP),
|
||||
end_reason = 'AUTHORIZATION_CHANGED'
|
||||
WHERE state = 'ACTIVE'
|
||||
AND (
|
||||
lower(trim(provider, ' ' || char(9) || char(10) || char(11) || char(12) || char(13)))
|
||||
IN ('microsoft-designer-web', 'msdesigner')
|
||||
OR connection_id IN (
|
||||
SELECT id
|
||||
FROM provider_connections
|
||||
WHERE lower(trim(provider, ' ' || char(9) || char(10) || char(11) || char(12) || char(13)))
|
||||
IN ('microsoft-designer-web', 'msdesigner')
|
||||
)
|
||||
);
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS trg_retire_microsoft_designer_web_provider_insert
|
||||
AFTER INSERT ON provider_connections
|
||||
WHEN lower(trim(NEW.provider, ' ' || char(9) || char(10) || char(11) || char(12) || char(13)))
|
||||
IN ('microsoft-designer-web', 'msdesigner')
|
||||
BEGIN
|
||||
UPDATE provider_connections
|
||||
SET is_active = 0,
|
||||
test_status = 'unavailable',
|
||||
last_error = 'Provider retired from OmniRoute runtime.',
|
||||
last_error_at = COALESCE(last_error_at, CURRENT_TIMESTAMP),
|
||||
last_error_type = 'provider_retired',
|
||||
last_error_source = 'migration:retire-microsoft-designer-web'
|
||||
WHERE id = NEW.id;
|
||||
|
||||
UPDATE exclusive_connection_leases
|
||||
SET state = 'INVALIDATED',
|
||||
ended_at = COALESCE(ended_at, CURRENT_TIMESTAMP),
|
||||
end_reason = 'AUTHORIZATION_CHANGED'
|
||||
WHERE connection_id = NEW.id AND state = 'ACTIVE';
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS trg_retire_microsoft_designer_web_provider_update
|
||||
AFTER UPDATE OF provider, is_active, test_status, last_error, last_error_type, last_error_source
|
||||
ON provider_connections
|
||||
WHEN lower(trim(NEW.provider, ' ' || char(9) || char(10) || char(11) || char(12) || char(13)))
|
||||
IN ('microsoft-designer-web', 'msdesigner')
|
||||
AND (
|
||||
COALESCE(NEW.is_active, 0) <> 0
|
||||
OR COALESCE(NEW.test_status, '') <> 'unavailable'
|
||||
OR COALESCE(NEW.last_error, '') <> 'Provider retired from OmniRoute runtime.'
|
||||
OR COALESCE(NEW.last_error_type, '') <> 'provider_retired'
|
||||
OR COALESCE(NEW.last_error_source, '') <> 'migration:retire-microsoft-designer-web'
|
||||
)
|
||||
BEGIN
|
||||
UPDATE provider_connections
|
||||
SET is_active = 0,
|
||||
test_status = 'unavailable',
|
||||
last_error = 'Provider retired from OmniRoute runtime.',
|
||||
last_error_at = COALESCE(last_error_at, CURRENT_TIMESTAMP),
|
||||
last_error_type = 'provider_retired',
|
||||
last_error_source = 'migration:retire-microsoft-designer-web'
|
||||
WHERE id = NEW.id;
|
||||
|
||||
UPDATE exclusive_connection_leases
|
||||
SET state = 'INVALIDATED',
|
||||
ended_at = COALESCE(ended_at, CURRENT_TIMESTAMP),
|
||||
end_reason = 'AUTHORIZATION_CHANGED'
|
||||
WHERE connection_id = NEW.id AND state = 'ACTIVE';
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS trg_retire_microsoft_designer_web_lease_insert
|
||||
AFTER INSERT ON exclusive_connection_leases
|
||||
WHEN NEW.state = 'ACTIVE'
|
||||
AND (
|
||||
lower(trim(NEW.provider, ' ' || char(9) || char(10) || char(11) || char(12) || char(13)))
|
||||
IN ('microsoft-designer-web', 'msdesigner')
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM provider_connections
|
||||
WHERE id = NEW.connection_id
|
||||
AND lower(trim(provider, ' ' || char(9) || char(10) || char(11) || char(12) || char(13)))
|
||||
IN ('microsoft-designer-web', 'msdesigner')
|
||||
)
|
||||
)
|
||||
BEGIN
|
||||
UPDATE exclusive_connection_leases
|
||||
SET state = 'INVALIDATED',
|
||||
ended_at = COALESCE(ended_at, CURRENT_TIMESTAMP),
|
||||
end_reason = 'AUTHORIZATION_CHANGED'
|
||||
WHERE id = NEW.id AND state = 'ACTIVE';
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS trg_retire_microsoft_designer_web_lease_update
|
||||
AFTER UPDATE OF provider, connection_id, state ON exclusive_connection_leases
|
||||
WHEN NEW.state = 'ACTIVE'
|
||||
AND (
|
||||
lower(trim(NEW.provider, ' ' || char(9) || char(10) || char(11) || char(12) || char(13)))
|
||||
IN ('microsoft-designer-web', 'msdesigner')
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM provider_connections
|
||||
WHERE id = NEW.connection_id
|
||||
AND lower(trim(provider, ' ' || char(9) || char(10) || char(11) || char(12) || char(13)))
|
||||
IN ('microsoft-designer-web', 'msdesigner')
|
||||
)
|
||||
)
|
||||
BEGIN
|
||||
UPDATE exclusive_connection_leases
|
||||
SET state = 'INVALIDATED',
|
||||
ended_at = COALESCE(ended_at, CURRENT_TIMESTAMP),
|
||||
end_reason = 'AUTHORIZATION_CHANGED'
|
||||
WHERE id = NEW.id AND state = 'ACTIVE';
|
||||
END;
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
isMatchingOauthIdentity,
|
||||
} from "./webSessionDedup";
|
||||
import { pickCodexConnectionForUser } from "@/lib/oauth/utils/codexConnectionSelection";
|
||||
import { isMicrosoftDesignerWebRetiredProviderId } from "@/shared/constants/designerWebRetirement";
|
||||
import { reconcileCodexUsageHistory } from "./providers/usageIdentityReconciliation";
|
||||
|
||||
/**
|
||||
@@ -595,6 +596,10 @@ export async function createProviderConnection(data: JsonRecord) {
|
||||
_updateConnectionRow(db, existingId, encryptConnectionFields(persistence));
|
||||
})();
|
||||
backupDbFile("pre-write");
|
||||
if (isMicrosoftDesignerWebRetiredProviderId(merged.provider)) {
|
||||
invalidateDbCache("connections");
|
||||
return getProviderConnectionById(existingId);
|
||||
}
|
||||
return withNullableRateLimitOverrides(
|
||||
withNullableQuotaWindowThresholds(
|
||||
withNullableMaxConcurrent(cleanNulls(merged), merged),
|
||||
@@ -722,6 +727,10 @@ export async function createProviderConnection(data: JsonRecord) {
|
||||
backupDbFile("pre-write");
|
||||
invalidateDbCache("connections"); // Bust connections read cache
|
||||
|
||||
if (isMicrosoftDesignerWebRetiredProviderId(data.provider)) {
|
||||
return getProviderConnectionById(String(connection.id));
|
||||
}
|
||||
|
||||
return withNullableRateLimitOverrides(
|
||||
withNullableQuotaWindowThresholds(
|
||||
withNullableMaxConcurrent(cleanNulls(connection), connection),
|
||||
@@ -967,6 +976,10 @@ export async function updateProviderConnection(id: string, data: JsonRecord) {
|
||||
reorderConnections(db, providerId);
|
||||
}
|
||||
|
||||
if (isMicrosoftDesignerWebRetiredProviderId(merged.provider)) {
|
||||
return getProviderConnectionById(id);
|
||||
}
|
||||
|
||||
return withNullableRateLimitOverrides(
|
||||
withNullableQuotaWindowThresholds(
|
||||
withNullableMaxConcurrent(cleanNulls(merged), merged),
|
||||
|
||||
@@ -19,6 +19,7 @@ import { resolveComboTargets } from "@omniroute/open-sse/services/combo.ts";
|
||||
|
||||
import { getComboByName, getCombos } from "@/lib/db/combos";
|
||||
import { getCachedProviderNodes } from "@/lib/localDb";
|
||||
import { assertMicrosoftDesignerWebProviderAvailable } from "@/shared/constants/designerWebRetirement";
|
||||
|
||||
/**
|
||||
* Rewrite a `prefix/model` custom image model to its internal `<nodeId>/<model>` form.
|
||||
@@ -32,6 +33,7 @@ export async function resolveImageModelPrefix(modelStr: string): Promise<string>
|
||||
if (slash <= 0) return modelStr;
|
||||
|
||||
const prefixPart = modelStr.slice(0, slash);
|
||||
assertMicrosoftDesignerWebProviderAvailable(prefixPart);
|
||||
const rest = modelStr.slice(slash + 1);
|
||||
if (!rest) return modelStr;
|
||||
|
||||
@@ -75,6 +77,8 @@ export async function resolveSingleImageComboTarget(name: string): Promise<strin
|
||||
*/
|
||||
export async function resolveImageRouteModel(modelStr: string): Promise<string> {
|
||||
if (typeof modelStr !== "string" || !modelStr.trim()) return modelStr;
|
||||
const slash = modelStr.indexOf("/");
|
||||
assertMicrosoftDesignerWebProviderAvailable(slash > 0 ? modelStr.slice(0, slash) : modelStr);
|
||||
const parsedModel = parseImageModel(modelStr);
|
||||
const hasSlash = modelStr.includes("/");
|
||||
|
||||
|
||||
33
src/shared/constants/designerWebRetirement.ts
Normal file
33
src/shared/constants/designerWebRetirement.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
export const RETIRED_MICROSOFT_DESIGNER_WEB_PROVIDER_IDS: ReadonlySet<string> = new Set([
|
||||
"microsoft-designer-web",
|
||||
"msdesigner",
|
||||
]);
|
||||
|
||||
export const MICROSOFT_DESIGNER_WEB_RETIRED_MESSAGE =
|
||||
"Provider has been retired from OmniRoute runtime.";
|
||||
|
||||
function normalizeProviderId(providerId: unknown): string {
|
||||
return typeof providerId === "string" ? providerId.trim().toLowerCase() : "";
|
||||
}
|
||||
|
||||
export function isMicrosoftDesignerWebRetiredProviderId(providerId: unknown): boolean {
|
||||
return RETIRED_MICROSOFT_DESIGNER_WEB_PROVIDER_IDS.has(normalizeProviderId(providerId));
|
||||
}
|
||||
|
||||
export function assertMicrosoftDesignerWebProviderAvailable(providerId: unknown): void {
|
||||
if (!isMicrosoftDesignerWebRetiredProviderId(providerId)) return;
|
||||
|
||||
const error = new Error(MICROSOFT_DESIGNER_WEB_RETIRED_MESSAGE);
|
||||
(error as Error & { status?: number }).status = 410;
|
||||
throw error;
|
||||
}
|
||||
|
||||
export function isMicrosoftDesignerWebProviderRetiredError(
|
||||
error: unknown
|
||||
): error is Error & { status: 410 } {
|
||||
return (
|
||||
error instanceof Error &&
|
||||
(error as Error & { status?: number }).status === 410 &&
|
||||
error.message === MICROSOFT_DESIGNER_WEB_RETIRED_MESSAGE
|
||||
);
|
||||
}
|
||||
@@ -156,19 +156,6 @@ export const WEB_COOKIE_PROVIDERS = {
|
||||
subscriptionRisk: true,
|
||||
riskNoticeVariant: "webCookie",
|
||||
},
|
||||
"microsoft-designer-web": {
|
||||
id: "microsoft-designer-web",
|
||||
alias: "msdesigner",
|
||||
name: "Microsoft Designer (Image Generation)",
|
||||
icon: "auto_awesome",
|
||||
color: "#0078D4",
|
||||
textIcon: "MSD",
|
||||
website: "https://designer.microsoft.com",
|
||||
authHint:
|
||||
"Sign in at designer.microsoft.com, then open DevTools → Network, generate an image, and find the request to DallE.ashx?action=GetDallEImagesCogSci. Copy the value of its Authorization: Bearer header (the access_token — no 'Bearer ' prefix). The token is short-lived; this is an unofficial, reverse-engineered integration.",
|
||||
subscriptionRisk: true,
|
||||
riskNoticeVariant: "webCookie",
|
||||
},
|
||||
"t3-web": {
|
||||
id: "t3-web",
|
||||
alias: "t3chat",
|
||||
|
||||
@@ -11,15 +11,21 @@
|
||||
// provider (tokenrouter bug: "No active credentials for provider:
|
||||
// tokenrouter" despite a fully configured compatible node).
|
||||
//
|
||||
// Semantics (mirror the original inline runtime guard exactly):
|
||||
// - REGISTRY entry ids + aliases only. Manual alias ids outside REGISTRY
|
||||
// (xiaomi/llamacpp/aq) do NOT intercept nodes at runtime and are therefore
|
||||
// deliberately NOT reserved — including them would cause false-positive
|
||||
// rejections.
|
||||
// - Case-sensitive: mixed-case input like "TokenRouter" does not collide with
|
||||
// the runtime lookup (`Set.has` is exact-match), so it stays allowed.
|
||||
// Semantics:
|
||||
// - Live REGISTRY entry ids + aliases, plus exact retired provider ids that
|
||||
// must remain unavailable after their registry entries are removed. Manual
|
||||
// aliases outside REGISTRY (xiaomi/llamacpp/aq) do NOT intercept nodes at
|
||||
// runtime and are therefore deliberately NOT reserved — including them would
|
||||
// cause false-positive rejections.
|
||||
// - Live REGISTRY entries remain case-sensitive. Retired ids use their retirement
|
||||
// normalizer (trim + lowercase), so casing cannot revive a removed provider.
|
||||
import { REGISTRY } from "@omniroute/open-sse/config/providerRegistry.ts";
|
||||
|
||||
import {
|
||||
isMicrosoftDesignerWebRetiredProviderId,
|
||||
RETIRED_MICROSOFT_DESIGNER_WEB_PROVIDER_IDS,
|
||||
} from "@/shared/constants/designerWebRetirement";
|
||||
|
||||
let _reserved: Set<string> | null = null;
|
||||
|
||||
function buildReservedProviderPrefixes(): Set<string> {
|
||||
@@ -29,13 +35,16 @@ function buildReservedProviderPrefixes(): Set<string> {
|
||||
if (entry?.id) reserved.add(entry.id);
|
||||
if (entry?.alias) reserved.add(entry.alias);
|
||||
}
|
||||
for (const providerId of RETIRED_MICROSOFT_DESIGNER_WEB_PROVIDER_IDS) {
|
||||
reserved.add(providerId);
|
||||
}
|
||||
_reserved = reserved;
|
||||
return reserved;
|
||||
}
|
||||
|
||||
/**
|
||||
* All reserved provider prefixes (REGISTRY ids + aliases). Built lazily so the
|
||||
* registry is only walked once per process.
|
||||
* All canonical reserved provider prefixes (REGISTRY ids + aliases + retired ids).
|
||||
* Built lazily so the registry is only walked once per process.
|
||||
*/
|
||||
export function getReservedProviderPrefixes(): ReadonlySet<string> {
|
||||
return buildReservedProviderPrefixes();
|
||||
@@ -58,7 +67,10 @@ export const RESERVED_PROVIDER_PREFIXES: ReadonlySet<string> = getReservedProvid
|
||||
* reserved (mirrors the runtime guard's typeof check).
|
||||
*/
|
||||
export function isReservedProviderPrefix(value: unknown): boolean {
|
||||
return typeof value === "string" && buildReservedProviderPrefixes().has(value);
|
||||
return (
|
||||
(typeof value === "string" && buildReservedProviderPrefixes().has(value)) ||
|
||||
isMicrosoftDesignerWebRetiredProviderId(value)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -163,13 +163,6 @@ export const WEB_SESSION_CREDENTIAL_REQUIREMENTS = {
|
||||
acceptsFullCookieHeader: false,
|
||||
storageKeys: ["token", "access_token", "accessToken"],
|
||||
},
|
||||
"microsoft-designer-web": {
|
||||
kind: "token",
|
||||
credentialName: "access_token",
|
||||
placeholder: "access_token=... (Authorization: Bearer header from the DallE.ashx request)",
|
||||
acceptsFullCookieHeader: false,
|
||||
storageKeys: ["token", "access_token", "accessToken"],
|
||||
},
|
||||
"copilot-m365-web": {
|
||||
kind: "token",
|
||||
credentialName: "access_token + chathubPath",
|
||||
|
||||
@@ -135,6 +135,7 @@ import { generateRequestId } from "../../shared/utils/requestId";
|
||||
import { logAuditEvent } from "../../lib/compliance/index";
|
||||
import { enforceApiKeyPolicy } from "../../shared/utils/apiKeyPolicy";
|
||||
import { hasProviderQuotaBypassScope } from "../../shared/constants/apiKeyPolicyScopes";
|
||||
import { isMicrosoftDesignerWebProviderRetiredError } from "../../shared/constants/designerWebRetirement";
|
||||
import { cloneBoundedForLog } from "@omniroute/open-sse/utils/requestLogger.ts";
|
||||
import { handleInternalUsageCommand } from "@/lib/usage/internalUsageCommand";
|
||||
import {
|
||||
@@ -948,7 +949,15 @@ async function handleChatImplementation(
|
||||
// prefix may differ from the credential provider ID (e.g. model
|
||||
// "xiaomi/mimo-v2-flash" resolves to provider "xiaomi" but the combo
|
||||
// target specifies providerId: "opengate" for credential lookup).
|
||||
const modelInfo = await getModelInfo(modelString);
|
||||
let modelInfo;
|
||||
try {
|
||||
modelInfo = await getModelInfo(modelString);
|
||||
} catch (error) {
|
||||
// Persisted explicit combos may still reference the retired provider. Treat
|
||||
// that target as unavailable so priority/fallback strategies can continue.
|
||||
if (isMicrosoftDesignerWebProviderRetiredError(error)) return false;
|
||||
throw error;
|
||||
}
|
||||
// Apply the same prefix-override guard as handleSingleModelChat:
|
||||
// if providerId is just the prefix already in the model string, use
|
||||
// the fully-resolved modelInfo.provider for a precise credential check.
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
} from "../../shared/utils/circuitBreaker";
|
||||
import { classify429FromError, type FailureKind } from "../../shared/utils/classify429";
|
||||
import { resolveUseUpstream429BreakerHints } from "../../shared/utils/providerHints";
|
||||
import { isMicrosoftDesignerWebProviderRetiredError } from "../../shared/constants/designerWebRetirement";
|
||||
|
||||
import { logProxyEvent } from "../../lib/proxyLogger";
|
||||
import { logTranslationEvent } from "../../lib/translatorEvents";
|
||||
@@ -120,7 +121,15 @@ export async function resolveModelOrError(
|
||||
endpointPath: string = "",
|
||||
requestHeaders: Record<string, unknown> | null | undefined = null
|
||||
) {
|
||||
const modelInfo = await getModelInfo(modelStr);
|
||||
let modelInfo;
|
||||
try {
|
||||
modelInfo = await getModelInfo(modelStr);
|
||||
} catch (error) {
|
||||
if (isMicrosoftDesignerWebProviderRetiredError(error)) {
|
||||
return { error: errorResponse(HTTP_STATUS.GONE, error.message) };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
const sourceFormat = detectFormatFromEndpoint(body, endpointPath);
|
||||
|
||||
if (
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
import { buildJinaEnvCredentials } from "@/lib/providers/jina";
|
||||
import { buildGeminiEnvCredentials } from "@/lib/providers/gemini";
|
||||
import { toNumber } from "@/shared/utils/numeric";
|
||||
import { isMicrosoftDesignerWebRetiredProviderId } from "@/shared/constants/designerWebRetirement";
|
||||
import {
|
||||
createLazyConnectionView,
|
||||
toProviderConnection,
|
||||
@@ -1283,6 +1284,12 @@ export async function getProviderCredentials(
|
||||
requestedModel: string | null = null,
|
||||
options: CredentialSelectionOptions = {}
|
||||
) {
|
||||
if (isMicrosoftDesignerWebRetiredProviderId(provider)) {
|
||||
invalidateManagedLease(options, "AUTHORIZATION_CHANGED");
|
||||
log.warn("AUTH", "Retired provider credential selection denied");
|
||||
return null;
|
||||
}
|
||||
|
||||
const selectionLock = options._leaseRetryWithLockHeld
|
||||
? null
|
||||
: createSelectionLock(getSelectionMutexKey(provider, options));
|
||||
|
||||
@@ -21,6 +21,7 @@ import { getLearnedReasoningEffortForModel } from "@omniroute/open-sse/services/
|
||||
import { REGISTRY } from "@omniroute/open-sse/config/providerRegistry.ts";
|
||||
import { getRegisteredProviderEffortBaseModelId } from "@omniroute/open-sse/utils/registeredEffortVariants.ts";
|
||||
import { getReservedProviderPrefixes } from "@/shared/constants/reservedProviderPrefixes";
|
||||
import { assertMicrosoftDesignerWebProviderAvailable } from "@/shared/constants/designerWebRetirement";
|
||||
|
||||
export { parseModel, stripContextWindowSuffix };
|
||||
|
||||
@@ -425,6 +426,10 @@ export async function getModelInfo(modelStr) {
|
||||
const parsed = parseModel(modelStr);
|
||||
const { extendedContext } = parsed;
|
||||
|
||||
// Fail closed before a custom compatible node or stripModelPrefix can reinterpret
|
||||
// an exact retired provider id/alias as an unrelated live provider.
|
||||
assertMicrosoftDesignerWebProviderAvailable(parsed.providerAlias || parsed.provider);
|
||||
|
||||
const attachRuntimeModelMeta = async (info: any) => {
|
||||
if (!info?.provider || !info?.model) return info;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user