mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-16 23:01:06 +02:00
Add Requesty (https://requesty.ai), an OpenAI-compatible LLM router, as a model provider by mirroring the existing OpenRouter integration. Backend: - app/services/requesty_model_normalizer.py: normalizes Requesty's /v1/models catalogue, mapping its flat capability booleans (supports_tool_calling/ supports_vision/supports_image_generation) and context_window field onto the shared normalized shape (Requesty differs from OpenRouter's architecture + supported_parameters + context_length layout) - provider_registry.py: Requesty ProviderSpec (OpenAI-compatible, base URL https://router.requesty.ai/v1, REQUESTY_API_KEY bearer auth) - model_connection_service.py: key verification + live model discovery - quality_score.py: Requesty score entry - unit tests mirroring the OpenRouter normalizer coverage Frontend: - Requesty provider icon + registration, metadata entry, and base-url hint Signed-off-by: Thibault Jaigu <thibault.jaigu@gmail.com>
67 lines
1.9 KiB
TypeScript
67 lines
1.9 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { ApiBaseUrlField, ApiKeyField } from "./connect-fields";
|
|
import type { ProviderConnectFormProps } from "./provider-metadata";
|
|
|
|
const OPTIONAL_API_KEY_PROVIDERS = new Set(["ollama_chat", "lm_studio", "openai_compatible"]);
|
|
|
|
function baseUrlHint(provider: string) {
|
|
if (provider === "ollama_chat" || provider === "lm_studio") {
|
|
return "For local servers, use host.docker.internal instead of localhost.";
|
|
}
|
|
if (provider === "openai_compatible") {
|
|
return "Enter the full endpoint URL.";
|
|
}
|
|
if (
|
|
provider === "openai" ||
|
|
provider === "anthropic" ||
|
|
provider === "openrouter" ||
|
|
provider === "requesty"
|
|
) {
|
|
return "Override only if you route through a proxy or gateway.";
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Connect form for OpenAI-compatible / native key providers (OpenAI, Anthropic,
|
|
* OpenRouter, OpenAI-Compatible, LM Studio, Ollama, …). The base URL is
|
|
* prefilled from the provider default.
|
|
*/
|
|
export function DefaultConnectForm({
|
|
provider,
|
|
defaultBaseUrl,
|
|
baseUrlRequired,
|
|
onDraftChange,
|
|
}: ProviderConnectFormProps) {
|
|
const [baseUrl, setBaseUrl] = useState(defaultBaseUrl);
|
|
const [apiKey, setApiKey] = useState("");
|
|
const isApiKeyOptional = OPTIONAL_API_KEY_PROVIDERS.has(provider);
|
|
const hint = baseUrlHint(provider);
|
|
const apiKeyValue = apiKey.trim();
|
|
const canSubmit =
|
|
!(baseUrlRequired && !baseUrl.trim()) && (isApiKeyOptional || Boolean(apiKeyValue));
|
|
|
|
useEffect(() => {
|
|
onDraftChange(
|
|
{ base_url: baseUrl || null, api_key: apiKeyValue || null, extra: {} },
|
|
canSubmit
|
|
);
|
|
}, [apiKeyValue, baseUrl, canSubmit, onDraftChange]);
|
|
|
|
return (
|
|
<div className="flex flex-col gap-4">
|
|
<ApiBaseUrlField
|
|
value={baseUrl}
|
|
onChange={setBaseUrl}
|
|
placeholder={defaultBaseUrl}
|
|
hint={hint}
|
|
/>
|
|
<ApiKeyField
|
|
value={apiKey}
|
|
onChange={setApiKey}
|
|
label={isApiKeyOptional ? "API Key (optional)" : "API Key"}
|
|
placeholder="Enter your API key"
|
|
/>
|
|
</div>
|
|
);
|
|
}
|