"use client"; import { useState, useEffect } from "react"; import PropTypes from "prop-types"; import { Button, Badge, Input, Modal, Select } from "@/shared/components"; export default function EditCompatibleNodeModal({ isOpen, node, onSave, onClose, isAnthropic }) { const [formData, setFormData] = useState({ name: "", prefix: "", apiType: "chat", baseUrl: "https://api.openai.com/v1", }); const [saving, setSaving] = useState(false); const [checkKey, setCheckKey] = useState(""); const [checkModelId, setCheckModelId] = useState(""); const [validating, setValidating] = useState(false); const [validationResult, setValidationResult] = useState(null); useEffect(() => { if (node) { setFormData({ name: node.name || "", prefix: node.prefix || "", apiType: node.apiType || "chat", baseUrl: node.baseUrl || (isAnthropic ? "https://api.anthropic.com/v1" : "https://api.openai.com/v1"), }); } }, [node, isAnthropic]); const apiTypeOptions = [ { value: "chat", label: "Chat Completions" }, { value: "responses", label: "Responses API" }, ]; const handleSubmit = async () => { if (!formData.name.trim() || !formData.prefix.trim() || !formData.baseUrl.trim()) return; setSaving(true); try { const payload = { name: formData.name, prefix: formData.prefix, baseUrl: formData.baseUrl, }; if (!isAnthropic) { payload.apiType = formData.apiType; } await onSave(payload); } finally { setSaving(false); } }; const handleValidate = async () => { setValidating(true); try { const res = await fetch("/api/provider-nodes/validate", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ baseUrl: formData.baseUrl, apiKey: checkKey, type: isAnthropic ? "anthropic-compatible" : "openai-compatible", modelId: checkModelId.trim() || undefined }), }); const data = await res.json(); setValidationResult(data.valid ? "success" : "failed"); } catch { setValidationResult("failed"); } finally { setValidating(false); } }; if (!node) return null; return (
setFormData({ ...formData, name: e.target.value })} placeholder={`${isAnthropic ? "Anthropic" : "OpenAI"} Compatible (Prod)`} hint="Required. A friendly label for this node." /> setFormData({ ...formData, prefix: e.target.value })} placeholder={isAnthropic ? "ac-prod" : "oc-prod"} hint="Required. Used as the provider prefix for model IDs." /> {!isAnthropic && ( setFormData({ ...formData, baseUrl: e.target.value })} placeholder={isAnthropic ? "https://api.anthropic.com/v1" : "https://api.openai.com/v1"} hint={`Use the base URL (ending in /v1) for your ${isAnthropic ? "Anthropic" : "OpenAI"}-compatible API.`} />
setCheckKey(e.target.value)} className="flex-1" />
setCheckModelId(e.target.value)} placeholder="e.g. my-model-id" hint="If provider lacks /models endpoint, enter a model ID to validate via chat/completions instead." /> {validationResult && ( {validationResult === "success" ? "Valid" : "Invalid"} )}
); } EditCompatibleNodeModal.propTypes = { isOpen: PropTypes.bool.isRequired, node: PropTypes.shape({ id: PropTypes.string, name: PropTypes.string, prefix: PropTypes.string, apiType: PropTypes.string, baseUrl: PropTypes.string, }), onSave: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired, isAnthropic: PropTypes.bool, };