mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-06 07:12:12 +03:00
Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
This commit is contained in:
committed by
GitHub
parent
bd1bcb5aba
commit
5e1750ff07
@@ -14,6 +14,7 @@ _In development — bullets added per PR; finalized at release._
|
||||
- **Onboarding**: add the missing `onboarding.tiers` step-title translation so the setup wizard no longer crashes with `MISSING_MESSAGE: onboarding.tiers` (#4698).
|
||||
- **deepseek-web**: fold `role:"tool"` results into the single-prompt transcript (`messagesToPrompt`) so tool outputs reach the model instead of being silently dropped when a follow-up turn omits the `tools[]` array (#4712).
|
||||
- **Dashboard**: remove the dead, unconditional `useLiveRequests()` call from `HomePageClient.tsx` — it crashed the `/home` page in production builds with `ReferenceError: useLiveRequests is not defined` (#4759, #4745) and opened the live-dashboard WebSocket even when Provider Topology was hidden (#4596). The live feed remains owned by the settings-gated `HomeProviderTopologySection`.
|
||||
- **Providers dashboard**: dedupe provider nodes by id when adding a compatible provider (`upsertProviderNodeById`) so the same provider can no longer appear twice and no-op adds don't invalidate the compatible-provider memo (#4746).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
filterConfiguredProviderEntries,
|
||||
shouldFilterProviderEntriesForDisplayMode,
|
||||
shouldShowFirstProviderHint,
|
||||
upsertProviderNodeById,
|
||||
} from "./providerPageUtils";
|
||||
import type { ProviderEntry } from "./providerPageUtils";
|
||||
import {
|
||||
@@ -1745,7 +1746,7 @@ export default function ProvidersPage() {
|
||||
mode="openai"
|
||||
onClose={() => setShowAddCompatibleModal(false)}
|
||||
onCreated={(node) => {
|
||||
setProviderNodes((prev) => [...prev, node]);
|
||||
setProviderNodes((prev) => upsertProviderNodeById(prev, node));
|
||||
setShowAddCompatibleModal(false);
|
||||
router.push(`/dashboard/providers/${node.id}`);
|
||||
}}
|
||||
@@ -1755,7 +1756,7 @@ export default function ProvidersPage() {
|
||||
mode="anthropic"
|
||||
onClose={() => setShowAddAnthropicCompatibleModal(false)}
|
||||
onCreated={(node) => {
|
||||
setProviderNodes((prev) => [...prev, node]);
|
||||
setProviderNodes((prev) => upsertProviderNodeById(prev, node));
|
||||
setShowAddAnthropicCompatibleModal(false);
|
||||
router.push(`/dashboard/providers/${node.id}`);
|
||||
}}
|
||||
@@ -1767,7 +1768,7 @@ export default function ProvidersPage() {
|
||||
title={addCcCompatibleLabel}
|
||||
onClose={() => setShowAddCcCompatibleModal(false)}
|
||||
onCreated={(node) => {
|
||||
setProviderNodes((prev) => [...prev, node]);
|
||||
setProviderNodes((prev) => upsertProviderNodeById(prev, node));
|
||||
setShowAddCcCompatibleModal(false);
|
||||
router.push(`/dashboard/providers/${node.id}`);
|
||||
}}
|
||||
|
||||
@@ -276,3 +276,24 @@ export function resolveDashboardProviderInfo(
|
||||
): ResolvedProviderCatalogEntry | null {
|
||||
return resolveProviderCatalogEntry(providerId, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append or replace a provider node by `id`, never appending a duplicate (#4746).
|
||||
*
|
||||
* The compatible-provider "add" modals previously did `setProviderNodes((prev) => [...prev, node])`,
|
||||
* so adding the same provider twice (refresh-then-add, double-click, retry, or React StrictMode
|
||||
* double-invocation in dev) left the same `id` in the array twice — surfacing duplicate cards and
|
||||
* invalidating the `compatibleProviderGroups` memo on every no-op add. This upsert dedups by id:
|
||||
* - new id → append a new array,
|
||||
* - same id, deep-equal payload → return `prev` unchanged (stable identity ⇒ memo does not re-run),
|
||||
* - same id, changed payload → replace in place.
|
||||
*/
|
||||
export function upsertProviderNodeById<T extends { id?: string | null }>(prev: T[], node: T): T[] {
|
||||
if (!node || node.id == null) return [...prev, node];
|
||||
const idx = prev.findIndex((p) => p?.id === node.id);
|
||||
if (idx === -1) return [...prev, node];
|
||||
if (JSON.stringify(prev[idx]) === JSON.stringify(node)) return prev;
|
||||
const next = prev.slice();
|
||||
next[idx] = node;
|
||||
return next;
|
||||
}
|
||||
|
||||
35
tests/unit/provider-node-dedup-4746.test.ts
Normal file
35
tests/unit/provider-node-dedup-4746.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
// #4746 — the compatible-provider "add" modals appended provider nodes with
|
||||
// `setProviderNodes((prev) => [...prev, node])`, so the same provider id could land in the
|
||||
// array more than once (refresh-then-add, double-click, retry, StrictMode double-invocation),
|
||||
// producing duplicate cards and invalidating the compatibleProviderGroups memo on no-op adds.
|
||||
// upsertProviderNodeById dedups by id and keeps array identity stable for no-op adds.
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { upsertProviderNodeById } from "../../src/app/(dashboard)/dashboard/providers/providerPageUtils.ts";
|
||||
|
||||
test("appends a node with a new id (#4746)", () => {
|
||||
const prev = [{ id: "a", name: "A" }];
|
||||
const next = upsertProviderNodeById(prev, { id: "b", name: "B" });
|
||||
assert.deepEqual(next.map((n) => n.id), ["a", "b"]);
|
||||
});
|
||||
|
||||
test("does not append a duplicate id — same identical payload returns prev unchanged (#4746)", () => {
|
||||
const prev = [{ id: "a", name: "A" }];
|
||||
const next = upsertProviderNodeById(prev, { id: "a", name: "A" });
|
||||
assert.equal(next.length, 1);
|
||||
assert.equal(next, prev, "no-op add must keep the same array reference (memo stability)");
|
||||
});
|
||||
|
||||
test("replaces an existing id when the payload changed (#4746)", () => {
|
||||
const prev = [{ id: "a", name: "A" }, { id: "b", name: "B" }];
|
||||
const next = upsertProviderNodeById(prev, { id: "a", name: "A2" });
|
||||
assert.equal(next.length, 2);
|
||||
assert.equal(next.find((n) => n.id === "a")?.name, "A2");
|
||||
assert.notEqual(next, prev);
|
||||
});
|
||||
|
||||
test("appends when id is missing/null (cannot dedup) (#4746)", () => {
|
||||
const prev = [{ id: "a" }];
|
||||
const next = upsertProviderNodeById(prev, { id: null } as { id: string | null });
|
||||
assert.equal(next.length, 2);
|
||||
});
|
||||
Reference in New Issue
Block a user