mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
feat: Misskey channel adapter, agent reliability hardening & stream error resilience (#359)
This commit is contained in:
@@ -24,6 +24,7 @@ import {
|
||||
Wechatoa,
|
||||
Wecom,
|
||||
Matrix,
|
||||
Misskey,
|
||||
} from '@memohai/icon'
|
||||
|
||||
const channelIcons: Record<string, Component> = {
|
||||
@@ -37,6 +38,7 @@ const channelIcons: Record<string, Component> = {
|
||||
wechatoa: Wechatoa,
|
||||
wecom: Wecom,
|
||||
matrix: Matrix,
|
||||
misskey: Misskey,
|
||||
dingtalk: Dingtalk,
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ export interface ChatWebSocket {
|
||||
|
||||
function resolveWebSocketUrl(botId: string): string {
|
||||
const baseUrl = String(client.getConfig().baseUrl || '').trim()
|
||||
const path = `/bots/${encodeURIComponent(botId)}/local/ws`
|
||||
const path = `/bots/${encodeURIComponent(botId)}/web/ws`
|
||||
|
||||
if (!baseUrl || baseUrl.startsWith('/')) {
|
||||
const loc = window.location
|
||||
|
||||
@@ -144,6 +144,8 @@
|
||||
},
|
||||
"chat": {
|
||||
"greeting": "Hi! How can I help you today?",
|
||||
"emptySubagent": "No messages recorded for this subagent task",
|
||||
"emptySystemSession": "No messages recorded for this system session",
|
||||
"selectBot": "Select a Bot",
|
||||
"selectBotHint": "Choose a bot from the sidebar to start chatting",
|
||||
"thinking": "Thinking…",
|
||||
@@ -1022,6 +1024,7 @@
|
||||
"discord": "Discord",
|
||||
"qq": "QQ",
|
||||
"matrix": "Matrix",
|
||||
"misskey": "Misskey",
|
||||
"telegram": "Telegram",
|
||||
"weixin": "WeChat",
|
||||
"wechatoa": "WeChat Official Account",
|
||||
@@ -1036,6 +1039,7 @@
|
||||
"discord": "DC",
|
||||
"qq": "QQ",
|
||||
"matrix": "MX",
|
||||
"misskey": "MK",
|
||||
"telegram": "TG",
|
||||
"weixin": "WX",
|
||||
"wechatoa": "OA",
|
||||
|
||||
@@ -140,6 +140,8 @@
|
||||
},
|
||||
"chat": {
|
||||
"greeting": "你好!有什么我可以帮你的吗?",
|
||||
"emptySubagent": "子代理任务暂无记录",
|
||||
"emptySystemSession": "系统会话暂无记录",
|
||||
"selectBot": "选择一个 Bot",
|
||||
"selectBotHint": "从侧边栏选择一个 Bot 开始对话",
|
||||
"thinking": "思考中…",
|
||||
@@ -1018,6 +1020,7 @@
|
||||
"discord": "Discord",
|
||||
"qq": "QQ",
|
||||
"matrix": "Matrix",
|
||||
"misskey": "Misskey",
|
||||
"telegram": "Telegram",
|
||||
"weixin": "微信",
|
||||
"wechatoa": "微信服务号",
|
||||
@@ -1032,6 +1035,7 @@
|
||||
"discord": "DC",
|
||||
"qq": "QQ",
|
||||
"matrix": "MX",
|
||||
"misskey": "MK",
|
||||
"telegram": "TG",
|
||||
"weixin": "WX",
|
||||
"wechatoa": "OA",
|
||||
|
||||
@@ -212,17 +212,16 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Timezone -->
|
||||
<div class="space-y-2">
|
||||
<Label>{{ $t('bots.timezone') }}</Label>
|
||||
<TimezoneSelect
|
||||
v-model="form.timezone"
|
||||
:model-value="form.timezone || emptyTimezoneValue"
|
||||
:placeholder="$t('bots.timezonePlaceholder')"
|
||||
allow-empty
|
||||
:placeholder="defaultTimezone"
|
||||
:empty-label="timezoneEmptyLabel"
|
||||
:empty-label="$t('bots.timezoneInherited')"
|
||||
@update:model-value="(val: string) => form.timezone = val === emptyTimezoneValue ? '' : val"
|
||||
/>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ $t('bots.timezoneInheritedHint') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
@@ -237,8 +236,21 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Reasoning -->
|
||||
<!-- Timezone -->
|
||||
<div class="space-y-2">
|
||||
<Label>{{ $t('bots.timezone') }}</Label>
|
||||
<TimezoneSelect
|
||||
:model-value="form.timezone || emptyTimezoneValue"
|
||||
:placeholder="$t('bots.timezonePlaceholder')"
|
||||
allow-empty
|
||||
:empty-label="$t('bots.timezoneInherited')"
|
||||
@update:model-value="(val: string) => form.timezone = val === emptyTimezoneValue ? '' : val"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<!-- Reasoning -->
|
||||
<div class="space-y-2">
|
||||
<Label>{{ $t('bots.settings.reasoningEffort') }}</Label>
|
||||
<Popover v-model:open="reasoningPopoverOpen">
|
||||
@@ -348,7 +360,7 @@ import { getBotsById, putBotsById, getBotsByBotIdSettings, putBotsByBotIdSetting
|
||||
import type { SettingsSettings } from '@memohai/sdk'
|
||||
import type { Ref } from 'vue'
|
||||
import { resolveApiErrorMessage } from '@/utils/api-error'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { emptyTimezoneValue } from '@/utils/timezones'
|
||||
|
||||
const props = defineProps<{
|
||||
botId: string
|
||||
@@ -356,13 +368,8 @@ const props = defineProps<{
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const botIdRef = computed(() => props.botId) as Ref<string>
|
||||
const defaultTimezone = computed(() => userStore.userInfo.timezone || 'UTC')
|
||||
const timezoneEmptyLabel = computed(() =>
|
||||
`${t('bots.timezoneInherited')} (${defaultTimezone.value})`,
|
||||
)
|
||||
|
||||
// ---- Data ----
|
||||
const queryCache = useQueryCache()
|
||||
@@ -639,6 +646,7 @@ watch(settings, (val) => {
|
||||
form.tts_model_id = val.tts_model_id ?? ''
|
||||
form.browser_context_id = val.browser_context_id ?? ''
|
||||
form.language = val.language ?? ''
|
||||
form.timezone = val.timezone ?? ''
|
||||
form.reasoning_enabled = val.reasoning_enabled ?? false
|
||||
form.reasoning_effort = val.reasoning_effort || 'medium'
|
||||
}
|
||||
@@ -660,6 +668,7 @@ const hasSettingsChanges = computed(() => {
|
||||
|| form.tts_model_id !== (s.tts_model_id ?? '')
|
||||
|| form.browser_context_id !== (s.browser_context_id ?? '')
|
||||
|| form.language !== (s.language ?? '')
|
||||
|| form.timezone !== (s.timezone ?? '')
|
||||
|| form.reasoning_enabled !== (s.reasoning_enabled ?? false)
|
||||
|| form.reasoning_effort !== (s.reasoning_effort || 'medium')
|
||||
)
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
</FormField>
|
||||
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
v-slot="{ value, handleChange }"
|
||||
name="timezone"
|
||||
>
|
||||
<FormItem>
|
||||
@@ -67,28 +67,13 @@
|
||||
<span class="text-muted-foreground text-xs ml-1">({{ $t('common.optional') }})</span>
|
||||
</Label>
|
||||
<FormControl>
|
||||
<Select
|
||||
:model-value="componentField.modelValue || emptyTimezoneValue"
|
||||
@update:model-value="(value) => componentField['onUpdate:modelValue'](value === emptyTimezoneValue ? '' : value)"
|
||||
>
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue :placeholder="$t('bots.timezonePlaceholder')" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem :value="emptyTimezoneValue">
|
||||
{{ $t('bots.timezoneInherited') }}
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
v-for="timezoneOption in timezones"
|
||||
:key="timezoneOption"
|
||||
:value="timezoneOption"
|
||||
>
|
||||
{{ timezoneOption }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<TimezoneSelect
|
||||
:model-value="value || emptyTimezoneValue"
|
||||
:placeholder="$t('bots.timezonePlaceholder')"
|
||||
allow-empty
|
||||
:empty-label="$t('bots.timezoneInherited')"
|
||||
@update:model-value="(val) => handleChange(val === emptyTimezoneValue ? '' : val)"
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
@@ -133,12 +118,6 @@ import {
|
||||
FormItem,
|
||||
Separator,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
Spinner,
|
||||
} from '@memohai/ui'
|
||||
import { Plus } from 'lucide-vue-next'
|
||||
@@ -150,7 +129,8 @@ import { useMutation, useQueryCache } from '@pinia/colada'
|
||||
import { postBotsMutation, getBotsQueryKey } from '@memohai/sdk/colada'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useDialogMutation } from '@/composables/useDialogMutation'
|
||||
import { emptyTimezoneValue, timezones } from '@/utils/timezones'
|
||||
import { emptyTimezoneValue } from '@/utils/timezones'
|
||||
import TimezoneSelect from '@/components/timezone-select/index.vue'
|
||||
|
||||
const open = defineModel<boolean>('open', { default: false })
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -40,7 +40,22 @@
|
||||
v-if="messages.length === 0 && !loadingChats"
|
||||
class="flex items-center justify-center min-h-[300px]"
|
||||
>
|
||||
<p class="text-muted-foreground text-xs">
|
||||
<p
|
||||
v-if="activeSession?.type === 'subagent'"
|
||||
class="text-muted-foreground text-xs"
|
||||
>
|
||||
{{ $t('chat.emptySubagent') }}
|
||||
</p>
|
||||
<p
|
||||
v-else-if="activeSession?.type === 'heartbeat' || activeSession?.type === 'schedule'"
|
||||
class="text-muted-foreground text-xs"
|
||||
>
|
||||
{{ $t('chat.emptySystemSession') }}
|
||||
</p>
|
||||
<p
|
||||
v-else
|
||||
class="text-muted-foreground text-xs"
|
||||
>
|
||||
{{ $t('chat.greeting') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -51,11 +51,13 @@
|
||||
>
|
||||
<LoaderCircle class="size-3 animate-spin" />
|
||||
</div>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<div
|
||||
v-else
|
||||
class="shiki-diff-container overflow-x-auto text-xs [&_pre]:bg-transparent! [&_pre]:p-3 [&_pre]:m-0 [&_code]:text-xs"
|
||||
v-html="shiki.html.value"
|
||||
/>
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
</div>
|
||||
|
||||
@@ -51,11 +51,13 @@
|
||||
>
|
||||
<LoaderCircle class="size-3 animate-spin" />
|
||||
</div>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<div
|
||||
v-else
|
||||
class="shiki-container overflow-x-auto text-xs [&_pre]:bg-transparent! [&_pre]:p-3 [&_pre]:m-0 [&_code]:text-xs"
|
||||
v-html="shiki.html.value"
|
||||
/>
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
</div>
|
||||
|
||||
@@ -571,7 +571,7 @@ export const useChatStore = defineStore('chat', () => {
|
||||
} else {
|
||||
const activeSessionId = sessionId.value && visible.some(session => session.id === sessionId.value)
|
||||
? sessionId.value
|
||||
: visible[0]!.id
|
||||
: (visible.find((s) => s.type === 'chat' || s.type === 'discuss')?.id ?? visible[0]!.id)
|
||||
sessionId.value = activeSessionId
|
||||
await loadMessages(bid, activeSessionId)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user