Enhance token refresh functionality across multiple executors
- Updated refreshCredentials methods in various executors (Antigravity, Base, Default, Github, Kiro) to accept optional proxyOptions for improved proxy handling. - Modified token refresh logic to utilize proxy-aware fetch for better network management. - Enhanced usage retrieval functions to support proxy options, ensuring seamless integration with proxy configurations. - Updated ModelSelectModal and ProviderInfoCard components to incorporate kind filtering for improved user experience in model selection. - Added validation for API keys in the provider validation route, including support for webSearch/webFetch providers.
This commit is contained in:
parent
1bb621317d
commit
8f81363675
45 changed files with 2924 additions and 289 deletions
|
|
@ -4,7 +4,7 @@ import { useState, useMemo, useEffect } from "react";
|
|||
import PropTypes from "prop-types";
|
||||
import Modal from "./Modal";
|
||||
import { getModelsByProviderId } from "@/shared/constants/models";
|
||||
import { OAUTH_PROVIDERS, APIKEY_PROVIDERS, FREE_PROVIDERS, FREE_TIER_PROVIDERS, isOpenAICompatibleProvider, isAnthropicCompatibleProvider, getProviderAlias } from "@/shared/constants/providers";
|
||||
import { OAUTH_PROVIDERS, APIKEY_PROVIDERS, FREE_PROVIDERS, FREE_TIER_PROVIDERS, AI_PROVIDERS, isOpenAICompatibleProvider, isAnthropicCompatibleProvider, getProviderAlias } from "@/shared/constants/providers";
|
||||
|
||||
// Provider order: OAuth first, then Free Tier, then API Key (matches dashboard/providers)
|
||||
const PROVIDER_ORDER = [
|
||||
|
|
@ -25,7 +25,17 @@ export default function ModelSelectModal({
|
|||
activeProviders = [],
|
||||
title = "Select Model",
|
||||
modelAliases = {},
|
||||
kindFilter = null,
|
||||
}) {
|
||||
// Filter activeProviders by serviceKinds when kindFilter set (e.g. "webSearch", "webFetch")
|
||||
const filteredActiveProviders = useMemo(() => {
|
||||
if (!kindFilter) return activeProviders;
|
||||
return activeProviders.filter((p) => {
|
||||
const info = AI_PROVIDERS[p.provider];
|
||||
const kinds = info?.serviceKinds || ["llm"];
|
||||
return kinds.includes(kindFilter);
|
||||
});
|
||||
}, [activeProviders, kindFilter]);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [combos, setCombos] = useState([]);
|
||||
const [providerNodes, setProviderNodes] = useState([]);
|
||||
|
|
@ -85,13 +95,18 @@ export default function ModelSelectModal({
|
|||
const groupedModels = useMemo(() => {
|
||||
const groups = {};
|
||||
|
||||
// Get all active provider IDs from connections
|
||||
const activeConnectionIds = activeProviders.map(p => p.provider);
|
||||
// Get all active provider IDs from connections (filtered by kindFilter if set)
|
||||
const activeConnectionIds = filteredActiveProviders.map(p => p.provider);
|
||||
|
||||
// No-auth providers: filter by kindFilter as well
|
||||
const noAuthIds = kindFilter
|
||||
? NO_AUTH_PROVIDER_IDS.filter((id) => (AI_PROVIDERS[id]?.serviceKinds || ["llm"]).includes(kindFilter))
|
||||
: NO_AUTH_PROVIDER_IDS;
|
||||
|
||||
// Only show connected providers (including both standard and custom)
|
||||
const providerIdsToShow = new Set([
|
||||
...activeConnectionIds, // Only connected providers
|
||||
...NO_AUTH_PROVIDER_IDS, // No-auth providers always visible
|
||||
...noAuthIds, // No-auth providers (kind-filtered)
|
||||
]);
|
||||
|
||||
// Sort by PROVIDER_ORDER
|
||||
|
|
@ -203,14 +218,15 @@ export default function ModelSelectModal({
|
|||
});
|
||||
|
||||
return groups;
|
||||
}, [activeProviders, modelAliases, allProviders, providerNodes, customModels]);
|
||||
}, [filteredActiveProviders, modelAliases, allProviders, providerNodes, customModels, kindFilter]);
|
||||
|
||||
// Filter combos by search query
|
||||
// Filter combos by search query (and hide combos when kindFilter is set — combos are LLM-only by design)
|
||||
const filteredCombos = useMemo(() => {
|
||||
if (kindFilter) return [];
|
||||
if (!searchQuery.trim()) return combos;
|
||||
const query = searchQuery.toLowerCase();
|
||||
return combos.filter(c => c.name.toLowerCase().includes(query));
|
||||
}, [combos, searchQuery]);
|
||||
}, [combos, searchQuery, kindFilter]);
|
||||
|
||||
// Filter models by search query
|
||||
const filteredGroups = useMemo(() => {
|
||||
|
|
@ -384,5 +400,6 @@ ModelSelectModal.propTypes = {
|
|||
),
|
||||
title: PropTypes.string,
|
||||
modelAliases: PropTypes.object,
|
||||
kindFilter: PropTypes.string,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,24 +2,20 @@
|
|||
|
||||
import Card from "./Card";
|
||||
|
||||
// Field schema — config-driven, used for both searchConfig and fetchConfig
|
||||
// Only show fields user actually cares about
|
||||
const FIELD_SCHEMA = {
|
||||
mode: { label: "Mode", format: (v) => v },
|
||||
defaultModel: { label: "Model", format: (v) => v, mono: true },
|
||||
baseUrl: { label: "Endpoint", format: (v) => v, isLink: true, mono: true },
|
||||
method: { label: "Method", format: (v) => v },
|
||||
authType: { label: "Auth", format: (v) => v },
|
||||
authHeader: { label: "Auth Header", format: (v) => v, mono: true },
|
||||
costPerQuery: { label: "Cost / call", format: (v) => v === 0 ? "Free" : `$${v.toFixed(4)}` },
|
||||
freeMonthlyQuota: { label: "Free quota", format: (v) => v === 0 ? "—" : v >= 999999 ? "Unlimited" : `${v.toLocaleString()} / mo` },
|
||||
searchTypes: { label: "Types", format: (v) => v.join(", ") },
|
||||
formats: { label: "Formats", format: (v) => v.join(", ") },
|
||||
defaultMaxResults: { label: "Default results", format: (v) => v },
|
||||
maxMaxResults: { label: "Max results", format: (v) => v },
|
||||
maxCharacters: { label: "Max chars", format: (v) => v.toLocaleString() },
|
||||
timeoutMs: { label: "Timeout", format: (v) => `${v / 1000}s` },
|
||||
cacheTTLMs: { label: "Cache TTL", format: (v) => `${v / 60000}m` },
|
||||
};
|
||||
|
||||
export default function ProviderInfoCard({ config, title = "Provider Info" }) {
|
||||
export default function ProviderInfoCard({ config, provider, title = "Provider Info" }) {
|
||||
if (!config) return null;
|
||||
|
||||
const rows = Object.entries(FIELD_SCHEMA)
|
||||
|
|
@ -33,9 +29,24 @@ export default function ProviderInfoCard({ config, title = "Provider Info" }) {
|
|||
raw: config[key],
|
||||
}));
|
||||
|
||||
const signupUrl = provider?.notice?.apiKeyUrl || provider?.website;
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<h2 className="text-lg font-semibold mb-3">{title}</h2>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-lg font-semibold">{title}</h2>
|
||||
{signupUrl && (
|
||||
<a
|
||||
href={signupUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-xs text-primary hover:underline inline-flex items-center gap-1"
|
||||
>
|
||||
<span className="material-symbols-outlined text-sm">open_in_new</span>
|
||||
Get API Key
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-2">
|
||||
{rows.map((r) => (
|
||||
<div key={r.key} className="flex items-center gap-3 min-w-0">
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ import Button from "./Button";
|
|||
import { ConfirmModal } from "./Modal";
|
||||
|
||||
// const VISIBLE_MEDIA_KINDS = ["embedding", "image", "imageToText", "tts", "stt", "webSearch", "webFetch", "video", "music"];
|
||||
const VISIBLE_MEDIA_KINDS = ["embedding", "image", "tts", "webSearch", "webFetch"];
|
||||
const VISIBLE_MEDIA_KINDS = ["embedding", "image", "tts"];
|
||||
// Combined entry: webSearch + webFetch share one page at /dashboard/media-providers/web
|
||||
const COMBINED_WEB_ITEM = { id: "web", label: "Web Fetch & Search", icon: "travel_explore", href: "/dashboard/media-providers/web" };
|
||||
|
||||
const navItems = [
|
||||
{ href: "/dashboard/endpoint", label: "Endpoint", icon: "api" },
|
||||
|
|
@ -234,6 +236,20 @@ export default function Sidebar({ onClose }) {
|
|||
<span className="text-sm">{kind.label}</span>
|
||||
</Link>
|
||||
))}
|
||||
<Link
|
||||
key={COMBINED_WEB_ITEM.id}
|
||||
href={COMBINED_WEB_ITEM.href}
|
||||
onClick={onClose}
|
||||
className={cn(
|
||||
"flex items-center gap-3 px-4 py-1.5 rounded-lg transition-all group",
|
||||
pathname.startsWith(COMBINED_WEB_ITEM.href)
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-text-muted hover:bg-surface/50 hover:text-text-main"
|
||||
)}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[16px]">{COMBINED_WEB_ITEM.icon}</span>
|
||||
<span className="text-sm">{COMBINED_WEB_ITEM.label}</span>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue