feat(providers): optional AI Horde API key and live image catalog (#10542)

* feat(providers): optional AI Horde API key and live image catalog

Allow a registered Horde key on the no-auth connection and send it for
chat and image jobs. List only image models that currently have workers,
and generate through Horde's native async API.

# Conflicts:
#	open-sse/config/imageRegistry.ts
#	src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx
#	src/shared/constants/providers.ts
#	src/sse/services/auth.ts

* fix(providers): validate AI Horde keys against find_user

The OpenAI-compatible /v1/models probe returns 200 for any Bearer token
on oai.aihorde.net, so Check always succeeded. Use Horde's /v2/find_user
lookup instead; an empty key still counts as the optional anonymous path.

* chore(changelog): name the AI Horde fragment for #10542

* fix(images): harden AI Horde optional-key selection and outbound fetches

- Optional-key selection now honors connection health (rate-limit cooldown
  and terminal/unavailable test status) before handing a stored key back,
  rotating to the next healthy key or falling back to the anonymous no-auth
  path instead of using an unhealthy stored key.
- Route the Horde submit/check/status/cancel and catalog calls through the
  repository's bounded outbound-fetch helper (timeout, no more bare fetch())
  and route R2 image downloads through the established bounded remote-image
  fetch (SSRF host guard, DNS-rebinding pin, streaming byte cap, redirect
  limit) instead of an unbounded fetch().
- Extend the generation deadline to cover the full request lifecycle
  (catalog freshness check, submit, polling, and image download), and add a
  regression test proving that exceeding the deadline issues a DELETE
  cancel to Horde's API rather than only timing out locally.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: pqr <pqr@soraka.ititti.es>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
This commit is contained in:
pageragatz
2026-08-18 08:51:57 -05:00
committed by GitHub
parent f92075bb63
commit b9cd5ed138
20 changed files with 1525 additions and 26 deletions

View File

@@ -22,6 +22,7 @@ import {
getAllImageModels,
isRegisteredImageModel,
} from "@omniroute/open-sse/config/imageRegistry";
import { aiHordeImageCatalog } from "@omniroute/open-sse/services/aihordeImageCatalog";
import { getAllRerankModels } from "@omniroute/open-sse/config/rerankRegistry";
import { getAllAudioModels } from "@omniroute/open-sse/config/audioRegistry";
import { getAllModerationModels } from "@omniroute/open-sse/config/moderationRegistry";
@@ -1163,7 +1164,15 @@ async function buildUnifiedModelsResponseCore(
});
}
// Add image models (filtered by active providers)
// Add image models (filtered by active providers).
// AI Horde image workers come and go — refresh the live detector first.
if (isProviderActive("aihorde")) {
try {
await aiHordeImageCatalog.ensureFresh();
} catch {
// Keep the last good snapshot (or none) if Horde is unreachable.
}
}
for (const imgModel of getAllImageModels()) {
if (!isProviderActive(imgModel.provider)) continue;
const rawModelId = getSpecialtyModelRelativeId(imgModel.id, imgModel.provider);