feat: add support for Grok Web and Perplexity Web providers

This commit is contained in:
decolua committed 2026-04-22 11:58:53 +07:00
1 parent eeb2dc9e30
commit abb04c5366
17 files changed
+1583 -13

No files matched your search

@@ -4,9 +4,14 @@ import { useState } from "react";
import PropTypes from "prop-types";
import { Button, Badge, Input, Modal, Select } from "@/shared/components";
export default function AddApiKeyModal({ isOpen, provider, providerName, isCompatible, isAnthropic, proxyPools, onSave, onClose }) {
export default function AddApiKeyModal({ isOpen, provider, providerName, isCompatible, isAnthropic, authType, authHint, website, proxyPools, onSave, onClose }) {
const NONE_PROXY_POOL_VALUE = "__none__";
const isOllamaLocal = provider === "ollama-local";
const isCookie = authType === "cookie";
const credentialLabel = isCookie ? "Cookie Value" : "API Key";
const credentialPlaceholder = isCookie
? (provider === "grok-web" ? "sso=xxxxx... or just the raw value" : "eyJhbGciOi...")
: "";
const [formData, setFormData] = useState({
name: "",
@@ -87,7 +92,7 @@ export default function AddApiKeyModal({ isOpen, provider, providerName, isCompa
if (!provider) return null;
return (
<Modal isOpen={isOpen} title={`Add ${providerName || provider} API Key`} onClose={onClose}>
<Modal isOpen={isOpen} title={`Add ${providerName || provider} ${credentialLabel}`} onClose={onClose}>
<div className="flex flex-col gap-4">
<Input
label="Name"
@@ -114,10 +119,11 @@ export default function AddApiKeyModal({ isOpen, provider, providerName, isCompa
{!isOllamaLocal && (
<div className="flex gap-2">
<Input
label="API Key"
type="password"
label={credentialLabel}
type={isCookie ? "text" : "password"}
value={formData.apiKey}
onChange={(e) => setFormData({ ...formData, apiKey: e.target.value })}
placeholder={credentialPlaceholder}
className="flex-1"
/>
<div className="pt-6">
@@ -127,6 +133,19 @@ export default function AddApiKeyModal({ isOpen, provider, providerName, isCompa
</div>
</div>
)}
{isCookie && authHint && (
<p className="text-xs text-text-muted">
{authHint}
{website && (
<>
{" "}
<a href={website} target="_blank" rel="noopener noreferrer" className="text-primary underline">
Open {website.replace(/^https?:\/\//, "")}
</a>
</>
)}
</p>
)}
{isOllamaLocal && (
<p className="text-xs text-text-muted">
Leave blank to use <code>http://localhost:11434</code>. For remote Ollama, enter the full host URL (e.g. <code>http://192.168.1.10:11434</code>).
@@ -192,6 +211,9 @@ AddApiKeyModal.propTypes = {
providerName: PropTypes.string,
isCompatible: PropTypes.bool,
isAnthropic: PropTypes.bool,
authType: PropTypes.string,
authHint: PropTypes.string,
website: PropTypes.string,
proxyPools: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string,
name: PropTypes.string,
@@ -5,7 +5,7 @@ import { useParams, useRouter } from "next/navigation";
import Link from "next/link";
import Image from "next/image";
import { Card, Button, Badge, Input, Modal, CardSkeleton, OAuthModal, KiroOAuthWrapper, CursorAuthModal, IFlowCookieModal, GitLabAuthModal, Toggle, Select, EditConnectionModal } from "@/shared/components";
import { OAUTH_PROVIDERS, APIKEY_PROVIDERS, FREE_PROVIDERS, FREE_TIER_PROVIDERS, getProviderAlias, isOpenAICompatibleProvider, isAnthropicCompatibleProvider, AI_PROVIDERS, THINKING_CONFIG } from "@/shared/constants/providers";
import { OAUTH_PROVIDERS, APIKEY_PROVIDERS, FREE_PROVIDERS, FREE_TIER_PROVIDERS, WEB_COOKIE_PROVIDERS, getProviderAlias, isOpenAICompatibleProvider, isAnthropicCompatibleProvider, AI_PROVIDERS, THINKING_CONFIG } from "@/shared/constants/providers";
import { getModelsByProviderId } from "@/shared/constants/models";
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
import { fetchSuggestedModels } from "@/shared/utils/providerModelsFetcher";
@@ -58,7 +58,7 @@ export default function ProviderDetailPage() {
baseUrl: providerNode.baseUrl,
type: providerNode.type,
}
: (OAUTH_PROVIDERS[providerId] || APIKEY_PROVIDERS[providerId] || FREE_PROVIDERS[providerId] || FREE_TIER_PROVIDERS[providerId]);
: (OAUTH_PROVIDERS[providerId] || APIKEY_PROVIDERS[providerId] || FREE_PROVIDERS[providerId] || FREE_TIER_PROVIDERS[providerId] || WEB_COOKIE_PROVIDERS[providerId]);
const isOAuth = !!OAUTH_PROVIDERS[providerId] || !!FREE_PROVIDERS[providerId];
const isFreeNoAuth = !!FREE_PROVIDERS[providerId]?.noAuth;
const models = getModelsByProviderId(providerId);
@@ -1012,6 +1012,9 @@ export default function ProviderDetailPage() {
providerName={providerInfo.name}
isCompatible={isCompatible}
isAnthropic={isAnthropicCompatible}
authType={providerInfo?.authType}
authHint={providerInfo?.authHint}
website={providerInfo?.website}
proxyPools={proxyPools}
onSave={handleSaveApiKey}
onClose={() => setShowAddApiKeyModal(false)}