feat: modelscope provider (#493)

This commit is contained in:
yrk111222
2025-08-28 12:37:54 +08:00
committed by GitHub
parent fdd46408e1
commit 252ca8ef7b
10 changed files with 271 additions and 0 deletions
+1
View File
@@ -207,6 +207,7 @@ npx bumpp --no-commit --no-tag
- [x] [Baichuan](https://platform.baichuan-ai.com)
- [x] [Minimax](https://api.minimax.chat/)
- [x] [Moonshot AI](https://platform.moonshot.cn/)
- [x] [ModelScope](https://modelscope.cn/docs/model-service/API-Inference/intro)
- [x] [Player2](https://player2.game/)
- [x] [Tencent Cloud](https://cloud.tencent.com/document/product/1729)
- [ ] [Sparks](https://www.xfyun.cn/doc/spark/Web.html) (PR welcome)
@@ -0,0 +1,107 @@
<script setup lang="ts">
import type { RemovableRef } from '@vueuse/core'
import {
ProviderAdvancedSettings,
ProviderApiKeyInput,
ProviderBaseUrlInput,
ProviderBasicSettings,
ProviderSettingsContainer,
ProviderSettingsLayout,
} from '@proj-airi/stage-ui/components'
import { useProvidersStore } from '@proj-airi/stage-ui/stores/providers'
import { storeToRefs } from 'pinia'
import { computed, onMounted, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
const { t } = useI18n()
const router = useRouter()
const providersStore = useProvidersStore()
const { providers } = storeToRefs(providersStore) as { providers: RemovableRef<Record<string, any>> }
// Get provider metadata
const providerId = 'modelscope'
const providerMetadata = computed(() => providersStore.getProviderMetadata(providerId))
// Use computed properties for settings
const apiKey = computed({
get: () => providers.value[providerId]?.apiKey || '',
set: (value) => {
if (!providers.value[providerId])
providers.value[providerId] = {}
providers.value[providerId].apiKey = value
},
})
const baseUrl = computed({
get: () => providers.value[providerId]?.baseUrl || '',
set: (value) => {
if (!providers.value[providerId])
providers.value[providerId] = {}
providers.value[providerId].baseUrl = value
},
})
onMounted(() => {
// Initialize provider if it doesn't exist
providersStore.initializeProvider(providerId)
// Initialize refs with current values
apiKey.value = providers.value[providerId]?.apiKey || ''
baseUrl.value = providers.value[providerId]?.baseUrl || ''
})
// Watch settings and update the provider configuration
watch([apiKey, baseUrl], () => {
providers.value[providerId] = {
...providers.value[providerId],
apiKey: apiKey.value,
baseUrl: baseUrl.value || '',
}
})
function handleResetSettings() {
providers.value[providerId] = {
...(providerMetadata.value?.defaultOptions as any),
}
}
</script>
<template>
<ProviderSettingsLayout
:provider-name="providerMetadata?.localizedName"
:provider-icon="providerMetadata?.icon"
:on-back="() => router.back()"
>
<ProviderSettingsContainer>
<ProviderBasicSettings
:title="t('settings.pages.providers.common.section.basic.title')"
:description="t('settings.pages.providers.common.section.basic.description')"
:on-reset="handleResetSettings"
>
<ProviderApiKeyInput
v-model="apiKey"
:provider-name="providerMetadata?.localizedName"
placeholder="ms-..."
/>
</ProviderBasicSettings>
<ProviderAdvancedSettings :title="t('settings.pages.providers.common.section.advanced.title')">
<ProviderBaseUrlInput
v-model="baseUrl"
placeholder="https://api-inference.modelscope.cn/v1/"
/>
</ProviderAdvancedSettings>
</ProviderSettingsContainer>
</ProviderSettingsLayout>
</template>
<route lang="yaml">
meta:
layout: settings
stageTransition:
name: slide
</route>
@@ -0,0 +1,107 @@
<script setup lang="ts">
import type { RemovableRef } from '@vueuse/core'
import {
ProviderAdvancedSettings,
ProviderApiKeyInput,
ProviderBaseUrlInput,
ProviderBasicSettings,
ProviderSettingsContainer,
ProviderSettingsLayout,
} from '@proj-airi/stage-ui/components'
import { useProvidersStore } from '@proj-airi/stage-ui/stores/providers'
import { storeToRefs } from 'pinia'
import { computed, onMounted, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
const { t } = useI18n()
const router = useRouter()
const providersStore = useProvidersStore()
const { providers } = storeToRefs(providersStore) as { providers: RemovableRef<Record<string, any>> }
// Get provider metadata
const providerId = 'modelscope'
const providerMetadata = computed(() => providersStore.getProviderMetadata(providerId))
// Use computed properties for settings
const apiKey = computed({
get: () => providers.value[providerId]?.apiKey || '',
set: (value) => {
if (!providers.value[providerId])
providers.value[providerId] = {}
providers.value[providerId].apiKey = value
},
})
const baseUrl = computed({
get: () => providers.value[providerId]?.baseUrl || '',
set: (value) => {
if (!providers.value[providerId])
providers.value[providerId] = {}
providers.value[providerId].baseUrl = value
},
})
onMounted(() => {
// Initialize provider if it doesn't exist
providersStore.initializeProvider(providerId)
// Initialize refs with current values
apiKey.value = providers.value[providerId]?.apiKey || ''
baseUrl.value = providers.value[providerId]?.baseUrl || ''
})
// Watch settings and update the provider configuration
watch([apiKey, baseUrl], () => {
providers.value[providerId] = {
...providers.value[providerId],
apiKey: apiKey.value,
baseUrl: baseUrl.value || '',
}
})
function handleResetSettings() {
providers.value[providerId] = {
...(providerMetadata.value?.defaultOptions as any),
}
}
</script>
<template>
<ProviderSettingsLayout
:provider-name="providerMetadata?.localizedName"
:provider-icon="providerMetadata?.icon"
:on-back="() => router.back()"
>
<ProviderSettingsContainer>
<ProviderBasicSettings
:title="t('settings.pages.providers.common.section.basic.title')"
:description="t('settings.pages.providers.common.section.basic.description')"
:on-reset="handleResetSettings"
>
<ProviderApiKeyInput
v-model="apiKey"
:provider-name="providerMetadata?.localizedName"
placeholder="ms-..."
/>
</ProviderBasicSettings>
<ProviderAdvancedSettings :title="t('settings.pages.providers.common.section.advanced.title')">
<ProviderBaseUrlInput
v-model="baseUrl"
placeholder="https://api-inference.modelscope.cn/v1/"
/>
</ProviderAdvancedSettings>
</ProviderSettingsContainer>
</ProviderSettingsLayout>
</template>
<route lang="yaml">
meta:
layout: settings
stageTransition:
name: slide
</route>
+1
View File
@@ -182,6 +182,7 @@ pnpm dev:docs
- [x] [Baichuan](https://platform.baichuan-ai.com)
- [x] [Minimax](https://api.minimax.chat/)
- [x] [Moonshot AI](https://platform.moonshot.cn/)
- [x] [ModelScope](https://modelscope.cn/docs/model-service/API-Inference/intro)
- [x] [Player2](https://player2.game/)
- [x] [Tencent Cloud](https://cloud.tencent.com/document/product/1729)
- [ ] [Sparks](https://www.xfyun.cn/doc/spark/Web.html)PR歓迎)
+1
View File
@@ -174,6 +174,7 @@ pnpm -F @proj-airi/docs dev
- [x] [百川](https://platform.baichuan-ai.com)
- [x] [Minimax](https://api.minimax.chat/)
- [x] [月之暗面](https://platform.moonshot.cn/)
- [x] [魔搭社区](https://modelscope.cn/docs/model-service/API-Inference/intro)
- [x] [Player2](https://player2.game/)
- [x] [腾讯混元](https://cloud.tencent.com/document/product/1729)
- [ ] [讯飞星火](https://www.xfyun.cn/doc/spark/Web.html)
@@ -406,6 +406,9 @@ pages:
moonshot:
description: moonshot.ai
title: Moonshot AI
modelscope:
description: modelscope.cn
title: ModelScope
novita:
description: novita.ai
title: Novita
@@ -403,6 +403,9 @@ pages:
moonshot:
description: moonshot.ai
title: Moonshot AI
modelscope:
description: modelscope.cn
title: ModelScope
novita:
description: novita.ai
title: Novita
@@ -375,6 +375,9 @@ pages:
moonshot:
description: Moonshot.ai
title: 月之暗面
modelscope:
description: modelscope.cn
title: 魔搭社区 ModelScope
novita:
description: novita.ai
title: Novita
@@ -119,6 +119,7 @@ function getApiKeyPlaceholder(_providerId: string): string {
'together-ai': 'togetherapi-...',
'mistral-ai': 'mis-...',
'moonshot-ai': 'ms-...',
'modelscope': 'ms-...',
'fireworks-ai': 'fw-...',
'featherless-ai': 'fw-...',
'novita-ai': 'nvt-...',
+44
View File
@@ -2140,6 +2140,50 @@ export const useProvidersStore = defineStore('providers', () => {
},
},
},
'modelscope': {
id: 'modelscope',
category: 'chat',
tasks: ['text-generation'],
nameKey: 'settings.pages.providers.provider.modelscope.title',
name: 'ModelScope',
descriptionKey: 'settings.pages.providers.provider.modelscope.description',
description: 'modelscope',
icon: 'i-lobe-icons:modelscope',
defaultOptions: () => ({
baseUrl: 'https://api-inference.modelscope.cn/v1/',
}),
createProvider: async config => createOpenAI((config.apiKey as string).trim(), (config.baseUrl as string).trim()),
capabilities: {
listModels: async (config) => {
return (await listModels({
...createOpenAI((config.apiKey as string).trim(), (config.baseUrl as string).trim()).model(),
})).map((model) => {
return {
id: model.id,
name: model.id,
provider: 'modelscope',
description: '',
contextLength: 0,
deprecated: false,
} satisfies ModelInfo
})
},
},
validators: {
validateProviderConfig: (config) => {
const errors = [
!config.apiKey && new Error('API key is required.'),
!config.baseUrl && new Error('Base URL is required.'),
].filter(Boolean)
return {
errors,
reason: errors.filter(e => e).map(e => String(e)).join(', ') || '',
valid: !!config.apiKey && !!config.baseUrl,
}
},
},
},
'player2': {
id: 'player2',
category: 'chat',