diff --git a/.gitignore b/.gitignore index be36d9a..912a1a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,6 @@ -ui_example node_modules ui/node_modules ui/ModelModal/node_modules ui/ModelModal/dist .DS_Store -main -main.go -handler -templates +test/ui_example/dist diff --git a/consts/model.go b/consts/model.go index 7b9f14f..15d05aa 100644 --- a/consts/model.go +++ b/consts/model.go @@ -1,7 +1,6 @@ package consts import ( - "errors" "strings" ) @@ -23,47 +22,88 @@ const ( ModelTypeFunctionCall ModelType = "function_call" ) -func ParseModelType(s string) (ModelType, error) { +func ParseModelType(s string) ModelType { switch s { case "llm", "chat": - return ModelTypeChat, nil + return ModelTypeChat case "coder", "code": - return ModelTypeCoder, nil + return ModelTypeCoder case "embedding": - return ModelTypeEmbedding, nil + return ModelTypeEmbedding case "reranker", "rerank": - return ModelTypeRerank, nil + return ModelTypeRerank case "vision": - return ModelTypeVision, nil + return ModelTypeVision case "function_call": - return ModelTypeFunctionCall, nil + return ModelTypeFunctionCall default: - return "", errors.New("invalid model type") + return ModelTypeChat } } type ModelProvider string const ( - ModelProviderSiliconFlow ModelProvider = "SiliconFlow" - ModelProviderOpenAI ModelProvider = "OpenAI" - ModelProviderOllama ModelProvider = "Ollama" - ModelProviderDeepSeek ModelProvider = "DeepSeek" - ModelProviderMoonshot ModelProvider = "Moonshot" - ModelProviderAzureOpenAI ModelProvider = "AzureOpenAI" - ModelProviderBaiZhiCloud ModelProvider = "BaiZhiCloud" - ModelProviderHunyuan ModelProvider = "Hunyuan" - ModelProviderBaiLian ModelProvider = "BaiLian" - ModelProviderVolcengine ModelProvider = "Volcengine" - ModelProviderGemini ModelProvider = "Gemini" - ModelProviderZhiPu ModelProvider = "ZhiPu" - ModelProviderOther ModelProvider = "Other" + ModelProviderSiliconFlow ModelProvider = "SiliconFlow" + ModelProviderOpenAI ModelProvider = "OpenAI" + ModelProviderOllama ModelProvider = "Ollama" + ModelProviderDeepSeek ModelProvider = "DeepSeek" + ModelProviderMoonshot ModelProvider = "Moonshot" + ModelProviderAzureOpenAI ModelProvider = "AzureOpenAI" + ModelProviderBaiZhiCloud ModelProvider = "BaiZhiCloud" + ModelProviderHunyuan ModelProvider = "Hunyuan" + ModelProviderBaiLian ModelProvider = "BaiLian" + ModelProviderVolcengine ModelProvider = "Volcengine" + ModelProviderGemini ModelProvider = "Gemini" + ModelProviderZhiPu ModelProvider = "ZhiPu" + ModelProviderAiHubMix ModelProvider = "AiHubMix" + ModelProviderOcoolAI ModelProvider = "OcoolAI" + ModelProviderPPIO ModelProvider = "PPIO" + ModelProviderAlayaNew ModelProvider = "AlayaNew" + ModelProviderQiniu ModelProvider = "Qiniu" + ModelProviderDMXAPI ModelProvider = "DMXAPI" + ModelProviderBurnCloud ModelProvider = "BurnCloud" + ModelProviderTokenFlux ModelProvider = "TokenFlux" + ModelProvider302AI ModelProvider = "302AI" + ModelProviderCephalon ModelProvider = "Cephalon" + ModelProviderLanyun ModelProvider = "Lanyun" + ModelProviderPH8 ModelProvider = "PH8" + ModelProviderOpenRouter ModelProvider = "OpenRouter" + ModelProviderNewAPI ModelProvider = "NewAPI" + ModelProviderLMStudio ModelProvider = "LMStudio" + ModelProviderAnthropic ModelProvider = "Anthropic" + ModelProviderVertexAI ModelProvider = "VertexAI" + ModelProviderGithub ModelProvider = "Github" + ModelProviderCopilot ModelProvider = "Copilot" + ModelProviderYi ModelProvider = "Yi" + ModelProviderBaichuan ModelProvider = "Baichuan" + ModelProviderStepFun ModelProvider = "StepFun" + ModelProviderInfini ModelProvider = "Infini" + ModelProviderMiniMax ModelProvider = "MiniMax" + ModelProviderGroq ModelProvider = "Groq" + ModelProviderTogether ModelProvider = "Together" + ModelProviderFireworks ModelProvider = "Fireworks" + ModelProviderNvidia ModelProvider = "Nvidia" + ModelProviderGrok ModelProvider = "Grok" + ModelProviderHyperbolic ModelProvider = "Hyperbolic" + ModelProviderMistral ModelProvider = "Mistral" + ModelProviderJina ModelProvider = "Jina" + ModelProviderPerplexity ModelProvider = "Perplexity" + ModelProviderModelScope ModelProvider = "ModelScope" + ModelProviderXirang ModelProvider = "Xirang" + ModelProviderTencentCloudTI ModelProvider = "TencentCloudTI" + ModelProviderBaiduCloud ModelProvider = "BaiduCloud" + ModelProviderGPUStack ModelProvider = "GPUStack" + ModelProviderVoyageAI ModelProvider = "VoyageAI" + ModelProviderAWSBedrock ModelProvider = "AWSBedrock" + ModelProviderPoe ModelProvider = "Poe" + ModelProviderOther ModelProvider = "Other" ) func ParseModelProvider(s string) ModelProvider { // 转换为小写进行不区分大小写的比较 switch strings.ToLower(s) { - case "siliconflow": + case "siliconflow", "silicon": return ModelProviderSiliconFlow case "openai": return ModelProviderOpenAI @@ -73,7 +113,7 @@ func ParseModelProvider(s string) ModelProvider { return ModelProviderDeepSeek case "moonshot": return ModelProviderMoonshot - case "azureopenai": + case "azureopenai", "azure-openai": return ModelProviderAzureOpenAI case "baizhicloud", "baizhiyun": return ModelProviderBaiZhiCloud @@ -81,12 +121,94 @@ func ParseModelProvider(s string) ModelProvider { return ModelProviderHunyuan case "bailian": return ModelProviderBaiLian - case "volcengine": + case "volcengine", "doubao": return ModelProviderVolcengine case "gemini": return ModelProviderGemini case "zhipu": return ModelProviderZhiPu + case "aihubmix": + return ModelProviderAiHubMix + case "ocoolai": + return ModelProviderOcoolAI + case "ppio": + return ModelProviderPPIO + case "alayanew": + return ModelProviderAlayaNew + case "qiniu": + return ModelProviderQiniu + case "dmxapi": + return ModelProviderDMXAPI + case "burncloud": + return ModelProviderBurnCloud + case "tokenflux": + return ModelProviderTokenFlux + case "302ai": + return ModelProvider302AI + case "cephalon": + return ModelProviderCephalon + case "lanyun": + return ModelProviderLanyun + case "ph8": + return ModelProviderPH8 + case "openrouter": + return ModelProviderOpenRouter + case "new-api": + return ModelProviderNewAPI + case "lmstudio": + return ModelProviderLMStudio + case "anthropic": + return ModelProviderAnthropic + case "vertexai": + return ModelProviderVertexAI + case "github": + return ModelProviderGithub + case "copilot": + return ModelProviderCopilot + case "yi": + return ModelProviderYi + case "baichuan": + return ModelProviderBaichuan + case "stepfun": + return ModelProviderStepFun + case "infini": + return ModelProviderInfini + case "minimax": + return ModelProviderMiniMax + case "groq": + return ModelProviderGroq + case "together": + return ModelProviderTogether + case "fireworks": + return ModelProviderFireworks + case "nvidia": + return ModelProviderNvidia + case "grok": + return ModelProviderGrok + case "hyperbolic": + return ModelProviderHyperbolic + case "mistral": + return ModelProviderMistral + case "jina": + return ModelProviderJina + case "perplexity": + return ModelProviderPerplexity + case "modelscope": + return ModelProviderModelScope + case "xirang": + return ModelProviderXirang + case "tencent-cloud-ti": + return ModelProviderTencentCloudTI + case "baidu-cloud": + return ModelProviderBaiduCloud + case "gpustack": + return ModelProviderGPUStack + case "voyageai": + return ModelProviderVoyageAI + case "aws-bedrock": + return ModelProviderAWSBedrock + case "poe": + return ModelProviderPoe default: return ModelProviderOther } diff --git a/domain/domain.go b/domain/domain.go new file mode 100644 index 0000000..53a725f --- /dev/null +++ b/domain/domain.go @@ -0,0 +1,106 @@ +package domain + +import ( + "github.com/chaitin/ModelKit/consts" +) + +type ModelKit interface { + // CheckModel(ctx context.Context, req *CheckModelReq) (*Model, error) +} + +type ModelListReq struct { + Provider string `json:"provider" query:"provider" validate:"required"` + BaseURL string `json:"base_url" query:"base_url" validate:"required"` + APIKey string `json:"api_key" query:"api_key"` + APIHeader string `json:"api_header" query:"api_header"` + Type string `json:"type" query:"type" validate:"required"` +} + +type Response struct { + Message string `json:"message"` + Success bool `json:"success"` + Data any `json:"data,omitempty"` +} + +type ModelListResp struct { + Models []ModelListItem `json:"models"` +} + +type ModelListItem struct { + Model string `json:"model"` +} + +type CheckModelReq struct { + Provider string `json:"provider" query:"provider" validate:"required,oneof=OpenAI Ollama DeepSeek SiliconFlow Moonshot Other AzureOpenAI BaiZhiCloud Hunyuan BaiLian Volcengine Gemini ZhiPu AiHubMix"` + Model string `json:"model" query:"model_name" validate:"required"` + BaseURL string `json:"base_url" query:"base_url" validate:"required"` + APIKey string `json:"api_key" query:"api_key"` + APIHeader string `json:"api_header" query:"api_header"` + APIVersion string `json:"api_version" query:"api_version"` // for azure openai + Type string `json:"type" query:"model_type" validate:"required,oneof=chat embedding rerank llm"` +} + +type CheckModelResp struct { + Error string `json:"error"` + Content string `json:"content"` +} + +func getModelsByOwner(owner consts.ModelProvider) []ModelMetadata { + var ms []ModelMetadata + for i := range Models { + if Models[i].Provider == owner { + ms = append(ms, Models[i]) + } + } + return ms +} + +func init() { + initModels() + initModelProviders() +} + +func From(ModelProvider ModelProvider) []ModelListItem { + var result []ModelListItem + for _, model := range ModelProvider.Models { + result = append(result, ModelListItem{ + Model: model.ModelName, + }) + } + return result +} + +// ModelResponseParser 定义模型响应解析器接口 +type ModelResponseParser interface { + ParseModels() []ModelListItem +} + +type GithubModel struct { + ID string `json:"id"` + Name string `json:"name"` + Registry string `json:"registry"` + Publisher string `json:"publisher"` + Summary string `json:"summary"` + RateLimitTier string `json:"rate_limit_tier"` + HTMLURL string `json:"html_url"` + Version string `json:"version"` + Capabilities []string `json:"capabilities"` + Limits struct { + MaxInputTokens int `json:"max_input_tokens"` + MaxOutputTokens int `json:"max_output_tokens"` + } `json:"limits"` + Tags []string `json:"tags"` + SupportedInputModalities []string `json:"supported_input_modalities"` + SupportedOutputModalities []string `json:"supported_output_modalities"` +} + +type GithubResp []GithubModel + +// ParseModels 实现ModelResponseParser接口 +func (g *GithubResp) ParseModels() []ModelListItem { + var models []ModelListItem + for _, item := range *g { + models = append(models, ModelListItem{Model: item.ID}) + } + return models +} diff --git a/domain/modelkit.go b/domain/model.go similarity index 88% rename from domain/modelkit.go rename to domain/model.go index 432bb84..a230c2d 100644 --- a/domain/modelkit.go +++ b/domain/model.go @@ -1,58 +1,6 @@ package domain -import ( - "github.com/chaitin/ModelKit/consts" -) - -type ModelKit interface { - // CheckModel(ctx context.Context, req *CheckModelReq) (*Model, error) -} - -type ModelListReq struct { - Provider string `json:"provider" query:"provider" validate:"required,oneof=SiliconFlow OpenAI Ollama DeepSeek Moonshot AzureOpenAI BaiZhiCloud Hunyuan BaiLian Volcengine Gemini ZhiPu"` - BaseURL string `json:"base_url" query:"base_url" validate:"required"` - APIKey string `json:"api_key" query:"api_key"` - APIHeader string `json:"api_header" query:"api_header"` - Type string `json:"type" query:"type" validate:"required,oneof=chat embedding rerank"` -} - -type Response struct { - Message string `json:"message"` - Success bool `json:"success"` - Data any `json:"data,omitempty"` -} - -type ModelListResp struct { - Models []ModelListItem `json:"models"` -} - -type ModelListItem struct { - Model string `json:"model"` -} - -type CheckModelReq struct { - Provider string `json:"provider" validate:"required,oneof=OpenAI Ollama DeepSeek SiliconFlow Moonshot Other AzureOpenAI BaiZhiCloud Hunyuan BaiLian Volcengine Gemini ZhiPu"` - Model string `json:"model" validate:"required"` - BaseURL string `json:"base_url" validate:"required"` - APIKey string `json:"api_key"` - APIHeader string `json:"api_header"` - APIVersion string `json:"api_version"` // for azure openai - Type string `json:"type" validate:"required,oneof=chat embedding rerank"` -} - -type ModelProvider struct { - OwnerName consts.ModelProvider `json:"owner_name"` // 提供商 - APIBase string `json:"api_base"` // 接口地址 如:https://api.qwen.com - APIKey string `json:"api_key"` // 接口密钥 如:sk-xxxx - APIVersion string `json:"api_version"` // 接口版本 如:2023-05-15 - APIHeader string `json:"api_header"` // 接口头 如:Authorization: Bearer sk-xxxx - Models []ModelMetadata `json:"models"` // 模型列表 -} - -type CheckModelResp struct { - Error string `json:"error"` - Content string `json:"content"` -} +import "github.com/chaitin/ModelKit/consts" type ModelMetadata struct { ModelName string `json:"id"` // 模型的名字 @@ -68,23 +16,6 @@ type ModelMetadata struct { } var Models []ModelMetadata -var ModelProviders map[consts.ModelProvider]ModelProvider -var TypeModelMap map[consts.ModelType][]ModelMetadata - -func getModelsByOwner(owner consts.ModelProvider) []ModelMetadata { - var ms []ModelMetadata - for i := range Models { - if Models[i].Provider == owner { - ms = append(ms, Models[i]) - } - } - return ms -} - -func init() { - initModels() - initModelProviders() -} // getBaiZhiCloudModels 返回百智云模型列表 func getBaiZhiCloudModels() []ModelMetadata { @@ -486,92 +417,4 @@ func initModels() { Models = append(Models, getAzureOpenAIModels()...) Models = append(Models, getZhiPuModels()...) Models = append(Models, getGeminiModels()...) -} - -func initModelProviders() { - // 初始化模型提供商及其模型 - ModelProviders = map[consts.ModelProvider]ModelProvider{ - consts.ModelProviderBaiZhiCloud: { - OwnerName: consts.ModelProviderBaiZhiCloud, - Models: getModelsByOwner(consts.ModelProviderBaiZhiCloud), - APIBase: "https://model-square.app.baizhi.cloud/v1", - }, - consts.ModelProviderDeepSeek: { - OwnerName: consts.ModelProviderDeepSeek, - Models: getModelsByOwner(consts.ModelProviderDeepSeek), - APIBase: "https://api.deepseek.com/v1", - }, - consts.ModelProviderSiliconFlow: { - OwnerName: consts.ModelProviderSiliconFlow, - Models: getModelsByOwner(consts.ModelProviderSiliconFlow), - APIBase: "https://api.siliconflow.cn/v1", - }, - consts.ModelProviderOpenAI: { - OwnerName: consts.ModelProviderOpenAI, - Models: getModelsByOwner(consts.ModelProviderOpenAI), - APIBase: "https://api.openai.com/v1", - }, - consts.ModelProviderOllama: { - OwnerName: consts.ModelProviderOllama, - Models: getModelsByOwner(consts.ModelProviderOllama), - APIBase: "http://localhost:11434", - }, - consts.ModelProviderMoonshot: { - OwnerName: consts.ModelProviderMoonshot, - Models: getModelsByOwner(consts.ModelProviderMoonshot), - APIBase: "https://api.moonshot.cn/v1", - }, - consts.ModelProviderAzureOpenAI: { - OwnerName: consts.ModelProviderAzureOpenAI, - Models: getModelsByOwner(consts.ModelProviderAzureOpenAI), - }, - consts.ModelProviderHunyuan: { - OwnerName: consts.ModelProviderHunyuan, - Models: getModelsByOwner(consts.ModelProviderHunyuan), - APIBase: "https://api.hunyuan.cloud.tencent.com/v1", - }, - consts.ModelProviderBaiLian: { - OwnerName: consts.ModelProviderBaiLian, - Models: getModelsByOwner(consts.ModelProviderBaiLian), - APIBase: "https://dashscope.aliyuncs.com/compatible-mode/v1", - }, - consts.ModelProviderVolcengine: { - OwnerName: consts.ModelProviderVolcengine, - Models: getModelsByOwner(consts.ModelProviderVolcengine), - APIBase: "https://ark.cn-beijing.volces.com/api/v3", - }, - consts.ModelProviderGemini: { - OwnerName: consts.ModelProviderGemini, - Models: getModelsByOwner(consts.ModelProviderGemini), - APIBase: "https://generativelanguage.googleapis.com/v1beta", - }, - consts.ModelProviderZhiPu: { - OwnerName: consts.ModelProviderZhiPu, - Models: getModelsByOwner(consts.ModelProviderZhiPu), - APIBase: "https://open.bigmodel.cn/api/paas/v4", - }, - } - - // 初始化按类型分组的模型映射 - TypeModelMap = make(map[consts.ModelType][]ModelMetadata) - for i := range Models { - model := Models[i] - TypeModelMap[model.ModelType] = append(TypeModelMap[model.ModelType], model) - } -} - -type Resp struct { - Code int `json:"code"` - Message string `json:"message"` - Data any `json:"data,omitempty"` -} - -func From(ModelProvider ModelProvider) []ModelListItem { - var result []ModelListItem - for _, model := range ModelProvider.Models { - result = append(result, ModelListItem{ - Model: model.ModelName, - }) - } - return result -} +} \ No newline at end of file diff --git a/domain/modelprovider.go b/domain/modelprovider.go index 8595b29..edd42a1 100644 --- a/domain/modelprovider.go +++ b/domain/modelprovider.go @@ -1,5 +1,296 @@ package domain +import "github.com/chaitin/ModelKit/consts" + type IModelProvider[T any] interface { ListModel(subType string, provider string) ([]T, error) +} + +var ModelProviders map[consts.ModelProvider]ModelProvider +var TypeModelMap map[consts.ModelType][]ModelMetadata + +type ModelProvider struct { + OwnerName consts.ModelProvider `json:"owner_name"` // 提供商 + APIBase string `json:"api_base"` // 接口地址 如:https://api.qwen.com + APIKey string `json:"api_key"` // 接口密钥 如:sk-xxxx + APIVersion string `json:"api_version"` // 接口版本 如:2023-05-15 + APIHeader string `json:"api_header"` // 接口头 如:Authorization: Bearer sk-xxxx + Models []ModelMetadata `json:"models"` // 模型列表 +} + +func initModelProviders() { + // 初始化模型提供商及其模型 + ModelProviders = map[consts.ModelProvider]ModelProvider{ + consts.ModelProviderBaiZhiCloud: { + OwnerName: consts.ModelProviderBaiZhiCloud, + Models: getModelsByOwner(consts.ModelProviderBaiZhiCloud), + APIBase: "https://model-square.app.baizhi.cloud/v1", + }, + consts.ModelProviderDeepSeek: { + OwnerName: consts.ModelProviderDeepSeek, + Models: getModelsByOwner(consts.ModelProviderDeepSeek), + APIBase: "https://api.deepseek.com/v1", + }, + consts.ModelProviderSiliconFlow: { + OwnerName: consts.ModelProviderSiliconFlow, + Models: getModelsByOwner(consts.ModelProviderSiliconFlow), + APIBase: "https://api.siliconflow.cn/v1", + }, + consts.ModelProviderOpenAI: { + OwnerName: consts.ModelProviderOpenAI, + Models: getModelsByOwner(consts.ModelProviderOpenAI), + APIBase: "https://api.openai.com/v1", + }, + consts.ModelProviderOllama: { + OwnerName: consts.ModelProviderOllama, + Models: getModelsByOwner(consts.ModelProviderOllama), + APIBase: "http://localhost:11434", + }, + consts.ModelProviderMoonshot: { + OwnerName: consts.ModelProviderMoonshot, + Models: getModelsByOwner(consts.ModelProviderMoonshot), + APIBase: "https://api.moonshot.cn/v1", + }, + consts.ModelProviderAzureOpenAI: { + OwnerName: consts.ModelProviderAzureOpenAI, + Models: getModelsByOwner(consts.ModelProviderAzureOpenAI), + }, + consts.ModelProviderHunyuan: { + OwnerName: consts.ModelProviderHunyuan, + Models: getModelsByOwner(consts.ModelProviderHunyuan), + APIBase: "https://api.hunyuan.cloud.tencent.com/v1", + }, + consts.ModelProviderBaiLian: { + OwnerName: consts.ModelProviderBaiLian, + Models: getModelsByOwner(consts.ModelProviderBaiLian), + APIBase: "https://dashscope.aliyuncs.com/compatible-mode/v1", + }, + consts.ModelProviderVolcengine: { + OwnerName: consts.ModelProviderVolcengine, + Models: getModelsByOwner(consts.ModelProviderVolcengine), + APIBase: "https://ark.cn-beijing.volces.com/api/v3", + }, + consts.ModelProviderGemini: { + OwnerName: consts.ModelProviderGemini, + Models: getModelsByOwner(consts.ModelProviderGemini), + APIBase: "https://generativelanguage.googleapis.com/v1beta", + }, + consts.ModelProviderZhiPu: { + OwnerName: consts.ModelProviderZhiPu, + Models: getModelsByOwner(consts.ModelProviderZhiPu), + APIBase: "https://open.bigmodel.cn/api/paas/v4", + }, + consts.ModelProviderAiHubMix: { + OwnerName: consts.ModelProviderAiHubMix, + Models: getModelsByOwner(consts.ModelProviderAiHubMix), + APIBase: "https://aihubmix.com/v1", + }, + consts.ModelProviderOcoolAI: { + OwnerName: consts.ModelProviderOcoolAI, + Models: getModelsByOwner(consts.ModelProviderOcoolAI), + APIBase: "https://api.ocoolai.com/v1", + }, + consts.ModelProviderPPIO: { + OwnerName: consts.ModelProviderPPIO, + Models: getModelsByOwner(consts.ModelProviderPPIO), + APIBase: "https://api.ppinfra.com/v3/openai", + }, + consts.ModelProviderAlayaNew: { + OwnerName: consts.ModelProviderAlayaNew, + Models: getModelsByOwner(consts.ModelProviderAlayaNew), + APIBase: "https://deepseek.alayanew.com/v1", + }, + consts.ModelProviderQiniu: { + OwnerName: consts.ModelProviderQiniu, + Models: getModelsByOwner(consts.ModelProviderQiniu), + APIBase: "https://api.qnaigc.com/v1", + }, + consts.ModelProviderDMXAPI: { + OwnerName: consts.ModelProviderDMXAPI, + Models: getModelsByOwner(consts.ModelProviderDMXAPI), + APIBase: "https://www.dmxapi.cn/v1", + }, + consts.ModelProviderBurnCloud: { + OwnerName: consts.ModelProviderBurnCloud, + Models: getModelsByOwner(consts.ModelProviderBurnCloud), + APIBase: "https://ai.burncloud.com/v1", + }, + consts.ModelProviderTokenFlux: { + OwnerName: consts.ModelProviderTokenFlux, + Models: getModelsByOwner(consts.ModelProviderTokenFlux), + APIBase: "https://tokenflux.ai/v1", + }, + consts.ModelProvider302AI: { + OwnerName: consts.ModelProvider302AI, + Models: getModelsByOwner(consts.ModelProvider302AI), + APIBase: "https://api.302.ai/v1", + }, + consts.ModelProviderCephalon: { + OwnerName: consts.ModelProviderCephalon, + Models: getModelsByOwner(consts.ModelProviderCephalon), + APIBase: "https://cephalon.cloud/user-center/v1/model", + }, + consts.ModelProviderLanyun: { + OwnerName: consts.ModelProviderLanyun, + Models: getModelsByOwner(consts.ModelProviderLanyun), + APIBase: "https://maas-api.lanyun.net/v1", + }, + consts.ModelProviderPH8: { + OwnerName: consts.ModelProviderPH8, + Models: getModelsByOwner(consts.ModelProviderPH8), + APIBase: "https://ph8.co/v1", + }, + consts.ModelProviderOpenRouter: { + OwnerName: consts.ModelProviderOpenRouter, + Models: getModelsByOwner(consts.ModelProviderOpenRouter), + APIBase: "https://openrouter.ai/api/v1", + }, + consts.ModelProviderNewAPI: { + OwnerName: consts.ModelProviderNewAPI, + Models: getModelsByOwner(consts.ModelProviderNewAPI), + APIBase: "http://localhost:3000/v1", + }, + consts.ModelProviderLMStudio: { + OwnerName: consts.ModelProviderLMStudio, + Models: getModelsByOwner(consts.ModelProviderLMStudio), + APIBase: "http://localhost:1234/v1", + }, + consts.ModelProviderAnthropic: { + OwnerName: consts.ModelProviderAnthropic, + Models: getModelsByOwner(consts.ModelProviderAnthropic), + APIBase: "https://api.anthropic.com", + }, + consts.ModelProviderVertexAI: { + OwnerName: consts.ModelProviderVertexAI, + Models: getModelsByOwner(consts.ModelProviderVertexAI), + APIBase: "https://aiplatform.googleapis.com", + }, + consts.ModelProviderGithub: { + OwnerName: consts.ModelProviderGithub, + Models: getModelsByOwner(consts.ModelProviderGithub), + APIBase: "https://models.github.ai/inference/v1", + }, + consts.ModelProviderCopilot: { + OwnerName: consts.ModelProviderCopilot, + Models: getModelsByOwner(consts.ModelProviderCopilot), + APIBase: "https://api.githubcopilot.com/v1", + }, + consts.ModelProviderYi: { + OwnerName: consts.ModelProviderYi, + Models: getModelsByOwner(consts.ModelProviderYi), + APIBase: "https://api.lingyiwanwu.com/v1", + }, + consts.ModelProviderBaichuan: { + OwnerName: consts.ModelProviderBaichuan, + Models: getModelsByOwner(consts.ModelProviderBaichuan), + APIBase: "https://api.baichuan-ai.com/v1", + }, + consts.ModelProviderStepFun: { + OwnerName: consts.ModelProviderStepFun, + Models: getModelsByOwner(consts.ModelProviderStepFun), + APIBase: "https://api.stepfun.com/v1", + }, + consts.ModelProviderInfini: { + OwnerName: consts.ModelProviderInfini, + Models: getModelsByOwner(consts.ModelProviderInfini), + APIBase: "https://cloud.infini-ai.com/maas/v1", + }, + consts.ModelProviderMiniMax: { + OwnerName: consts.ModelProviderMiniMax, + Models: getModelsByOwner(consts.ModelProviderMiniMax), + APIBase: "https://api.minimax.chat/v1", + }, + consts.ModelProviderGroq: { + OwnerName: consts.ModelProviderGroq, + Models: getModelsByOwner(consts.ModelProviderGroq), + APIBase: "https://api.groq.com/openai/v1", + }, + consts.ModelProviderTogether: { + OwnerName: consts.ModelProviderTogether, + Models: getModelsByOwner(consts.ModelProviderTogether), + APIBase: "https://api.together.xyz/v1", + }, + consts.ModelProviderFireworks: { + OwnerName: consts.ModelProviderFireworks, + Models: getModelsByOwner(consts.ModelProviderFireworks), + APIBase: "https://api.fireworks.ai/inference/v1", + }, + consts.ModelProviderNvidia: { + OwnerName: consts.ModelProviderNvidia, + Models: getModelsByOwner(consts.ModelProviderNvidia), + APIBase: "https://integrate.api.nvidia.com/v1", + }, + consts.ModelProviderGrok: { + OwnerName: consts.ModelProviderGrok, + Models: getModelsByOwner(consts.ModelProviderGrok), + APIBase: "https://api.x.ai/v1", + }, + consts.ModelProviderHyperbolic: { + OwnerName: consts.ModelProviderHyperbolic, + Models: getModelsByOwner(consts.ModelProviderHyperbolic), + APIBase: "https://api.hyperbolic.xyz/v1", + }, + consts.ModelProviderMistral: { + OwnerName: consts.ModelProviderMistral, + Models: getModelsByOwner(consts.ModelProviderMistral), + APIBase: "https://api.mistral.ai/v1", + }, + consts.ModelProviderJina: { + OwnerName: consts.ModelProviderJina, + Models: getModelsByOwner(consts.ModelProviderJina), + APIBase: "https://api.jina.ai/v1", + }, + consts.ModelProviderPerplexity: { + OwnerName: consts.ModelProviderPerplexity, + Models: getModelsByOwner(consts.ModelProviderPerplexity), + APIBase: "https://api.perplexity.ai/v1", + }, + consts.ModelProviderModelScope: { + OwnerName: consts.ModelProviderModelScope, + Models: getModelsByOwner(consts.ModelProviderModelScope), + APIBase: "https://api-inference.modelscope.cn/v1", + }, + consts.ModelProviderXirang: { + OwnerName: consts.ModelProviderXirang, + Models: getModelsByOwner(consts.ModelProviderXirang), + APIBase: "https://wishub-x1.ctyun.cn/v1", + }, + consts.ModelProviderTencentCloudTI: { + OwnerName: consts.ModelProviderTencentCloudTI, + Models: getModelsByOwner(consts.ModelProviderTencentCloudTI), + APIBase: "https://api.lkeap.cloud.tencent.com/v1", + }, + consts.ModelProviderBaiduCloud: { + OwnerName: consts.ModelProviderBaiduCloud, + Models: getModelsByOwner(consts.ModelProviderBaiduCloud), + APIBase: "https://qianfan.baidubce.com/v2", + }, + consts.ModelProviderGPUStack: { + OwnerName: consts.ModelProviderGPUStack, + Models: getModelsByOwner(consts.ModelProviderGPUStack), + APIBase: "", + }, + consts.ModelProviderVoyageAI: { + OwnerName: consts.ModelProviderVoyageAI, + Models: getModelsByOwner(consts.ModelProviderVoyageAI), + APIBase: "https://api.voyageai.com/v1", + }, + consts.ModelProviderAWSBedrock: { + OwnerName: consts.ModelProviderAWSBedrock, + Models: getModelsByOwner(consts.ModelProviderAWSBedrock), + APIBase: "", + }, + consts.ModelProviderPoe: { + OwnerName: consts.ModelProviderPoe, + Models: getModelsByOwner(consts.ModelProviderPoe), + APIBase: "https://api.poe.com/v1", + }, + } + + // 初始化按类型分组的模型映射 + TypeModelMap = make(map[consts.ModelType][]ModelMetadata) + for i := range Models { + model := Models[i] + TypeModelMap[model.ModelType] = append(TypeModelMap[model.ModelType], model) + } } \ No newline at end of file diff --git a/domain/openai.go b/domain/openai.go index 57d6828..4cfb373 100644 --- a/domain/openai.go +++ b/domain/openai.go @@ -7,4 +7,13 @@ type OpenAIData struct { type OpenAIResp struct { Object string `json:"object"` Data []*OpenAIData `json:"data"` +} + +// ParseModels 实现ModelResponseParser接口 +func (o *OpenAIResp) ParseModels() []ModelListItem { + var models []ModelListItem + for _, item := range o.Data { + models = append(models, ModelListItem{Model: item.ID}) + } + return models } \ No newline at end of file diff --git a/test/main.go b/test/main.go new file mode 100644 index 0000000..930dad0 --- /dev/null +++ b/test/main.go @@ -0,0 +1,101 @@ +package main + +import ( + "fmt" + "net/http" + + "github.com/labstack/echo/v4/middleware" + + "github.com/chaitin/ModelKit/domain" + "github.com/chaitin/ModelKit/pkg/log" + "github.com/chaitin/ModelKit/usecase" + "github.com/labstack/echo/v4" +) + +type ModelKit struct{} + +func NewModelKit( + echo *echo.Echo, + logger *log.Logger, + isApmEnabled bool, +) *ModelKit { + m := &ModelKit{} + + // 注册路由 + g := echo.Group("/api/v1/modelkit") + g.GET("/listmodel", m.GetModelList) + g.GET("/checkmodel", m.CheckModel) + + return m +} + +func (p *ModelKit) GetModelList(c echo.Context) error { + var req domain.ModelListReq + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, domain.Response{ + Success: false, + Message: "参数绑定失败: " + err.Error(), + }) + } + fmt.Println("list model req:", req) + + resp, err := usecase.ModelList(c.Request().Context(), &req) + if err != nil { + fmt.Println("err:", err) + return c.JSON(http.StatusInternalServerError, domain.Response{ + Success: false, + Message: err.Error(), + }) + } + + return c.JSON(http.StatusOK, domain.Response{ + Success: true, + Message: "获取模型列表成功", + Data: resp, + }) +} + +func (p *ModelKit) CheckModel(c echo.Context) error { + var req domain.CheckModelReq + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, domain.Response{ + Success: false, + Message: "参数绑定失败: " + err.Error(), + }) + } + fmt.Println("check model req:", req) + + resp, _ := usecase.CheckModel(c.Request().Context(), &req) + + // 如果检查过程中有错误,返回错误响应 + if resp.Error != "" { + fmt.Println("resp.Error:", resp.Error) + return c.JSON(http.StatusOK, domain.Response{ + Success: false, + Message: resp.Error, + Data: resp, + }) + } + + return c.JSON(http.StatusOK, domain.Response{ + Success: true, + Message: "模型检查成功", + Data: resp, + }) +} + +// @title ModelKit API +// @version 1.0 +// @description ModelKit API server for model management +// @host localhost:8080 +// @BasePath / +func main() { + echo := echo.New() + + // 添加CORS中间件 + echo.Use(middleware.CORS()) + + NewModelKit(echo, nil, false) + + echo.Start(":8080") +} diff --git a/test/model_test.go b/test/model_test.go deleted file mode 100644 index 0a86e4e..0000000 --- a/test/model_test.go +++ /dev/null @@ -1,4 +0,0 @@ -package test - - - diff --git a/test/ui_example/index.html b/test/ui_example/index.html new file mode 100644 index 0000000..d10aa4b --- /dev/null +++ b/test/ui_example/index.html @@ -0,0 +1,13 @@ + + + + + + + ModelModal Example + + +
+ + + \ No newline at end of file diff --git a/test/ui_example/package.json b/test/ui_example/package.json new file mode 100644 index 0000000..7db6e8b --- /dev/null +++ b/test/ui_example/package.json @@ -0,0 +1,31 @@ +{ + "name": "modelmodal-example", + "version": "1.0.0", + "description": "Mock example for ModelModal component", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@emotion/react": "^11.11.0", + "@emotion/styled": "^11.11.0", + "@mui/icons-material": "^6.0.0", + "@mui/material": "^6.0.0", + "@yokowu/modelkit-ui": "link:../../ui/ModelModal", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-hook-form": "^7.48.0", + "react-hot-toast": "^2.6.0" + }, + "devDependencies": { + "@types/node": "^24.2.1", + "@types/react": "^19.1.10", + "@types/react-dom": "^19.1.7", + "@vitejs/plugin-react": "^4.2.0", + "typescript": "^5.3.0", + "vite": "^5.0.0" + } +} diff --git a/test/ui_example/pnpm-lock.yaml b/test/ui_example/pnpm-lock.yaml new file mode 100644 index 0000000..26d0c39 --- /dev/null +++ b/test/ui_example/pnpm-lock.yaml @@ -0,0 +1,1646 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@emotion/react': + specifier: ^11.11.0 + version: 11.14.0(@types/react@19.1.10)(react@19.1.1) + '@emotion/styled': + specifier: ^11.11.0 + version: 11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1) + '@mui/icons-material': + specifier: ^6.0.0 + version: 6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.10)(react@19.1.1) + '@mui/material': + specifier: ^6.0.0 + version: 6.5.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@yokowu/modelkit-ui': + specifier: link:../../ui/ModelModal + version: link:../../ui/ModelModal + react: + specifier: ^19.1.0 + version: 19.1.1 + react-dom: + specifier: ^19.1.0 + version: 19.1.1(react@19.1.1) + react-hook-form: + specifier: ^7.48.0 + version: 7.62.0(react@19.1.1) + react-hot-toast: + specifier: ^2.6.0 + version: 2.6.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + devDependencies: + '@types/node': + specifier: ^24.2.1 + version: 24.3.0 + '@types/react': + specifier: ^19.1.10 + version: 19.1.10 + '@types/react-dom': + specifier: ^19.1.7 + version: 19.1.7(@types/react@19.1.10) + '@vitejs/plugin-react': + specifier: ^4.2.0 + version: 4.7.0(vite@5.4.19(@types/node@24.3.0)) + typescript: + specifier: ^5.3.0 + version: 5.9.2 + vite: + specifier: ^5.0.0 + version: 5.4.19(@types/node@24.3.0) + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.0': + resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.3': + resolution: {integrity: sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.3': + resolution: {integrity: sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.3': + resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.28.3': + resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.3': + resolution: {integrity: sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.2': + resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} + engines: {node: '>=6.9.0'} + + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@1.3.1': + resolution: {integrity: sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/styled@11.14.1': + resolution: {integrity: sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==} + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.30': + resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + + '@mui/core-downloads-tracker@6.5.0': + resolution: {integrity: sha512-LGb8t8i6M2ZtS3Drn3GbTI1DVhDY6FJ9crEey2lZ0aN2EMZo8IZBZj9wRf4vqbZHaWjsYgtbOnJw5V8UWbmK2Q==} + + '@mui/icons-material@6.5.0': + resolution: {integrity: sha512-VPuPqXqbBPlcVSA0BmnoE4knW4/xG6Thazo8vCLWkOKusko6DtwFV6B665MMWJ9j0KFohTIf3yx2zYtYacvG1g==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@mui/material': ^6.5.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/material@6.5.0': + resolution: {integrity: sha512-yjvtXoFcrPLGtgKRxFaH6OQPtcLPhkloC0BML6rBG5UeldR0nPULR/2E2BfXdo5JNV7j7lOzrrLX2Qf/iSidow==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material-pigment-css': ^6.5.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/material-pigment-css': + optional: true + '@types/react': + optional: true + + '@mui/private-theming@6.4.9': + resolution: {integrity: sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/styled-engine@6.5.0': + resolution: {integrity: sha512-8woC2zAqF4qUDSPIBZ8v3sakj+WgweolpyM/FXf8jAx6FMls+IE4Y8VDZc+zS805J7PRz31vz73n2SovKGaYgw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/system@6.5.0': + resolution: {integrity: sha512-XcbBYxDS+h/lgsoGe78ExXFZXtuIlSBpn/KsZq8PtZcIkUNJInkuDqcLd2rVBQrDC1u+rvVovdaWPf2FHKJf3w==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/types@7.2.24': + resolution: {integrity: sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@6.4.9': + resolution: {integrity: sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/rollup-android-arm-eabi@4.46.2': + resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.46.2': + resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.46.2': + resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.46.2': + resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.46.2': + resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.46.2': + resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.46.2': + resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.46.2': + resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.46.2': + resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.46.2': + resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.46.2': + resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.46.2': + resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.46.2': + resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.46.2': + resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.46.2': + resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.46.2': + resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.46.2': + resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.46.2': + resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.46.2': + resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.46.2': + resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==} + cpu: [x64] + os: [win32] + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/node@24.3.0': + resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/react-dom@19.1.7': + resolution: {integrity: sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==} + peerDependencies: + '@types/react': ^19.0.0 + + '@types/react-transition-group@4.4.12': + resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==} + peerDependencies: + '@types/react': '*' + + '@types/react@19.1.10': + resolution: {integrity: sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + browserslist@4.25.2: + resolution: {integrity: sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001735: + resolution: {integrity: sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + + electron-to-chromium@1.5.203: + resolution: {integrity: sha512-uz4i0vLhfm6dLZWbz/iH88KNDV+ivj5+2SA+utpgjKaj9Q0iDLuwk6Idhe9BTxciHudyx6IvTvijhkPvFGUQ0g==} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + goober@2.1.16: + resolution: {integrity: sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==} + peerDependencies: + csstype: ^3.0.10 + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + react-dom@19.1.1: + resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} + peerDependencies: + react: ^19.1.1 + + react-hook-form@7.62.0: + resolution: {integrity: sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + + react-hot-toast@2.6.0: + resolution: {integrity: sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==} + engines: {node: '>=10'} + peerDependencies: + react: '>=16' + react-dom: '>=16' + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@19.1.1: + resolution: {integrity: sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA==} + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + + react@19.1.1: + resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + rollup@4.46.2: + resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + typescript@5.9.2: + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.10.0: + resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + vite@5.4.19: + resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.0': {} + + '@babel/core@7.28.3': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3) + '@babel/helpers': 7.28.3 + '@babel/parser': 7.28.3 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.3': + dependencies: + '@babel/parser': 7.28.3 + '@babel/types': 7.28.2 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.3': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + + '@babel/parser@7.28.3': + dependencies: + '@babel/types': 7.28.2 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/runtime@7.28.3': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.3 + '@babel/types': 7.28.2 + + '@babel/traverse@7.28.3': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.3 + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.2': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/runtime': 7.28.3 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@1.3.1': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.3 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.1) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.1.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.3 + '@emotion/babel-plugin': 11.13.5 + '@emotion/is-prop-valid': 1.3.1 + '@emotion/react': 11.14.0(@types/react@19.1.10)(react@19.1.1) + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.1) + '@emotion/utils': 1.4.2 + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + transitivePeerDependencies: + - supports-color + + '@emotion/unitless@0.10.0': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.30': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@mui/core-downloads-tracker@6.5.0': {} + + '@mui/icons-material@6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.3 + '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + + '@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.3 + '@mui/core-downloads-tracker': 6.5.0 + '@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1) + '@mui/types': 7.2.24(@types/react@19.1.10) + '@mui/utils': 6.4.9(@types/react@19.1.10)(react@19.1.1) + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.12(@types/react@19.1.10) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-is: 19.1.1 + react-transition-group: 4.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.1.10)(react@19.1.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1) + '@types/react': 19.1.10 + + '@mui/private-theming@6.4.9(@types/react@19.1.10)(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.3 + '@mui/utils': 6.4.9(@types/react@19.1.10)(react@19.1.1) + prop-types: 15.8.1 + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + + '@mui/styled-engine@6.5.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.3 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/sheet': 1.4.0 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 19.1.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.1.10)(react@19.1.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1) + + '@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.3 + '@mui/private-theming': 6.4.9(@types/react@19.1.10)(react@19.1.1) + '@mui/styled-engine': 6.5.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(react@19.1.1) + '@mui/types': 7.2.24(@types/react@19.1.10) + '@mui/utils': 6.4.9(@types/react@19.1.10)(react@19.1.1) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 19.1.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.1.10)(react@19.1.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1) + '@types/react': 19.1.10 + + '@mui/types@7.2.24(@types/react@19.1.10)': + optionalDependencies: + '@types/react': 19.1.10 + + '@mui/utils@6.4.9(@types/react@19.1.10)(react@19.1.1)': + dependencies: + '@babel/runtime': 7.28.3 + '@mui/types': 7.2.24(@types/react@19.1.10) + '@types/prop-types': 15.7.15 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 19.1.1 + react-is: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + + '@popperjs/core@2.11.8': {} + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/rollup-android-arm-eabi@4.46.2': + optional: true + + '@rollup/rollup-android-arm64@4.46.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.46.2': + optional: true + + '@rollup/rollup-darwin-x64@4.46.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.46.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.46.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.46.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.46.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.46.2': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.46.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.46.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.46.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.46.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.46.2': + optional: true + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.28.3 + '@babel/types': 7.28.2 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.28.2 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.28.3 + '@babel/types': 7.28.2 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.28.2 + + '@types/estree@1.0.8': {} + + '@types/node@24.3.0': + dependencies: + undici-types: 7.10.0 + + '@types/parse-json@4.0.2': {} + + '@types/prop-types@15.7.15': {} + + '@types/react-dom@19.1.7(@types/react@19.1.10)': + dependencies: + '@types/react': 19.1.10 + + '@types/react-transition-group@4.4.12(@types/react@19.1.10)': + dependencies: + '@types/react': 19.1.10 + + '@types/react@19.1.10': + dependencies: + csstype: 3.1.3 + + '@vitejs/plugin-react@4.7.0(vite@5.4.19(@types/node@24.3.0))': + dependencies: + '@babel/core': 7.28.3 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.3) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.3) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 5.4.19(@types/node@24.3.0) + transitivePeerDependencies: + - supports-color + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.28.3 + cosmiconfig: 7.1.0 + resolve: 1.22.10 + + browserslist@4.25.2: + dependencies: + caniuse-lite: 1.0.30001735 + electron-to-chromium: 1.5.203 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.2) + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001735: {} + + clsx@2.1.1: {} + + convert-source-map@1.9.0: {} + + convert-source-map@2.0.0: {} + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + csstype@3.1.3: {} + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.28.3 + csstype: 3.1.3 + + electron-to-chromium@1.5.203: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + find-root@1.1.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + goober@2.1.16(csstype@3.1.3): + dependencies: + csstype: 3.1.3 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + is-arrayish@0.2.1: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + js-tokens@4.0.0: {} + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json5@2.2.3: {} + + lines-and-columns@1.2.4: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + node-releases@2.0.19: {} + + object-assign@4.1.1: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-parse@1.0.7: {} + + path-type@4.0.0: {} + + picocolors@1.1.1: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + react-dom@19.1.1(react@19.1.1): + dependencies: + react: 19.1.1 + scheduler: 0.26.0 + + react-hook-form@7.62.0(react@19.1.1): + dependencies: + react: 19.1.1 + + react-hot-toast@2.6.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + csstype: 3.1.3 + goober: 2.1.16(csstype@3.1.3) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + react-is@16.13.1: {} + + react-is@19.1.1: {} + + react-refresh@0.17.0: {} + + react-transition-group@4.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + '@babel/runtime': 7.28.3 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + react@19.1.1: {} + + resolve-from@4.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rollup@4.46.2: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.46.2 + '@rollup/rollup-android-arm64': 4.46.2 + '@rollup/rollup-darwin-arm64': 4.46.2 + '@rollup/rollup-darwin-x64': 4.46.2 + '@rollup/rollup-freebsd-arm64': 4.46.2 + '@rollup/rollup-freebsd-x64': 4.46.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.46.2 + '@rollup/rollup-linux-arm-musleabihf': 4.46.2 + '@rollup/rollup-linux-arm64-gnu': 4.46.2 + '@rollup/rollup-linux-arm64-musl': 4.46.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.46.2 + '@rollup/rollup-linux-ppc64-gnu': 4.46.2 + '@rollup/rollup-linux-riscv64-gnu': 4.46.2 + '@rollup/rollup-linux-riscv64-musl': 4.46.2 + '@rollup/rollup-linux-s390x-gnu': 4.46.2 + '@rollup/rollup-linux-x64-gnu': 4.46.2 + '@rollup/rollup-linux-x64-musl': 4.46.2 + '@rollup/rollup-win32-arm64-msvc': 4.46.2 + '@rollup/rollup-win32-ia32-msvc': 4.46.2 + '@rollup/rollup-win32-x64-msvc': 4.46.2 + fsevents: 2.3.3 + + scheduler@0.26.0: {} + + semver@6.3.1: {} + + source-map-js@1.2.1: {} + + source-map@0.5.7: {} + + stylis@4.2.0: {} + + supports-preserve-symlinks-flag@1.0.0: {} + + typescript@5.9.2: {} + + undici-types@7.10.0: {} + + update-browserslist-db@1.1.3(browserslist@4.25.2): + dependencies: + browserslist: 4.25.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + vite@5.4.19(@types/node@24.3.0): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.6 + rollup: 4.46.2 + optionalDependencies: + '@types/node': 24.3.0 + fsevents: 2.3.3 + + yallist@3.1.1: {} + + yaml@1.10.2: {} diff --git a/test/ui_example/src/App.tsx b/test/ui_example/src/App.tsx new file mode 100644 index 0000000..733b211 --- /dev/null +++ b/test/ui_example/src/App.tsx @@ -0,0 +1,185 @@ +import React, { useState } from 'react'; +import { + Box, + Button, + Container, + Typography, + Paper, + Stack, + FormControl, + InputLabel, + Select, + MenuItem, + SelectChangeEvent, +} from '@mui/material'; +import { ThemeProvider, createTheme } from '@mui/material/styles'; +import CssBaseline from '@mui/material/CssBaseline'; +import { Model} from '@yokowu/modelkit-ui'; +import { localModelService } from './localService'; +import { ModelModal } from '@yokowu/modelkit-ui'; +import toast, { Toaster } from 'react-hot-toast'; + +// 创建Material-UI主题 +const theme = createTheme({ + palette: { + mode: 'light', + primary: { + main: '#1976d2', + }, + secondary: { + main: '#dc004e', + }, + }, +}); + +function App() { + const [modalOpen, setModalOpen] = useState(false); + const [selectedType, setSelectedType] = useState('llm'); + const [editingModel, setEditingModel] = useState(null); + + // 使用react-hot-toast的消息组件 + const messageComponent = { + error: (content: string | Error) => { + const message = content instanceof Error ? content.message : String(content); + return toast.error(message); + }, + success: (content: string) => toast.success(String(content)), + info: (content: string) => toast(String(content)), + warning: (content: string) => toast(String(content), { icon: '⚠️' }), + }; + + const handleOpenEditModal = () => { + // 模拟编辑一个现有模型 + const mockModel: Model = { + id: 'edit-example', + model_name: 'gpt-4', + show_name: 'GPT-4 示例', + provider: 'OpenAI' as any, + model_type: 'llm', + base_url: 'https://api.openai.com/v1', + api_key: 'sk-example-key', + api_version: '2023-05-15', + status: 'active' as any, + is_active: true, + created_at: Date.now(), + updated_at: Date.now(), + }; + setEditingModel(mockModel); + setModalOpen(true); + }; + + const handleCloseModal = () => { + setModalOpen(false); + setEditingModel(null); + }; + + const handleRefresh = () => { + console.log('刷新模型列表'); + }; + + const handleTypeChange = (event: SelectChangeEvent) => { + setSelectedType(event.target.value); + }; + + + + return ( + <> + + + + + + + ModelModal 示例程序 + + + + 这是一个用于测试 ModelModal 组件的示例应用程序 + + + + + 模型类型 + + + + + + + + + + + + + + + 功能说明: + + +
  • 支持多种模型类型选择
  • +
  • 支持添加和编辑模型配置
  • +
  • 集成多个AI服务提供商
  • +
  • 实时模型验证和测试
  • +
  • 响应式设计,支持移动端
  • +
    +
    +
    + + {/* SimpleModelModal 组件 */} + + + +
    +
    + + ); +} + +export default App; \ No newline at end of file diff --git a/test/ui_example/src/index.css b/test/ui_example/src/index.css new file mode 100644 index 0000000..59ed942 --- /dev/null +++ b/test/ui_example/src/index.css @@ -0,0 +1,22 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} + +#root { + width: 100vw; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background-color: #f5f5f5; +} \ No newline at end of file diff --git a/test/ui_example/src/localService.ts b/test/ui_example/src/localService.ts new file mode 100644 index 0000000..d410a75 --- /dev/null +++ b/test/ui_example/src/localService.ts @@ -0,0 +1,148 @@ +import { + Model, + ModelService, + CreateModelReq, + ListModelReq, + CheckModelReq, + UpdateModelReq, + ModelListItem, +} from '@yokowu/modelkit-ui'; + + +// 本地Go服务的基础URL +const BASE_URL = 'http://localhost:8080/api/v1/modelkit'; + +// API响应格式 +interface ApiResponse { + success: boolean; + message: string; + data?: T; +} + +// 模型列表响应 +interface ModelListResponse { + models: ModelListItem[]; +} + +// 模型检查响应 +interface CheckModelResponse { + model: Model; + error?: string; +} + +export class LocalModelService implements ModelService { + private async request(url: string, options: RequestInit = {}): Promise { + const response = await fetch(`${BASE_URL}${url}`, { + headers: { + 'Content-Type': 'application/json', + ...options.headers, + }, + ...options, + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const result: ApiResponse = await response.json(); + + if (!result.success) { + throw new Error(result.message || 'API request failed'); + } + + return result.data as T; + } + + async createModel(data: CreateModelReq): Promise<{ model: Model }> { + // 转换为Go服务期望的格式 + const requestData = { + model_name: data.model_name, + show_name: data.show_name, + provider: data.provider, + model_type: data.model_type, + base_url: data.base_url, + api_key: data.api_key, + api_version: data.api_version, + api_header: data.api_header, + param: data.param, + }; + + const result = await this.request<{ model: Model }>('/models', { + method: 'POST', + body: JSON.stringify(requestData), + }); + + return result; + } + + async listModel(data: ListModelReq): Promise<{ models: ModelListItem[] }> { + try { + const queryParams = new URLSearchParams(); + if (data.provider) queryParams.append('provider', data.provider); + if (data.model_type) queryParams.append('model_type', data.model_type); + if (data.base_url) queryParams.append('base_url', data.base_url); + if (data.api_key) queryParams.append('api_key', data.api_key); + if (data.api_header) queryParams.append('api_header', data.api_header); + + const url = `/listmodel${queryParams.toString() ? '?' + queryParams.toString() : ''}`; + const response = await this.request(url, { + method: 'GET', + }); + return { models: response.models }; + } catch (error) { + console.error('Failed to list models:', error); + throw error; + } + } + + async checkModel(data: CheckModelReq): Promise<{ model: Model; error?: string }> { + try { + const queryParams = new URLSearchParams(); + console.log('checkModel data:', data); + if (data.provider) queryParams.append('provider', data.provider); + if (data.model_name) queryParams.append('model_name', data.model_name); + if (data.base_url) queryParams.append('base_url', data.base_url); + if (data.api_key) queryParams.append('api_key', data.api_key); + if (data.api_header) queryParams.append('api_header', data.api_header); + if (data.model_type) queryParams.append('model_type', data.model_type); + const url = `/checkmodel${queryParams.toString() ? '?' + queryParams.toString() : ''}`; + const response = await this.request(url, { + method: 'GET', + }); + + if (response.error) { + throw new Error(response.error); + } + + return { model: response.model }; + } catch (error) { + console.error('Failed to list models:', error); + throw error; + } + } + + async updateModel(data: UpdateModelReq): Promise<{ model: Model }> { + const requestData = { + id: data.id, + model_name: data.model_name, + show_name: data.show_name, + provider: data.provider, + base_url: data.base_url, + api_key: data.api_key, + api_version: data.api_version, + api_header: data.api_header, + param: data.param, + status: data.status, + }; + + const result = await this.request<{ model: Model }>(`/models/${data.id}`, { + method: 'PUT', + body: JSON.stringify(requestData), + }); + + return result; + } +} + +// 导出服务实例 +export const localModelService = new LocalModelService(); \ No newline at end of file diff --git a/test/ui_example/src/main.tsx b/test/ui_example/src/main.tsx new file mode 100644 index 0000000..24a16b6 --- /dev/null +++ b/test/ui_example/src/main.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; +import './index.css'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); \ No newline at end of file diff --git a/test/ui_example/tsconfig.json b/test/ui_example/tsconfig.json new file mode 100644 index 0000000..ab4edaa --- /dev/null +++ b/test/ui_example/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src"] +} \ No newline at end of file diff --git a/test/ui_example/tsconfig.node.json b/test/ui_example/tsconfig.node.json new file mode 100644 index 0000000..6cbe316 --- /dev/null +++ b/test/ui_example/tsconfig.node.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true, + "noEmit": true + }, + "include": ["vite.config.ts"], + "exclude": ["node_modules"] +} \ No newline at end of file diff --git a/test/ui_example/vite.config.ts b/test/ui_example/vite.config.ts new file mode 100644 index 0000000..964a592 --- /dev/null +++ b/test/ui_example/vite.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import path from 'path'; + +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + '@': path.resolve(__dirname, '../ui/ModelModal/src'), + }, + }, + server: { + host: '0.0.0.0', + port: 3301, + }, + define: { + 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), + }, +}); \ No newline at end of file diff --git a/ui/ModelModal/src/ModelModal.tsx b/ui/ModelModal/src/ModelModal.tsx index 476454d..4a296df 100644 --- a/ui/ModelModal/src/ModelModal.tsx +++ b/ui/ModelModal/src/ModelModal.tsx @@ -335,7 +335,7 @@ export const ModelModal: React.FC = ({ disabled: !success && providerBrand !== 'Other', }} > - + = ({ bgcolor: 'background.paper2', borderRadius: '10px', p: 1, + height: '100%', + display: 'flex', + flexDirection: 'column', }} > 模型供应商 - {Object.values(providers) - .filter((it) => { - // 当model_type为chat或llm时显示所有供应商 - if (model_type === 'chat' || model_type === 'llm' || model_type === 'code' || model_type === 'coder') { - return true; - } - // 其他情况只显示百智云和其它 - return it.label === 'BaiZhiCloud' || it.label === 'Other'; - }) - .map((it) => ( - { - if (data && data.provider === it.label) { - resetCurData(data); - } else { - setModelUserList([]); - setError(''); - setModelLoading(false); - setSuccess(false); - reset({ - provider: it.label as keyof typeof DEFAULT_MODEL_PROVIDERS, - base_url: - it.label === 'AzureOpenAI' ? '' : it.defaultBaseUrl, - model_name: '', - api_version: '', - api_key: '', - api_header_key: '', - api_header_value: '', - show_name: '', - // 重置高级设置 - context_window_size: 64000, - max_output_tokens: 8192, - enable_r1_params: false, - support_image: false, - support_compute: false, - support_prompt_caching: false, - }); - } - }} - > - - {it.cn || it.label || '其他'} + + + {Object.values(providers) + .filter((it) => { + // 当model_type为chat或llm时显示所有供应商 + if (model_type === 'chat' || model_type === 'llm' || model_type === 'code' || model_type === 'coder') { + return true; + } + // 其他情况只显示百智云和其它 + return it.label === 'BaiZhiCloud' || it.label === 'Other'; + }) + .map((it) => ( + { + if (data && data.provider === it.label) { + resetCurData(data); + } else { + setModelUserList([]); + setError(''); + setModelLoading(false); + setSuccess(false); + reset({ + provider: it.label as keyof typeof DEFAULT_MODEL_PROVIDERS, + base_url: + it.label === 'AzureOpenAI' ? '' : it.defaultBaseUrl, + model_name: '', + api_version: '', + api_key: '', + api_header_key: '', + api_header_value: '', + show_name: '', + // 重置高级设置 + context_window_size: 64000, + max_output_tokens: 8192, + enable_r1_params: false, + support_image: false, + support_compute: false, + support_prompt_caching: false, + }); + } + }} + > + + {it.cn || it.label || '其他'} + + ))} - ))} + - + API 地址{' '} diff --git a/ui/ModelModal/src/constants/providers.ts b/ui/ModelModal/src/constants/providers.ts index ea721d8..d58e245 100644 --- a/ui/ModelModal/src/constants/providers.ts +++ b/ui/ModelModal/src/constants/providers.ts @@ -19,7 +19,7 @@ export const DEFAULT_MODEL_PROVIDERS: ModelProviderMap = { urlWrite: false, secretRequired: true, customHeader: false, - modelDocumentUrl: 'https://open.bigmodel.cn/usercenter/apikeys', + modelDocumentUrl: 'https://docs.bigmodel.cn/', defaultBaseUrl: 'https://open.bigmodel.cn/api/paas/v4', }, DeepSeek: { @@ -29,7 +29,7 @@ export const DEFAULT_MODEL_PROVIDERS: ModelProviderMap = { urlWrite: false, secretRequired: true, customHeader: false, - modelDocumentUrl: 'https://platform.deepseek.com/api_keys', + modelDocumentUrl: 'https://platform.deepseek.com/api-docs/', defaultBaseUrl: 'https://api.deepseek.com/v1', }, Hunyuan: { @@ -39,7 +39,7 @@ export const DEFAULT_MODEL_PROVIDERS: ModelProviderMap = { urlWrite: false, secretRequired: true, customHeader: false, - modelDocumentUrl: 'https://console.cloud.tencent.com/hunyuan/start', + modelDocumentUrl: 'https://cloud.tencent.com/document/product/1729/111007', defaultBaseUrl: 'https://api.hunyuan.cloud.tencent.com/v1', }, BaiLian: { @@ -49,7 +49,7 @@ export const DEFAULT_MODEL_PROVIDERS: ModelProviderMap = { urlWrite: false, secretRequired: true, customHeader: false, - modelDocumentUrl: 'https://bailian.console.aliyun.com/?tab=model#/api-key', + modelDocumentUrl: 'https://help.aliyun.com/zh/model-studio/getting-started/', defaultBaseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1', }, Volcengine: { @@ -59,7 +59,7 @@ export const DEFAULT_MODEL_PROVIDERS: ModelProviderMap = { urlWrite: false, secretRequired: true, customHeader: false, - modelDocumentUrl: 'https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey', + modelDocumentUrl: 'https://www.volcengine.com/docs/82379/1182403', defaultBaseUrl: 'https://ark.cn-beijing.volces.com/api/v3', }, OpenAI: { @@ -69,7 +69,7 @@ export const DEFAULT_MODEL_PROVIDERS: ModelProviderMap = { urlWrite: false, secretRequired: true, customHeader: false, - modelDocumentUrl: 'https://platform.openai.com/api-keys', + modelDocumentUrl: 'https://platform.openai.com/docs', defaultBaseUrl: 'https://api.openai.com/v1', }, Ollama: { @@ -79,7 +79,7 @@ export const DEFAULT_MODEL_PROVIDERS: ModelProviderMap = { urlWrite: true, secretRequired: false, customHeader: true, - modelDocumentUrl: '', + modelDocumentUrl: 'https://github.com/ollama/ollama/tree/main/docs', defaultBaseUrl: 'http://127.0.0.1:11434', }, SiliconFlow: { @@ -89,7 +89,7 @@ export const DEFAULT_MODEL_PROVIDERS: ModelProviderMap = { urlWrite: false, secretRequired: true, customHeader: false, - modelDocumentUrl: 'https://cloud.siliconflow.cn/account/ak', + modelDocumentUrl: 'https://docs.siliconflow.cn/', defaultBaseUrl: 'https://api.siliconflow.cn/v1', }, Moonshot: { @@ -99,7 +99,7 @@ export const DEFAULT_MODEL_PROVIDERS: ModelProviderMap = { urlWrite: false, secretRequired: true, customHeader: false, - modelDocumentUrl: 'https://platform.moonshot.cn/console/api-keys', + modelDocumentUrl: 'https://platform.moonshot.cn/docs/', defaultBaseUrl: 'https://api.moonshot.cn/v1', }, AzureOpenAI: { @@ -109,7 +109,7 @@ export const DEFAULT_MODEL_PROVIDERS: ModelProviderMap = { urlWrite: true, secretRequired: true, customHeader: false, - modelDocumentUrl: 'https://portal.azure.com/#view/Microsoft_Azure_ProjectOxford/CognitiveServicesHub/~/OpenAI', + modelDocumentUrl: 'https://learn.microsoft.com/en-us/azure/ai-services/openai/', defaultBaseUrl: 'https://.openai.azure.com', }, Gemini: { @@ -122,6 +122,377 @@ export const DEFAULT_MODEL_PROVIDERS: ModelProviderMap = { modelDocumentUrl: 'https://ai.google.dev/gemini-api/docs', defaultBaseUrl: 'https://generativelanguage.googleapis.com', }, + Qiniu: { + label: 'Qiniu', + cn: '七牛云', + icon: 'icon-qiniu', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://developer.qiniu.com/aitokenapi', + defaultBaseUrl: 'https://api.qnaigc.com/v1', + }, + // NewAPI: { + // label: 'NewAPI', + // cn: 'New API', + // icon: 'icon-newapi', + // urlWrite: true, + // secretRequired: true, + // customHeader: false, + // modelDocumentUrl: '', + // defaultBaseUrl: 'http://localhost:3000/v1', + // }, + // LMStudio: { + // label: 'LMStudio', + // cn: 'LM Studio', + // icon: 'icon-lmstudio', + // urlWrite: true, + // secretRequired: false, + // customHeader: false, + // modelDocumentUrl: '', + // defaultBaseUrl: 'http://localhost:1234/v1', + // }, + // Anthropic: { + // label: 'Anthropic', + // cn: 'Anthropic', + // icon: 'icon-anthropic', + // urlWrite: false, + // secretRequired: true, + // customHeader: false, + // modelDocumentUrl: 'https://console.anthropic.com/account/keys', + // defaultBaseUrl: 'https://api.anthropic.com', + // }, + // GitHub: { + // label: 'GitHub', + // cn: 'GitHub Models', + // icon: 'icon-github', + // urlWrite: false, + // secretRequired: true, + // customHeader: false, + // modelDocumentUrl: 'https://github.com/settings/tokens', + // defaultBaseUrl: 'https://models.github.ai/catalog', + // }, + Yi: { + label: 'Yi', + cn: '零一万物', + icon: 'icon-yi', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://platform.lingyiwanwu.com/docs', + defaultBaseUrl: 'https://api.lingyiwanwu.com/v1', + }, + // Baichuan: { + // label: 'Baichuan', + // cn: '百川智能', + // icon: 'icon-baichuan', + // urlWrite: false, + // secretRequired: true, + // customHeader: false, + // modelDocumentUrl: 'https://platform.baichuan-ai.com/console/apikey', + // defaultBaseUrl: 'https://api.baichuan-ai.com/v1', + // }, + // Ph8: { + // label: 'Ph8', + // cn: 'PH8大模型开放平台', + // icon: 'icon-ph8', + // urlWrite: false, + // secretRequired: true, + // customHeader: false, + // modelDocumentUrl: '', + // defaultBaseUrl: 'https://ph8.co/v1', + // }, + // MiniMax: { + // label: 'MiniMax', + // cn: 'MiniMax', + // icon: 'icon-minimax', + // urlWrite: false, + // secretRequired: true, + // customHeader: false, + // modelDocumentUrl: 'https://api.minimax.chat/user-center/basic-information/interface-key', + // defaultBaseUrl: 'https://api.minimaxi.com/v1', + // }, + // Groq: { + // label: 'Groq', + // cn: 'Groq', + // icon: 'icon-groq', + // urlWrite: false, + // secretRequired: true, + // customHeader: false, + // modelDocumentUrl: 'https://console.groq.com/keys', + // defaultBaseUrl: 'https://api.groq.com/openai/v1', + // }, + // Together: { + // label: 'Together', + // cn: 'Together', + // icon: 'icon-together', + // urlWrite: false, + // secretRequired: true, + // customHeader: false, + // modelDocumentUrl: 'https://api.together.xyz/settings/api-keys', + // defaultBaseUrl: 'https://api.together.xyz/v1', + // }, + // Jina: { + // label: 'Jina', + // cn: 'Jina', + // icon: 'icon-hyperbolic', + // urlWrite: false, + // secretRequired: true, + // customHeader: false, + // modelDocumentUrl: '', + // defaultBaseUrl: 'https://api.jina.ai/v1', + // }, + + CTYun: { + label: 'CTYun', + cn: '天翼云息壤', + icon: 'icon-ctyun', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://www.ctyun.cn/products/ctxirang', + defaultBaseUrl: 'https://wishub-x1.ctyun.cn/v1', + }, + TencentTI: { + label: 'TencentTI', + cn: '腾讯云TI', + icon: 'icon-tencentcloud', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://cloud.tencent.com/document/product/1772', + defaultBaseUrl: 'https://api.lkeap.cloud.tencent.com/v1', + }, + BaiDuQianFan: { + label: 'BaiDuQianFan', + cn: '百度云千帆', + icon: 'icon-baiduqianfan', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://cloud.baidu.com/doc/index.html', + defaultBaseUrl: 'https://qianfan.baidubce.com/v2', + }, + ModelScope: { + label: 'ModelScope', + cn: '魔搭社区', + icon: 'icon-modelscope', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://modelscope.cn/docs/model-service/API-Inference/intro', + defaultBaseUrl: 'https://api-inference.modelscope.cn/v1', + }, + Infini: { + label: 'Infini', + cn: '无问芯穹', + icon: 'icon-infini', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://docs.infini-ai.com/gen-studio/api/maas.html#/operations/chatCompletions', + defaultBaseUrl: 'https://cloud.infini-ai.com/maas/v1', + }, + StepFun: { + label: 'StepFun', + cn: '阶跃星辰', + icon: 'icon-stepfun', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://platform.stepfun.com/docs/overview/concept', + defaultBaseUrl: 'https://api.stepfun.com/v1', + }, + LanYun: { + label: 'LanYun', + cn: '蓝耘科技', + icon: 'icon-lanyun', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://archive.lanyun.net/#/maas/', + defaultBaseUrl: 'https://maas-api.lanyun.net/v1', + }, + AlayaNew: { + label: 'AlayaNew', + cn: '九章智算云', + icon: 'icon-alayanew', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://docs.alayanew.com/docs/modelService/interview?utm_source=cherrystudio', + defaultBaseUrl: 'https://deepseek.alayanew.com/v1', + }, + PPIO: { + label: 'PPIO', + cn: '欧派云', + icon: 'icon-ppio', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://docs.cherry-ai.com/pre-basic/providers/ppio?invited_by=JYT9GD&utm_source=github_cherry-studio', + defaultBaseUrl: 'https://api.ppinfra.com/v3/openai', + }, + AiHubMix: { + label: 'AiHubMix', + cn: 'AiHubMix', + icon: 'icon-aihubmix', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://doc.aihubmix.com/', + defaultBaseUrl: 'https://aihubmix.com/v1', + }, + OcoolAI: { + label: 'OcoolAI', + cn: 'OcoolAI', + icon: 'icon-ocoolai', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://docs.ocoolai.com/', + defaultBaseUrl: 'https://api.ocoolai.com/v1', + }, + DMXAPI: { + label: 'DMXAPI', + cn: 'DMXAPI', + icon: 'icon-dmxapi', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://dmxapi.cn/models.html#code-block', + defaultBaseUrl: 'https://www.dmxapi.cn/v1', + }, + BurnCloud: { + label: 'BurnCloud', + cn: 'BurnCloud', + icon: 'icon-burncloud', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://ai.burncloud.com/docs', + defaultBaseUrl: 'https://ai.burncloud.com/v1', + }, + Grok: { + label: 'Grok', + cn: 'Grok', + icon: 'icon-grok', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://docs.x.ai/', + defaultBaseUrl: 'https://api.x.ai/v1', + }, + Nvidia: { + label: 'Nvidia', + cn: '英伟达', + icon: 'icon-nvidia', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://docs.api.nvidia.com/nim/reference/llm-apis', + defaultBaseUrl: 'https://integrate.api.nvidia.com/v1', + }, + TokenFlux: { + label: 'TokenFlux', + cn: 'TokenFlux', + icon: 'icon-tokenflux', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://tokenflux.ai/docs', + defaultBaseUrl: 'https://tokenflux.ai/v1', + }, + AI302: { + label: 'AI302', + cn: '302.AI', + icon: 'icon-302ai', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://302ai.apifox.cn/api-147522039', + defaultBaseUrl: 'https://api.302.ai/v1', + }, + Cephalon: { + label: 'Cephalon', + cn: 'Cephalon', + icon: 'icon-cephalon', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://cephalon.cloud/apitoken/1864244127731589124', + defaultBaseUrl: 'https://cephalon.cloud/user-center/v1/model', + }, + OpenRouter: { + label: 'OpenRouter', + cn: 'OpenRouter', + icon: 'icon-openrouter', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://openrouter.ai/docs/quick-start', + defaultBaseUrl: 'https://openrouter.ai/api/v1', + }, + Fireworks: { + label: 'Fireworks', + cn: 'Fireworks', + icon: 'icon-fireworks', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://docs.fireworks.ai/getting-started/introduction', + defaultBaseUrl: 'https://api.fireworks.ai/inference/v1', + }, + Mistral: { + label: 'Mistral', + cn: 'Mistral', + icon: 'icon-mistral', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://docs.mistral.ai', + defaultBaseUrl: 'https://api.mistral.ai/v1', + }, + Perplexity: { + label: 'Perplexity', + cn: 'Perplexity', + icon: 'icon-perplexity', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://docs.perplexity.ai/home', + defaultBaseUrl: 'https://api.perplexity.ai', + }, + Hyperbolic: { + label: 'Hyperbolic', + cn: 'Hyperbolic', + icon: 'icon-hyperbolic', + urlWrite: false, + secretRequired: true, + customHeader: false, + modelDocumentUrl: 'https://docs.hyperbolic.xyz', + defaultBaseUrl: 'https://api.hyperbolic.xyz/v1', + }, + // VoyageAI: { + // label: 'VoyageAI', + // cn: 'Voyage AI', + // icon: 'icon-voyageai', + // urlWrite: false, + // secretRequired: true, + // customHeader: false, + // modelDocumentUrl: '', + // defaultBaseUrl: 'https://api.voyageai.com/v1', + // }, + // Poe: { + // label: 'Poe', + // cn: 'Poe', + // icon: 'icon-poe', + // urlWrite: false, + // secretRequired: true, + // customHeader: false, + // modelDocumentUrl: '', + // defaultBaseUrl: 'https://api.poe.com/v1', + // }, Other: { label: 'Other', cn: '其他', diff --git a/usecase/modelkit.go b/usecase/modelkit.go index 109dc65..59d004e 100644 --- a/usecase/modelkit.go +++ b/usecase/modelkit.go @@ -31,6 +31,36 @@ import ( "github.com/chaitin/ModelKit/utils" ) +// reqModelListApi 获取OpenAI兼容API的模型列表 +// 使用泛型和接口抽象来支持不同供应商的响应格式 +func reqModelListApi[T domain.ModelResponseParser](req *domain.ModelListReq, httpClient *http.Client, responseType T) ([]domain.ModelListItem, error) { + u, err := url.Parse(req.BaseURL) + if err != nil { + return nil, err + } + u.Path = path.Join(u.Path, "/models") + + client := request.NewClient(u.Scheme, u.Host, httpClient.Timeout, request.WithClient(httpClient)) + query, err := utils.GetQuery(req) + if err != nil { + return nil, err + } + resp, err := request.Get[T]( + client, u.Path, + request.WithHeader( + request.Header{ + "Authorization": fmt.Sprintf("Bearer %s", req.APIKey), + }, + ), + request.WithQuery(query), + ) + if err != nil { + return nil, err + } + + return (*resp).ParseModels(), nil +} + func ModelList(ctx context.Context, req *domain.ModelListReq) (*domain.ModelListResp, error) { httpClient := &http.Client{ Timeout: time.Second * 30, @@ -44,49 +74,14 @@ func ModelList(ctx context.Context, req *domain.ModelListReq) (*domain.ModelList provider := consts.ParseModelProvider(req.Provider) switch provider { + // 人工返回模型列表 case consts.ModelProviderAzureOpenAI, consts.ModelProviderZhiPu, consts.ModelProviderVolcengine: return &domain.ModelListResp{ Models: domain.From(domain.ModelProviders[provider]), }, nil - case consts.ModelProviderOpenAI, - consts.ModelProviderHunyuan, - consts.ModelProviderMoonshot, - consts.ModelProviderDeepSeek, - consts.ModelProviderSiliconFlow, - consts.ModelProviderBaiZhiCloud, - consts.ModelProviderBaiLian: - u, err := url.Parse(req.BaseURL) - if err != nil { - return nil, err - } - u.Path = path.Join(u.Path, "/models") - - client := request.NewClient(u.Scheme, u.Host, httpClient.Timeout, request.WithClient(httpClient)) - query := utils.GetQuery(req) - resp, err := request.Get[domain.OpenAIResp]( - client, u.Path, - request.WithHeader( - request.Header{ - "Authorization": fmt.Sprintf("Bearer %s", req.APIKey), - }, - ), - request.WithQuery(query), - ) - if err != nil { - return nil, err - } - - var models []domain.ModelListItem - for _, item := range resp.Data { - models = append(models, domain.ModelListItem{Model: item.ID}) - } - - return &domain.ModelListResp{ - Models: models, - }, nil - + // 以下模型供应商需要特殊处理 case consts.ModelProviderOllama: // get from ollama http://10.10.16.24:11434/api/tags u, err := url.Parse(req.BaseURL) @@ -143,18 +138,30 @@ func ModelList(ctx context.Context, req *domain.ModelListReq) (*domain.ModelList return &domain.ModelListResp{ Models: modelsList, }, nil + case consts.ModelProviderGithub: + models, err := reqModelListApi(req, httpClient, &domain.GithubResp{}) + if err != nil { + return nil, err + } + return &domain.ModelListResp{ + Models: models, + }, nil + // openai 兼容模型 default: - return nil, fmt.Errorf("invalid provider: %s", req.Provider) + models, err := reqModelListApi(req, httpClient, &domain.OpenAIResp{}) + + if err != nil { + return nil, err + } + return &domain.ModelListResp{ + Models: models, + }, nil } } func CheckModel(ctx context.Context, req *domain.CheckModelReq) (*domain.CheckModelResp, error) { checkResp := &domain.CheckModelResp{} - modelType, err := consts.ParseModelType(req.Type) - if err != nil { - checkResp.Error = err.Error() - return checkResp, nil - } + modelType := consts.ParseModelType(req.Type) if modelType == consts.ModelTypeEmbedding || modelType == consts.ModelTypeRerank { url := req.BaseURL @@ -261,6 +268,7 @@ func GetChatModel(ctx context.Context, model *domain.ModelMetadata) (model.BaseC config.HTTPClient = client } } + switch modelProvider { case consts.ModelProviderDeepSeek: chatModel, err := deepseek.NewChatModel(ctx, &deepseek.ChatModelConfig{ @@ -311,6 +319,7 @@ func GetChatModel(ctx context.Context, model *domain.ModelMetadata) (model.BaseC return nil, fmt.Errorf("create chat model failed: %w", err) } return chatModel, nil + // 兼容 openai api default: chatModel, err := openai.NewChatModel(ctx, config) if err != nil { diff --git a/utils/utils.go b/utils/utils.go index 16bdd00..62e6a92 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -297,12 +297,13 @@ func GetHttpClientWithAPIHeaderMap(header string) *http.Client { return nil } -func GetQuery(req *domain.ModelListReq) request.Query { +func GetQuery(req *domain.ModelListReq) (request.Query, error) { q := make(request.Query, 0) - provider := consts.ModelProvider(req.Provider) - modelType := consts.ModelType(req.Type) + provider := consts.ParseModelProvider(req.Provider) + modelType := consts.ParseModelType(req.Type) + if provider != consts.ModelProviderBaiZhiCloud && provider != consts.ModelProviderSiliconFlow { - return q + return q, nil } q["type"] = "text" q["sub_type"] = string(req.Type) @@ -313,5 +314,5 @@ func GetQuery(req *domain.ModelListReq) request.Query { if provider == consts.ModelProviderSiliconFlow && modelType == consts.ModelTypeCoder { q["sub_type"] = "chat" } - return q + return q, nil }