File size: 4,727 Bytes
20ec4ad 8a0e39e 20ec4ad 8a0e39e 20ec4ad 8a0e39e 20ec4ad 8a0e39e 20ec4ad |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
/* src/components/panels/APIKeysConfig.tsx */
import { useLocalStorage } from "react-use";
import { ChangeEvent, useState, useEffect } from "react";
import { toast } from "react-toastify";
import { FiAlertTriangle } from "react-icons/fi";
type RowProps = {
label: string;
link: string;
storageKey: string;
placeholder: string;
validator?: (key: string) => boolean;
};
function InputRow({ label, link, storageKey, placeholder, validator }: RowProps) {
const [value, setValue] = useLocalStorage<string>(storageKey, "");
const [error, setError] = useState<string | null>(null);
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value.trim();
setValue(newValue);
if (validator && newValue) {
const isValid = validator(newValue);
if (!isValid) {
setError("Formato de chave inválido");
} else {
setError(null);
}
} else {
setError(null);
}
};
return (
<div className="mb-4">
<label className="block font-semibold mb-1">
{label}{" "}
<a
href={link}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 underline"
>
obter chave
</a>
</label>
<input
className={`w-full border ${error ? "border-red-500" : "border-gray-400"} px-2 py-1 text-sm`}
placeholder={placeholder}
type="text"
value={value ?? ""}
onChange={onChange}
/>
{error && <p className="text-red-500 text-xs mt-1">{error}</p>}
</div>
);
}
export default function APIKeysConfig() {
const validateOpenAI = (key: string) => key.startsWith("sk-");
const validateGemini = (key: string) => key.startsWith("AIza");
const validateHF = (key: string) => key.startsWith("hf_");
const [showQuotaWarning, setShowQuotaWarning] = useState(false);
// Verifica se deve mostrar o aviso de quota
useEffect(() => {
// Busca no localStorage se houve um erro de quota recentemente (armazenado pelo ChatInterface)
const hasQuotaError = localStorage.getItem("openai_quota_error") === "true";
setShowQuotaWarning(hasQuotaError);
}, []);
const testConnections = () => {
toast.info("Testando conexões...");
// Aqui poderíamos implementar um teste real de conexão
setTimeout(() => toast.success("Chaves salvas com sucesso!"), 1000);
};
return (
<div className="p-4">
<h1 className="text-2xl font-bold mb-6">Configurar chaves de API</h1>
{showQuotaWarning && (
<div className="mb-4 p-3 bg-yellow-800/30 border-l-4 border-yellow-500 text-yellow-200 rounded">
<h3 className="font-bold flex items-center">
<FiAlertTriangle className="mr-2" /> Problema de quota detectado
</h3>
<p className="mt-2">
Sua chave da OpenAI (ChatGPT) atingiu o limite de quota.
Você pode:
</p>
<ul className="list-disc pl-5 mt-2 space-y-1">
<li>Utilizar outra chave de API</li>
<li>Aguardar até o próximo ciclo quando sua quota for renovada</li>
<li>Atualizar seu plano na OpenAI para obter mais créditos</li>
<li>Usar outros agentes que não dependem da API do ChatGPT</li>
</ul>
<button
onClick={() => {
localStorage.removeItem("openai_quota_error");
setShowQuotaWarning(false);
}}
className="mt-2 text-xs bg-yellow-800 hover:bg-yellow-700 px-2 py-1 rounded"
>
Entendi, esconder este aviso
</button>
</div>
)}
<InputRow
label="OpenAI (ChatGPT)"
link="https://platform.openai.com/account/api-keys"
storageKey="chatgptKey"
placeholder="sk-..."
validator={validateOpenAI}
/>
<InputRow
label="Google Gemini"
link="https://makersuite.google.com/app/apikey"
storageKey="geminiKey"
placeholder="AIzaSy..."
validator={validateGemini}
/>
<InputRow
label="Hugging Face Token"
link="https://huggingface.co/settings/tokens"
storageKey="hfToken"
placeholder="hf_..."
validator={validateHF}
/>
<button
onClick={testConnections}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
Salvar e Testar Conexões
</button>
<p className="text-sm text-gray-600 mt-4">
As chaves são salvas apenas no <i>localStorage</i> do seu navegador e
enviadas ao servidor somente quando você usa o respectivo modelo.
</p>
</div>
);
}
|