mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-08 05:50:12 +08:00
Compare commits
1 Commits
master
...
codex/prov
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d455e37f92 |
@@ -24,7 +24,6 @@ from astrbot.core.platform.register import platform_cls_map, platform_registry
|
||||
from astrbot.core.provider.register import provider_registry
|
||||
from astrbot.core.star.star import star_registry
|
||||
from astrbot.core.utils.astrbot_path import get_astrbot_plugin_data_path
|
||||
from astrbot.core.utils.llm_metadata import LLM_METADATAS
|
||||
from astrbot.core.utils.totp import (
|
||||
is_totp_enabled,
|
||||
revoke_user_trusted_devices,
|
||||
@@ -1299,14 +1298,18 @@ class ProviderConfigService:
|
||||
if provider.default_config_tmpl:
|
||||
provider_default_tmpl[provider.type] = provider.default_config_tmpl
|
||||
providers = copy.deepcopy(self.config.get("provider", []))
|
||||
from astrbot.core.utils.llm_metadata import LLM_METADATAS
|
||||
|
||||
model_metadata = {}
|
||||
for provider in providers:
|
||||
model_id = provider.get("model")
|
||||
if isinstance(model_id, str) and model_id in LLM_METADATAS:
|
||||
provider["model_metadata"] = LLM_METADATAS[model_id]
|
||||
model_metadata[model_id] = LLM_METADATAS[model_id]
|
||||
return {
|
||||
"config_schema": config_schema,
|
||||
"providers": providers,
|
||||
"provider_sources": self.config.get("provider_sources", []),
|
||||
"model_metadata": model_metadata,
|
||||
}
|
||||
|
||||
def list_provider_sources(self) -> dict:
|
||||
@@ -1549,8 +1552,11 @@ class ProviderConfigService:
|
||||
source_id: str | None = None,
|
||||
enabled: bool | None = None,
|
||||
) -> dict:
|
||||
from astrbot.core.utils.llm_metadata import LLM_METADATAS
|
||||
|
||||
provider_type = self._resolve_provider_type(capability)
|
||||
providers = []
|
||||
model_metadata = {}
|
||||
source_provider_type = {
|
||||
source["id"]: source.get("provider_type", "chat_completion")
|
||||
for source in self.provider_manager.provider_sources_config
|
||||
@@ -1575,9 +1581,9 @@ class ProviderConfigService:
|
||||
provider_response = copy.deepcopy(provider)
|
||||
model_id = provider_response.get("model")
|
||||
if isinstance(model_id, str) and model_id in LLM_METADATAS:
|
||||
provider_response["model_metadata"] = LLM_METADATAS[model_id]
|
||||
model_metadata[model_id] = LLM_METADATAS[model_id]
|
||||
providers.append(provider_response)
|
||||
return {"providers": providers}
|
||||
return {"providers": providers, "model_metadata": model_metadata}
|
||||
|
||||
def list_providers_for_dashboard_types(
|
||||
self, provider_type: str | None
|
||||
@@ -1607,10 +1613,14 @@ class ProviderConfigService:
|
||||
)
|
||||
if provider is None:
|
||||
raise ValueError(f"Provider {provider_id} not found")
|
||||
model_id = provider.get("model")
|
||||
provider_response = copy.deepcopy(provider)
|
||||
from astrbot.core.utils.llm_metadata import LLM_METADATAS
|
||||
|
||||
model_id = provider_response.get("model")
|
||||
model_metadata = {}
|
||||
if isinstance(model_id, str) and model_id in LLM_METADATAS:
|
||||
provider["model_metadata"] = LLM_METADATAS[model_id]
|
||||
return {"provider": provider}
|
||||
model_metadata[model_id] = LLM_METADATAS[model_id]
|
||||
return {"provider": provider_response, "model_metadata": model_metadata}
|
||||
|
||||
async def create_provider(self, config: dict, source_id: str | None = None) -> None:
|
||||
config = copy.deepcopy(config)
|
||||
|
||||
@@ -78,6 +78,21 @@ export interface ProviderSchemaData {
|
||||
config_schema?: OpenConfig;
|
||||
providers?: OpenConfig[];
|
||||
provider_sources?: OpenConfig[];
|
||||
model_metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface ProviderListData {
|
||||
providers?: OpenConfig[];
|
||||
model_metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface ProviderByTypeEnvelope extends ApiEnvelope<OpenConfig[]> {
|
||||
model_metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface ProviderByIdData {
|
||||
provider?: OpenConfig;
|
||||
model_metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface ProviderSourceModelsData {
|
||||
@@ -496,11 +511,13 @@ export const providerApi = {
|
||||
);
|
||||
},
|
||||
list(params?: ProviderListParams) {
|
||||
return typed<{ providers: OpenConfig[] }>(
|
||||
return typed<ProviderListData>(
|
||||
openApiV1.listProviders({ query: generatedQuery(params) }),
|
||||
);
|
||||
},
|
||||
async listByProviderType(providerType: string): Promise<AxiosResponse<ApiEnvelope<OpenConfig[]>>> {
|
||||
async listByProviderType(
|
||||
providerType: string,
|
||||
): Promise<AxiosResponse<ProviderByTypeEnvelope>> {
|
||||
const capabilities = providerTypeToCapabilities(providerType);
|
||||
if (capabilities.length === 0) {
|
||||
const response = await providerApi.list();
|
||||
@@ -509,6 +526,7 @@ export const providerApi = {
|
||||
data: {
|
||||
...response.data,
|
||||
data: response.data.data.providers || [],
|
||||
model_metadata: response.data.data.model_metadata || {},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -517,11 +535,21 @@ export const providerApi = {
|
||||
capabilities.map((capability) => providerApi.list({ capability })),
|
||||
);
|
||||
const first = responses[0];
|
||||
const modelMetadata = responses.reduce<Record<string, unknown>>(
|
||||
(acc, response) => ({
|
||||
...acc,
|
||||
...(response.data.data.model_metadata || {}),
|
||||
}),
|
||||
{},
|
||||
);
|
||||
return {
|
||||
...first,
|
||||
data: {
|
||||
...first.data,
|
||||
data: responses.flatMap((response) => response.data.data.providers || []),
|
||||
data: responses.flatMap(
|
||||
(response) => response.data.data.providers || [],
|
||||
),
|
||||
model_metadata: modelMetadata,
|
||||
},
|
||||
};
|
||||
},
|
||||
@@ -545,7 +573,7 @@ export const providerApi = {
|
||||
);
|
||||
},
|
||||
get(providerId: string, merged = false) {
|
||||
return typed<{ provider: OpenConfig }>(
|
||||
return typed<ProviderByIdData>(
|
||||
openApiV1.getProviderById({
|
||||
query: { provider_id: providerId, merged },
|
||||
}),
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
<span>{{ item.tooltip }}</span>
|
||||
</v-tooltip>
|
||||
<v-tooltip
|
||||
v-if="formatContextLimit(provider)"
|
||||
v-if="formatContextLimit(provider, metadataForProvider(provider))"
|
||||
location="top"
|
||||
max-width="320"
|
||||
>
|
||||
@@ -83,12 +83,15 @@
|
||||
class="meta-context-badge"
|
||||
@click.stop
|
||||
>
|
||||
{{ formatContextLimit(provider) }}
|
||||
{{ formatContextLimit(provider, metadataForProvider(provider)) }}
|
||||
</span>
|
||||
</template>
|
||||
<span>{{
|
||||
tm("models.metadata.context", {
|
||||
tokens: formatContextLimit(provider),
|
||||
tokens: formatContextLimit(
|
||||
provider,
|
||||
metadataForProvider(provider),
|
||||
),
|
||||
})
|
||||
}}</span>
|
||||
</v-tooltip>
|
||||
@@ -141,6 +144,7 @@ import { useToast } from "@/utils/toast";
|
||||
import {
|
||||
formatContextLimit,
|
||||
providerCapabilityBadges,
|
||||
type ProviderModelMetadata,
|
||||
type ProviderMetadataSource,
|
||||
} from "@/utils/providerMetadata";
|
||||
|
||||
@@ -170,6 +174,7 @@ const menuOpen = ref(false);
|
||||
const loadingProviders = ref(false);
|
||||
const providersLoaded = ref(false);
|
||||
const testingProviderIds = ref<string[]>([]);
|
||||
const modelMetadata = ref<Record<string, ProviderModelMetadata>>({});
|
||||
const { tm } = useModuleI18n("features/provider");
|
||||
const { success: toastSuccess, error: toastError } = useToast();
|
||||
|
||||
@@ -230,6 +235,9 @@ async function loadProviderConfigs(force = false) {
|
||||
try {
|
||||
const response = await providerApi.listByProviderType("chat_completion");
|
||||
if (response.data.status === "ok") {
|
||||
modelMetadata.value = (
|
||||
response.data.model_metadata || {}
|
||||
) as Record<string, ProviderModelMetadata>;
|
||||
providerConfigs.value = (
|
||||
(response.data.data || []) as unknown as ProviderConfig[]
|
||||
).filter((provider: ProviderConfig) => provider.enable !== false);
|
||||
@@ -255,7 +263,11 @@ function selectProvider(provider: ProviderConfig) {
|
||||
}
|
||||
|
||||
function capabilityBadges(provider: ProviderConfig) {
|
||||
return providerCapabilityBadges(provider, tm);
|
||||
return providerCapabilityBadges(provider, metadataForProvider(provider), tm);
|
||||
}
|
||||
|
||||
function metadataForProvider(provider: ProviderConfig) {
|
||||
return provider.model ? modelMetadata.value[provider.model] || null : null;
|
||||
}
|
||||
|
||||
async function testProvider(provider: ProviderConfig) {
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
<span>{{ item.tooltip }}</span>
|
||||
</v-tooltip>
|
||||
<v-tooltip
|
||||
v-if="formatContextLimit(provider)"
|
||||
v-if="formatContextLimit(provider, metadataForProvider(provider))"
|
||||
location="top"
|
||||
max-width="320"
|
||||
>
|
||||
@@ -107,12 +107,15 @@
|
||||
class="regenerate-model-context-badge"
|
||||
@click.stop
|
||||
>
|
||||
{{ formatContextLimit(provider) }}
|
||||
{{ formatContextLimit(provider, metadataForProvider(provider)) }}
|
||||
</span>
|
||||
</template>
|
||||
<span>{{
|
||||
providerTm("models.metadata.context", {
|
||||
tokens: formatContextLimit(provider),
|
||||
tokens: formatContextLimit(
|
||||
provider,
|
||||
metadataForProvider(provider),
|
||||
),
|
||||
})
|
||||
}}</span>
|
||||
</v-tooltip>
|
||||
@@ -137,6 +140,7 @@ import { useModuleI18n } from "@/i18n/composables";
|
||||
import {
|
||||
formatContextLimit,
|
||||
providerCapabilityBadges,
|
||||
type ProviderModelMetadata,
|
||||
type ProviderMetadataSource,
|
||||
} from "@/utils/providerMetadata";
|
||||
|
||||
@@ -161,6 +165,7 @@ const { tm: providerTm } = useModuleI18n("features/provider");
|
||||
const providerConfigs = ref<ProviderConfig[]>([]);
|
||||
const loadingProviders = ref(false);
|
||||
const providersLoaded = ref(false);
|
||||
const modelMetadata = ref<Record<string, ProviderModelMetadata>>({});
|
||||
|
||||
async function loadProviderConfigs(force = false) {
|
||||
if (loadingProviders.value || (providersLoaded.value && !force)) return;
|
||||
@@ -168,6 +173,9 @@ async function loadProviderConfigs(force = false) {
|
||||
try {
|
||||
const response = await providerApi.listByProviderType("chat_completion");
|
||||
if (response.data.status === "ok") {
|
||||
modelMetadata.value = (
|
||||
response.data.model_metadata || {}
|
||||
) as Record<string, ProviderModelMetadata>;
|
||||
providerConfigs.value = (
|
||||
(response.data.data || []) as unknown as ProviderConfig[]
|
||||
).filter((provider: ProviderConfig) => provider.enable !== false);
|
||||
@@ -200,7 +208,15 @@ function retryWithModel(provider: ProviderConfig) {
|
||||
}
|
||||
|
||||
function capabilityBadges(provider: ProviderConfig) {
|
||||
return providerCapabilityBadges(provider, providerTm);
|
||||
return providerCapabilityBadges(
|
||||
provider,
|
||||
metadataForProvider(provider),
|
||||
providerTm,
|
||||
);
|
||||
}
|
||||
|
||||
function metadataForProvider(provider: ProviderConfig) {
|
||||
return provider.model ? modelMetadata.value[provider.model] || null : null;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
{{ provider.type || provider.provider_type || tm('providerSelector.unknownType') }}
|
||||
<span v-if="provider.model">- {{ provider.model }}</span>
|
||||
<span
|
||||
v-if="capabilityBadges(provider).length || formatContextLimit(provider)"
|
||||
v-if="capabilityBadges(provider).length || formatContextLimit(provider, metadataForProvider(provider))"
|
||||
class="provider-selector-meta"
|
||||
>
|
||||
<v-tooltip
|
||||
@@ -147,7 +147,7 @@
|
||||
<span>{{ item.tooltip }}</span>
|
||||
</v-tooltip>
|
||||
<v-tooltip
|
||||
v-if="formatContextLimit(provider)"
|
||||
v-if="formatContextLimit(provider, metadataForProvider(provider))"
|
||||
location="top"
|
||||
max-width="320"
|
||||
>
|
||||
@@ -157,12 +157,12 @@
|
||||
class="provider-selector-meta__context"
|
||||
@click.stop
|
||||
>
|
||||
{{ formatContextLimit(provider) }}
|
||||
{{ formatContextLimit(provider, metadataForProvider(provider)) }}
|
||||
</span>
|
||||
</template>
|
||||
<span>{{
|
||||
providerTm('models.metadata.context', {
|
||||
tokens: formatContextLimit(provider)
|
||||
tokens: formatContextLimit(provider, metadataForProvider(provider))
|
||||
})
|
||||
}}</span>
|
||||
</v-tooltip>
|
||||
@@ -280,6 +280,7 @@ const selectedProvider = ref('')
|
||||
const selectedProviders = ref([])
|
||||
const providerDrawer = ref(false)
|
||||
const testingProviders = ref([])
|
||||
const modelMetadata = ref({})
|
||||
|
||||
const hasSelection = computed(() => {
|
||||
if (props.multiple) {
|
||||
@@ -329,6 +330,7 @@ async function loadProviders() {
|
||||
try {
|
||||
const response = await providerApi.listByProviderType(props.providerType)
|
||||
if (response.data.status === 'ok') {
|
||||
modelMetadata.value = response.data.model_metadata || {}
|
||||
const providers = response.data.data || []
|
||||
providerList.value = props.providerSubtype
|
||||
? providers.filter((provider) => matchesProviderSubtype(provider, props.providerSubtype))
|
||||
@@ -398,7 +400,11 @@ function isProviderSelected(providerId) {
|
||||
}
|
||||
|
||||
function capabilityBadges(provider) {
|
||||
return providerCapabilityBadges(provider, providerTm)
|
||||
return providerCapabilityBadges(provider, metadataForProvider(provider), providerTm)
|
||||
}
|
||||
|
||||
function metadataForProvider(provider) {
|
||||
return provider?.model ? modelMetadata.value[provider.model] || null : null
|
||||
}
|
||||
|
||||
function isProviderTesting(providerId) {
|
||||
|
||||
@@ -59,7 +59,6 @@ export function useProviderModelConfigDialog(options: UseProviderModelConfigDial
|
||||
|
||||
function openProviderEdit(provider: any) {
|
||||
const editableProvider = JSON.parse(JSON.stringify(provider))
|
||||
delete editableProvider.model_metadata
|
||||
providerEditData.value = editableProvider
|
||||
providerEditOriginalId.value = provider.id
|
||||
providerEditMode.value = 'edit'
|
||||
@@ -91,7 +90,6 @@ export function useProviderModelConfigDialog(options: UseProviderModelConfigDial
|
||||
|
||||
savingProviders.value.push(providerEditData.value.id)
|
||||
try {
|
||||
delete providerEditData.value.model_metadata
|
||||
const isAdding = providerEditMode.value === 'add'
|
||||
const sourceId = providerEditData.value.provider_source_id || selectedProviderSource.value?.id
|
||||
if (isAdding && !sourceId) {
|
||||
|
||||
@@ -151,7 +151,7 @@ export function useProviderSources(options: UseProviderSourcesOptions) {
|
||||
|
||||
const mergedModelEntries = computed(() => {
|
||||
const configuredEntries = (sourceProviders.value || []).map((provider: any) => {
|
||||
const metadata = getModelMetadata(provider.model) || provider.model_metadata || null
|
||||
const metadata = getModelMetadata(provider.model)
|
||||
return {
|
||||
type: 'configured',
|
||||
provider,
|
||||
@@ -691,6 +691,7 @@ export function useProviderSources(options: UseProviderSourcesOptions) {
|
||||
providerTemplates.value = configSchema.value.provider.config_template
|
||||
}
|
||||
providerSources.value = response.data.data.provider_sources || []
|
||||
modelMetadata.value = (response.data.data.model_metadata || {}) as Record<string, any>
|
||||
providers.value = response.data.data.providers || []
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -6,8 +6,8 @@ export interface ProviderModelMetadata {
|
||||
}
|
||||
|
||||
export interface ProviderMetadataSource {
|
||||
model?: string
|
||||
modalities?: string[]
|
||||
model_metadata?: ProviderModelMetadata | null
|
||||
max_context_tokens?: number
|
||||
reasoning?: boolean
|
||||
}
|
||||
@@ -19,8 +19,11 @@ export interface ProviderCapabilityBadge {
|
||||
tooltip: string
|
||||
}
|
||||
|
||||
export function formatContextLimit(provider: ProviderMetadataSource | null | undefined): string {
|
||||
const context = provider?.model_metadata?.limit?.context || provider?.max_context_tokens
|
||||
export function formatContextLimit(
|
||||
provider: ProviderMetadataSource | null | undefined,
|
||||
metadata?: ProviderModelMetadata | null
|
||||
): string {
|
||||
const context = metadata?.limit?.context || provider?.max_context_tokens
|
||||
if (!context || typeof context !== 'number') return ''
|
||||
if (context >= 1_000_000) return `${Math.round(context / 1_000_000)}M`
|
||||
if (context >= 1_000) return `${Math.round(context / 1_000)}K`
|
||||
@@ -29,9 +32,10 @@ export function formatContextLimit(provider: ProviderMetadataSource | null | und
|
||||
|
||||
export function providerCapabilityBadges(
|
||||
provider: ProviderMetadataSource | null | undefined,
|
||||
metadata: ProviderModelMetadata | null | undefined,
|
||||
tm: (key: string, params?: Record<string, string>) => string
|
||||
): ProviderCapabilityBadge[] {
|
||||
const inputs = provider?.model_metadata?.modalities?.input || []
|
||||
const inputs = metadata?.modalities?.input || []
|
||||
const providerModalities = provider?.modalities
|
||||
const modalities = Array.isArray(providerModalities) ? providerModalities : []
|
||||
const definitions = [
|
||||
@@ -52,14 +56,14 @@ export function providerCapabilityBadges(
|
||||
{
|
||||
key: 'tool_use',
|
||||
icon: 'mdi-wrench-outline',
|
||||
supported: Boolean(provider?.model_metadata?.tool_call),
|
||||
supported: Boolean(metadata?.tool_call),
|
||||
enabled: modalities.includes('tool_use'),
|
||||
label: tm('models.metadata.toolUse')
|
||||
},
|
||||
{
|
||||
key: 'reasoning',
|
||||
icon: 'mdi-brain',
|
||||
supported: Boolean(provider?.model_metadata?.reasoning),
|
||||
supported: Boolean(metadata?.reasoning),
|
||||
enabled: Boolean(provider?.reasoning),
|
||||
label: tm('models.metadata.reasoning')
|
||||
}
|
||||
@@ -70,9 +74,9 @@ export function providerCapabilityBadges(
|
||||
.map((item) => ({
|
||||
key: item.key,
|
||||
icon: item.icon,
|
||||
enabled: !provider?.model_metadata || item.enabled,
|
||||
enabled: !metadata || item.enabled,
|
||||
tooltip:
|
||||
provider?.model_metadata && !item.enabled
|
||||
metadata && !item.enabled
|
||||
? tm('models.metadata.supportedDisabled', { capability: item.label })
|
||||
: tm('models.metadata.enabled', { capability: item.label })
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user