mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-04 22:32:12 +03:00
Closes #3507
This commit is contained in:
committed by
GitHub
parent
7e7fdbd30d
commit
34c278324b
@@ -34,6 +34,7 @@
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
- **fix(home topology): restore live in-flight request pulse** ([#3507]): the animated "pulse" edges in the home Provider Topology panel went dead after PR #3401 unified request visibility, because `activeRequests` was hardcoded to `[]`. Re-wired to `useLiveRequests()` (the existing WebSocket hook on port 20129) so that every pending/running request drives the animation in real time. A pure `selectActiveRequests` mapping helper was extracted to `home/topologyUtils.ts` with 5 unit tests.
|
||||
- **Electron desktop**: launch the peer-stamping `server-ws.mjs` entrypoint so local-only routes (AgentBridge, MCP, services) no longer return 403 LOCAL_ONLY (#3386)
|
||||
- **Provider Topology**: stop flagging healthy providers as errored based on stale historical failures; use current request status (#3619)
|
||||
- **OpenCode Free**: fetch the live model catalog from the provider's `modelsUrl` for the no-auth model picker instead of serving a stale hardcoded list (#3611)
|
||||
|
||||
@@ -13,6 +13,8 @@ import { useNotificationStore } from "@/store/notificationStore";
|
||||
import { copyToClipboard } from "@/shared/utils/clipboard";
|
||||
import { getProviderDisplayLabel } from "@/shared/utils/providerDisplayLabel";
|
||||
import { useIsElectron, useOpenExternal } from "@/shared/hooks/useElectron";
|
||||
import { useLiveRequests } from "@/hooks/useLiveDashboard";
|
||||
import { selectActiveRequests } from "../home/topologyUtils";
|
||||
|
||||
const ProviderTopology = dynamic(() => import("../home/ProviderTopology"), { ssr: false });
|
||||
const ProviderQuotaWidget = dynamic(() => import("../home/ProviderQuotaWidget"), { ssr: false });
|
||||
@@ -114,6 +116,9 @@ export default function HomePageClient({ machineId }: HomePageClientProps) {
|
||||
Array<{ id?: string; prefix?: string; name?: string }>
|
||||
>([]);
|
||||
|
||||
// Live in-flight requests for Provider Topology pulse animation (#3507)
|
||||
const { activeRequests: liveActiveRequests } = useLiveRequests();
|
||||
|
||||
const [versionInfo, setVersionInfo] = useState<VersionInfo | null>(null);
|
||||
const [updating, setUpdating] = useState(false);
|
||||
|
||||
@@ -1178,7 +1183,7 @@ export default function HomePageClient({ machineId }: HomePageClientProps) {
|
||||
</div>
|
||||
<ProviderTopology
|
||||
providers={topologyProviders}
|
||||
activeRequests={[]}
|
||||
activeRequests={selectActiveRequests(liveActiveRequests)}
|
||||
lastProvider={lastProvider}
|
||||
errorProvider={errorProvider}
|
||||
/>
|
||||
|
||||
27
src/app/(dashboard)/home/topologyUtils.ts
Normal file
27
src/app/(dashboard)/home/topologyUtils.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Pure helpers for the home-page Provider Topology panel.
|
||||
*/
|
||||
|
||||
/** Minimal shape expected by <ProviderTopology activeRequests={...}> */
|
||||
export interface TopologyActiveRequest {
|
||||
provider: string;
|
||||
model: string;
|
||||
}
|
||||
|
||||
/** Minimal in-flight request shape (subset of LiveRequest from useLiveRequests) */
|
||||
interface InFlightRequest {
|
||||
provider: string;
|
||||
model: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps an array of in-flight LiveRequest entries to the flat
|
||||
* { provider, model }[] shape consumed by <ProviderTopology>.
|
||||
*
|
||||
* The input is expected to contain only pending/running entries — the
|
||||
* useLiveRequests hook already filters out completed and failed requests
|
||||
* before exposing them via `activeRequests`.
|
||||
*/
|
||||
export function selectActiveRequests(requests: InFlightRequest[]): TopologyActiveRequest[] {
|
||||
return requests.map(({ provider, model }) => ({ provider, model }));
|
||||
}
|
||||
72
tests/unit/topology-active-requests-3507.test.ts
Normal file
72
tests/unit/topology-active-requests-3507.test.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* #3507 — the home Provider Topology pulse was dead because activeRequests was
|
||||
* hardcoded to []. Fix: map the in-flight LiveRequest entries (pending/running)
|
||||
* from useLiveRequests to the { provider, model }[] shape expected by
|
||||
* <ProviderTopology>.
|
||||
*
|
||||
* selectActiveRequests() is a pure mapping function — no React required.
|
||||
*/
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { selectActiveRequests } from "../../src/app/(dashboard)/home/topologyUtils.ts";
|
||||
|
||||
// ── helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
function makeRequest(
|
||||
overrides: Partial<{
|
||||
id: string;
|
||||
model: string;
|
||||
provider: string;
|
||||
status: "pending" | "running" | "success" | "error";
|
||||
}>
|
||||
) {
|
||||
return {
|
||||
id: "req-1",
|
||||
model: "gpt-4",
|
||||
provider: "openai",
|
||||
timestamp: Date.now(),
|
||||
status: "pending" as const,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
// ── tests ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
test("selectActiveRequests: maps pending requests to {provider, model}", () => {
|
||||
const input = [makeRequest({ id: "a", provider: "anthropic", model: "claude-opus-4", status: "pending" })];
|
||||
const result = selectActiveRequests(input);
|
||||
assert.deepEqual(result, [{ provider: "anthropic", model: "claude-opus-4" }]);
|
||||
});
|
||||
|
||||
test("selectActiveRequests: maps running requests to {provider, model}", () => {
|
||||
const input = [makeRequest({ id: "b", provider: "openai", model: "gpt-4o", status: "running" })];
|
||||
const result = selectActiveRequests(input);
|
||||
assert.deepEqual(result, [{ provider: "openai", model: "gpt-4o" }]);
|
||||
});
|
||||
|
||||
test("selectActiveRequests: returns empty array for empty input", () => {
|
||||
assert.deepEqual(selectActiveRequests([]), []);
|
||||
});
|
||||
|
||||
test("selectActiveRequests: maps multiple concurrent in-flight requests", () => {
|
||||
const input = [
|
||||
makeRequest({ id: "c1", provider: "anthropic", model: "claude-sonnet-4", status: "pending" }),
|
||||
makeRequest({ id: "c2", provider: "gemini", model: "gemini-2.5-pro", status: "running" }),
|
||||
];
|
||||
const result = selectActiveRequests(input);
|
||||
assert.deepEqual(result, [
|
||||
{ provider: "anthropic", model: "claude-sonnet-4" },
|
||||
{ provider: "gemini", model: "gemini-2.5-pro" },
|
||||
]);
|
||||
});
|
||||
|
||||
test("selectActiveRequests: only extracts provider and model fields (not id/timestamp/etc)", () => {
|
||||
const input = [makeRequest({ id: "d", provider: "groq", model: "llama3-70b", status: "running" })];
|
||||
const result = selectActiveRequests(input);
|
||||
assert.equal(Object.keys(result[0]).length, 2);
|
||||
assert.ok("provider" in result[0]);
|
||||
assert.ok("model" in result[0]);
|
||||
assert.ok(!("id" in result[0]));
|
||||
assert.ok(!("timestamp" in result[0]));
|
||||
});
|
||||
Reference in New Issue
Block a user