feat: Add OpenAI provider support
This commit is contained in:
parent
acce9b97f6
commit
f1e40d5908
@ -1,5 +1,11 @@
|
||||
import { ChromeIcon, CpuIcon } from "lucide-react"
|
||||
import { OllamaIcon } from "../Icons/Ollama"
|
||||
import { FireworksMonoIcon } from "../Icons/Fireworks"
|
||||
import { GroqMonoIcon } from "../Icons/Groq"
|
||||
import { LMStudioIcon } from "../Icons/LMStudio"
|
||||
import { OpenAiIcon } from "../Icons/OpenAI"
|
||||
import { TogtherMonoIcon } from "../Icons/Togther"
|
||||
import { OpenRouterIcon } from "../Icons/OpenRouter"
|
||||
|
||||
export const ProviderIcons = ({
|
||||
provider,
|
||||
@ -13,6 +19,18 @@ export const ProviderIcons = ({
|
||||
return <ChromeIcon className={className} />
|
||||
case "custom":
|
||||
return <CpuIcon className={className} />
|
||||
case "fireworks":
|
||||
return <FireworksMonoIcon className={className} />
|
||||
case "groq":
|
||||
return <GroqMonoIcon className={className} />
|
||||
case "lmstudio":
|
||||
return <LMStudioIcon className={className} />
|
||||
case "openai":
|
||||
return <OpenAiIcon className={className} />
|
||||
case "together":
|
||||
return <TogtherMonoIcon className={className} />
|
||||
case "openrouter":
|
||||
return <OpenRouterIcon className={className} />
|
||||
default:
|
||||
return <OllamaIcon className={className} />
|
||||
}
|
||||
|
19
src/components/Icons/Fireworks.tsx
Normal file
19
src/components/Icons/Fireworks.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React from "react"
|
||||
|
||||
export const FireworksMonoIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 638 315"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M318.563 221.755c-17.7 0-33.584-10.508-40.357-26.777L196.549 0h47.793l74.5 178.361L393.273 0h47.793L358.92 195.048c-6.808 16.199-22.657 26.707-40.357 26.707zM425.111 314.933c-17.63 0-33.444-10.439-40.287-26.567-6.877-16.269-3.317-34.842 9.112-47.445l148.721-150.64 18.572 43.813-136.153 137.654 194.071-1.082 18.573 43.813-212.574.524-.07-.07h.035zM0 314.408l18.573-43.813 194.07 1.082L76.525 133.988l18.573-43.813 148.721 150.641c12.428 12.568 16.024 31.21 9.111 47.444-6.842 16.164-22.727 26.567-40.287 26.567L.07 314.339l-.07.069z"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
18
src/components/Icons/Groq.tsx
Normal file
18
src/components/Icons/Groq.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react"
|
||||
|
||||
export const GroqMonoIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}>
|
||||
<path d="M12.036 2c-3.853-.035-7 3-7.036 6.781-.035 3.782 3.055 6.872 6.908 6.907h2.42v-2.566h-2.292c-2.407.028-4.38-1.866-4.408-4.23-.029-2.362 1.901-4.298 4.308-4.326h.1c2.407 0 4.358 1.915 4.365 4.278v6.305c0 2.342-1.944 4.25-4.323 4.279a4.375 4.375 0 01-3.033-1.252l-1.851 1.818A7 7 0 0012.029 22h.092c3.803-.056 6.858-3.083 6.879-6.816v-6.5C18.907 4.963 15.817 2 12.036 2z"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
33
src/components/Icons/LMStudio.tsx
Normal file
33
src/components/Icons/LMStudio.tsx
Normal file
File diff suppressed because one or more lines are too long
18
src/components/Icons/OpenAI.tsx
Normal file
18
src/components/Icons/OpenAI.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react"
|
||||
|
||||
export const OpenAiIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}>
|
||||
<path d="M21.55 10.004a5.416 5.416 0 00-.478-4.501c-1.217-2.09-3.662-3.166-6.05-2.66A5.59 5.59 0 0010.831 1C8.39.995 6.224 2.546 5.473 4.838A5.553 5.553 0 001.76 7.496a5.487 5.487 0 00.691 6.5 5.416 5.416 0 00.477 4.502c1.217 2.09 3.662 3.165 6.05 2.66A5.586 5.586 0 0013.168 23c2.443.006 4.61-1.546 5.361-3.84a5.553 5.553 0 003.715-2.66 5.488 5.488 0 00-.693-6.497v.001zm-8.381 11.558a4.199 4.199 0 01-2.675-.954c.034-.018.093-.05.132-.074l4.44-2.53a.71.71 0 00.364-.623v-6.176l1.877 1.069c.02.01.033.029.036.05v5.115c-.003 2.274-1.87 4.118-4.174 4.123zM4.192 17.78a4.059 4.059 0 01-.498-2.763c.032.02.09.055.131.078l4.44 2.53c.225.13.504.13.73 0l5.42-3.088v2.138a.068.068 0 01-.027.057L9.9 19.288c-1.999 1.136-4.552.46-5.707-1.51h-.001zM3.023 8.216A4.15 4.15 0 015.198 6.41l-.002.151v5.06a.711.711 0 00.364.624l5.42 3.087-1.876 1.07a.067.067 0 01-.063.005l-4.489-2.559c-1.995-1.14-2.679-3.658-1.53-5.63h.001zm15.417 3.54l-5.42-3.088L14.896 7.6a.067.067 0 01.063-.006l4.489 2.557c1.998 1.14 2.683 3.662 1.529 5.633a4.163 4.163 0 01-2.174 1.807V12.38a.71.71 0 00-.363-.623zm1.867-2.773a6.04 6.04 0 00-.132-.078l-4.44-2.53a.731.731 0 00-.729 0l-5.42 3.088V7.325a.068.068 0 01.027-.057L14.1 4.713c2-1.137 4.555-.46 5.707 1.513.487.833.664 1.809.499 2.757h.001zm-11.741 3.81l-1.877-1.068a.065.065 0 01-.036-.051V6.559c.001-2.277 1.873-4.122 4.181-4.12.976 0 1.92.338 2.671.954-.034.018-.092.05-.131.073l-4.44 2.53a.71.71 0 00-.365.623l-.003 6.173v.002zm1.02-2.168L12 9.25l2.414 1.375v2.75L12 14.75l-2.415-1.375v-2.75z"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
18
src/components/Icons/OpenRouter.tsx
Normal file
18
src/components/Icons/OpenRouter.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react"
|
||||
|
||||
export const OpenRouterIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path d="M16.804 1.957l7.22 4.105v.087L16.73 10.21l.017-2.117-.821-.03c-1.059-.028-1.611.002-2.268.11-1.064.175-2.038.577-3.147 1.352L8.345 11.03c-.284.195-.495.336-.68.455l-.515.322-.397.234.385.23.53.338c.476.314 1.17.796 2.701 1.866 1.11.775 2.083 1.177 3.147 1.352l.3.045c.694.091 1.375.094 2.825.033l.022-2.159 7.22 4.105v.087L16.589 22l.014-1.862-.635.022c-1.386.042-2.137.002-3.138-.162-1.694-.28-3.26-.926-4.881-2.059l-2.158-1.5a21.997 21.997 0 00-.755-.498l-.467-.28a55.927 55.927 0 00-.76-.43C2.908 14.73.563 14.116 0 14.116V9.888l.14.004c.564-.007 2.91-.622 3.809-1.124l1.016-.58.438-.274c.428-.28 1.072-.726 2.686-1.853 1.621-1.133 3.186-1.78 4.881-2.059 1.152-.19 1.974-.213 3.814-.138l.02-1.907z"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
23
src/components/Icons/Togther.tsx
Normal file
23
src/components/Icons/Togther.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React from "react"
|
||||
|
||||
export const TogtherMonoIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}>
|
||||
<g>
|
||||
<path
|
||||
d="M17.385 11.23a4.615 4.615 0 100-9.23 4.615 4.615 0 000 9.23zm0 10.77a4.615 4.615 0 100-9.23 4.615 4.615 0 000 9.23zm-10.77 0a4.615 4.615 0 100-9.23 4.615 4.615 0 000 9.23z"
|
||||
opacity=".2"></path>
|
||||
<circle cx="6.615" cy="6.615" r="4.615"></circle>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -8,7 +8,7 @@ import {
|
||||
updateOpenAIConfig
|
||||
} from "@/db/openai"
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
||||
import { Pencil, Trash2, RotateCwIcon } from "lucide-react"
|
||||
import { Pencil, Trash2, RotateCwIcon, DownloadIcon } from "lucide-react"
|
||||
import { OpenAIFetchModel } from "./openai-fetch-model"
|
||||
import { OAI_API_PROVIDERS } from "@/utils/oai-api-providers"
|
||||
|
||||
@ -20,6 +20,7 @@ export const OpenAIApp = () => {
|
||||
const [form] = Form.useForm()
|
||||
const [openaiId, setOpenaiId] = useState<string | null>(null)
|
||||
const [openModelModal, setOpenModelModal] = useState(false)
|
||||
const [provider, setProvider] = useState("custom")
|
||||
|
||||
const { data: configs, isLoading } = useQuery({
|
||||
queryKey: ["openAIConfigs"],
|
||||
@ -69,8 +70,13 @@ export const OpenAIApp = () => {
|
||||
if (editingConfig) {
|
||||
updateMutation.mutate({ id: editingConfig.id, ...values })
|
||||
} else {
|
||||
addMutation.mutate(values)
|
||||
addMutation.mutate({
|
||||
...values,
|
||||
provider
|
||||
})
|
||||
}
|
||||
|
||||
setProvider("custom")
|
||||
}
|
||||
|
||||
const handleEdit = (record: any) => {
|
||||
@ -144,7 +150,7 @@ export const OpenAIApp = () => {
|
||||
setOpenaiId(record.id)
|
||||
}}
|
||||
disabled={!record.id}>
|
||||
<RotateCwIcon className="size-4" />
|
||||
<DownloadIcon className="size-4" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("delete")}>
|
||||
@ -180,18 +186,20 @@ export const OpenAIApp = () => {
|
||||
onCancel={() => {
|
||||
setOpen(false)
|
||||
setEditingConfig(null)
|
||||
setProvider("custom")
|
||||
form.resetFields()
|
||||
}}
|
||||
footer={null}>
|
||||
{!editingConfig && (
|
||||
<Select
|
||||
defaultValue="custom"
|
||||
value={provider}
|
||||
onSelect={(e) => {
|
||||
const value = OAI_API_PROVIDERS.find((item) => item.value === e)
|
||||
form.setFieldsValue({
|
||||
baseUrl: value?.baseUrl,
|
||||
name: value?.label
|
||||
})
|
||||
setProvider(e)
|
||||
}}
|
||||
className="w-full !mb-4"
|
||||
options={OAI_API_PROVIDERS}
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { getOpenAIConfigById as providerInfo } from "./openai"
|
||||
import {
|
||||
getAllOpenAIConfig,
|
||||
getOpenAIConfigById as providerInfo
|
||||
} from "./openai"
|
||||
|
||||
type Model = {
|
||||
id: string
|
||||
@ -16,11 +19,15 @@ export const generateID = () => {
|
||||
}
|
||||
|
||||
export const removeModelSuffix = (id: string) => {
|
||||
return id.replace(/_model-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{3,4}-[a-f0-9]{4}/, "")
|
||||
return id.replace(
|
||||
/_model-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{3,4}-[a-f0-9]{4}/,
|
||||
""
|
||||
)
|
||||
}
|
||||
|
||||
export const isCustomModel = (model: string) => {
|
||||
const customModelRegex = /_model-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{3,4}-[a-f0-9]{4}/
|
||||
const customModelRegex =
|
||||
/_model-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{3,4}-[a-f0-9]{4}/
|
||||
return customModelRegex.test(model)
|
||||
}
|
||||
export class ModelDb {
|
||||
@ -174,6 +181,17 @@ export const deleteModel = async (id: string) => {
|
||||
await db.delete(id)
|
||||
}
|
||||
|
||||
export const deleteAllModelsByProviderId = async (provider_id: string) => {
|
||||
const db = new ModelDb()
|
||||
const models = await db.getAll()
|
||||
const modelsToDelete = models.filter(
|
||||
(model) => model.provider_id === provider_id
|
||||
)
|
||||
for (const model of modelsToDelete) {
|
||||
await db.delete(model.id)
|
||||
}
|
||||
}
|
||||
|
||||
export const isLookupExist = async (lookup: string) => {
|
||||
const db = new ModelDb()
|
||||
const models = await db.getAll()
|
||||
@ -181,17 +199,19 @@ export const isLookupExist = async (lookup: string) => {
|
||||
return model ? true : false
|
||||
}
|
||||
|
||||
|
||||
export const ollamaFormatAllCustomModels = async () => {
|
||||
|
||||
const allModles = await getAllCustomModels()
|
||||
|
||||
const allProviders = await getAllOpenAIConfig()
|
||||
|
||||
const ollamaModels = allModles.map((model) => {
|
||||
return {
|
||||
name: model.name,
|
||||
model: model.id,
|
||||
modified_at: "",
|
||||
provider: "custom",
|
||||
provider:
|
||||
allProviders.find((provider) => provider.id === model.provider_id)
|
||||
?.provider || "custom",
|
||||
size: 0,
|
||||
digest: "",
|
||||
details: {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { cleanUrl } from "@/libs/clean-url"
|
||||
import { deleteAllModelsByProviderId } from "./models"
|
||||
|
||||
type OpenAIModelConfig = {
|
||||
id: string
|
||||
@ -93,7 +94,7 @@ export class OpenAIModelDb {
|
||||
}
|
||||
|
||||
|
||||
export const addOpenAICofig = async ({ name, baseUrl, apiKey }: { name: string, baseUrl: string, apiKey: string }) => {
|
||||
export const addOpenAICofig = async ({ name, baseUrl, apiKey, provider }: { name: string, baseUrl: string, apiKey: string, provider?: string }) => {
|
||||
const openaiDb = new OpenAIModelDb()
|
||||
const id = generateID()
|
||||
const config: OpenAIModelConfig = {
|
||||
@ -102,7 +103,8 @@ export const addOpenAICofig = async ({ name, baseUrl, apiKey }: { name: string,
|
||||
baseUrl: cleanUrl(baseUrl),
|
||||
apiKey,
|
||||
createdAt: Date.now(),
|
||||
db_type: "openai"
|
||||
db_type: "openai",
|
||||
provider
|
||||
}
|
||||
await openaiDb.create(config)
|
||||
return id
|
||||
@ -117,13 +119,15 @@ export const getAllOpenAIConfig = async () => {
|
||||
|
||||
export const updateOpenAIConfig = async ({ id, name, baseUrl, apiKey }: { id: string, name: string, baseUrl: string, apiKey: string }) => {
|
||||
const openaiDb = new OpenAIModelDb()
|
||||
const oldData = await openaiDb.getById(id)
|
||||
const config: OpenAIModelConfig = {
|
||||
...oldData,
|
||||
id,
|
||||
name,
|
||||
baseUrl: cleanUrl(baseUrl),
|
||||
apiKey,
|
||||
createdAt: Date.now(),
|
||||
db_type: "openai"
|
||||
db_type: "openai",
|
||||
}
|
||||
|
||||
await openaiDb.update(config)
|
||||
@ -135,6 +139,7 @@ export const updateOpenAIConfig = async ({ id, name, baseUrl, apiKey }: { id: st
|
||||
export const deleteOpenAIConfig = async (id: string) => {
|
||||
const openaiDb = new OpenAIModelDb()
|
||||
await openaiDb.delete(id)
|
||||
await deleteAllModelsByProviderId(id)
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,25 +1,40 @@
|
||||
type Model = {
|
||||
id: string
|
||||
name?: string
|
||||
display_name?: string
|
||||
type: string
|
||||
}
|
||||
|
||||
export const getAllOpenAIModels = async (baseUrl: string, apiKey?: string) => {
|
||||
const url = `${baseUrl}/models`
|
||||
const headers = apiKey
|
||||
? {
|
||||
try {
|
||||
const url = `${baseUrl}/models`
|
||||
const headers = apiKey
|
||||
? {
|
||||
Authorization: `Bearer ${apiKey}`
|
||||
}
|
||||
: {}
|
||||
}
|
||||
: {}
|
||||
|
||||
const res = await fetch(url, {
|
||||
headers
|
||||
})
|
||||
const res = await fetch(url, {
|
||||
headers
|
||||
})
|
||||
|
||||
if (!res.ok) {
|
||||
if (!res.ok) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (baseUrl === "https://api.together.xyz/v1") {
|
||||
const data = (await res.json()) as Model[]
|
||||
return data.map(model => ({
|
||||
id: model.id,
|
||||
name: model.display_name,
|
||||
}))
|
||||
}
|
||||
|
||||
const data = (await res.json()) as { data: Model[] }
|
||||
|
||||
return data.data
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return []
|
||||
}
|
||||
|
||||
const data = (await res.json()) as { data: Model[] }
|
||||
|
||||
return data.data
|
||||
}
|
||||
|
@ -24,6 +24,11 @@ export const OAI_API_PROVIDERS = [
|
||||
value: "together",
|
||||
baseUrl: "https://api.together.xyz/v1"
|
||||
},
|
||||
{
|
||||
label: "OpenRouter",
|
||||
value: "openrouter",
|
||||
baseUrl: "https://openrouter.ai/api/v1"
|
||||
},
|
||||
{
|
||||
label: "Custsom",
|
||||
value: "custom",
|
||||
|
Loading…
x
Reference in New Issue
Block a user