mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-22 04:00:19 +02:00
Handle warning and no routing to remote exit nodes for ai providers
This commit is contained in:
@@ -1891,6 +1891,7 @@
|
||||
"aiProviderRoutingModeTargetDescription": "Route through targets on your sites",
|
||||
"aiProviderRoutingModeTargetNote": "After creating this provider, configure site targets on the Network Settings tab.",
|
||||
"aiProviderTargetNoOne": "This provider doesn't have any targets. Add a target to route requests through your sites.",
|
||||
"aiProviderRemoteNodeTargetsWarning": "Sites connected to remote nodes are inaccessable to be routed to on AI Gateway providers.",
|
||||
"aiProviderSkipTlsVerification": "Skip TLS Verification",
|
||||
"aiProviderSkipTlsVerificationDescription": "Disable TLS certificate verification for the upstream connection",
|
||||
"aiProviderBudget": "Budget",
|
||||
|
||||
@@ -99,6 +99,7 @@ async function fetchProviderTargets(
|
||||
method: targets.method,
|
||||
exitNodeSubnet: sites.exitNodeSubnet,
|
||||
reachableAt: exitNodes.reachableAt,
|
||||
exitNodeType: exitNodes.type,
|
||||
hcHealth: targetHealthCheck.hcHealth
|
||||
})
|
||||
.from(targets)
|
||||
@@ -119,6 +120,12 @@ async function fetchProviderTargets(
|
||||
if (!row.exitNodeSubnet || !row.reachableAt) {
|
||||
continue;
|
||||
}
|
||||
// Sites connected to a remote exit node aren't reachable via a
|
||||
// gerbil sidecar's /router/* proxy - only "gerbil" type exit nodes
|
||||
// run that endpoint.
|
||||
if (row.exitNodeType !== "gerbil") {
|
||||
continue;
|
||||
}
|
||||
// A target with an active health check that's currently failing is
|
||||
// taken out of rotation. No health check (null) or "unknown" (check
|
||||
// hasn't run yet / hcEnabled is off) still routes normally, matching
|
||||
|
||||
@@ -345,6 +345,7 @@ export default function AiProviderNetworkPage() {
|
||||
ref={targetsFormRef}
|
||||
orgId={orgId}
|
||||
isHttp
|
||||
isAiProvider
|
||||
providerId={provider.providerId}
|
||||
initialTargets={
|
||||
isTargetModeSaved ? remoteTargets : []
|
||||
|
||||
@@ -682,6 +682,7 @@ export default function CreateAiProviderPage() {
|
||||
<ProxyResourceTargetsForm
|
||||
orgId={orgId}
|
||||
isHttp
|
||||
isAiProvider
|
||||
onChange={(nextTargets) => {
|
||||
targetsRef.current = nextTargets;
|
||||
}}
|
||||
|
||||
@@ -113,6 +113,8 @@ type ProxyResourceTargetsFormProps = {
|
||||
hideSaveButton?: boolean;
|
||||
/** Hide the advanced mode toggle and always use non-advanced mode (e.g. AI providers) */
|
||||
disableAdvancedMode?: boolean;
|
||||
/** Targets picker is for an AI provider (changes which routing warnings are shown) */
|
||||
isAiProvider?: boolean;
|
||||
};
|
||||
|
||||
export const ProxyResourceTargetsForm = forwardRef<
|
||||
@@ -131,7 +133,8 @@ export const ProxyResourceTargetsForm = forwardRef<
|
||||
emptyMessage,
|
||||
embedded = false,
|
||||
hideSaveButton = false,
|
||||
disableAdvancedMode = false
|
||||
disableAdvancedMode = false,
|
||||
isAiProvider = false
|
||||
},
|
||||
ref
|
||||
) {
|
||||
@@ -259,6 +262,14 @@ export const ProxyResourceTargetsForm = forwardRef<
|
||||
})
|
||||
);
|
||||
|
||||
const { data: remoteExitNodes = [] } = useQuery({
|
||||
...orgQueries.remoteExitNodes({ orgId }),
|
||||
enabled: build === "saas" && isAiProvider
|
||||
});
|
||||
const hasRemoteExitNodes = remoteExitNodes.some(
|
||||
(node) => node.exitNodeId !== null
|
||||
);
|
||||
|
||||
const updateTarget = useCallback(
|
||||
(targetId: number, data: Partial<LocalTarget>) => {
|
||||
setTargets((prevTargets) => {
|
||||
@@ -972,6 +983,7 @@ export const ProxyResourceTargetsForm = forwardRef<
|
||||
</div>
|
||||
)}
|
||||
{build === "saas" &&
|
||||
!isAiProvider &&
|
||||
targets.length > 1 &&
|
||||
new Set(targets.map((t) => t.siteId)).size > 1 && (
|
||||
<p className="text-sm text-muted-foreground mt-3">
|
||||
@@ -988,6 +1000,11 @@ export const ProxyResourceTargetsForm = forwardRef<
|
||||
.
|
||||
</p>
|
||||
)}
|
||||
{build === "saas" && isAiProvider && hasRemoteExitNodes && (
|
||||
<p className="text-sm text-muted-foreground mt-3">
|
||||
{t("aiProviderRemoteNodeTargetsWarning")}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ import type {
|
||||
import type { GetResourceResponse } from "@server/routers/resource/getResource";
|
||||
import type { GetResourceAuthInfoResponse } from "@server/routers/resource/getResourceAuthInfo";
|
||||
import type { ListResourcePoliciesResponse } from "@server/routers/resource/types";
|
||||
import type { ListRemoteExitNodesResponse } from "@server/routers/remoteExitNode/types";
|
||||
import type { ListRolesResponse } from "@server/routers/role";
|
||||
import type { ListSitesResponse } from "@server/routers/site";
|
||||
import type {
|
||||
@@ -330,6 +331,17 @@ export const orgQueries = {
|
||||
}
|
||||
}),
|
||||
|
||||
remoteExitNodes: ({ orgId }: { orgId: string }) =>
|
||||
queryOptions({
|
||||
queryKey: ["ORG", orgId, "REMOTE_EXIT_NODES"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
const res = await meta!.api.get<
|
||||
AxiosResponse<ListRemoteExitNodesResponse>
|
||||
>(`/org/${orgId}/remote-exit-nodes`, { signal });
|
||||
return res.data.data.remoteExitNodes;
|
||||
}
|
||||
}),
|
||||
|
||||
labels: ({
|
||||
orgId,
|
||||
query,
|
||||
|
||||
Reference in New Issue
Block a user