mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-06 07:12:12 +03:00
feat(dahl): add manual API key option alongside auto-generated token (#9077)
Validated in local merge-train T5 (base49+contributors+pacocartones)
This commit is contained in:
@@ -93,6 +93,36 @@ export default function NoAuthProviderControls({
|
||||
[blockedProviders, noAuthT, notify, providerAlias, providerId, providerName]
|
||||
);
|
||||
|
||||
const handleManualApiKeyAdd = useCallback(
|
||||
async (apiKey: string) => {
|
||||
setSavingEnabled(true);
|
||||
try {
|
||||
const response = await fetch("/api/providers", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
provider: "dahl",
|
||||
apiKey: apiKey.trim(),
|
||||
name: "Dahl Manual",
|
||||
priority: 1,
|
||||
isActive: true,
|
||||
testStatus: "unknown",
|
||||
}),
|
||||
});
|
||||
const data = await response.json().catch(() => ({}));
|
||||
if (!response.ok) {
|
||||
throw new Error(data?.error?.message || data?.error || noAuthT("updateProviderFailed"));
|
||||
}
|
||||
notify.success(noAuthT("providerAdded", { provider: providerName }));
|
||||
} catch (error) {
|
||||
notify.error(error instanceof Error ? error.message : noAuthT("updateProviderFailed"));
|
||||
} finally {
|
||||
setSavingEnabled(false);
|
||||
}
|
||||
},
|
||||
[noAuthT, notify, providerName]
|
||||
);
|
||||
|
||||
const accountProviderName = ACCOUNT_PROVIDER_NAMES[providerId];
|
||||
const host = providerProxy?.host;
|
||||
const providerProxyControl = supportsNoAuthProviderProxy(providerId) ? (
|
||||
@@ -129,6 +159,8 @@ export default function NoAuthProviderControls({
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
showManualKeyInput={providerId === "dahl"}
|
||||
onManualApiKeyAdd={providerId === "dahl" ? handleManualApiKeyAdd : undefined}
|
||||
enabled={enabled}
|
||||
savingEnabled={savingEnabled}
|
||||
onEnabledChange={handleEnabledChange}
|
||||
|
||||
@@ -19,6 +19,8 @@ interface NoAuthAccountCardProps {
|
||||
savingEnabled?: boolean;
|
||||
onEnabledChange?: (enabled: boolean) => void;
|
||||
providerProxyControl?: ReactNode;
|
||||
showManualKeyInput?: boolean;
|
||||
onManualApiKeyAdd?: (apiKey: string) => Promise<void>;
|
||||
}
|
||||
|
||||
interface Connection {
|
||||
@@ -99,6 +101,7 @@ export default function NoAuthAccountCard({
|
||||
savingEnabled = false,
|
||||
onEnabledChange,
|
||||
providerProxyControl,
|
||||
onManualApiKeyAdd,
|
||||
}: NoAuthAccountCardProps) {
|
||||
const t = useTranslations("noAuthProvider");
|
||||
const resolvedDescription = description || t("accountDescription");
|
||||
@@ -116,6 +119,9 @@ export default function NoAuthAccountCard({
|
||||
const [proxyUsername, setProxyUsername] = useState("");
|
||||
const [proxyPassword, setProxyPassword] = useState("");
|
||||
const [savingProxy, setSavingProxy] = useState(false);
|
||||
const [manualApiKey, setManualApiKey] = useState("");
|
||||
const [addingManualKey, setAddingManualKey] = useState(false);
|
||||
const [showManualKeyInput, setShowManualKeyInput] = useState(false);
|
||||
const popoverRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const fetchConnections = useCallback(async () => {
|
||||
@@ -212,6 +218,23 @@ export default function NoAuthAccountCard({
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddManualApiKey = async () => {
|
||||
if (!manualApiKey.trim()) return;
|
||||
setAddingManualKey(true);
|
||||
try {
|
||||
if (onManualApiKeyAdd) {
|
||||
await onManualApiKeyAdd(manualApiKey.trim());
|
||||
}
|
||||
setManualApiKey("");
|
||||
setShowManualKeyInput(false);
|
||||
await fetchConnections();
|
||||
} catch (err) {
|
||||
console.error("Failed to add manual API key:", err);
|
||||
} finally {
|
||||
setAddingManualKey(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveAccount = async (accountId: string) => {
|
||||
if (!conn) return;
|
||||
const updated = allAccountIds.filter((id) => id !== accountId);
|
||||
@@ -375,6 +398,45 @@ export default function NoAuthAccountCard({
|
||||
<Button size="sm" icon="add" onClick={handleAddAccount} disabled={adding || !enabled}>
|
||||
{adding ? t("adding") : resolvedAddLabel}
|
||||
</Button>
|
||||
{showManualKeyInput && (
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="password"
|
||||
value={manualApiKey}
|
||||
onChange={(e) => setManualApiKey(e.target.value)}
|
||||
placeholder="Paste API key..."
|
||||
className="rounded-md border border-black/10 bg-bg px-2 py-1 text-xs dark:border-white/10"
|
||||
disabled={addingManualKey || !enabled}
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
icon="add"
|
||||
onClick={handleAddManualApiKey}
|
||||
disabled={addingManualKey || !manualApiKey.trim() || !enabled}
|
||||
>
|
||||
{addingManualKey ? t("adding") : t("add")}
|
||||
</Button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setShowManualKeyInput(false);
|
||||
setManualApiKey("");
|
||||
}}
|
||||
className="rounded p-1 text-text-muted hover:bg-black/5 dark:hover:bg-white/5"
|
||||
>
|
||||
<span className="material-symbols-outlined text-[16px]">close</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{!showManualKeyInput && onManualApiKeyAdd && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowManualKeyInput(true)}
|
||||
className="rounded-md px-2 py-1 text-xs text-text-muted transition-colors hover:bg-black/5 hover:text-text-main dark:hover:bg-white/5"
|
||||
>
|
||||
{t("manualApiKey")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -541,12 +603,14 @@ export default function NoAuthAccountCard({
|
||||
)}
|
||||
<div className="flex justify-end gap-2 pt-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setProxyAccountId(null)}
|
||||
className="rounded-md px-3 py-1.5 text-xs text-text-muted transition-colors hover:bg-black/5 hover:text-text-main dark:hover:bg-white/5"
|
||||
>
|
||||
{t("cancel")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSaveProxy}
|
||||
disabled={savingProxy}
|
||||
className="rounded-md bg-primary/10 px-3 py-1.5 text-xs text-primary transition-colors hover:bg-primary/20 disabled:opacity-50"
|
||||
|
||||
@@ -234,14 +234,14 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
|
||||
textIcon: "DA",
|
||||
website: "https://inference.dahl.global",
|
||||
hasFree: true,
|
||||
freeNote: "Free — MiniMax M2.7, Kimi K2.6. Click 'Add Account' to auto-generate a token.",
|
||||
authHint: "Click 'Add Account' to auto-generate a token.",
|
||||
apiHint: "No manual API key needed. Click 'Add Account' to auto-generate a token.",
|
||||
freeNote: "Free — MiniMax M2.7, Kimi K2.6. Click 'Add Account' to auto-generate a token, or add your own API key.",
|
||||
authHint: "Click 'Add Account' to auto-generate a token, or add a manual API key.",
|
||||
apiHint: "Auto-generate a token or paste your own API key.",
|
||||
apiKeyUrl: "https://inference.dahl.global/tokens",
|
||||
passthroughModels: false,
|
||||
managedAccount: true,
|
||||
notice: {
|
||||
text: "Dahl auto-generates tokens via https://inference.dahl.global/tokens. No signup needed. Rate limits apply.",
|
||||
text: "Dahl auto-generates tokens via https://inference.dahl.global/tokens. No signup needed. Rate limits apply. You can also add your own API key.",
|
||||
},
|
||||
},
|
||||
puter: {
|
||||
|
||||
24
tests/unit/dahl-manual-api-key.test.ts
Normal file
24
tests/unit/dahl-manual-api-key.test.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { APIKEY_PROVIDERS_GATEWAYS } from "../../src/shared/constants/providers/apikey/gateways.ts";
|
||||
|
||||
test("dahl freeNote mentions manual API key option", () => {
|
||||
const dahl = APIKEY_PROVIDERS_GATEWAYS.dahl;
|
||||
assert.ok(dahl.freeNote.includes("manual API key") || dahl.freeNote.includes("your own API key"), "dahl freeNote should mention manual API key option");
|
||||
});
|
||||
|
||||
test("dahl authHint mentions manual API key option", () => {
|
||||
const dahl = APIKEY_PROVIDERS_GATEWAYS.dahl;
|
||||
assert.ok(dahl.authHint.includes("manual API key") || dahl.authHint.includes("your own API key"), "dahl authHint should mention manual API key option");
|
||||
});
|
||||
|
||||
test("dahl apiHint mentions manual API key option", () => {
|
||||
const dahl = APIKEY_PROVIDERS_GATEWAYS.dahl;
|
||||
assert.ok(dahl.apiHint.includes("manual API key") || dahl.apiHint.includes("your own API key") || dahl.apiHint.includes("paste"), "dahl apiHint should mention manual API key option");
|
||||
});
|
||||
|
||||
test("dahl notice text mentions manual API key option", () => {
|
||||
const dahl = APIKEY_PROVIDERS_GATEWAYS.dahl;
|
||||
assert.ok(dahl.notice.text.includes("manual API key") || dahl.notice.text.includes("your own API key"), "dahl notice should mention manual API key option");
|
||||
});
|
||||
Reference in New Issue
Block a user