mirror of
https://github.com/moeru-ai/airi.git
synced 2026-08-14 08:52:42 +00:00
fix(stage-ui): sync module selections to active character card (#1979)
This commit is contained in:
@@ -324,6 +324,9 @@ pages:
|
||||
provider: Chat / Provider
|
||||
consciousness:
|
||||
model: Consciousness / Model
|
||||
vision:
|
||||
provider: Vision / Provider
|
||||
model: Vision / Model
|
||||
created_by: created by
|
||||
creator_notes: Creator Notes
|
||||
delete: Delete
|
||||
|
||||
@@ -310,6 +310,9 @@ pages:
|
||||
provider: Chat / Proveedor
|
||||
consciousness:
|
||||
model: Consciencia / Modelo
|
||||
vision:
|
||||
provider: Visión / Proveedor
|
||||
model: Visión / Modelo
|
||||
created_by: creado por
|
||||
creator_notes: Notas del Creador
|
||||
delete: Eliminar
|
||||
|
||||
@@ -310,6 +310,9 @@ pages:
|
||||
provider: Chat / Fournisseur
|
||||
consciousness:
|
||||
model: Conscience / Modèle
|
||||
vision:
|
||||
provider: Vision / Fournisseur
|
||||
model: Vision / Modèle
|
||||
created_by: créé par
|
||||
creator_notes: Notes du créateur
|
||||
delete: Supprimer
|
||||
|
||||
@@ -310,6 +310,9 @@ pages:
|
||||
provider: チャット / プロバイダー
|
||||
consciousness:
|
||||
model: 意識 / モデル
|
||||
vision:
|
||||
provider: ビジョン / プロバイダー
|
||||
model: ビジョン / モデル
|
||||
created_by: 作成者
|
||||
creator_notes: 作成者のメモ
|
||||
delete: 削除
|
||||
|
||||
@@ -310,6 +310,9 @@ pages:
|
||||
provider: 채팅 / 제공자
|
||||
consciousness:
|
||||
model: 의식 / 모델
|
||||
vision:
|
||||
provider: 비전 / 제공자
|
||||
model: 비전 / 모델
|
||||
created_by: 만든이
|
||||
creator_notes: 제작자 노트
|
||||
delete: 삭제
|
||||
|
||||
@@ -310,6 +310,9 @@ pages:
|
||||
provider: Чат / провайдеры
|
||||
consciousness:
|
||||
model: Сознание / Модель
|
||||
vision:
|
||||
provider: Зрение / Провайдер
|
||||
model: Зрение / Модель
|
||||
created_by: создано
|
||||
creator_notes: Заметки автора
|
||||
delete: Удалить
|
||||
|
||||
@@ -310,6 +310,9 @@ pages:
|
||||
provider: Trò chuyện / Nhà cung cấp
|
||||
consciousness:
|
||||
model: Ý thức / Mô hình
|
||||
vision:
|
||||
provider: Thị giác / Nhà cung cấp
|
||||
model: Thị giác / Mô hình
|
||||
created_by: được tạo bởi
|
||||
creator_notes: Ghi chú của tác giả
|
||||
delete: Xóa
|
||||
|
||||
@@ -310,6 +310,9 @@ pages:
|
||||
provider: 聊天 / 提供商
|
||||
consciousness:
|
||||
model: 意识 / 模型
|
||||
vision:
|
||||
provider: 视觉 / 提供商
|
||||
model: 视觉 / 模型
|
||||
created_by: 创建者
|
||||
creator_notes: 创建者笔记
|
||||
delete: 删除
|
||||
|
||||
@@ -310,6 +310,9 @@ pages:
|
||||
provider: 聊天 / 供應商
|
||||
consciousness:
|
||||
model: 意識 / 模型
|
||||
vision:
|
||||
provider: 視覺 / 供應商
|
||||
model: 視覺 / 模型
|
||||
created_by: 建立者
|
||||
creator_notes: 建立者筆記
|
||||
delete: 刪除
|
||||
|
||||
+72
-1
@@ -11,6 +11,7 @@ import { useAiriCardStore } from '@proj-airi/stage-ui/stores/modules/airi-card'
|
||||
import { useArtistryStore } from '@proj-airi/stage-ui/stores/modules/artistry'
|
||||
import { useConsciousnessStore } from '@proj-airi/stage-ui/stores/modules/consciousness'
|
||||
import { useSpeechStore } from '@proj-airi/stage-ui/stores/modules/speech'
|
||||
import { useVisionStore } from '@proj-airi/stage-ui/stores/modules/vision'
|
||||
import { useProvidersStore } from '@proj-airi/stage-ui/stores/providers'
|
||||
import { useSettingsStageModel } from '@proj-airi/stage-ui/stores/settings/stage-model'
|
||||
import { Button, FieldInput, FieldValues } from '@proj-airi/ui'
|
||||
@@ -59,6 +60,7 @@ const modelValue = defineModel<boolean>()
|
||||
const { t } = useI18n()
|
||||
const cardStore = useAiriCardStore()
|
||||
const consciousnessStore = useConsciousnessStore()
|
||||
const visionStore = useVisionStore()
|
||||
const speechStore = useSpeechStore()
|
||||
const providersStore = useProvidersStore()
|
||||
const displayModelsStore = useDisplayModelsStore()
|
||||
@@ -66,6 +68,7 @@ const stageModelStore = useSettingsStageModel()
|
||||
const artistryStore = useArtistryStore()
|
||||
|
||||
const { activeProvider: consciousnessProvider, activeModel: defaultConsciousnessModel } = storeToRefs(consciousnessStore)
|
||||
const { activeProvider: visionProvider, activeModel: defaultVisionModel } = storeToRefs(visionStore)
|
||||
const { activeSpeechProvider: speechProvider, activeSpeechModel: defaultSpeechModel, activeSpeechVoiceId: defaultSpeechVoiceId } = storeToRefs(speechStore)
|
||||
const { displayModels } = storeToRefs(displayModelsStore)
|
||||
const { stageModelSelected: defaultDisplayModelId } = storeToRefs(stageModelStore)
|
||||
@@ -77,6 +80,8 @@ const isEditMode = computed(() => !!props.cardId)
|
||||
// Modules configuration
|
||||
const selectedConsciousnessProvider = ref<string>('')
|
||||
const selectedConsciousnessModel = ref<string>('')
|
||||
const selectedVisionProvider = ref<string>('')
|
||||
const selectedVisionModel = ref<string>('')
|
||||
const selectedSpeechProvider = ref<string>('')
|
||||
const selectedSpeechModel = ref<string>('')
|
||||
const selectedSpeechVoiceId = ref<string>('')
|
||||
@@ -120,6 +125,26 @@ const consciousnessModelOptions = computed(() => {
|
||||
}))
|
||||
})
|
||||
|
||||
// Computed: available vision provider options
|
||||
const visionProviderOptions = computed(() => {
|
||||
return providersStore.configuredVisionProvidersMetadata.map(provider => ({
|
||||
value: provider.id,
|
||||
label: provider.localizedName || provider.name,
|
||||
}))
|
||||
})
|
||||
|
||||
// Computed: available vision models options
|
||||
const visionModelOptions = computed(() => {
|
||||
const provider = selectedVisionProvider.value || visionProvider.value
|
||||
if (!provider)
|
||||
return []
|
||||
const models = providersStore.getModelsForProvider(provider)
|
||||
return models.map(model => ({
|
||||
value: model.id,
|
||||
label: model.name || model.id,
|
||||
}))
|
||||
})
|
||||
|
||||
// Computed: available speech provider options
|
||||
const speechProviderOptions = computed(() => {
|
||||
return providersStore.configuredSpeechProvidersMetadata.map(provider => ({
|
||||
@@ -167,10 +192,13 @@ const artistryProviderOptions = computed(() => {
|
||||
})
|
||||
|
||||
// Load models for current providers on init
|
||||
watch(() => [consciousnessProvider.value, speechProvider.value], async ([consProvider, spProvider]) => {
|
||||
watch(() => [consciousnessProvider.value, visionProvider.value, speechProvider.value], async ([consProvider, visProvider, spProvider]) => {
|
||||
if (consProvider) {
|
||||
await consciousnessStore.loadModelsForProvider(consProvider)
|
||||
}
|
||||
if (visProvider) {
|
||||
await visionStore.loadModelsForProvider(visProvider)
|
||||
}
|
||||
if (spProvider) {
|
||||
await speechStore.loadVoicesForProvider(spProvider)
|
||||
const metadata = providersStore.getProviderMetadata(spProvider)
|
||||
@@ -189,6 +217,14 @@ watch(selectedConsciousnessProvider, async (newProvider, oldProvider) => {
|
||||
}
|
||||
})
|
||||
|
||||
// Watch vision provider changes and reload models
|
||||
watch(selectedVisionProvider, async (newProvider, oldProvider) => {
|
||||
if (oldProvider !== undefined && newProvider !== oldProvider && newProvider) {
|
||||
await visionStore.loadModelsForProvider(newProvider)
|
||||
selectedVisionModel.value = ''
|
||||
}
|
||||
})
|
||||
|
||||
// Watch speech provider changes and reload models/voices
|
||||
watch(selectedSpeechProvider, async (newProvider, oldProvider) => {
|
||||
if (oldProvider !== undefined && newProvider !== oldProvider && newProvider) {
|
||||
@@ -353,6 +389,10 @@ function saveCard(card: Card): boolean {
|
||||
provider: selectedConsciousnessProvider.value || consciousnessProvider.value,
|
||||
model: selectedConsciousnessModel.value || defaultConsciousnessModel.value,
|
||||
},
|
||||
vision: {
|
||||
provider: selectedVisionProvider.value || visionProvider.value,
|
||||
model: selectedVisionModel.value || defaultVisionModel.value,
|
||||
},
|
||||
speech: {
|
||||
provider: selectedSpeechProvider.value || speechProvider.value,
|
||||
model: selectedSpeechModel.value || defaultSpeechModel.value,
|
||||
@@ -398,6 +438,8 @@ function initializeCard(): Card {
|
||||
// Initialize module selections with fallback logic (handles all cases: create, edit with/without extension)
|
||||
selectedConsciousnessProvider.value = airiExt?.modules?.consciousness?.provider || consciousnessProvider.value
|
||||
selectedConsciousnessModel.value = airiExt?.modules?.consciousness?.model || defaultConsciousnessModel.value
|
||||
selectedVisionProvider.value = airiExt?.modules?.vision?.provider || visionProvider.value
|
||||
selectedVisionModel.value = airiExt?.modules?.vision?.model || defaultVisionModel.value
|
||||
selectedSpeechProvider.value = airiExt?.modules?.speech?.provider || speechProvider.value
|
||||
selectedSpeechModel.value = airiExt?.modules?.speech?.model || defaultSpeechModel.value
|
||||
selectedSpeechVoiceId.value = airiExt?.modules?.speech?.voice_id || defaultSpeechVoiceId.value
|
||||
@@ -594,6 +636,35 @@ function getDefaultPlaceholder(defaultValue: string | undefined): string {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Vision Provider -->
|
||||
<div :class="['flex', 'flex-col', 'gap-2']">
|
||||
<label :class="['flex', 'flex-row', 'items-center', 'gap-2', 'text-sm', 'text-neutral-500', 'dark:text-neutral-400']">
|
||||
<div i-lucide:eye />
|
||||
{{ t('settings.pages.card.vision.provider') }}
|
||||
</label>
|
||||
<ComboboxSelect
|
||||
v-model="selectedVisionProvider"
|
||||
:options="visionProviderOptions"
|
||||
:placeholder="getDefaultPlaceholder(visionProvider)"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Vision Model -->
|
||||
<div :class="['flex', 'flex-col', 'gap-2']">
|
||||
<label :class="['flex', 'flex-row', 'items-center', 'gap-2', 'text-sm', 'text-neutral-500', 'dark:text-neutral-400']">
|
||||
<div i-lucide:scan-eye />
|
||||
{{ t('settings.pages.card.vision.model') }}
|
||||
</label>
|
||||
<ComboboxSelect
|
||||
v-model="selectedVisionModel"
|
||||
:options="visionModelOptions"
|
||||
:placeholder="getDefaultPlaceholder(defaultVisionModel)"
|
||||
:disabled="!selectedVisionProvider && !visionProvider"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Speech Provider -->
|
||||
<div :class="['flex', 'flex-col', 'gap-2']">
|
||||
<label :class="['flex', 'flex-row', 'items-center', 'gap-2', 'text-sm', 'text-neutral-500', 'dark:text-neutral-400']">
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useBackgroundStore } from '@proj-airi/stage-ui/stores/background'
|
||||
import { useAiriCardStore } from '@proj-airi/stage-ui/stores/modules/airi-card'
|
||||
import { useConsciousnessStore } from '@proj-airi/stage-ui/stores/modules/consciousness'
|
||||
import { useSpeechStore } from '@proj-airi/stage-ui/stores/modules/speech'
|
||||
import { useVisionStore } from '@proj-airi/stage-ui/stores/modules/vision'
|
||||
import { Button, Select } from '@proj-airi/ui'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import {
|
||||
@@ -36,12 +37,14 @@ const { t } = useI18n()
|
||||
const cardStore = useAiriCardStore()
|
||||
const consciousnessStore = useConsciousnessStore()
|
||||
const speechStore = useSpeechStore()
|
||||
const visionStore = useVisionStore()
|
||||
const backgroundStore = useBackgroundStore()
|
||||
|
||||
const { removeCard } = cardStore
|
||||
const { activeCardId } = storeToRefs(cardStore)
|
||||
const { activeProvider: consciousnessProvider, activeModel: defaultConsciousnessModel } = storeToRefs(consciousnessStore)
|
||||
const { activeSpeechProvider: speechProvider, activeSpeechModel: defaultSpeechModel, activeSpeechVoiceId: defaultVoiceId } = storeToRefs(speechStore)
|
||||
const { activeProvider: visionProvider, activeModel: defaultVisionModel } = storeToRefs(visionStore)
|
||||
|
||||
const isRefreshingGallery = ref(false)
|
||||
|
||||
@@ -63,6 +66,8 @@ const moduleSettings = computed(() => {
|
||||
return {
|
||||
consciousnessProvider: '',
|
||||
consciousness: '',
|
||||
visionProvider: '',
|
||||
vision: '',
|
||||
speechProvider: '',
|
||||
speech: '',
|
||||
voice: '',
|
||||
@@ -73,6 +78,8 @@ const moduleSettings = computed(() => {
|
||||
return {
|
||||
consciousnessProvider: airiExt.consciousness?.provider || '',
|
||||
consciousness: airiExt.consciousness?.model || '',
|
||||
visionProvider: airiExt.vision?.provider || '',
|
||||
vision: airiExt.vision?.model || '',
|
||||
speechProvider: airiExt.speech?.provider || '',
|
||||
speech: airiExt.speech?.model || '',
|
||||
voice: airiExt.speech?.voice_id || '',
|
||||
@@ -443,6 +450,40 @@ function getModuleDisplayValue(value: string | undefined, defaultValue: string |
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
flex="~ col"
|
||||
bg="white/60 dark:black/30"
|
||||
gap-1 rounded-lg p-3
|
||||
border="~ neutral-200/50 dark:neutral-700/30"
|
||||
transition="all duration-200"
|
||||
hover="bg-white/80 dark:bg-black/40"
|
||||
>
|
||||
<span flex="~ row" items-center gap-2 text-sm text-neutral-500 dark:text-neutral-400>
|
||||
<div i-lucide:eye />
|
||||
{{ t('settings.pages.card.vision.provider') }}
|
||||
</span>
|
||||
<div truncate font-medium>
|
||||
{{ getModuleDisplayValue(moduleSettings.visionProvider, visionProvider) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
flex="~ col"
|
||||
bg="white/60 dark:black/30"
|
||||
gap-1 rounded-lg p-3
|
||||
border="~ neutral-200/50 dark:neutral-700/30"
|
||||
transition="all duration-200"
|
||||
hover="bg-white/80 dark:bg-black/40"
|
||||
>
|
||||
<span flex="~ row" items-center gap-2 text-sm text-neutral-500 dark:text-neutral-400>
|
||||
<div i-lucide:scan-eye />
|
||||
{{ t('settings.pages.card.vision.model') }}
|
||||
</span>
|
||||
<div truncate font-medium>
|
||||
{{ getModuleDisplayValue(moduleSettings.vision, defaultVisionModel) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
flex="~ col"
|
||||
bg="white/60 dark:black/30"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Alert, ErrorContainer, RadioCardManySelect, RadioCardSimple } from '@proj-airi/stage-ui/components'
|
||||
import { useAnalytics } from '@proj-airi/stage-ui/composables'
|
||||
import { useAiriCardStore } from '@proj-airi/stage-ui/stores/modules/airi-card'
|
||||
import { useConsciousnessStore } from '@proj-airi/stage-ui/stores/modules/consciousness'
|
||||
import { useProvidersStore } from '@proj-airi/stage-ui/stores/providers'
|
||||
import { storeToRefs } from 'pinia'
|
||||
@@ -9,6 +10,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
const providersStore = useProvidersStore()
|
||||
const airiCardStore = useAiriCardStore()
|
||||
const consciousnessStore = useConsciousnessStore()
|
||||
const { persistedChatProvidersMetadata, configuredProviders } = storeToRefs(providersStore)
|
||||
const {
|
||||
@@ -37,6 +39,10 @@ watch(activeProvider, async (provider, oldProvider) => {
|
||||
await consciousnessStore.loadModelsForProvider(provider)
|
||||
}, { immediate: true })
|
||||
|
||||
watch([activeProvider, activeModel], ([provider, model]) => {
|
||||
airiCardStore.updateActiveCardConsciousness({ provider, model })
|
||||
})
|
||||
|
||||
function updateCustomModelName(value: string) {
|
||||
customModelName.value = value
|
||||
}
|
||||
|
||||
@@ -162,6 +162,10 @@ watch(activeSpeechModel, async () => {
|
||||
}
|
||||
})
|
||||
|
||||
watch([activeSpeechProvider, activeSpeechModel, activeSpeechVoiceId], ([provider, model, voiceId]) => {
|
||||
airiCardStore.updateActiveCardSpeech({ provider, model, voice_id: voiceId })
|
||||
})
|
||||
|
||||
// Function to generate speech
|
||||
async function generateTestSpeech() {
|
||||
if (!testText.value.trim() && !useSSML.value)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Alert, ErrorContainer, RadioCardManySelect, RadioCardSimple } from '@proj-airi/stage-ui/components'
|
||||
import { useAnalytics } from '@proj-airi/stage-ui/composables'
|
||||
import { useAiriCardStore } from '@proj-airi/stage-ui/stores/modules/airi-card'
|
||||
import { useVisionProcessingStore, useVisionStore } from '@proj-airi/stage-ui/stores/modules/vision'
|
||||
import { useProvidersStore } from '@proj-airi/stage-ui/stores/providers'
|
||||
import { FieldCheckbox, FieldRange } from '@proj-airi/ui'
|
||||
@@ -10,6 +11,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
const providersStore = useProvidersStore()
|
||||
const airiCardStore = useAiriCardStore()
|
||||
const visionStore = useVisionStore()
|
||||
const visionProcessingStore = useVisionProcessingStore()
|
||||
const { persistedVisionProvidersMetadata, configuredProviders } = storeToRefs(providersStore)
|
||||
@@ -47,6 +49,10 @@ watch(activeProvider, async (provider, oldProvider) => {
|
||||
await visionStore.loadModelsForProvider(provider)
|
||||
}, { immediate: true })
|
||||
|
||||
watch([activeProvider, activeModel], ([provider, model]) => {
|
||||
airiCardStore.updateActiveCardVision({ provider, model })
|
||||
})
|
||||
|
||||
function updateCustomModelName(value: string) {
|
||||
customModelName.value = value
|
||||
}
|
||||
|
||||
@@ -75,6 +75,10 @@ describe('store character', () => {
|
||||
provider: 'mock-provider',
|
||||
model: 'mock-model',
|
||||
},
|
||||
vision: {
|
||||
provider: 'mock-vision-provider',
|
||||
model: 'mock-vision-model',
|
||||
},
|
||||
speech: {
|
||||
provider: 'mock-speech-provider',
|
||||
model: 'mock-speech-model',
|
||||
|
||||
@@ -129,6 +129,10 @@ describe('store character-orchestrator', () => {
|
||||
provider: 'mock-provider',
|
||||
model: 'mock-model',
|
||||
},
|
||||
vision: {
|
||||
provider: 'mock-vision-provider',
|
||||
model: 'mock-vision-model',
|
||||
},
|
||||
speech: {
|
||||
provider: 'mock-speech-provider',
|
||||
model: 'mock-speech-model',
|
||||
|
||||
@@ -54,6 +54,19 @@ vi.mock('./speech', async () => {
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('./vision', async () => {
|
||||
const { defineStore } = await import('pinia')
|
||||
|
||||
return {
|
||||
useVisionStore: defineStore('vision', {
|
||||
state: () => ({
|
||||
activeProvider: 'mock-vision-provider',
|
||||
activeModel: 'mock-vision-model',
|
||||
}),
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('vue-i18n', () => ({
|
||||
useI18n: () => ({
|
||||
t: (key: string) => key,
|
||||
@@ -71,19 +84,25 @@ describe('airi-card store', () => {
|
||||
|
||||
/**
|
||||
* @example
|
||||
* it('persists selected display model on active card', () => {})
|
||||
* it('persists selected module config on active card', () => {})
|
||||
*/
|
||||
it('persists selected display model on active card', () => {
|
||||
it('persists selected module config on active card', () => {
|
||||
const stageModelStore = useSettingsStageModel()
|
||||
stageModelStore.stageModelSelected = 'preset-live2d-1'
|
||||
|
||||
const cardStore = useAiriCardStore()
|
||||
cardStore.initialize()
|
||||
|
||||
const updated = cardStore.updateActiveCardDisplayModel('display-model-iru-v2')
|
||||
|
||||
expect(updated).toBe(true)
|
||||
expect(cardStore.activeCard?.extensions.airi.modules.displayModelId).toBe('display-model-iru-v2')
|
||||
expect(cardStore.updateActiveCardDisplayModel('display-model-iru-v2')).toBe(true)
|
||||
expect(cardStore.updateActiveCardConsciousness({ provider: 'openrouter-ai', model: 'anthropic/claude-sonnet' })).toBe(true)
|
||||
expect(cardStore.updateActiveCardVision({ provider: 'ollama', model: 'llava' })).toBe(true)
|
||||
expect(cardStore.updateActiveCardSpeech({ provider: 'elevenlabs', model: 'eleven_multilingual_v2', voice_id: 'aria' })).toBe(true)
|
||||
expect(cardStore.activeCard?.extensions.airi.modules).toMatchObject({
|
||||
displayModelId: 'display-model-iru-v2',
|
||||
consciousness: { provider: 'openrouter-ai', model: 'anthropic/claude-sonnet' },
|
||||
vision: { provider: 'ollama', model: 'llava' },
|
||||
speech: { provider: 'elevenlabs', model: 'eleven_multilingual_v2', voice_id: 'aria' },
|
||||
})
|
||||
expect(stageModelStore.stageModelSelected).toBe('preset-live2d-1')
|
||||
})
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { useSettingsStageModel } from '../settings/stage-model'
|
||||
import { useArtistryStore } from './artistry'
|
||||
import { useConsciousnessStore } from './consciousness'
|
||||
import { useSpeechStore } from './speech'
|
||||
import { useVisionStore } from './vision'
|
||||
|
||||
export type VoicePackParams = Record<string, string | number | boolean | null>
|
||||
|
||||
@@ -48,6 +49,11 @@ export interface AiriExtension {
|
||||
model: string // Example: "gpt-4o"
|
||||
}
|
||||
|
||||
vision: {
|
||||
provider: string // Example: "ollama"
|
||||
model: string // Example: "llava"
|
||||
}
|
||||
|
||||
speech: {
|
||||
provider: string // Example: "elevenlabs"
|
||||
model: string // Example: "eleven_multilingual_v2"
|
||||
@@ -114,6 +120,7 @@ export const useAiriCardStore = defineStore('airi-card', () => {
|
||||
const activeCard = computed(() => cards.value.get(activeCardId.value))
|
||||
|
||||
const consciousnessStore = useConsciousnessStore()
|
||||
const visionStore = useVisionStore()
|
||||
const speechStore = useSpeechStore()
|
||||
const artistryStore = useArtistryStore()
|
||||
const stageModelStore = useSettingsStageModel()
|
||||
@@ -123,6 +130,11 @@ export const useAiriCardStore = defineStore('airi-card', () => {
|
||||
activeModel: activeConsciousnessModel,
|
||||
} = storeToRefs(consciousnessStore)
|
||||
|
||||
const {
|
||||
activeProvider: activeVisionProvider,
|
||||
activeModel: activeVisionModel,
|
||||
} = storeToRefs(visionStore)
|
||||
|
||||
const {
|
||||
activeSpeechProvider,
|
||||
activeSpeechVoiceId,
|
||||
@@ -158,25 +170,23 @@ export const useAiriCardStore = defineStore('airi-card', () => {
|
||||
return cards.value.get(id)
|
||||
}
|
||||
|
||||
function updateActiveCardDisplayModel(displayModelId: string | undefined) {
|
||||
function updateActiveCardModules(patch: (extension: AiriExtension) => Partial<AiriExtension['modules']>) {
|
||||
const cardId = activeCardId.value
|
||||
const card = cards.value.get(cardId)
|
||||
if (!card)
|
||||
return false
|
||||
|
||||
const extension = resolveAiriExtension(card)
|
||||
const modules: AiriExtension['modules'] = {
|
||||
...extension.modules,
|
||||
displayModelId,
|
||||
}
|
||||
|
||||
cards.value.set(cardId, {
|
||||
...card,
|
||||
extensions: {
|
||||
...card.extensions,
|
||||
airi: {
|
||||
...extension,
|
||||
modules,
|
||||
modules: {
|
||||
...extension.modules,
|
||||
...patch(extension),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -184,6 +194,35 @@ export const useAiriCardStore = defineStore('airi-card', () => {
|
||||
return true
|
||||
}
|
||||
|
||||
function updateActiveCardDisplayModel(displayModelId: string | undefined) {
|
||||
return updateActiveCardModules(() => ({ displayModelId }))
|
||||
}
|
||||
|
||||
function updateActiveCardConsciousness(consciousness: AiriExtension['modules']['consciousness']) {
|
||||
return updateActiveCardModules(() => ({ consciousness }))
|
||||
}
|
||||
|
||||
function updateActiveCardVision(vision: AiriExtension['modules']['vision']) {
|
||||
return updateActiveCardModules(() => ({ vision }))
|
||||
}
|
||||
|
||||
function updateActiveCardSpeech(speech: Pick<AiriExtension['modules']['speech'], 'provider' | 'model' | 'voice_id'>) {
|
||||
return updateActiveCardModules(({ modules }) => {
|
||||
const existingVoicePack = modules.speech.voicePack
|
||||
const shouldKeepVoicePack = speech.provider === OFFICIAL_SPEECH_PROVIDER_ID
|
||||
&& existingVoicePack?.ttsModelId === speech.model
|
||||
&& existingVoicePack.voiceId === speech.voice_id
|
||||
|
||||
return {
|
||||
speech: {
|
||||
...modules.speech,
|
||||
...speech,
|
||||
voicePack: shouldKeepVoicePack ? existingVoicePack : undefined,
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function resolveAiriExtension(card: Card | ccv3.CharacterCardV3): AiriExtension {
|
||||
// Get existing extension if available
|
||||
const existingExtension = ('data' in card
|
||||
@@ -196,6 +235,10 @@ export const useAiriCardStore = defineStore('airi-card', () => {
|
||||
provider: activeConsciousnessProvider.value,
|
||||
model: activeConsciousnessModel.value,
|
||||
},
|
||||
vision: {
|
||||
provider: activeVisionProvider.value,
|
||||
model: activeVisionModel.value,
|
||||
},
|
||||
speech: {
|
||||
provider: activeSpeechProvider.value,
|
||||
model: activeSpeechModel.value,
|
||||
@@ -231,6 +274,10 @@ export const useAiriCardStore = defineStore('airi-card', () => {
|
||||
provider: existingExtension.modules?.consciousness?.provider ?? defaultModules.consciousness.provider,
|
||||
model: existingExtension.modules?.consciousness?.model ?? defaultModules.consciousness.model,
|
||||
},
|
||||
vision: {
|
||||
provider: existingExtension.modules?.vision?.provider ?? defaultModules.vision.provider,
|
||||
model: existingExtension.modules?.vision?.model ?? defaultModules.vision.model,
|
||||
},
|
||||
speech: {
|
||||
provider: existingExtension.modules?.speech?.provider ?? defaultModules.speech.provider,
|
||||
model: existingExtension.modules?.speech?.model ?? defaultModules.speech.model,
|
||||
@@ -387,6 +434,9 @@ export const useAiriCardStore = defineStore('airi-card', () => {
|
||||
activeConsciousnessProvider.value = extension?.modules?.consciousness?.provider
|
||||
activeConsciousnessModel.value = extension?.modules?.consciousness?.model
|
||||
|
||||
activeVisionProvider.value = extension?.modules?.vision?.provider
|
||||
activeVisionModel.value = extension?.modules?.vision?.model
|
||||
|
||||
activeSpeechProvider.value = extension?.modules?.speech?.provider
|
||||
activeSpeechModel.value = extension?.modules?.speech?.model
|
||||
activeSpeechVoiceId.value = extension?.modules?.speech?.voice_id
|
||||
@@ -423,7 +473,10 @@ export const useAiriCardStore = defineStore('airi-card', () => {
|
||||
removeCard,
|
||||
updateCard,
|
||||
bindVoicePackToActiveCard,
|
||||
updateActiveCardConsciousness,
|
||||
updateActiveCardDisplayModel,
|
||||
updateActiveCardSpeech,
|
||||
updateActiveCardVision,
|
||||
getCard,
|
||||
resetState,
|
||||
initialize,
|
||||
@@ -434,6 +487,10 @@ export const useAiriCardStore = defineStore('airi-card', () => {
|
||||
provider: activeConsciousnessProvider.value,
|
||||
model: activeConsciousnessModel.value,
|
||||
},
|
||||
vision: {
|
||||
provider: activeVisionProvider.value,
|
||||
model: activeVisionModel.value,
|
||||
},
|
||||
speech: {
|
||||
provider: activeSpeechProvider.value,
|
||||
model: activeSpeechModel.value,
|
||||
|
||||
Reference in New Issue
Block a user