Skip to content

Commit 00baef3

Browse files
committed
fix(image): respect caller-pinned model on mature/private-adult routing
editImage and generateImage unconditionally re-ran PolicyAwareImageRouter for mature/private-adult tiers and overwrote any explicit provider/model the caller had pre-resolved. This silently discarded callers (e.g. wilds-ai's image-jobs layer) that had substituted a working pinned-version model for a known-broken catalog entry, reverting the resolution to the broken slug while keeping the pinned version SHA in providerOptions — a model+version mismatch that broke the Replicate call. Now: when opts.provider or opts.model is set, the catalog re-resolution is skipped and the caller's pin survives. The disable_safety_checker bypass for Replicate still always fires for mature+ (load-bearing for uncensored prompts) regardless of which model the caller pinned, but caller-set replicate.disableSafetyChecker is honored to allow opt-out.
1 parent 9556b7f commit 00baef3

2 files changed

Lines changed: 89 additions & 35 deletions

File tree

src/api/editImage.ts

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -191,27 +191,34 @@ export async function editImage(opts: EditImageOptions): Promise<EditImageResult
191191
opts.policyTier
192192
&& (opts.policyTier === 'mature' || opts.policyTier === 'private-adult')
193193
) {
194-
const { PolicyAwareImageRouter } = await import(
195-
'../media/images/PolicyAwareImageRouter.js'
196-
);
197-
const { createUncensoredModelCatalog } = await import(
198-
'../core/llm/routing/UncensoredModelCatalog.js'
199-
);
200-
const imageRouter = new PolicyAwareImageRouter(createUncensoredModelCatalog());
201-
// When the caller didn't pin a capability, default to img2img so
202-
// the catalog never picks a txt2img-only model for an edit call.
203-
const capabilities = opts.capabilities ?? ['img2img'];
204-
const pref = imageRouter.getPreferredProvider(
205-
opts.policyTier as 'mature' | 'private-adult',
206-
capabilities,
207-
);
208-
if (pref) {
209-
providerId = pref.providerId;
210-
modelId = pref.modelId;
211-
const existingReplicate =
212-
(effectiveProviderOptions as Record<string, unknown> | undefined)?.replicate as
213-
| Record<string, unknown>
214-
| undefined;
194+
// Caller-pin precedence: when a caller explicitly passes
195+
// `provider` and/or `model` for a mature-tier edit, that
196+
// resolution carries information the agentos catalog can't
197+
// (specifically: pinned version SHAs for community Replicate
198+
// models that 422 on the modern endpoint without a version,
199+
// and face-anchor models the catalog flags as broken). The
200+
// wilds-ai image-jobs layer relies on this — it substitutes
201+
// `lucataco/ip-adapter-faceid-sdxl` (a known-broken catalog
202+
// entry) with `zsxkib/pulid` plus a pinned version SHA before
203+
// calling editImage. Re-running the router here would silently
204+
// discard that substitution and revert to the broken slug.
205+
const callerPinnedModel =
206+
(typeof opts.model === 'string' && opts.model.length > 0)
207+
|| (typeof opts.provider === 'string' && opts.provider.length > 0);
208+
209+
// Always set disableSafetyChecker for Replicate on mature+
210+
// tiers, regardless of which model the caller pinned. This is
211+
// load-bearing for uncensored prompts to render — Replicate's
212+
// default NSFW filter vetoes them otherwise. Caller's existing
213+
// explicit setting wins (allows opt-out for test surfaces).
214+
const existingReplicate =
215+
(effectiveProviderOptions as Record<string, unknown> | undefined)?.replicate as
216+
| Record<string, unknown>
217+
| undefined;
218+
if (
219+
existingReplicate === undefined
220+
|| typeof existingReplicate.disableSafetyChecker === 'undefined'
221+
) {
215222
effectiveProviderOptions = {
216223
...(effectiveProviderOptions ?? {}),
217224
replicate: {
@@ -220,6 +227,27 @@ export async function editImage(opts: EditImageOptions): Promise<EditImageResult
220227
},
221228
} as ImageProviderOptionBag;
222229
}
230+
231+
if (!callerPinnedModel) {
232+
const { PolicyAwareImageRouter } = await import(
233+
'../media/images/PolicyAwareImageRouter.js'
234+
);
235+
const { createUncensoredModelCatalog } = await import(
236+
'../core/llm/routing/UncensoredModelCatalog.js'
237+
);
238+
const imageRouter = new PolicyAwareImageRouter(createUncensoredModelCatalog());
239+
// When the caller didn't pin a capability, default to img2img so
240+
// the catalog never picks a txt2img-only model for an edit call.
241+
const capabilities = opts.capabilities ?? ['img2img'];
242+
const pref = imageRouter.getPreferredProvider(
243+
opts.policyTier as 'mature' | 'private-adult',
244+
capabilities,
245+
);
246+
if (pref) {
247+
providerId = pref.providerId;
248+
modelId = pref.modelId;
249+
}
250+
}
223251
}
224252

225253
const resolved = resolveMediaProvider(providerId, modelId, {

src/api/generateImage.ts

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -302,27 +302,53 @@ export async function generateImage(opts: GenerateImageOptions): Promise<Generat
302302
// `'face-consistency'` from the presence of a reference image so
303303
// character portraits keep the right face instead of drifting.
304304
if (opts.policyTier && (opts.policyTier === 'mature' || opts.policyTier === 'private-adult')) {
305-
const { PolicyAwareImageRouter } = await import('../media/images/PolicyAwareImageRouter.js');
306-
const { createUncensoredModelCatalog } = await import('../core/llm/routing/UncensoredModelCatalog.js');
307-
const imageRouter = new PolicyAwareImageRouter(createUncensoredModelCatalog());
308-
const inferredCaps =
309-
opts.capabilities
310-
?? (opts.referenceImageUrl ? ['face-consistency'] : undefined);
311-
const pref = imageRouter.getPreferredProvider(
312-
opts.policyTier as 'mature' | 'private-adult',
313-
inferredCaps,
314-
);
315-
if (pref) {
316-
providerId = pref.providerId;
317-
modelId = pref.modelId;
305+
// Caller-pin precedence: when a caller explicitly passes
306+
// `provider` and/or `model` for a mature-tier generate, that
307+
// resolution carries information the agentos catalog can't
308+
// (specifically: pinned version SHAs for community Replicate
309+
// models that 422 on the modern endpoint, and face-anchor
310+
// models the catalog flags as broken). Re-running the router
311+
// here would silently discard the substitution. Same fix
312+
// shape as editImage.ts.
313+
const callerPinnedModel =
314+
(typeof opts.model === 'string' && opts.model.length > 0)
315+
|| (typeof opts.provider === 'string' && opts.provider.length > 0);
316+
317+
// Always set disableSafetyChecker for Replicate on mature+
318+
// tiers, regardless of which model the caller pinned —
319+
// load-bearing for uncensored prompts to render. Caller's
320+
// existing explicit setting wins (allows opt-out).
321+
const existingReplicate =
322+
(opts.providerOptions?.replicate as Record<string, unknown>) ?? undefined;
323+
if (
324+
existingReplicate === undefined
325+
|| typeof existingReplicate.disableSafetyChecker === 'undefined'
326+
) {
318327
opts.providerOptions = {
319328
...opts.providerOptions,
320329
replicate: {
321-
...((opts.providerOptions?.replicate as Record<string, unknown>) ?? {}),
330+
...(existingReplicate ?? {}),
322331
disableSafetyChecker: true,
323332
},
324333
};
325334
}
335+
336+
if (!callerPinnedModel) {
337+
const { PolicyAwareImageRouter } = await import('../media/images/PolicyAwareImageRouter.js');
338+
const { createUncensoredModelCatalog } = await import('../core/llm/routing/UncensoredModelCatalog.js');
339+
const imageRouter = new PolicyAwareImageRouter(createUncensoredModelCatalog());
340+
const inferredCaps =
341+
opts.capabilities
342+
?? (opts.referenceImageUrl ? ['face-consistency'] : undefined);
343+
const pref = imageRouter.getPreferredProvider(
344+
opts.policyTier as 'mature' | 'private-adult',
345+
inferredCaps,
346+
);
347+
if (pref) {
348+
providerId = pref.providerId;
349+
modelId = pref.modelId;
350+
}
351+
}
326352
}
327353

328354
const resolved = resolveMediaProvider(providerId, modelId, {

0 commit comments

Comments
 (0)